Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)

目录

1、IOC

1.1、IOC容器

1.1.1、IOC思想

①获取资源的传统方式

②反转控制方式获取资源

③DI

1.1.2、IOC容器在Spring中的实现

①BeanFactory

②ApplicationContext

③ApplicationContext的主要实现类

ClassPathXmlApplicationContext

FileSystemXmlApplicationContext

ConfigurableApplicationContext

WebApplicationContext

1.2、基于XML管理bean

1.2.1、实验一

①创建Maven Module

②引入依赖

③创建类

④创建Spring的配置文件

⑤在Spring的配置文件中配置bean

⑥创建测试类测试

⑦思路

⑧注意

1.2.2、获取bean

①方式一:根据id获取

②方式二:根据类型获取

③方式三:根据id和类型

扩展

⑤结论

1.2.3、依赖注入之setter注入

①配置bean时为属性赋值

②测试

1.2.4、依赖注入之构造器注入

①在Student类中的有参构造

②配置bean

③测试

拓展

1.2.5、特殊值处理

①字面量赋值

②null值

③xml实体

④CDATA节

1.2.6、为类类型属性赋值

①创建班级类Clazz

②修改Student类

③方式一:引用外部已声明的bean

④方式二:级联属性赋值

⑤方式二:内部bean

1.2.7、为数组类型属性赋值

①修改Student类

②配置bean

1.2.8、为集合类型属性赋值

①为List集合类型属性赋值

②为Map集合类型属性赋值

1.2.9、p命名空间

1.2.10、引入外部属性文件

①加入依赖

②创建外部属性文件

③引入属性文件并配置bean

④测试

1.2.11、bean的作用域

①概念

单例(singleton)测试

多例(prototype )测试

1.2.12、bean的生命周期

③配置bean

④测试

⑤bean的后置处理器

1.2.13、FactoryBean

①简介

②创建类UserFactoryBean

③配置bean

④测试

1.2.14、基于xml的自动装配

①场景模拟(三层架构)

②配置bean(不用自动装配的方式)

③测试

④配置bean(自动装配的方式)

⑤测试

1.3、基于注解管理bean

1.3.1、标记(注解)与扫描

①注解

②扫描

③新建一个Maven Module并导入依赖

④创建Spring配置文件

⑤标识组件的常用注解

⑥创建组件

⑦扫描组件

⑨组件所对应的bean的id

1.3.2、实验二:基于注解的自动装配

①场景模拟

②@Autowired注解

@AutoWired注解能够标识的位置

③@Autowired工作流程


1、IOC

1.1、IOC容器

1.1.1、IOC思想

IOC:Inversion of Control,翻译过来是反转控制

①获取资源的传统方式

在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的 模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。

②反转控制方式获取资源

反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。

③DI

DI:Dependency Injection,翻译过来是依赖注入

DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器 的资源注入。相对于IOC而言,这种表述更直接。

所以结论是:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现。

1.1.2、IOC容器在Spring中的实现

Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:

①BeanFactory

这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

②ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用ApplicationContext 而不是底层的 BeanFactory。

③ApplicationContext的主要实现类

快捷键双击shift

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第1张图片

快捷键Ctrl+H

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第2张图片

ClassPathXmlApplicationContext

通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象

FileSystemXmlApplicationContext

通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象

ConfigurableApplicationContext

ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、 关闭和刷新上下文的能力。

WebApplicationContext

专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对 象,并将对象引入存入 ServletContext 域中。

1.2、基于XML管理bean

1.2.1、实验一
①创建Maven Module
②引入依赖

  
  
    org.springframework
    spring-context
    5.3.24
  
  
  
    junit
    junit
    4.12
  
③创建类
public class hello {
    public void sayHello(){
        System.out.println("hello word!!");
    }
}
④创建Spring的配置文件

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第3张图片

⑤在Spring的配置文件中配置bean


  
  
⑥创建测试类测试

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第4张图片

public class helloTest {
    @Test
    public void test(){
        //resources和java最终加载到同一个目录(类路径下)
        //获取IOC容器
        ApplicationContext ioc = new ClassPathXmlApplicationContext("bean.xml");
        //获取IOC容器中的bean
        hello hello = (com.pjp.spring6.bean.hello) ioc.getBean("hello");
        hello.sayHello();
    }
}
⑦思路

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第5张图片

⑧注意

注释其中的无参构造方法

public class Student {
    private String name;
    private int age;
    private String sex;

    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    //    public Student() {
    //    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要 无参构造器时,没有无参构造器,则会抛出下面的异常:

Caused by: java.lang.NoSuchMethodException: com.pjp.spring.Student.()

Student.():具体的无参构造方法

在配置文件中也相应报错 在类“Student”中找不到匹配的构造函数

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第6张图片

1.2.2、获取bean
①方式一:根据id获取

由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。

ioc.getBean(String name)

Student studentOne = (Student) ioc.getBean("studentOne");

②方式二:根据类型获取

ioc.getBean(Class requireType)

Student student = ioc.getBean(Student.class);

注意

当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个

当IOC容器中一共配置了两个:


根据类型获取时会抛出异常

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    Student student = ioc.getBean(Student.class);
    System.out.println(student);
}

org.springframework.beans.factory.NoUniqueBeanDefinitionException(没有唯一Bean定义异常): No qualifying bean of type 'com.pjp.spring.Student' available: expected single matching bean but found 2: studentOne,studentTwo

③方式三:根据id和类型

ioc.getBean(String name,Class requireType)

Student studentOne = ioc.getBean("StudentOne", Student.class);

扩展

如果组件类实现了接口,根据接口类型可以获取 bean 前提是bean唯一

public class Student implements Person {
    private String name;
    private int age;
    private String sex;

    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", sex='" + sex + '\'' +
            '}';
    }
}
public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    Person person = ioc.getBean(Person.class);
    System.out.println(person);
}

输出:Student{name='null', age=0, sex='null'}

如果一个接口有多个实现类,这些实现类都配置了 bean,不能根据接口类型获取 bean 因为bean不唯一

⑤结论

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。

即通过bean的类型bean所继承的类的类型bean所实现的接口的类型都可以获取bean

1.2.3、依赖注入之setter注入

就是属性复制

①配置bean时为属性赋值


  
  
    
    
    
  

property标签:通过组件类的Set方法给主键对象设置属性

name属性:指定属性名(这个属性名是getter和setter方法定义的,和成员变量无关)

value属性:指定属性值

②测试
@Test
    public void testSetter(){
        ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        Student studentTwo = ioc.getBean("studentTwo", Student.class);
        System.out.println(studentTwo);
    }

输出 Student{name='pjp', age=21, sex='男'}
1.2.4、依赖注入之构造器注入
①在Student类中的有参构造
public Student(String name, int age, String sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
②配置bean

  
  
  

constructor-arg标签还有两个属性可以进一步描述构造器参数:

index属性:指定参数所在位置的索引(从0开始)

name属性:指定参数名

③测试
public void testConstructor(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    Student studentThree = ioc.getBean("studentThree", Student.class);
    System.out.println(studentThree);
}
输出 Student{name='jack', age=24, sex='男'}
拓展

增加两个属性及其构造方法

private Integer id;
    private double score;
    
    
    public Student(String name, int age, String sex,double score) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.score = score;
    }

    public Student(String name, int age, String sex,Integer id) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.id = id;
    }

配置bean


  
  
  
  

测试

public void testConstructor2(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    Student studentFour = ioc.getBean("studentFour", Student.class);
    System.out.println(studentFour);
}
输出 Student{name='Tom', age=25, sex='男', id=10, score=0.0}

优先匹配了下面这个构造器

public Student(String name, int age, String sex, Integer id) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.id = id;
}

指定sroce


  
  
  
  

输出

Student{name='Tom', age=25, sex='男', id=null, score=10.0}

则匹配下面这个构造器

public Student(String name, int age, String sex, double score) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.score = score;
}
1.2.5、特殊值处理
①字面量赋值

什么是字面量?

int a = 10;

声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a 的时候,我们实际上拿到的值是10。

而如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。


②null值


注意

这种写法,为name赋值是字符串null

③xml实体


输出:Student{name='a < b', age=0, sex='null', id=null, score=0.0}

xml中转义字符

========>< 小于号转义

========>> 大于号转义

========>& &符号转义

========>' 单引号转义

========>" 双引号转义

④CDATA节


  
输出:Student{name='a < b', age=0, sex='a

转义标签

快捷键 大写CD回车

1.2.6、为类类型属性赋值
①创建班级类Clazz
public class Clazz {
    private Integer cid;
    private String name;

    public Clazz(Integer cid, String name) {
        this.cid = cid;
        this.name = name;
    }

    public Clazz() {
    }

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Clazz{" +
            "cid=" + cid +
            ", name='" + name + '\'' +
            '}';
    }
}
②修改Student类
private Clazz clazz;

public Clazz getClazz() {
    return clazz;
}

public void setClazz(Clazz clazz) {
    this.clazz = clazz;
}

@Override
    public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", sex='" + sex + '\'' +
        ", id=" + id +
        ", score=" + score +
        ", clazz=" + clazz +
        '}';
}
③方式一:引用外部已声明的bean

配置Clazz类型的bean:


  
  

为Student中的clazz属性赋值:

ref属性:应用IOC容器中某个bean的id,将所对应的bean为属性赋值


  
  
  
  

测试后输出

Student{name='NoNo', age=19, sex='女', id=null, score=0.0, clazz=Clazz{cid=101, cname='java'}}

错误演示:


  
  
  
  

如果错把ref属性写成了value属性,会抛出异常: Cannot convert value of type 'java.lang.String' to required type 'com.pjp.spring.Clazz' for property 'clazz': no matching editors or conversion strategy found

意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个

属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值

④方式二:级联属性赋值

  
  
  
  
  
  
  
⑤方式二:内部

  
  
  
  
  
  
  

在一个bean内部声明再声明一个bean

内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性

输出:Student{name='王五', age=24, sex='男', id=null, score=0.0, clazz=Clazz{cid=303, cname='python'}}
1.2.7、为数组类型属性赋值
①修改Student类
在Student类中添加以下代码:

private String[] hobby;

public String[] getHobby() {
    return hobby;
}

public void setHobby(String[] hobby) {
    this.hobby = hobby;
}

@Override
    public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", sex='" + sex + '\'' +
        ", id=" + id +
        ", score=" + score +
        ", clazz=" + clazz +
        ", hobby=" + Arrays.toString(hobby) +
        '}';
}
②配置bean

  
  
  
  
    
      游泳
      健身
      打球
    
  
输出:Student{name='赵六', age=18, sex='女', id=null, score=0.0, clazz=null, hobby=[游泳, 健身, 打球]}
1.2.8、为集合类型属性赋值
①为List集合类型属性赋值

在Clazz类中添加以下代码:

private List students;

public List getStudents() {
    return students;
}

public void setStudents(List students) {
    this.students = students;
}

@Override
    public String toString() {
    return "Clazz{" +
        "cid=" + cid +
        ", cname='" + cname + '\'' +
        ", students=" + students +
        '}';
}

配置bean:


  
  
  
    
      
      
      
      
    
  
输出:Clazz{cid=202, cname='web', students=[Student{name='pjp', age=21, sex='男', id=null, score=0.0, clazz=null, hobby=null}, 
Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null}, 
    Student{name='Tom', age=25, sex='男', id=null, score=10.0, clazz=null, hobby=null}, 
Student{name='a < b', age=0, sex='a

配置util约束

配置一个集合类型的bean,需要使用util约束

实际上创建list集合的属性的bean


  
  
  
  

  
  
  
输出:Clazz{cid=202, cname='web', students=[Student{name='pjp', age=21, sex='男', id=null, score=0.0, clazz=null, hobby=null}, 
Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null}, 
    Student{name='Tom', age=25, sex='男', id=null, score=10.0, clazz=null, hobby=null}, 
Student{name='王五', age=24, sex='男', id=null, score=0.0, clazz=Clazz{cid=303, cname='python', students=null}, hobby=null}]}

输出list集合

@Test
    public void test3(){
        ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
        ArrayList studentList = ioc.getBean("studentList", ArrayList.class);
        System.out.println(studentList);
    }
输出:[Student{name='pjp', age=21, sex='男', id=null, score=0.0, clazz=null, hobby=null}, Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null}, 
Student{name='Tom', age=25, sex='男', id=null, score=10.0, clazz=null, hobby=null}, Student{name='王五', age=24, sex='男', id=null, score=0.0, clazz=Clazz{cid=303, cname='python', students=null}, hobby=null}]
②为Map集合类型属性赋值

创建教师类Teacher:

public class Teacher {
    private Integer Tid;
    private String Tname;

    public Teacher(Integer tid, String tname) {
        Tid = tid;
        Tname = tname;
    }

    public Teacher() {
    }

    public Integer getTid() {
        return Tid;
    }

    public void setTid(Integer tid) {
        Tid = tid;
    }

    public String getTname() {
        return Tname;
    }

    public void setTname(String tname) {
        Tname = tname;
    }

    @Override
    public String toString() {
        return "Teacher{" +
            "Tid=" + Tid +
            ", Tname='" + Tname + '\'' +
            '}';
    }
}

在Student类中添加以下代码:

private Map teacherMap;

public Map getTeacherMap() {
    return teacherMap;
}

public void setTeacherMap(Map teacherMap) {
    this.teacherMap = teacherMap;
}

@Override
    public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", sex='" + sex + '\'' +
        ", id=" + id +
        ", score=" + score +
        ", clazz=" + clazz +
        ", hobby=" + Arrays.toString(hobby) +
        ", teacherMap=" + teacherMap +
        '}';
}

配置bean:


  
  
  
  
    
      
      
      
    
  


  
  


  
  
输出:Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null, teacherMap={888=Teacher{Tid=888, Tname='LiMing'}, 777=Teacher{Tid=777, Tname='Wang'}}}

配置util约束(和List集合同理)


  
  


  
  
  
  

输出同上

1.2.9、p命名空间

引入p命名空间后,可以通过以下方式为bean的各个属性赋值

加入约束xmlns:p="http://www.springframework.org/schema/p"

输出:Student{name='田七', age=22, sex='null', id=null, score=0.0, clazz=null, hobby=null, teacherMap={888=Teacher{Tid=888, Tname='LiMing'}, 777=Teacher{Tid=777, Tname='Wang'}}}
1.2.10、引入外部属性文件
①加入依赖


  mysql
  mysql-connector-java
  8.0.16



  com.alibaba
  druid
  1.0.31
②创建外部属性文件

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第7张图片

jdbc.user=root
jdbc.password=pjp
jdbc.url=jdbc:mysql://localhost:3306/db01?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

serverTimezone=UTC 避免时区问题

实际上是指出核数据库的时区为美国。因为我们的数据库的时区是美国的,而我们连接的时候用的是中国的北京时间,然后比美国早上8个小时,然后呢用的时候就必须指出我们当前用的时间是美国的时间,这样才能连接上数据库。

③引入属性文件并配置bean


    
    
    
    
④测试
public void test() throws SQLException {
        ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
        DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
        System.out.println(dataSource.getConnection());
    }
输出:七月 02, 2023 5:26:24 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info
信息: {dataSource-1} inited
com.mysql.cj.jdbc.ConnectionImpl@1d76aeea

连接成功!!

1.2.11、bean的作用域
①概念

在Spring中可以通过配置bean标签的scope属性来指定bean的作用范围,各取值含义参考下表

取值

含义

创建对象的时机

singleton(默认)

在IOC容器中,这个bean的对象始终为单实例

IOC容器初始化时

prototype

这个bean在IOC容器中有多个实例

获取bean时

如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):

取值

含义

request

在一个请求范围内有效

session

在一个会话范围内有效

单例(singleton)测试
 

  
  
public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
    Student student1 = ioc.getBean(Student.class);
    Student student2 = ioc.getBean(Student.class);
    System.out.println(student1.hashCode());
    System.out.println(student2.hashCode());
}

输出
265119009
265119009

结果返回对象的hash地址相同,说明是同一个对象

多例(prototype )测试

  
  
@Test
    public void test(){
        ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
        Student student1 = ioc.getBean(Student.class);
        Student student2 = ioc.getBean(Student.class);
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());
    }
输出
1282811396
641853239

结果返回对象的hash地址不相同,说明不是同一个对象

1.2.12、bean的生命周期

①具体的生命周期过程

bean对象创建(调用无参构造器)

给bean对象设置属性

bean对象初始化之前操作(由bean的后置处理器负责)

bean对象初始化(需在配置bean时指定初始化方法)

bean对象初始化之后操作(由bean的后置处理器负责)

bean对象就绪可以使用

bean对象销毁(需在配置bean时指定销毁方法)

IOC容器关闭

②创建类User并添加bean的初始化和销毁方法

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    public User() {
        System.out.println("生命周期:1、创建对象");
    }
    public User(Integer id, String username, String password, Integer age) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        System.out.println("生命周期:2、依赖注入");
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void initMethod(){
        System.out.println("生命周期:3、初始化");
    }
    public void destroyMethod(){
        System.out.println("生命周期:5、销毁");
    }
    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", username='" + username + '\'' +
            ", password='" + password + '\'' +
            ", age=" + age +
            '}';
    }
}
③配置bean



  
  
  
  
④测试
public void test() {
   //ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    User user = ioc.getBean(User.class);
    System.out.println(user);
    ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、初始化
User{id=111, username='pjp', password='857857', age=21}
生命周期:4、销毁

注意

若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行

public void test() {
    //        ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    //        User user = ioc.getBean(User.class);
    //        System.out.println(user);
    //        ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、初始化

若bean的作用域为多例时,声明周期的前三个步骤会在获取bean时执行(没有执行销毁方法)


  
  
  
  
public void test() {
    //        ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    //        User user = ioc.getBean(User.class);
    //        System.out.println(user);
    //        ioc.close();
}
没有输出
public void test() {
    //        ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    User user = ioc.getBean(User.class);
    System.out.println(user);
    ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、初始化
User{id=111, username='pjp', password='857857', age=21}
⑤bean的后置处理器

bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口, 且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

创建bean的后置处理器:

public class MyBeanPostProcessor implements BeanPostProcessor {
    //    快捷键Ctrl+O 重写方法
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //        在bean的生命周期初始之前执行
        System.out.println("后置处理器postProcessBeforeInitialization"+bean+beanName);
        return bean;
        //这个bean是此时IOC管理的bean,beanName为bean的id
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //        在bean的生命周期初始化之后执行
        System.out.println("后置处理器postProcessAfterInitialization"+bean+beanName);
        return bean;
    }
}

在IOC容器中配置后置处理器:

测试输出

public void test() {
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    User user = ioc.getBean(User.class);
    System.out.println(user);
    ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
后置处理器postProcessBeforeInitializationUser{id=111, username='pjp', password='857857', age=21}com.pjp.spring.POJO.User#0
生命周期:3、初始化
后置处理器postProcessAfterInitializationUser{id=111, username='pjp', password='857857', age=21}com.pjp.spring.POJO.User#0
User{id=111, username='pjp', password='857857', age=21}
1.2.13、FactoryBean
①简介

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个 FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是 getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

②创建类UserFactoryBean

其中还有一个方法 设置是否是单例

default boolean isSingleton() {

return true;

}

public class UserFactoryBean implements FactoryBean {

    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class getObjectType() {
        return User.class;
    }
}
③配置bean
④测试
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
    User user = ioc.getBean(User.class);
    System.out.println(user);
}
输出
生命周期:1、创建对象
User{id=null, username='null', password='null', age=null}
1.2.14、基于xml的自动装配

自动装配:

根据指定的策略,在IOC容器中匹配某一个bean,自动为指定bean中所依赖的类型或接口类型属性赋值

①场景模拟(三层架构)

创建类UserController

public class UserController {
    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public  void saveUser(){
        userService.saveUser();
    }
}

创建接口UserService

public interface UserService {
    //保存用户信息
    void saveUser();
}

创建类UserServiceImpl实现接口UserService

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void saveUser() {
        userDao.saveUser();
    }
}

创建接口UserDao

public interface UserDao {
    void saveUser();
}

创建类UserDaoImpl实现接口UserDao

public class UserDaoImpl implements UserDao {
    @Override
    public void saveUser() {
        System.out.println("保存成功!!!");
    }
}
②配置bean(不用自动装配的方式)

  


  

③测试
public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire.xml");
    UserController userController = ioc.getBean(UserController.class);
    userController.saveUser();
}
输出
保存成功!!!
④配置bean(自动装配的方式)

使用bean标签的autowire属性设置自动装配效果

属性:

no,default:表示不装配,即bean中的属性不会自动匹配某个bean为属性赋值,此时属性使用认值

byType:根据要赋值的属性的类型,在IOC容器中匹配某个bean,为属性赋值

使用byType进行自动装配时,是利用Java的反射机制获取测试类中的set方法的参数类型,寻找此接口类型的实现类或子类,找到后将其首字母小写与IOC容器中的beanId进行匹配,匹配成功后则调用set方法进行依赖注入

注意

a>若通过类型没有找到任何一个类型匹配的bean,此时不装配,属性使用默认值

b>若通过类型找到了多个类型匹配的bean,此时会抛出异常 NoUniqueBeanDefinitionException

总结:当使用byType实现自动装配时,IOC容器中有且只有一个类型匹配的bean能够为属性赋值

自动装配方式:byType



⑤测试
public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire.xml");
    UserController userController = ioc.getBean(UserController.class);
    userController.saveUser();
}
输出
保存成功!!!

自动装配方式:byName

byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值

当类型匹配的bean有多个时,此时可以使用byName实现自动装配



测试输出同上

匹配机制:

userController中的属性名 userService 和 配置UserServiceImpl中的bean id: userService 相匹配,如果没有自定义UserServiceImpl的bean id

其默认名为userServiceImpl相匹配,因为名不同则匹配失败。

1.3、基于注解管理bean

1.3.1、标记(注解)与扫描

①注解

和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

举例:元旦联欢会要布置教室,蓝色的地方贴上元旦快乐四个字,红色的地方贴上拉花,黄色的地方贴上气球。

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第8张图片

班长做了所有标记,同学们来完成具体工作。墙上的标记相当于我们在代码中使用的注解,后面同学们做的工作,相当于框架的具体操作。

②扫描

Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作

③新建一个Maven Module并导入依赖
④创建Spring配置文件

⑤标识组件的常用注解

@Component:将类标识为普通组件 (组件:IOC管理下的一个bean)

@Controller:将类标识为控制层组件

@Service:将类标识为业务层组件

@Repository:将类标识为持久层组件

四个组件的区别

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第9张图片

通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。

对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。

⑥创建组件

创建控制层组件

@Controller
public class UserController {
}

创建接口UserService

public interface UserService {
}

创建业务层组件UserServiceImpl

@Service
public class UserServiceImpl {
}

创建接口UserDao

public interface UserDao {
}

创建持久层组件UserDaoImp

@Repository
public class UserDaoImpl {
}
⑦扫描组件

情况一:最基本的扫描方式


扫描的是com.pjp.spring这个目录下全部的类

测试

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class);
    System.out.println(userController);
    UserDaoImpl userDao = ioc.getBean(UserDaoImpl.class);
    System.out.println(userDao);
    UserServiceImpl userService = ioc.getBean(UserServiceImpl.class);
    System.out.println(userService);
}
输出
com.pjp.spring.controller.UserController@1460a8c0
com.pjp.spring.dao.impl.UserDaoImpl@4f638935
com.pjp.spring.service.impl.UserServiceImpl@4387b79e

情况二:指定要排除的组件


  

context:exclude-filter标签:指定排除规则

type:设置排除或包含的依据

type="annotation",根据注解排除,expression中设置要排除的注解的全类名

type="assignable",根据类型排除,expression中设置要排除的类型的全类名

测试

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    //        UserController userController = ioc.getBean(UserController.class);
    //        System.out.println(userController);
    UserDaoImpl userDao = ioc.getBean(UserDaoImpl.class);
    System.out.println(userDao);
    UserServiceImpl userService = ioc.getBean(UserServiceImpl.class);
    System.out.println(userService);
}
输出
com.pjp.spring.dao.impl.UserDaoImpl@8909f18
com.pjp.spring.service.impl.UserServiceImpl@79ca92b9

获取不了@Controller标记的类

情况三:仅扫描指定组件


  

context:include-filter标签:指定在原有扫描规则的基础上追加的规则

use-default-filters属性:取值false表示关闭默认扫描规则

此时必须设置use-default-filters=false,因为默认规则扫描指定包下所有的类

测试

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class);
    System.out.println(userController);
}
输出
com.pjp.spring.controller.UserController@7a52f2a2

只能获取到@Controller标记的类

⑨组件所对应的bean的id

在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。

通过注解+扫描所配置的bean的id:

默认情况 :

默认值为类的小驼峰,类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。

自定义bean的id :

可通过标识组件的注解的value属性设置自定义的bean的id。例如@Controller("controller")

默认:

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean("userController",UserController.class);
}
输出
com.pjp.spring.controller.UserController@d83da2e
自定义:
​​​​​​​public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean("controller",UserController.class);
    System.out.println(userController);
}
输出
com.pjp.spring.controller.UserController@d83da2e

1.3.2、实验二:基于注解的自动装配

①场景模拟

参考基于xml的自动装配

在UserController中声明UserService对象

在UserServiceImpl中声明UserDao对象

@Autowired注解

在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法

@Controller
public class UserController {
    @Autowired
    private UserService userService;

    public void saveUser() {
        userService.saveUser();
    }
}
public interface UserService {
    /**
* 保存用户信息
*/
    void saveUser();
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public void saveUser() {
        userDao.saveUser();
    }
}
public interface UserDao {
    /**
* 保存用户信息
*/
    void saveUser();
}
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void saveUser() {
        System.out.println("保存成功!!!!");
    }
}

配置扫描

测试

public void testAutoWired(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class);
    userController.saveUser();
}
输出
保存成功!!!!
@AutoWired注解能够标识的位置

>标识在成员变量上,此时不需要设置成员变量的set方法

>标识在set方法

>标识在为当前成员变量赋值的有参构造器

标记在set方法或有参构造器上

@Controller
public class UserController {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void saveUser() {
        userService.saveUser();
    }
}
@Controller
public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void saveUser() {
        userService.saveUser();
    }
}
③@Autowired工作流程

Spring-IOC(3.5w字总结,IOC思想及实现,基于XML和注解管理bean)_第10张图片

首先根据所需要的组件类型到IOC容器中查找

==>能够找到唯一的bean:直接执行装配

==>如果完全找不到匹配这个类型的bean:装配失败

==>所需类型匹配的bean不止一个

        --->没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配

                         ······>能够找到:执行装配

                         ······>找不到:装配失败

        ---> 使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配

                          ······> 能够找到:执行装配

                          ······> 找不到:装配失败

此时配置多个bean并且自定义id和属性名不相同


则报错NoUniqueBeanDefinitionException

使用@Qualifier注解

通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值

@Qualifier("service")中的value值service和bean id= service相匹配

@Controller
public class UserController {
    @Autowired
    @Qualifier("service")
    private UserService userService;

    public void saveUser() {
        userService.saveUser();
    }
}

测试成功

若IOC容器中没有任何一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException

@Autowired中有属性required,默认值为true,因此在自动装配无法找到相应的bean时,会装配失败

将required设置为false,此时能装配则装配,无法装配则使用属性的默认值

@Controller
public class UserController {
    @Autowired(required = false)
    @Qualifier("service")
    private UserService userService;

    public void saveUser() {
        userService.saveUser();
    }
}

此时抛出NullPointerException(属性的默认值为Null)

你可能感兴趣的:(spring,java,后端,xml,idea)