接上一篇文章,末尾提到了spring的bean的加载过程,那我们今天就来剖析一下spring中bean的加载过程
开始之前
在讲spring之前呢,我们首先来了解一下spring这个领域内一些常见的概念
BeanDefinition
我们经常把spring成为spring容器,通俗一点理解,容器里是需要装水的,那么需要装什么样的水,即我们需要怎么样定义我们的Bean,根据面向对象的思想,就需要抽象出一些Bean的通用属性和方法,于是乎引出第一个概念BeanDefinition.来看看spring是怎么解释BeanDefinition的.
再看看BeanDefinition中定义了哪些属性和方法
可以看到我们常见的一些方法,例如
- getScope
- isLazyInit
- isPrimary
- isPrototype
- getDependsOn
这些不就是我们在bean里面可以配置的属性吗,什么没见过?那我们来试一试
@Configuration
@Scope
@Lazy
@DependsOn
@Primary
public class MyBeanDefinition {
}
用这些注解直接加在对应的Bean上就可以了,有人就问了这些注解都什么意思,干什么使的,稍安勿燥,后面我们都会一一解释
BeanFactory
接下来说一下BeanFactory,同样先看看源码对BeanFactory的解释
什么意思呢,其实看BeanFactory里面的描述也可以理解,源码解释中很重要的一段(很重要,面试问!很重要,面试问!很重要,面试问!)
翻译过来:Bean工厂实现应尽可能支持标准Bean生命周期接口。全套初始化方法及其标准顺序为以下顺序,顺序如图所示
总结一下:
- 执行Aware接口相关方法
- 执行BeanPostProcessors中的postProcessBeforeInitialization
- 执行InitializingBean接口中的afterPropertiesSet
- 执行init-method方法
- 执行BeanPostProcessor中postProcessAfterInitialization
- 执行DisposableBean中的destroy方法
- 执行destory-method
顺水推舟,下面分别介绍一下Aware,BeanPostProcessor这两接口
Aware
同样,首先来看Aware接口是来干嘛的,翻源码看注释咯,解释如下
说的这么抽象,到底Aware该怎么玩,看看具体子接口ApplicationContextAware及其用法
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
一起康康方法注释什么意思
再来看看怎么应用的
@Component
public class UseAware implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void testUseAware(){
Object a = applicationContext.getBean("a");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
可以看到UseAware实现了ApplicationContextAware接口,并在testUseAware中使用了ApplicationContext相关方法,可以通俗点理解XXXAware接口一般都是赋能功能,实现了该接口就可以拥有相关能力.同样可以实现上述功能的另外一种写法
@Component
public class UseAware {
@Autowired
private ApplicationContext applicationContext;
public void testUseAware(){
Object a = applicationContext.getBean("a");
}
}
上面两种方式可以根据自己的喜好来进行选择,常用的XXXAware接口如下
- ApplicationContextAware
- EnvironmentAware
- BeanNameAware
- BeanFactoryAware
上面几个XXXAware具体赋予了bean什么能力,可以查看源码,并尝试自己使用下(莫名课后作业)
BeanPostProcessor
好了,终于到了动人心弦的BeanPostProcessor,同样的套路,先来看看源码是怎么解释BeanPostProcessor
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
源码对BeanPostProcess解释如下
核心就四个大字:修改新Bean.简单的理解下就是为我们的bean锦上添花,后面特别强调了两功能检查标记界面或者使用代理包装,(注意!代理是利用BeanPostProcess实现的)
观察源码我们看到BeanPostProcess有两个方法,一个postProcessBeforeInitialization,一个postProcessAfterInitialization,那么这两个方法的使用场景是什么呢?别着急,源码给你答案
总结一下:
- beforeXXX 填充bean
- afterXXX 代理包装bean
那么具体的应用呢?首当其冲的就是spring的AOP了,这个是标准的BeanPostProcess的应用,后面小帅会出一篇关于AOP的文章,那先让我们简单的康康AOP返回代理Bean的时机
一个AOP比较核心的类AnnotationAwareAspectJAutoProxyCreator和它的继承结构图
而在bean加载的过程中,createBean方法中有一段如下方法
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...略去一些代码...
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
看到resolveBeforeInstantiation方法上面有一行注释,翻译如下:给BeanPostProcessors一个返回代理而不是目标bean实例的机会。意思就是在doCreateBean之前会给BeanPostProcessors一个返回代理bean的机会,那再来看看resolveBeforeInstantiation方法中做了什么事情
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
哇偶,是不是代理就在这里被加载了呢,AOP是不是就在在起作用了,别高兴太早,很遗憾的告诉你,并不是,首先从注释上来说,给代理一个机会去加载bean,机会是给你了,可是你并不中用啊(手动滑稽),其次我们再来分析一下第一次为什么代理在这里没有生效,来看关键的代码
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
结果论:如果bean!=null 那么则返回代理后的bean,那就说明bean为null了,才进行下面的步骤,第一次加载bean的时候,bean还没有正儿八经的创建呢,所以bean肯定为null了。 上面是结果论,不相信的话可以自己跟源码来确认。
让我们在讨论下AOP返回代理bean的时机,先来看看一个大致的spring创建bean的思维导图(如果需要全流程的思维导图,可以加小帅联系方式)
可以看到,spring首先会实例化bean,它会选择一个最合适的构造函数来进行bean的实例化,接下来是填充bean(简单解释一下填充bean,譬如我们用@Autowired,@Value注入的属性),接下来会进行实例化bean,在实例化bean的时候有如图所示的几个步骤,首先会执行aware接口里面的相关方法,然后在执行initMethod,initMethod指的是什么呢
- @PostConstruct注解标注的方法
- bean实现了InitializingBean,那么就是afterPropertiesSet方法
- bean定义中的initMethod
哦豁,这么多,那么他们的执行顺序呢,talk is cheap, show me the code
public class Student implements InitializingBean {
private String name;
private Integer age;
public void initMethod() {
System.out.println("initMethod");
}
public void destroyMethod() {
System.out.println("destroyMethod");
}
@PostConstruct
public void postConstruct() {
System.out.println("postConstruct");
}
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet");
}
}
public class ConfigBean {
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public Student student(){
return new Student();
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean.class);
Student student = (Student)context.getBean("student");
System.out.println(student);
}
}
执行后结果如下图所示
可以看到结果与小帅写的顺序保持一致。啥你问为什么顺序是这样的?源码就是这样写的啊,哈哈
上面就是initMethod的执行,忘记了说什么的同学可以往上面翻一翻再来看下面的内容,在执行initMethod之前之后会分别执行BeanPostProcessors的postProcessBeforeInitialization与postProcessAfterInitialization方法,而我们的AOP第一次就是在postProcessAfterInitialization这里生效的
好了,BeanPostProcessor大概就先说这么多,后面在讲Bean实例化过程的时候会细讲细节
Listener
接下来说一下spring里另外一个比较核心的概念--事件,类似与观察者模式吧,三个角色
- 观察者
- 被观察者
- 事件 一句话总结下:观察者在被观察者干了A事件后做了B,大概就是这个意思吧 talk is cheap, show me the code,让我们来看看spring怎么应用
首先定义我们的消息体
@Data
public class SimpleEvent extends ApplicationEvent {
/**
* 定义业务属性,进行传递,如果传递的参数多,可以使用对象
*/
private String businessAttr;
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public SimpleEvent(Object source) {
super(source);
}
}
在者是我们的消息监听Listener,可以看到ApplicationEvent后有范型,范型是我们定义的消息体
@Component
public class SimpleEventListener implements ApplicationListener<SimpleEvent> {
public void onApplicationEvent(SimpleEvent event) {
System.out.println(event.getSource());
}
}
我们的业务代码中是这样使用的
@Component
public class BusinessService implements ApplicationEventPublisherAware {
ApplicationEventPublisher applicationEventPublisher;
public void method1(){
SimpleEvent simpleEvent = new SimpleEvent(new Object());
simpleEvent.setBusinessAttr("111333");
applicationEventPublisher.publishEvent(simpleEvent);
}
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
我们的Main函数是这样的
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.service.spring.listener");
BusinessService businessService = (BusinessService)context.getBean("businessService");
businessService.method1();
}
}
上面代码这么眼花缭乱的,业务方法还得实现接口,这不扯淡吗,记不住啊,不屑用这个listener,那么简单的方法来了 Event对象同上,一点变化都没,再来看看业务对象
@Component
public class BusinessService {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void method1(){
SimpleEvent simpleEvent = new SimpleEvent(new Object());
simpleEvent.setBusinessAttr("111333");
applicationEventPublisher.publishEvent(simpleEvent);
}
@EventListener(SimpleEvent.class)
public void method2(){
System.out.println("another event listener method");
}
}
Main方法同上
我们看到定了个method2方法,然后加上了@EventListener(SimpleEvent.class),这样就可以用了,不用实现多余的接口就可以实现监听
写在最后
上面大致介绍了spring领域内的几个概念,这些都比较基础。要想真正领略spring的魅力,那就得抱着源码分析了,可是源码实在太庞大,之前小帅一直嘲笑视频里讲spring的时候说这个步骤太复杂,并且对我们分析加载过程没什么用,所以跳过,你只要理解是什么意思就行。对此我嗤之以鼻,想着说等我写篇文章或者出个视频一定要把这些细节讲清楚,当你正儿八经自己去理这些东西然后想表达出来的时候,确实,非核心步骤确实可以略过,不过这不代表着小帅不会在以后的文章中说清楚这些事情。我是小帅,一个混迹在互联网公司的复制粘贴工程师,我们下一期再见!