转载自: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
定义**BeanForRequest
request
**,在类型中定义destroy()
方法,方法上加@PreDestory
注解,代码如下:
@Slf4j
@Scope("request")
@Component
public class BeanForRequest {
@PreDestroy
public void destory() {
log.debug("destroy");
}
}
定义**BeanForSession
session
**,在类型中定义destroy()
方法,方法上加@PreDestory
注解,代码如下:
@Slf4j
@Scope("request")
@Component
public class BeanForRequest {
@PreDestroy
public void destory() {
log.debug("destroy");
}
}
定义**BeanForApplication
application
**,在类型中定义destroy()
方法,方法上加@PreDestory
注解,代码如下:
@Slf4j @Scope("request") @Component public class BeanForRequest