转载自:https://blog.csdn.net/qq_38505969/article/details/123739542
介绍
代码仓库地址:https://gitee.com/CandyWall/spring-source-study 跟着做学习笔记,这个笔记和视频内容的项目名称和代码略有不同。我把49个代码的每个代码拆分成独立的springboot项目,并且项目名称尽量做到了见名知意,都是基于我自己的考量,代码都已经过运行验证过的,仅供参考。
视频教程地址:https://www.bilibili.com/video/BV1P44y1N7QG
注:
1. 每个对应一个二级标题,每个三级标题都是用子项目名命名的,与我的代码仓库项目一一对应; 2. 使用代码lombok简化了插件Bean中的get()、set()使用方法和日志记录lombok的@Slf4j注解。
每个子项目对应的视频链接和一些重要内容的笔记
第六讲 Aware和InitializingBean接口以及@Autowired注解故障分析
spring_06_aware_initializingbean
p26 025-第六讲-Aware与InitializingBean接口
Aware 接口用于注入与容器相关的信息,例如:
a. BeanNameAware 注入 Bean 的名字
b. BeanFactoryAware 注入 BeanFactory 容器
c. ApplicationContextAware 注入 ApplicationContext 容器
d. EmbeddedValueResolverAware 注入 分析,分析${}
定义一个MyBean类,实现BeanNameAware、ApplicationContextAware和InitializingBean接口和实现方法,然后定义两种方法,其中一种是添加的@Autowired注解,注入ApplicationContext容器,另一个加@PostConstruct具体代码如下:
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
@Override public void setBeanName(String name) {
log.debug("当前bean:" this ",实现 BeanNameAware 调用方法,名称为:" name); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("当前bean:" this ",实现 ApplicationContextAware 调用方法,容器称:" applicationContext); } @Override public void afterPropertiesSet() throws Exception {
log.debug("当前bean:" + this + ",实现 InitializingBean 调用的方法,初始化");
}
@Autowired
public void aaa(ApplicationContext applicationContext) {
log.debug("当前bean:" + this +",使用 @Autowired 容器是:" + applicationContext);
}
@PostConstruct
public void init() {
log.debug("当前bean:" + this + ",使用 @PostConstruct 初始化");
}
}
测试代码:
public class TestAwareAndInitializingBean {
@Test
public void testAware1() throws Exception {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
context.refresh();
context.close();
}
}
运行结果:

加了@Autowired和@PostConstruct注解的方法并没有被执行,而Aware和InitializingBean接口方法都被执行了。
修改测试代码,把解析@Autowired和@PostConstruct注解的Bean后处理加进来,然后再运行一下
public class TestAwareAndInitializingBean {
@Test
public void testAware1() throws Exception {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
// 解析 @Autowired 注解的Bean后处理器
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
// 解析 @PostConstruct 注解的Bean后处理器
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.refresh();
context.close();
}
}
运行结果:
可以看到这下都执行了
有人可能会问:b、c、d的功能用 @Autowired注解就能实现啊,为啥还要用 Aware 接口呢? InititalizingBean 接口可以用 @PostConstruct注解实现,为啥还要用InititalizingBean呢? 简单地说:
-
@Autowired和@PostConstruct注解的解析需要用到Bean后处理器,属于扩展功能,而Aware接口属于内置功能,不加任何扩展,Spring就能识别; -
某些情况下,扩展功能会失效,而内置功能不会失效
p27 026-第六讲-@Autowired失效分析
-
例1:比如没有把解析
@Autowired和@PostStruct注解的Bean的后处理器加到Bean工厂中,你会发现用Aware注入ApplicationContext成功, 而@Autowired注入ApplicationContext失败 -
例2:定义两个
Java Config类(类上加@Configuration注解),名字分别叫MyConfig1和MyConfig2,都实现注入ApplicationContext容器和初始化功能,MyConfig1用@Autowired和@PostConstruct注解实现,MyConfig2用实现Aware和InitializingBean接口的方式实现,另外,两个Config类中都通过@Bean注解的方式注入一个BeanFactoryPostProcessor,代码如下:MyConfig1:@Slf4j public class MyConfig1 { @Autowired public void setApplicationContext(ApplicationContext applicationContext) { log.debug("注入 ApplicationContext"); } @PostConstruct public void init() { log.debug("初始化"); } @Bean public BeanFactoryPostProcessor processor1() { return beanFactory -> { log.debug("执行 processor1"); }; } }测试代码:
@Slf4j public class TestAwareAndInitializingBean { @Test public void testAware_MyConfig1() { GenericApplicationContext context = new GenericApplicationContext(); // MyConfig1没有加上@ context.registerBean("myConfig1", MyConfig1.class); // 解析 @Autowired 注解的Bean后处理器 context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // 解析 @PostConstruct 注解的Bean后处理器 context.registerBean(CommonAnnotationBeanPostProcessor.class); // 解析@ComponentScan、@Bean、@Import、@ImportResource注解的后处理器 // 这个后处理器不加出不来效果 context.registerBean(ConfigurationClassPostProcessor.class); // 1. 添加beanfactory后处理器;2. 添加bean后处理器;3. 初始化单例。 context.refresh(); context.close(); }运行结果:
MyConfig2:@Slf4j public class MyConfig2 implements ApplicationContextAware, InitializingBean { @Override public void setApplicationContext(ApplicationContext applicationContext) { log.debug("注入 ApplicationContext"); } @Override public void afterPropertiesSet() throws Exception { log.debug("初始化"); } @Bean public BeanFactoryPostProcessor processor1() { return beanFactory -> { log.debug("执行 processor1"); }; } }测试代码:
@Slf4j public class TestAwareAndInitializingBean { @Test public void testAutowiredAndInitializingBean_MyConfig2() { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("myConfig2", MyConfig2.class); // 1. 添加beanfactory后处理器;2. 添加bean后处理器;3. 初始化单例。 context.refresh(); context.close(); } }运行结果:
Java配置类在添加了
bean工厂后处理器后,你会发现用传统接口方式的注入和初始化依然成功,而@Autowired和@PostConstruct的注入和初始化失败。那是什么原因导致的呢?
配置类
@Autowired注解失效分析-
Java 配置类不包含
BeanFactoryPostProcessor的情况
-
-
总结:
Aware接口提供了一种【内置】 的注入手段,可以注入BeanFactory,ApplicationContext;InitializingBean接口提供了一种 【内置】 的初始化手段;- 内置的注入和初始化不收扩展功能的影响,总会被执行,因此
spring框架内部的类常用它们。
第七讲 Bean的初始化与销毁
spring_07_init_destroy
p28 027-第七讲-初始化与销毁
定义Bean1类,实现InitializingBean接口和对应的接口方法afterPropertiesSet(),再定义init1()方法,在方法上加@PostConstruct注解,最后定义init3()
@Slf4j
public class Bean1 implements InitializingBean {
@PostConstruct
public void init1() {
log.debug("初始化1,@PostConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
log.debug("初始化2,InitializingBean接口");
}
public void init3() {
log.debug("初始化3,@Bean的initMethod");
}
}
定义Bean2类,实现DisposableBean接口和对应的接口方法destroy(),再定义destroy1()方法,在方法上加@PreDestroy注解,最后定义init3()
@Slf4j
public class Bean2 implements DisposableBean {
@PreDestroy
public void destroy1() {
log.debug("销毁1,@PreDestory");
}
@Override
public void destroy() throws Exception {
log.debug("销毁2,DisposableBean接口");
}
public void destroy3() {
log.debug("销毁3,@Bean的destroyMethod");
}
}
定义Config类,类上加@Configuration注解,类中通过@Bean注解把Bean1和Bean2加到Bean工厂中,分别在@Bean注解中指定initMethod = "init3",destroyMethod = "destroy"
@Configuration
public class Config {
@Bean(initMethod = "init3")
public Bean1 bean1() {
return new Bean1();
}
@Bean(destroyMethod = "destroy3")
public Bean2 bean2() {
return new Bean2();
}
}
编写测试代码,观察三个初始化方法和三个销毁方法的执行顺序
@Slf4j
public class TestInitAndDestroy {
@Test
public void testInitAndDestroy() throws Exception {
// ⬇️GenericApplicationContext 是一个【干净】的容器,这里只是为了看初始化步骤,就不用springboot启动类进行演示了
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 解析@PostConstruct注解的bean后处理器
context.registerBean(CommonAnnotationBeanPostProcessor.class);
// 解析@Configuration、@Component、@Bean注解的bean工厂后处理器
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
context.close();
}
}
运行结果:
可以看到,spring提供了多种初始化和销毁手段
-
对于
init,三个初始化方法的执行顺序是@PostConstruct->InitializingBean接口 ->@Bean的initMethod -
对于
destory, 三个销毁方法的执行顺序是@PreDestroy->DisposableBean接口 ->@Bean的destroy
第八讲 Scope类型、注意事项、销毁和失效分析
spring_08_scope
p29 028-第八讲-Scope
spring的scope类型:
singleton:单例prototype:多例request:web请求session:web的会话application:web的ServletContext
测试scope类型中的request、session、application
定义**BeanForRequestrequest**,在类型中定义destroy()方法,方法上加@PreDestory注解,代码如下:
@Slf4j
@Scope("request")
@Component
public class BeanForRequest {
@PreDestroy
public void destory() {
log.debug("destroy");
}
}
定义**BeanForSessionsession**,在类型中定义destroy()方法,方法上加@PreDestory注解,代码如下:
@Slf4j
@Scope("request")
@Component
public class BeanForRequest {
@PreDestroy
public void destory() {
log.debug("destroy");
}
}
定义**BeanForApplicationapplication**,在类型中定义destroy()方法,方法上加@PreDestory注解,代码如下:
@Slf4j @Scope("request") @Component public class BeanForRequest