IOC容器
IOC思想
IOC:Inversion of Control 即反转控制
-
获取资源的传统方式
自己做饭:买菜、洗菜、择菜、改刀、炒菜,全过程参与,费时费力,必须清楚了解资源创建整个过程中的全部细节且熟练掌握。
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。 -
反转控制方式获取资源
点外卖:下单、等、吃,省时省力,不必关心资源创建过程的所有细节。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向--------改由容器主动的将资源推送给需要的组件,开发人员不需要知道容路是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动开式。 -
DI
Dl: Dependency Injection,翻译过来是依赖注入。
DI是IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
所以结论是:IOC就是一种反转控制的思想,而DI是对IOC的一种具体实现
IOC容器在Spring中的实现
Spring的IOC容器就是IOC思想的一个落地的产品的实现。IOC容器中管理的组件也叫bean,在创建bean之前,首先需要创建IOC容器。Spring提供了IOC容器的两种实现方式
BeanFactory
这是IOC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发人员使用
ApplicationContext
BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory
ApplicationContext的主要实现类
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的XML格式的配置文件创建IOC容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取XML格式的配置文件创建IOC容器对象 |
ConfigurableApplicationContext | ApplicationContext的子接口,包含一些拓展方法refresh()和close(),让ApplicationContext具有启动、关闭和刷新上下文的能力 |
WebApplicationContext | 专门为web应用准备,基于web环境创建IOC容器对象,并将对象引入存入ServletContext域中 |
基于XML管理bean
实验一:入门案例
- 创建Maven Module
- 引入依赖
pom.xml
<dependencies>
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需的索引jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
- 创建HelloWorld
package com.bijing.spring.pojo;
/**
* @author 毕晶
* @date 2022/11/11 14:50
*/
public class HelloWorld {
public void sayHello(){
System.out.println("hello,spring");
}
}
-
创建Spring的配置文件
-
在Spring的配置文件中配置bean
application.xml
<?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">
<!--
bean:配置一个bean对象,将对象交给IOC容器管理
属性:
id:bean的唯一标识,不能重复
class:设置bean对象所对应的类型
-->
<bean id="helloworld" class="com.bijing.spring.pojo.HelloWorld"></bean>
</beans>
- 创建测试类测试
package com.bijing.spring.test;
import com.bijing.spring.pojo.HelloWorld;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 毕晶
* @date 2022/11/11 14:56
*/
public class HelloWorldTest {
@Test
public void testHelloWorld(){
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");
//获取IOC容器中的bean
HelloWorld helloworld = (HelloWorld) ioc.getBean("helloworld");
helloworld.sayHello();
}
}
实验二:获取bean
- 方式一:根据id获取
- 方式二:根据类型获取
- 方式三:根据id和类型
- 注意:注意:根据类型获取bean时,要求IOC中有且只有一个类型匹配的bean;若没有一个类型匹配的bean,也会抛出异常
- 拓展
如果组件类实现了接口,根据接口类型可以获取bean吗
可以,前提是bean唯一
//此时Student实现了Person接口,组件类是Student,所以编译类型是Person,运行类型是Student
Person person = ioc.getBean(Person.class);
System.out.println(person.getClass());//class com.bijing.spring.pojo.Student
如果一个接口有多个实现类,这些实现类都配置了bean,根据接口类型可以获取bean吗
不行,因为bean不唯一
此时,因为Person的实现类有多个,不知道用哪个实现类的bean
7. 结论
根据类型来获取bean时,在满足bean唯一的前提下,其实只是看:对象instanceof指定的类型 的返回结果,只要是true就可以认为和类型匹配,就能够获取到。即通过bean的类型,bean所继承的类的类型,bean所实现的接口类型都可以获取bean
实验三:依赖注入之setter注入
- 创建学生类Student
- 配置bean时为属性赋值
<bean id="studentTwo" class="com.bijing.spring.pojo.Student">
<!--
property:通过成员变量的set方法进行赋值
name:设置需要赋值的属性名(和set方法有关)
value:设置为属性所赋的值
-->
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
<property name="age" value="23"></property>
<property name="gender" value="男"></property>
</bean>
- 测试
@Test
public void testDI() {
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
//获取bean
Student studentTwo = ioc.getBean("studentTwo", Student.class);
System.out.println(studentTwo);
}
实验四:依赖注入之构造器注入
- 在Student类中添加有参构造器
- 配置bean
<!--
constructor-arg:通过构造器进行赋值,顺序和构造器中的参数顺序一样
value:设置为属性所赋的值
name:可以指定赋值的属性
-->
<bean id="studentThree" class="com.bijing.spring.pojo.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="24" name="age"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>
- 测试
Student studentThree = ioc.getBean("studentThree", Student.class);
System.out.println(studentThree);
实验五:特殊值处理
- 字面量赋值
什么是字面量?
int a=10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际拿到的值是10
如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母,这就是字面量。
- null值
<!-- 使用null标签给属性赋值null,另外属性赋值也可以不用value属性用value标签-->
<bean id="studentFour" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1003"></property>
<property name="sname" value="王五"></property>
<property name="gender">
<null></null>
</property>
</bean>
- xml实体
<!--
xml实体如sname="<王五>"
<: <
>: >
-->
<bean id="studentFour" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1003"></property>
<property name="sname" value="<王五>"></property>
<property name="gender">
<null></null>
</property>
</bean>
- CDATA节
<!-- CDATA区:里面内容原样输出,快捷键大写CD,是xml中的一个标签,不能写在属性中-->
<bean id="studentFour" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1003"></property>
<!-- <property name="sname" value="<王五>"></property>-->
<property name="sname">
<value><![CDATA[<王五>]]></value>
</property>
<property name="gender">
<null></null>
</property>
</bean>
实验六:为类类型属性赋值
- 创建班级类Clazz
package com.bijing.spring.pojo;
/**
* @author 毕晶
* @date 2022/11/11 19:27
*/
public class Clazz {
private Integer cid;
private String cname;
public Clazz(Integer cid, String cname) {
this.cid = cid;
this.cname = cname;
}
public Clazz() {
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "Clazz{" +
"cid=" + cid +
", cname='" + cname + '\'' +
'}';
}
}
- 修改Student类
package com.bijing.spring.pojo;
/**
* @author 毕晶
* @date 2022/11/11 15:09
*/
public class Student implements Person {
private Integer sid;
private String sname;
private Integer age;
private String gender;
private Clazz clazz;
public Student() {
}
public Student(Integer sid, String sname, Integer age, String gender) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", clazz=" + clazz +
'}';
}
}
- 方式一:引用外部已声明的bean
<!-- ref:引用IOC容器中的某个bean的id-->
<bean id="studentFive" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵六"></property>
<property name="age" value="26"></property>
<property name="gender" value="男"></property>
<property name="clazz" ref="clazzOne"></property>
</bean>
<bean id="clazzOne" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
</bean>
- 方式二:内部bean
<!-- 使用内部bean,内部bean对象不能通过IOC容器获取,就和内部类不能在外部访问一样-->
<bean id="studentFive" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵六"></property>
<property name="age" value="26"></property>
<property name="gender" value="男"></property>
<property name="clazz">
<bean id="clazzInner" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
</bean>
</property>
</bean>
- 方式三:级联属性赋值
<bean id="studentFive" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵六"></property>
<property name="age" value="26"></property>
<property name="gender" value="男"></property>
<!-- 使用级联的方式,要提前为clazz赋值或者实例化-->
<property name="clazz" ref="clazzOne"></property>
<property name="clazz.cid" value="1111"></property>
<property name="clazz.cname" value="三年一班"></property>
</bean>
<bean id="clazzOne" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
</bean>
实验七:为数组类型属性赋值
- 修改Student类
package com.bijing.spring.pojo;
import java.util.Arrays;
/**
* @author 毕晶
* @date 2022/11/11 15:09
*/
public class Student implements Person {
private Integer sid;
private String sname;
private Integer age;
private String gender;
private String[] hobby;
private Clazz clazz;
public Student() {
}
public Student(Integer sid, String sname, Integer age, String gender) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", hobby=" + Arrays.toString(hobby) +
", clazz=" + clazz +
'}';
}
}
- 配置bean
<!-- 字面量类型使用value来为数组中的每个变量赋值,如果是引用类型使用ref标签 <ref bean="hobby1"></ref>-->
<property name="hobby">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
<value>蹦迪</value>
</array>
</property>
实验八:为集合类型属性赋值
- 为List集合类型的属性赋值
通过list标签
<bean id="clazzOne" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
<property name="students">
<list>
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
<ref bean="studentFour"></ref>
<ref bean="studentFive"></ref>
<!-- 也可以直接新建Student-->
<bean id="newStudent1" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1009"></property>
<property name="sname" value="新同学"></property>
<property name="age" value="26"></property>
<property name="gender" value="男"></property>
<property name="clazz">
<bean id="clazzInner" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
</bean>
</property>
</bean>
</list>
</property>
</bean>
通过util:list
需要导入util约束
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
<bean id="clazzOne" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
<property name="students" ref="students">
</property>
</bean>
<!-- 配置一个集合类型的bean,需要使用一个util的约束-->
<util:list id="students">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
</util:list>
- 为Map集合类型的属性赋值
//Student增加下面属性
private Map<String, Teacher> teacherMap;
<bean id="studentFive" class="com.bijing.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵六"></property>
<property name="age" value="26"></property>
<property name="gender" value="男"></property>
<property name="clazz">
<bean id="clazzInner" class="com.bijing.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="三年一班"></property>
</bean>
</property>
<!-- 字面量类型使用value来为数组中的每个变量赋值,如果是引用类型使用ref标签 <ref bean="hobby1"></ref>-->
<property name="hobby">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
<value>蹦迪</value>
</array>
</property>
<!--
Map赋值:
第一种: map标签,一个entry表示一个键值对
第二种:util: map
-->
<!-- <property name="teacherMap">-->
<!-- <map>-->
<!-- <entry key="10086" value-ref="teacherOne"></entry>-->
<!-- <entry key="10087" value-ref="teacherTwo"></entry>-->
<!-- </map>-->
<!-- </property>-->
<property name="teacherMap" ref="teachers"></property>
</bean>
<bean id="teacherOne" class="com.bijing.spring.pojo.Teacher">
<property name="tid" value="10086"></property>
<property name="tname" value="大宝"></property>
</bean>
<bean id="teacherTwo" class="com.bijing.spring.pojo.Teacher">
<property name="tid" value="10087"></property>
<property name="tname" value="小宝"></property>
</bean>
<util:map id="teachers">
<entry key="10086" value-ref="teacherOne"></entry>
<entry key="10087" value-ref="teacherTwo"></entry>
</util:map>
- 引用集合类型的bean
引入util约束,使用util: list、util: map标签,具体使用见上面
实验九:p命名空间
引入约束p,应该是指property
xmlns:p="http://www.springframework.org/schema/p"
<!-- p命名空间,要引入约束p-->
<bean id="studentSix" class="com.bijing.spring.pojo.Student"
p:sid="1005" p:sname="小明" p:teacherMap-ref="teachers"></bean>
实验十:引入外部属性文件
jdbc为例
- 加入依赖
pom.xml
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
- 创建外部属性文件
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimeZone=UTC"
jdbc.username=root
jdbc.password=root1234
- 引入属性文件
引入约束context
xmlns:context="http://www.springframework.org/schema/context"
<!-- 引入jdbc.properties,之后可以通过${key}的方式访问value-->
<context:property-placeholder location="jdbc.properties"/>
- 配置bean
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
- 测试
public class DataSourceTest {
@Test
public void testDataSource() throws SQLException {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
DruidDataSource datasource = ioc.getBean("datasource", DruidDataSource.class);
DruidPooledConnection connection = datasource.getConnection();
System.out.println(connection);
}
}
实验十一:bean的作用域
- 概念
在Spring中可以通过配置bean标签的scope来指定bean的作用域范围,各取值含义参如下表
取值 | 含义 | 创建对象的时机 |
---|---|---|
singleton(默认) | 在IOC容器中,这个bean对象始终为单例 | IOC容器初始化 |
prototype | 这个bean在IOC容器中有多个实例 | 获取bean时 |
如果在WebApplicationContext环境下还有另外两个作用域(但不常用)
取值 | 含义 |
---|---|
request | 在一个请求范围内有效 |
session | 在一次会话范围内有效 |
- 创建类Student
就是之前使用的Student类 - 配置bean
<!--
scope:设置bean的作用域
scope="singleton|prototype"
singleton:表示获取该bean所对应的对象都是同一个对象(单例,用的比较多)
prototype:表示获取该bean所对应的对象不是同一个对象(多例)
-->
<bean id="student" class="com.bijing.spring.pojo.Student" scope="prototype">
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
</bean>
- 测试
@Test
public void testScope() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
Student student1 = ioc.getBean("student", Student.class);
Student student2 = ioc.getBean("student", Student.class);
System.out.println(student1 == student2);//true说明是单例模式创建的student,是同一个对象
}
实验十二:bean的生命周期
- 具体的生命周期过程
- bean对象创建(调用无参构造器)
- 给bean对象设置属性
- bean对象初始化之前操作(由bean的后置处理器负责)
- bean对象初始化(需在配置bean时指定初始化方法)
- bean对象初始化之后操作(由bean的后置处理器负责)
- bean对象就绪可以使用
- bean对象的销毁(需要在配置bean的时候指定销毁方法)
- IOC容器关闭
- 创建类User
package com.bijing.spring.pojo;
/**
* @author 毕晶
* @date 2022/11/11 22:31
*/
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
public void initMethod() {
System.out.println("生命周期3:初始化");
}
public void destroyMethod() {
System.out.println("生命周期4:销毁");
}
}
- 配置bean
<?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">
<bean id="user" class="com.bijing.spring.pojo.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="id" value="1"></property>
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
<property name="age" value="23"></property>
</bean>
</beans>
- 测试
package com.bijing.spring.test;
import com.bijing.spring.pojo.User;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 毕晶
* @date 2022/11/11 22:43
*/
public class LifeCycleTest {
/**
* 1.实例化
* 2.依赖注入
* 3.初始化 要通过bean标签的init-method属性指定方法
* 4.IOC容器关闭时销毁 要通过bean标签的destroy-method属性指定方法
*
* 注意:
* 若bean的作用域是单例时,生命周期的前三个步骤会在获取IOC容器时执行
* 若bean的作用域是多例时,生命周期的前三个步骤会在获取bean时执行,销毁步骤不由ioc控制了
*/
@Test
public void testLifeCycle() {
//ConfigurableApplicationContext是ApplicationContext的子接口,其中拓展了刷新和关闭容器的方法
//注意:1.如果是bean对象是单例模式获取的,则生命周期前三个步骤已经完成,即ioc容器创建的时候bean对象就已经创建并完成了初始化
// 2.如果是多例,destroy步骤不由ioc来控制了,即ioc.close()时销毁方法不执行
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
//ApplicationContext没有close方法
ioc.close();
}
}
- bean的后置处理器
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某—个bean生效,而是 针对IOC容器中所有bean都会执行
创建bean的后置处理器
package com.bijing.spring.pojo.com.bijing.spring.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author 毕晶
* @date 2022/11/12 10:17
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之前执行
System.out.println("此方法在bean的生命周期初始化之前执行");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之后执行
System.out.println("此方法在bean的生命周期初始化之后执行");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
配置bean的后置处理器
<bean id="myBeanPostProcessor" class="com.bijing.spring.pojo.com.bijing.spring.process.MyBeanPostProcessor"></bean>
实验十三:FactoryBean
-
简介
Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,但工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。 -
创建类UserFactoryBean
package com.bijing.spring.factory;
import com.bijing.spring.pojo.User;
import org.springframework.beans.factory.FactoryBean;
/**
* @author 毕晶
* @date 2022/11/12 10:35
*/
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
- 配置bean
<bean class="com.bijing.spring.factory.UserFactoryBean"></bean>
- 测试
public class FactoryBeanTest {
@Test
public void testFactoryBean() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
}
}
实验十四:基于xml的自动装配
自动装配:
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或者接口类型属性赋值
- 场景模拟
创建UserController
public class UserController {
private UserService userService;
//UserService会变动,不要直接写死,使用set和get方法有利于改动
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser() {
userService.saveUser();
}
}
- 配置bean
<?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">
<!-- UserController中有UserService属性,可以根据该属性所对应的类的类型或者接口实现类的类型去IOC容器中匹配,要求类型唯一-->
<!-- UserController中有UserService属性,可以根据UserService的 将set去掉并且首字母小写的 字符串作为name去IOC容器中匹配id-->
<bean id="userController" class="com.bijing.spring.controller.UserController" autowire="byType">
<!-- <property name="userService" ref="userService"></property>-->
</bean>
<bean id="userService" class="com.bijing.spring.service.impl.UserServiceIml" autowire="byType">
<!-- <property name="userDao" ref="userDao"></property>-->
</bean>
<bean id="userDao" class="com.bijing.spring.dao.impl.UserDaoImpl"></bean>
</beans>
- 测试
package com.bijing.spring.test;
import com.bijing.spring.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 毕晶
* @date 2022/11/12 17:13
*/
public class AutowireByXmlTest {
/**
* 自动装配:
* 根据指定的策略,在IOC容器中匹配某个bean,自动为bean中的类类型的属性或者接口类型属性赋值
* 可以通过bean标签中autowire属性设置自动装配的策略
* 自动装配策略:
* #no,default表示不装配,即bean 中的属性不会自动匹配某个bean为属性赋值,此时属性使用默认值
* #byType:根据要赋值的属性的类型,在IOC容器中匹配某个bean,为属性赋值
* 注意:1.若通过类型没有找到任何一个类型匹配的bean,此时不匹配,属性使用默认值
* 2.若通过类型找到了多个类型匹配的bean,此时会抛出异常:
* 总结:当使用byType实现自动装配时,IOC容器中有且只有一个类型匹配的bean能够为属性赋值
* #byName:将要赋值的属性的属性名作为bean的id在IOC容器中匹配某个bean为属性赋值
* 当类型匹配的bean有多个时,此时可以使用byName实现自动装配
*/
@Test
public void testAutowireByXml() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire-xml.xml");
UserController userController = ioc.getBean(UserController.class);
userController.saveUser();
}
}