Spring(二)-生命周期 + 自动装配(xml) +自动装配(注解)
1、生命周期
**Spring容器的 bean **的生命周期;
1.1 默认生命周期
1.1.1 生命周期
- 调用构造方法,创建实例对象;
- set方法,给实例对象赋值;
- init 初始化方法 初始化对象;(手写并配置到bean上init-method="")
- 使用容器中的bean对象;
- destroy 销毁方法 销毁对象 (手写并配置到bean上destroy-method="")
1.1.2 bean 实体类
Truck
@Data @ToString public class Truck { //品牌 private String brand; //厂商 private String factory; //价格 private Double price; public Truck() { //空参构造方法,观察bean什么时候实例化 System.out.println("------ 1.调用构造方法,创建实例对象 ------\n"); } public void setBrand(String brand) { //任意一个set方法,观察bean什么时候注入参数 System.out.println("------ 2.set方法,给实例对象赋值 ------"); this.brand = brand; } public void initTruck(){ //init初始化方法,观察bean什么时候初始化 //需要再配置bean的时候,配置init初始化方法 System.out.println("------ 3.Truck init 初始化方法 初始化对象 ------\n"); this.brand = "大运"; } public void destroyTruck(){ //destory方法,观察bean什么时候销毁 //需要再配置bean的时候,配置destory销毁方法 System.out.println("------ 5.Truck destroy 销毁方法 销毁对象 ------\n"); } //这里方法上标注的序号是测试后得来的; }
1.1.3 bean 配置
spring-lifecycle.xml
<!-- spring容器中bean的生命周期 默认生命周期 --> <bean id="truck" class="com.kgc.spring.lifecycle.Truck" init-method="initTruck" destroy-method="destroyTruck"> <property name="brand" value="江淮"></property> <property name="factory" value="安徽"></property> <property name="price" value="200000"></property> </bean>
1.1.4 测试
public class TestSpringLifeCycle { //定义全局容器对象,如果需要关闭容器对象, //必须使用ApplicationContext的子接口 ConfigurableApplicationContext //ApplicationContext接口主要各种属性的get方法; //ConfigurableApplicationContext重在对各种属性的配置; // private ApplicationContext context; private ConfigurableApplicationContext context; @Before public void initApplicationContext(){ context = new ClassPathXmlApplicationContext("spring-lifecycle.xml"); } //测试spring 容器的bean的生命周期,默认和加了处理器两种场景 @Test public void testSpringBeanLifeCycle(){ //从容器中,获取Truck的是实例对象 Truck truck = context.getBean("truck", Truck.class); //使用对象 System.out.println("------ 4.使用容器中的bean对象"+truck +" ------"); //关闭容器 context.close(); } }
输出结果:
//可以得出 spring中bean的 默认生命周期 ------ 1.调用构造方法,创建实例对象 ------ ------ 2.set方法,给实例对象赋值 ------ ------ 3.Truck init 初始化方法 初始化对象 ------ ------ 4.使用容器中的bean对象Truck(brand=大运, factory=安徽, price=200000.0) ------ ------ 5.Truck destroy 销毁方法 销毁对象 ------
1.1.5 ApplicationContext 和 ConfigurableApplicationContext
参考博客: ApplicationContext和ConfigurableApplicationContext解析
-
ApplicationContext接口主要 各种 属性的get方法 ;
-
ConfigurableApplicationContext重在对 各种 属性的配置 ;
1.2 增加后置处理器
1.2.1 生命周期
1.调用构造方法,创建实例对象;
2.set方法,给实例对象赋值;
3-1.后置处理的 before 方法;
3.初始化方法 初始化对象;
3+1.后置处理器的的 after 方法;
4.使用容器中的bean对象;
5.destroy 销毁方法 销毁对象;
1.2.2 后置处理器
-
要求:必须实现 BeanPostProcessor 接口
-
自定义 bean 的 后置处理器,对容器中 所有的bean统一处理(生效) ,
-
要生效的话,必须将此处理器放到容器中( 配置到spring的核心配置文件中 ,增加处理器的实例配置);
注意:
当前案例, 只对
容器中的 一个实例处理
;
MyBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //在容器中bean的实例对象调用 初始化方法 前 自动调用(init方法可以没有,不影响) //模拟处理容器中的bean,接下来的写法,仅限于当前的用法案例(容器中就 只有一个 卡车实例) Truck truck = (Truck)bean; System.out.println("++++++ 容器中的卡车对象 "+truck+"++++++"); System.out.println("++++++ 3-1,后置处理的 before 方法 ++++++"); truck.setBrand("后置处理的before方法"); System.out.println("++++++ 处理后的 卡车对象 "+truck+" ++++++\n"); return truck; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //bean 初始化方法 执行 后,调用此方法处理bean Truck truck = (Truck)bean; System.out.println("++++++ 初始化容器中的卡车对象 "+truck+"++++++"); System.out.println("++++++ 3+1,后置处理器的的 after 方法 ++++++"); truck.setBrand("after"); System.out.println("++++++ 初始化后 处理后的 卡车对象 "+truck+" ++++++\n"); return truck; } }
1.2.3 bean 配置
在配置文件中配置 MyBeanPostProcessor;
<!-- 配置后置处理器的实例,自动放入容器中,可以自动生效 (容器中所有的实例生效) --> <bean class="com.kgc.spring.lifecycle.MyBeanPostProcessor"></bean>
1.2.4 测试
跟默认生命周期的测试代码一致;
输出结果:
------ 1.调用构造方法,创建实例对象 ------ ------ 2.set方法,给实例对象赋值 ------ ++++++ 容器中的卡车对象 Truck(brand=江淮, factory=安徽, price=200000.0)++++++ ++++++ 3-1,后置处理的 before 方法 ++++++ ------ 2.set方法,给实例对象赋值 ------ ++++++ 处理后的 卡车对象 Truck(brand=后置处理的before方法, factory=安徽, price=200000.0) ++++++ ------ 3.Truck init 初始化方法 初始化对象 ------ ++++++ 初始化容器中的卡车对象 Truck(brand=大运, factory=安徽, price=200000.0)++++++ ++++++ 3+1,后置处理器的的 after 方法 ++++++ ------ 2.set方法,给实例对象赋值 ------ ++++++ 初始化后 处理后的 卡车对象 Truck(brand=after, factory=安徽, price=200000.0) ++++++ ------ 4.使用容器中的bean对象Truck(brand=after, factory=安徽, ------ 5.Truck destroy 销毁方法 销毁对象 ------
1.2.3 BeanPostProcesso
参考博客: BeanPostProcessor简介
BeanPostProcessor官方定义为工厂钩子,我们也俗称 后置处理器 。它 允许自定义修改新的bean实例 ,例如检查标记接口或用代理包装它们。应用程序上下文可以在其bean定义中自动检测BeanPostProcessor bean,并将它们应用于随后创建的任何bean。
BeanPostProcessor 的 前置处理 和 后置处理 ;
 +自动装配(注解).assets/image-20220826150821502.png)
2、自动装配(xml)
2.1 bean 实体类
Person
@Data public class Person { //昵称 private String nickName; //车子 private Car car; //房子 private House house; }
Car
@Data public class Car { //品牌 private String brand; //厂商 private String factory; //价格 private Double price; }
House
@Data public class House { //户型 private String type; //面积 private double area; //价格 private Integer price; }
2.2 bean 配置 (byType)
autowire="byType":根据 属性 的 类型 自动装配;
spring-auto.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"> <!-- spring的自动装配方式,基于xml配置文件方式,掌握 --> <!-- 容器中实例化一个容器的Car对象 --> <bean id="car" class="com.kgc.spring.auto.Car"> <property name="brand" value="Bnw520"></property> <property name="factory" value="华晨"></property> <property name="price" value="450000"></property> </bean> <!-- 容器中实例化一个容器的House对象 --> <bean id="house" class="com.kgc.spring.auto.House"> <property name="type" value="三室一厅"></property> <property name="area" value="96"></property> <property name="price" value="2800000"></property> </bean> <!-- 根据类型自动装配 --> <bean id="person" class="com.kgc.spring.auto.Person" autowire="byType"> <property name="nickName" value="huayu"></property> </bean> </beans>
2.3 测试
public class TestSpringAutoUserXml { private ApplicationContext context; @Before public void initApplicationContext(){ context = new ClassPathXmlApplicationContext("spring-auto.xml"); } @Test public void testSpringAuto(){ Person person = context.getBean("person", Person.class); //使用对象 System.out.println("容器中的person对象:"+person); } }
输出结果:
容器中的person对象:Person( nickName=huayu, car=Car(brand=Bnw520, factory=华晨, price=450000.0), house=House(type=三室一厅, area=96.0, price=2800000) )
2.4 bean 配置 (多个同类型bean)
bean 配置:
其他不变,多增加一个Car类型的实例bean;
<bean id="car" class="com.kgc.spring.auto.Car"> <property name="brand" value="Bnw520"></property> <property name="factory" value="华晨"></property> <property name="price" value="450000"></property> </bean> <bean id="carNew" class="com.kgc.spring.auto.Car"> <property name="brand" value="AudiA6"></property> <property name="factory" value="一汽"></property> <property name="price" value="450000"></property> </bean>
测试,报错信息:
No qualifying bean of type 'com.kgc.spring.auto.Car' available: expected single matching bean but found 2: car,carNew
总结
:autowire="byType" 当有 多个相同类型的bean时
, 无法确定
要装配的 bean;
2.5 bean 配置(byName)
其他配置信息不变,设置 autowire="byName" ,根据 属性 的 名字 自动装配;
<bean id="person" class="com.kgc.spring.auto.Person" autowire="byName"> <property name="nickName" value="hauyu"></property> </bean>
测试输出结果:
容器中的person对象:Person( nickName=huayu, car=Car(brand=Bnw520, factory=华晨, price=450000.0), house=House(type=三室一厅, area=96.0, price=2800000) )
总结
:
-
byType:根据类型自动装配:
- 根据实体属性的 类型 ,到容器中,根据 bean类型 进行唯一匹配,如果可以匹配到对应类型的bean的实例,就会执行自动装配, 如果不能唯一匹配(同类型的bean有多个),会报错;
-
byName: 根据名称自动装配:
- 根据 属性 的 属性名 ,到容器中,根据 bean的id 属性值,进行唯一匹配,如果能够成功匹配,执行自动装配, 如果匹配不到,不执行自动装配,实体属性为null;
3、自动装配 (注解)
3.1 注解
- @Component 普通组件注解;
- @Repository 持久层注解
- @Service 业务层注解
- @Controller 控制层注解
3.3.1 注解的原理
默认情况下:spring自动将分层组件(@Controller,@Service,@Repository,@component) 标识的类 (不是接口), 自动创建实例对象放入容器 中,使用bean的标识 id值为 对应 类名首字母小写 就相当于,帮我们手动添加了配置 :
<bean id="分层注解标识类的类名首字母小写" class="分层注解标识类的全类名"> ... <bean>
3.1.2 自定义id 属性
如果 不想使用默认的类名首字母小写 ,我们可以使用注解的value属性执行一个自定义的id值 ;
比如:@Service(value="自定义的id值"),注解只有value属性,可以省略value执行,简化为@Service("自定义的id值")
3.1.3 分层组件的目的
分层组件的目的,就仅仅是为了 方便开发人员 明确当前注解 所在的类所对应的角色 ,在使用上, 建议使用 ,按照官方定义的使用,防止模糊不清; 在springMVC框架中@Controller有特殊含义;
3.2 配置文件
spring创建容器对象时,如果解析到 component-scan 组件扫描配置,会将base-package指定的基包(父包)及其子包所有增加了分层组件的类,自动创建实例,放进容器中;
配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!-- 组件扫描:注解标识的组件,必须通过组件扫描配置,才可以添加到spring的容器中--> <context:component-scan base-package="com.kgc.spring.acnocation" > </context:component-scan> </beans>
3.3 默认id属性 测试
3.3.1 实体
@Component public class User { //用户名 @Value("huayu") //@Value() 自动装配参数 private String userName; //用户密码 @Value("123") private String userPwd; }
3.3.2 测试
@Test public void testSpringAutoUserAnnotation(){ User user = context.getBean("user", User.class); System.out.println(user); //User(userName=huayu, userPwd=123) }
3.4 自定义id属性 测试
3.4.1 实体(自定义id属性)
@Component(value = "myUser") public class User { //用户名 private String userName; //用户密码 private String userPwd; }
3.4.2 测试
@Test public void testSpringAutoUserAnnotation(){ //User user = context.getBean("user", User.class); //自定义id后,默认id不能使用: No bean named 'user' available //必须使用自定义id User user = context.getBean("myUser", User.class); System.out.println(user); //User(userName=huayu, userPwd=123) }
3.5 自动装配
3.5.1 @Autowired
- 组件自动装配,可以实现 实体属性类型的自动装配 ,自动到spring的容器中,根据当前 属性的类型或者名称进行注入 ,如果容器中 能匹配到 ,就 直接 将实例对象 注入到当前实体属性上 ,无序手动指定;
-
@Autowired自动装配原理:首先会根据byType方式,进行自动装配,
- 如果 不能唯一匹配 (存在同类型多个实例对象),会再次 尝试使用byName方式 ,根据当前实体属性名,到容器中进行匹配(容器中bean的id值),如果能唯一匹配,直接执行自动装配,
-
默认情况下,@Autowired注解标识的实体属性,必须被装配
- 如果 装配失败 ,就直接 抛出异常;
- 如果 不需要校验必须被装配 (项目启动,如果装配失败,项目是起不来);
- 通过指定 required = false ,去除必须执行自动装配的校验(即便容器中找不到装配的实例,也不会抛出异常);
- 如果自动装配,容器中 存在多个同类型的bean对象 ,可以使用注解@Qualifier("容器中同类型多个bean的某个id值"), 实现指定到容器中,找对应的bean实例对象 ,进行自动装配;
- 底层是如何做的:在指定要扫描的包时,<context:component-scan> 元素会 自动注册一个bean的后置处理器 :AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以 自动装配标记了@Autowired、@Resource或@Inject注解的属性 。
3.5.2 实体
People
@Data @Component("myPeople") public class People { //昵称 @Value("huayu") private String name; //玩具 @Autowired private Toy toy; }
Toy接口
public interface Toy { //得到玩具 public void getToy(); }
ToyImpl1
@Component("toy1") public class ToyImpl1 implements Toy { @Value("玩具车") private String toyName; @Override public void getToy() { System.out.println(this.toyName); } }
3.5.3 测试
注意
:可以 通过接口类型获取实现类
(推荐使用);
@Test public void testAutowired (){ People people = context.getBean("myPeople", People.class); people.getToy().getToy(); //玩具车 }
3.5.4 存在多个相同类型的bean
当存在 多个相同类型的bean , 不能唯一匹配 ,会 自动装配错误 ;
在写一个Toy实现类,ToyImpl2
@Component("toy2") public class ToyImpl2 implements Toy { @Value("尤克里里") private String toyName; @Override public void getToy() { System.out.println(this.toyName); } }
3.5.4.1 测试
报错信息(项目无法启动):
No qualifying bean of type 'com.kgc.spring.acnocation.bean.Toy' available: expected single matching bean but found 2: toy1,toy2
主要信息:类型无法唯一匹配;
3.5.4.2 required = false 允许不装配
People
@Data @Component("myPeople") public class People { //昵称 @Value("huayu") private String name; //玩具 @Autowired (required = false) private Toy toy; }
项目可以启动但是还是报错(一般项目中不会有两个相同类型的实现类;)
3.5.4.3 @Quailfy
People
@Data @Component("myPeople") public class People { //昵称 @Value("huayu") private String name; //玩具 @Autowired @Qualifier("toy2") //指定bean的id值 private Toy toy; }
3.5.4.4 测试
@Test public void testAutowired (){ People people = context.getBean("myPeople", People.class); System.out.println(people.getToy()); //com.kgc.spring.acnocation.bean.ToyImpl2@15d9bc04 people.getToy().getToy(); //尤克里里 }
3.6 指定扫描 排除扫描
3.6.1 指定扫描
include-filter
指定扫描(包含扫描):
- 只会扫描指定的 类 或者 某类组件 (使用分组扫描), 加入 到容器中;
- 但是 必须配合 父标签的 user-default-filter 使用,默认值是true,就是全部扫描;
- 指定扫描,如果要生效 必须改为false ;
- 指定扫描 某类组件 ,type="annotation" expression="某类组件注解的全类名";
- 指定扫描 某个类 ,type="assignable" expression="某个类的全类名";
3.6.1.1 指定扫描某类组件
type="annotation"
- org.springframework.stereotype.Component
- org.springframework.stereotype.Repository
- org.springframework.stereotype.Service
- org.springframework.stereotype.Controller
<!-- 指定扫描 @Component 组件 --> <context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="false" > <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
3.6.1.2 指定扫描某个类
type="assignable"
<!-- 指定扫描 ToyImpl1 --> <context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="false" > <context:include-filter type="assignable" expression="com.kgc.spring.acnocation.bean.ToyImpl1"/> </context:component-scan>
3.6.2 排除扫描
exclude-filter
- 排除扫描(剔除扫描):排除指定的 类 或 某类组件 , 不加入 到容器中,处理排除外的其他组件,仍然会被添加到容器中;
- 不需要配合父标签,use-default-filters="true" 因为,默认就是在全部扫描的基础上剔除;
- 排除扫描 某类组件 ,type="annotation" expression="某类组件注解的全类名";
- 排除扫描 某个类 ,type="assignable" expression="某个类的全类名";
3.6.2.1 排除扫描某类组件
type="annotation"
<!-- use-default-filters="true" 可写可不写 --> <!-- 排除扫描 @Component组件 --> <context:component-scan base-package="com.kgc.spring.acnocation" use-default-filters="true" > <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
3.6.2.2 排除扫描个类
type="assignable"
<!-- 排除扫描 ToyImpl1 --> <context:component-scan base-package="com.kgc.spring.acnocation" > <context:exclude-filter type="assignable" expression="com.kgc.spring.acnocation.bean.ToyImpl1"/> </context:component-scan>
- 线程池底层原理详解与源码分析
- 30分钟掌握 Webpack
- 线性回归大结局(岭(Ridge)、 Lasso回归原理、公式推导),你想要的这里都有
- 【前端必会】webpack loader 到底是什么
- 中心化决议管理——云端分析
- HashMap底层原理及jdk1.8源码解读
- 详解JS中 call 方法的实现
- 打印 Logger 日志时,需不需要再封装一下工具类?
- 初识设计模式 - 代理模式
- 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码
- Springboot之 Mybatis 多数据源实现
- CAS核心思想、底层实现
- 面试突击86:SpringBoot 事务不回滚?怎么解决?
- 基于electron vue element构建项目模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和数据简单、快速生成文件。
- 认识线程,初始并发
- 1-VSCode搭建GD32开发环境
- 初识设计模式 - 原型模式
- 线程安全问题的产生条件、解决方式
- 2>&1到底是什么意思?