spring学习笔记的理解-通俗易懂

文章由B站动力节点相关课程视频整理而成,这也是我第一篇博客也是借鉴于这位兄弟的,写得很好很有帮助希望能帮到你。
在写的过程中写得很详细所以会让你会不适但毕竟是我第一次写,在写的过程中也是对我的一次加深印象和理解 如有不适请告诉我.


一、Spring是什么?

1.1 概述:

Spring: 出现在2002年左右,降低企业级开发难度。帮助进行模块之间、类与类之间的管理,帮助开发人员创建对象,管理对象之间的关系。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)
Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。
Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。

1.2 Spring优点:

spring 是一个框架,是一个半成品的软件。有 20 个模块组成。它是一个容器管理对象,容器是装东西的,Spring 容器不装文本,数字。装的是对象。Spring 是存储对象的容器。

(1) 轻量

Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需的 jar 总共在 3M 左右。Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar

(2) 针对接口编程,解耦合

Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。

(3) AOP 编程的支持

通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付
在 Spring 中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。

(4) 方便集成各种优秀框架

Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。

1.3 Spring 体系结构

体系结构
Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、Web、面向切面编程(AOP, Aspects)、提供JVM的代理(Instrumentation)、消息发送(Messaging)、核心容器(Core Container)和测试(Test)。

二、IOC控制反转

1.概念:

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

1. 1 理解:

IOC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。

  • 描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。
  • 控制: 创建对象,对象的属性赋值,对象之间的关系管理。
  • 反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现由容器代替开发人员管理对象。创建对象,给属性赋值。
  • 正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。比如:

public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。–正转。
}

  • 容器: 一个服务器软件,一个框架(spring)
  • 为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。

2 .开发工具准备:

开发工具:idea2017 以上
依赖管理:maven3 以上
jdk:1.8 以上

3.Spring的第一个程序

实现步骤

  1. 创建maven项目
  2. 加入maven依赖
    spring的依赖,版本5.2.5版本
    Junit依赖
  3. 创建类(接口和它的实现类)
    和没有使用框架一样(就是普通的类)
  4. 创建spring需要使用的配置文件
    声明类的信息,这些类由spring创建和管理
  5. 测试由spring创建的

3.1 创建Maven项目,导入Spring依赖

Maven项目采用quickstart模板,然后在main文件夹下建立好resources目录,如果就平常拿来学习就选择下面这个。如果是用来web开发那么就选择后面是webapp的那个就OK啦。中间一些步骤我就省略了,如果不会那么就百度吧少年。

在这里插入图片描述
创建好后在pom.xml文件中添加依赖来方便后续学习

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <!--单元测试:通过单元测试可以测试每一个方法-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--Spring的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>

3.2 定义接口与实体类

(这是是拿例子来讲)
创建接口

package com.chengdumiao.service;

public interface SomeService {
    
    
    void dosome();

}

实现类:

package com.chengdumiao.service.impl;

import com.chengdumiao.service.SomeService;

public class SomeServiceImpl implements SomeService {
    
    
    @Override
    public void dosome() {
    
    
        System.out.println("执行了someServiceImpl的dosome方法!");
    }
/**
     * spring默认创建对象的时间:在创建spring的容器时,会创建配置文件中的所有对象
     * spring创建对象:默认调用的是无参数构造方法
     * 如果没有无参数构造方法,报错:No default constructor found
     */
    public SomeServiceImpl() {
    
    
        System.out.println("SomeServiceImpl的无参构造方法!");
    }
}

3.3 创建 Spring 配置文件

在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为applicationContext.xml。
spring 配置中需要加入约束文件才能正常使用,约束文件是 xsd 扩展名。

如同在Servlet中我们需要在web.xml中注册我们希望服务器自动创建管理的servlet对象一样,在Spring中也需要有类似的配置,来自动创建刚才的SomeServiceImpl对象。

右击resources–>new–>XML configuration file–>Spring Config

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- xsd文件是约束文件-->
       <!--Spring 配置文件中使用的约束文件为 xsd 文件。作用与Mybatis的sql映射文件的dtd约束文件类似,但xsd约束作用更强-->
</beans>

第一步 告诉spring创建对象
声明bean,就是告诉spring要创建某个类的对象
<bean/>是配置文件的根标签。在Spring中,java对象称之为bean。在这个标签下进行java对象的注册

<bean id="someService" class="com.chengdumiao.service.impl.SomeServiceImpl"></bean>

id:对象的自定义名称,唯一值,spring通过名称找到对象
class:类的全限定名称(不能是接口,因为spring是反射机制来创建对象,必须是类).
注意:

  1. 在这个创建的过程中spring是把创建好的对象放入到map中。spring框架有一个map存放对象的集合。用来存储对象。

springmap.put(id的值,对象);
例如: springmap.put(“someService”,new someServiceImpl());

  1. 一个bean标签只能申明一个对象:
    因为在bean标签中id值是唯一的不能重复的,所以想要创建多个对象则
<bean id="someService" class="com.chengdumiao.service.impl.SomeServiceImpl"></bean>
<bean id="someService1" class="com.chengdumiao.service.impl.SomeServiceImpl"></bean>
  1. spring能创建一个非定义类的对象吗,创建一个存在的某个类的对象。(可以创建但如果要赋值注入,那么就要看源代码其中的set方法进行注入如果没有则不行,不过我想一般都会有的且名字要一样)
<bean id="mydate" class="java.util.Date"/>

进行测试(多看注释 ,注释能解释啥子意思)

    @Test
    public void test02(){
    
    
        //使用spring容器来创建对象
        //1.指定spring配置文件的名称
        String config="ApplicationContext.xml";
        //2.创建表示spring容器的对象,ApplicationContet
        //ApplicationContext就是表示spring容器,通过容器来获取对象
        //ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
         //1.如果Spring的配置文件是在类路径(classpath),使用ClassPathXmlApplicationContext
        //2.如果Spring的配置文件是放在项目的根之下(与src、target同级目录),使用FileSystemXmlApplicationContext
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);    //创建容器
		
        //从容器中获取某个对象,你要调用对象的方法
        //getbean返回的是Object值 不是我们想要的值 这个时候强制转移成接口类型
        SomeService service=(SomeService)ac.getBean("someService");  //获取对象
        //执行业务方法
        service.dosome();
    }

结果:
结果
总结:事实证明不用传统new也能实现创建一个对象并调用其业务方法,并且达到进行配置文件的方式,好处有解耦合使代码更具有可维护性。

这里扩展一下:
获取spring容器中Java对象的信息

getBeanDefinitionCount()//使用spring提供的方法,获取容器中定义的对象的数量
getBeanDefinitionNames()//容器中每个定义的对象的名称

3.4 使用单元测试

junit : 单元测试, 一个工具类库,做测试方法使用的。
单元:指定的是方法, 一个类中有很多方法,一个方法称为单元。
使用单元测试
1.需要加入junit依赖。

 <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

2.创建测试作用的类:叫做测试类
src/test/java目录中创建类

3.创建测试方法

 1)public 方法
 2)没有返回值 void 
 3)方法名称自定义,建议名称是test + 你要测试方法名称
 4)方法没有参数
 5)方法的上面加入 @Test ,这样的方法是可以单独执行的。 不用使用main方法。

4. Bean的装配

4.1 默认装配方式

当我们创建ApplicationContext对象时,Spring会读取配置文件的<bean/>并执行对应类的无参构造器。会在容器对象初始化时,将其中的所有对象一次性全部装配好。以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。
此时容器中所有对象均已装载完毕

ApplicationContext ac=new ClassPathXmlApplicationContext(config);

4.2 容器中Bean的作用域

当通过 Spring 容器创建一个 Bean 实例时,不仅可以完成 Bean 的实例化,还可以通过scope 属性,为 Bean 指定特定的作用域。Spring 支持多种作用域。
(1)singleton:单例模式。即在整个 Spring 容器中,使用 singleton 定义的 Bean 将是单例 的,叫这个名称的对象只有一个实例。默认为单例的。
(2)prototype:原型模式。即每次使用 getBean 方法获取的同一个的实例都是一个 新的实例。
(3)request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
(4)session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。

注意:

  • 对于 scope 的值 request、session 只有在 Web 应用中使用 Spring 时,该作用域才有效。
  • 对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了;对于 scope 为 prototype的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行 装配的。

5.基于 XML 的 DI(了解)

通过在xml配置文件对对象的属性进行赋值。

5.1 注入分类

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化
是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类:set 注入、构造注入。

5.1.1 set注入(掌握)

set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。
(1)简单类型(spring中规定java的基本数据类型和string都是简单类型):
<bean/>标签下添加

<property name="属性名" value="简单类型的属性值" />

注意: 每一个property标签,完成一个属性的赋值。
Spring执行property标签的原理,是执行name属性值对应的set方法。而并不关心set方法的具体实现和属性是否真的存在。也就是属性可以不存在但setter方法必须存在才可以。
例子:
属性类:

public class Student {
    
    
    private String name;
    private int age;
    
 public void setName(String name) {
    
    
        System.out.println("setName:"+name);
        this.name = name;
public void setAge(int age) {
    
    
        System.out.println("setAge:"+age);
        this.age = age;
    }
@Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

applicationContext.xml配置文件:

<bean id="myStudent" class="chengdumiao.ba01.Student">
        <property name="name" value="李四"></property>   <!--set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例-->
        <property name="age" value="20"></property>
  </bean>

进行测试:

@Test
public void test01(){
    
    
    //从容器中获取student对象
    String config="ba01/ApplicationContext.xml";

    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    Student mystudent=(Student)ac.getBean("myStudent");
    System.out.println("student对象="+mystudent);
}

结果:
结果
总结: 就是赋值而已很简单。但是其中的getBean();获取到对象要转换,这是因为getBean获取到的是Object类型的而不是我们想要的类型,这个时候被迫强制转型才可以用。em我也是小白一个说个缘由有点困难但按照这个样子是对的我知道。

(2)引用类型
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref的值必须为某 bean 的 id 值。

在<bean/>标签下添加:
<bean id="idA" class="具体的类名">
	<property name="属性名" ref="idB" /> //ref相当于引用的目的牵引
<bean/>
<bean id="idB" class="具体的类名">   //把这个id名称跟上面ref值一样 算是指定引用
	......
<bean/>

例子:创建一个学校属性类:

package chengdumiao.ba02;

public class School {
    
    
    private String name;
    private String address;

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAddress(String address) {
    
    
        this.address = address;
    }

    @Override
    public String toString() {
    
    
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

创建一个学生属性类:

package chengdumiao.ba02;

public class Student {
    
    
    private String name;
    private int age;
    //声明一个引用类型
    private School school;


    public Student() {
    
    
        System.out.println("Spring会调用类的无参构造方法创建对象");
    }

    public void setName(String name) {
    
    
        System.out.println("setName:"+name);
        this.name = name;
    }
    //需要有set方法,没set方法会报错 bean调用的是set方法哪怕你是没有变量也能set值
    public void setAge(int age) {
    
    
        System.out.println("setAge:"+age);
        this.age = age;
    }

    public void setSchool(School school) {
    
    
        System.out.println("setSchool:"+school);
        this.school = school;
    }
    
    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +  //这里school引用school类 相当于有使用那么类的权限就可以调用其属性啊方法都可以.
                '}';
    }
}

ApplicationContext.xml配置文件类:

  <bean id="myStudent" class="chengdumiao.ba02.Student">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
        <!--引用类型-->
        <property name="school" ref="mySchool"/>  <!--setSchool(myschool)-->
  </bean>
    <!--声明school对象-->
    <bean id="mySchool" class="chengdumiao.ba02.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京的海淀区"/>
    </bean>
</beans>

测试类:

@Test
    public void test01(){
    
    
        System.out.println("======test01=======");
        //从容器中获取student对象
        String config="ba02/ApplicationContext.xml";

        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        Student mystudent=(Student)ac.getBean("myStudent");
        System.out.println("student对象="+mystudent);

    }

结果:
结果

5.1.2 构造注入(理解)

执行类的有参构造,在构造对象的同时给属性赋值。构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。
直接看例子相信你能更快理解:

属性类School:

public class School {
    
    
    private String name;
    private String address;

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAddress(String address) {
    
    
        this.address = address;
    }

    @Override
    public String toString() {
    
    
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

Student类采用构造方法的进行赋值,也就是说不采用setter方法进行赋值:

public class Student {
    
    
    private String name;
    private int age;
    //声明一个引用类型
    private School school;


    public Student() {
    
    
        System.out.println("Spring会调用类的无参构造方法创建对象");
    }

    /**
     * 创建有参数的构造方法
     */
    public Student(String myname,int myage,School school){
    
    
        System.out.println("======Student有参数构造方法========");
        //属性赋值
        this.name=myname;
        this.age=myage;
        this.school=school;
    }

    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }

ApplicationContext.xml配置文件

<!--使用name属性实现构造注入-->
  <bean id="myStudent" class="chengdumiao.ba03.Student">
        <constructor-arg name="myname" value="张三"/>
        <constructor-arg name="myage" value="20"/>
        <constructor-arg name="school" ref="mySchool"/>
  </bean>

<!--声明school对象-->
    <bean id="mySchool" class="chengdumiao.ba03.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京的海淀区11111"/>
    </bean>

测试代码:

 @Test
    public void test01(){
    
    
        System.out.println("======test01=======");
        //从容器中获取student对象
        String config="ba03/ApplicationContext.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        Student mystudent=(Student)ac.getBean("myStudent");
        System.out.println("student对象="+mystudent);
    }

结果:
结果
<constructor-arg />标签中用于指定参数的属性有:
1.平常简单类型赋值

<constructor-arg  name="属性名"   value="简单类型或者string类型" >

2.如果是引用类型赋值

<constructor-arg  name="属性名"  ref="另一个对象的bean的id名称" >

3.采用index方式赋值:

<constructor-arg index="0" value="小四"/>
<constructor-arg index="1" ref="mySchool"/>

<constructor-arg> 标签属性:
name:表示构造方法的形参名
value:构造方法的形参类型是简单类型的,使用value;
ref:构造方法的形参类型是引用类型的,使用ref;
index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,
但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数
顺序一致。

5.2 引用类型属性自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为<bean/>标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)说白了就是自动帮你注入不用你手动去建啥子property进行。根据自动注入判断标准的不同,可以分为两种:

  • byName:根据名称自动注入
  • byType: 根据类型自动注入

5.2.1 byName 方式自动注入

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
看理论迷迷糊糊还不如直接看例子:

public class Student {
    
    
    private String name;
    private int age;
    //声明一个引用类型
    private School school;
    public void setName(String name) {
    
    
        this.name = name;
    }
    public void setAge(int age) {
    
    
        this.age = age;
    }
    public void setSchool(School school) {
    
    
        this.school = school;
    }
    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }

School类:

public class School {
    
    
    private String name;
    private String address;

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAddress(String address) {
    
    
        this.address = address;
    }

    @Override
    public String toString() {
    
    
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

重点来了:ApplicationContext.xml配置文件:

<!--byname-->
<!-- 容器发现byname就会去对应的类中找引用类型 看属性名 然后就配置文件中每个bean的id去比 名字一样类型一样  -->
<bean id="myStudent" class="chengdumiao.ba04.Student" autowire="byName"> <!--走到这一步会调用无参构造方法创建对象-->
        <property name="name" value="李四"/>
        <property name="age" value="20"/> 
</bean>
    <!--声明school对象-->
    <bean id="school" class="chengdumiao.ba04.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京的海淀区"/>
    </bean>

测试代码:

@Test
    public void test01(){
    
    
        System.out.println("======test04=======");
        String config="ba04/ApplicationContext.xml";

//        加载配置文件时会去读这个配置文件
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

        //从容器中获取student对象
        Student mystudent=(Student)ac.getBean("myStudent");  //获取对象
        System.out.println("student对象="+mystudent);

    }

结果:
结果
结果证明也能实现引用的目的,这样能节省代码实现代码好处多多。现在来看看这个byName的语法和相关知识:
引用类型的自动注入: spring框架根据某些规则可以自动给引用类型赋值。不用你再手动给引用类型赋值了
byName(按名称注入):java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,且数据类型是一致的,注意的容器中的bean,spring能够赋值给引用类型.
语法:

<bean id="xx" class="yyy" autowire="byName">
简单类型赋值
</bean>
//分别是两个类
<bean id="值为上面那个类中的引用类型的属性名" class="yyy">

</bean>
例如:在Student类中,School类型的属性名是”school“

5.2.2 byType 方式自动注入

使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子 类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配 哪一个了。说白了byType就是按照引用类型来注入,咱们先看例子然后给你分析一下。
例子:School属性类也是其他类要引用的类:

public class School {
    
      //这里面就是属性然后对他的set方法和tostring方法就完了
    private String name;
    private String address;

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAddress(String address) {
    
    
        this.address = address;
    }

    @Override
    public String toString() {
    
    
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

student类 :

public class Student {
    
    
    private String name;
    private int age;
    //声明一个引用类型
    private School school;  //这里是重点 声明school类型的属性 引用

    public Student() {
    
    
        //System.out.println("Spring会调用类的无参构造方法创建对象");
    }
    public void setName(String name) {
    
    
        //System.out.println("setName:"+name);
        this.name = name;
    }
    //需要有set方法,没set方法会报错 bean调用的是set方法哪怕你是没有变量也能set值
    public void setAge(int age) {
    
    
        //System.out.println("setAge:"+age);
        this.age = age;
    }
    public void setSchool(School school) {
    
    
        System.out.println("setSchool:"+school);
        this.school = school;
    }
    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }

看配置文件application.xml怎么去使用bytype自动引用:

<bean id="myStudent" class="chengdumiao.ba05.Student" autowire="byType"> <!--走到这一步会调用无参构造方法创建对象-->
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
</bean>
<!--声明school对象-->
    <bean id="xxoo" class="chengdumiao.ba05.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京的海淀区"/>
    </bean>

肯定有点懵吧 来看看测试出来什么样子的:

 @Test
    public void test01(){
    
    
        System.out.println("======test04=======");
        String config="ba05/ApplicationContext.xml";

//        加载配置文件时会去读这个配置文件
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

        //从容器中获取student对象
        Student mystudent=(Student)ac.getBean("myStudent");  //获取对象
        System.out.println("student对象="+mystudent);

结果:
结果
总结:
byType(按类型注入) :java类中引用类型的数据类型和spring容器中(配置文件) <bean>中class属性是同源关系,这样的bean才能赋值给引用类型
比如就是这种:

private School school; //这是类中的引用类型嘛, 引用数据类型是School
配置文件中在bean中声明autowire=“byType”>
<bean id="school" class="chengdumiao.ba05.School">
//请注意看 这里的id值不像byName要跟上面引用类型的属性名一样而是可以随便修改的,
//重点是后面那个类是School。之前说引用类型和class属性是属于同源关系 所以自动注入了。

同源就是一类的关系:
1.java类中的引用类型的数据类型和bean的class的值是一样的
2.Java类中的引用类型的数据类型和bean的class的值父子关系的
3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的
语法:

<bean  id="xxx" class="yyy" autowire="byType">
                简单类型赋值
</bean>

注意:

  1. 有时就在想如果在配置文件中声明多个相同类多个对象效果又是怎么样?
 <bean id="myStudent" class="chengdumiao.ba05.Student" autowire="byType"> 
        <property name="name" value="李四"/>
        <property name="age" value="20"/> 
  </bean>
    <!--声明school对象-->
    <bean id="school" class="chengdumiao.ba05.School">  <!--这里是School-->
        <property name="name" value="清华大学"/>
        <property name="address" value="北京的海淀区"/>
    </bean>
    <!--声明school对象-->
    <bean id="school" class="chengdumiao.ba05.School">   <!--这里还是school-->
        <property name="name" value="tuopu大学"/>
        <property name="address" value="托普的海淀区"/>
    </bean>

你认为结果是怎么样的?来我们测试看看:
测试代码还是上面那些哈就不复制粘贴了

Configuration problem: Bean name 'school' is already used in this <beans> element
//这里报错了 说这里bean已经存在一个school了 违背了bean的ID值唯一原则

修改后再来测试

No qualifying bean of type 'chengdumiao.ba05.School' available:
  expected single matching bean but found 2: school,school1
 //同样的也报错了说同时有两个school是不行的,你在学习过程中当你这样改了在配置文件中就已经提示错误了. 

2.如果一个类有多个引用类型怎么办?会不会冲突啊?
之前我也这么认为会冲突,但经过新想法实践证明是不会的,首先说一个类声明两个对象

private School school;
private miao Miao;

然后在配置文件中是不会冲突虽然只声明了一个bytype但是是根据同源的关系来确定的, 比如前面是School引用类型那么就会去寻找class中的School类或者School的子类或者接口实现类 ,但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配 哪一个了。

 <!--声明school对象-->
    <bean id="school" class="chengdumiao.ba05.School">
    ....
    </bean>
<!--声明miao对象-->
    <bean id="miao2" class="chengdumiao.ba05.miao"> 
    .....
    </bean>
  1. 如果存在两个相同引用类型属性的话会不会只给前面的属性赋值而不给后面的属性赋值?
    来我们继续看看
  //声明一个引用类型
private School miao;
private School school;
其他不变
public void setMiao(School miao) {
    
    
        this.miao = miao;
    }
 @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", miao=" + miao +
                ", school=" + school +
                '}';
    }    

配置文件不变,来看结果:
结果
事实证明:都会赋值无论说调换位置啊什么的不会影响的。只要是School类型都会赋值。

5.2.3 为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变 得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将 Spring 配置文件分解成多个配置文件。
多个配置优势:

  1. 每个文件的大小比一个文件要小很多。效率高
  2. 避免多人竞争带来的冲突。

多文件的分配方式:

  1. 按功能模块,一个模块一个配置文件
  2. 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等

包含关系的配置文件: 多个配置文件中有一个总文件,总配置文件将各其它子文件通过<import/>引入。在 Java 代码中只需要使用总配置文件对容器进行初始化即可。例如:
之前都是合在一个配置文件applicationContext.xml进行配置,现在把每个bean进行单独声明一个配置文件然后进行加载:
spring-School.xml

<!--
        School模块所有bean的声明  School模块的配置文件
-->
    <!--声明school对象-->
    <bean id="school" class="chengdumiao.ba06.School">
        <property name="name" value="航空大学"/>
        <property name="address" value="北京的海淀区"/>
    </bean>

spring-student.xml

<!--
        student模块所有bean的声明
-->
    <!--byType-->
  <bean id="myStudent" class="chengdumiao.ba06.Student" autowire="byType"> <!--走到这一步会调用无参构造方法创建对象-->
        <property name="name" value="小王"/>
        <property name="age" value="22"/>
  </bean>

主配置文件 spring-total.xml(后面会改total.xml)

<import resource="classpath:ba06/spring-School.xml"/>
<import resource="classpath:ba06/spring-student.xml"/>

这里我懒一下测试代码还是上面没改
直接看结果:
结果是对的
总结:
包含关系的配置文件:(就是用一个文件来包含其他文件的)
spring-total表示主配置文件: 包含其他的配置文件的 , 主配置文件一般不定义对象的.
语法:

<import resourse="其他配置文件的路径"/>

关键字:"classpath:"表示类路径(class文件所在的目录);
在spring配置文件中要指定其他文件的位置,需要使用classpath,告诉spring到哪去加载读取文件.

位置

扩展:
在包含关系配置文件中,可以通配符(*:表示任意字符)。
上面文件列表一个一个列出 而通配符可以把一些符合条件的文件一次性加进来
如:

<import resource="classpath:ba06/spring-*.xml"/>

注意:

  1. 这个有一定限制 只能是ba06这个包里的 且文件开头是spring 后面则用通配符来代替
  2. 主配置文件名不能跟其他配置文件开头一样 不然这里导入只要spring开头的话那么就无无穷把自己套进去
    主的配置文件名称不能包含在通配符的范围内(不能叫做spring-total.xml)已改为total.xml。在idea中修改类名的快捷键为crtl+F6
  3. 如果要使用通配符方案那么你的其他配置文件就必须放在一个目录中(如都在ba06/中如果不在则引进无效)

6. 基于注解的DI(掌握)


猜你喜欢

转载自blog.csdn.net/MiaoWei_/article/details/109092684