醒醒,该学spring了(一)

373 阅读9分钟

接上一篇文章,末尾提到了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的时候说这个步骤太复杂,并且对我们分析加载过程没什么用,所以跳过,你只要理解是什么意思就行。对此我嗤之以鼻,想着说等我写篇文章或者出个视频一定要把这些细节讲清楚,当你正儿八经自己去理这些东西然后想表达出来的时候,确实,非核心步骤确实可以略过,不过这不代表着小帅不会在以后的文章中说清楚这些事情。我是小帅,一个混迹在互联网公司的复制粘贴工程师,我们下一期再见!