前言
还记得我在这篇博文:【小家Spring】Spring容器(含父子容器)的启动过程源码级别分析(含web.xml启动以及全注解驱动,和ContextLoader源码分析)
里留了几个非常重要,但是没有解释的方法。其中有一个非常重要的方法ApplicationContext#refresh()方法就是提到两次但都暂时忽略了(因为有父子容器,所以会刷新两次容器~)
refresh()方法是Spring容器启动的核心中的核心,逻辑也是异常的复杂,因为准备分两篇文章来叙述他的过程,以及源码的分析
Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)
Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)
Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)
Spring Bean声明周期流程图
Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。
我把这张图放在最开始的位置,初看可能觉得一脸懵逼,但是相信在接下来的阅读过程中,会一步一步的柳暗花明的~
简单分类如下:
- 工厂后置处理器(BeanFactoryPostProcessor):这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器 接口的方法。工厂后处理器也是容器级的。
- 容器级别生命周期处理器:包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”
- Bean级生命周期接口方法(仅作用于某个Bean):这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法
- Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中<bean>的init-method和destroy-method指定的方法
初识
因为我们项目的案例是全注解驱动的,因此我们的容器实例为:AnnotationConfigWebApplicationContext
(若你是xml配置驱动,则为XmlWebApplicationContext)
为了更方便了解容器类的继承关系,贴出如下类继承图:
refresh()方法所有的ApplicationContext子类都没重写,只有AbstractApplicationContext里有实现过(接口定义在ConfigurableApplicationContext),因此我们看起来也容易了,直接上源码~
refresh()源码 宏观步骤说明
此部分源码,把容器的刷新步骤体现得非常的清晰,十足的面向对象编程。因此我会保留Spring 英文备注的同事,把一些备注写在源码上~~~ 详细的下面都会具体再解释
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //容器刷新前的准备,设置上下文状态,获取属性,验证必要的属性等 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 获取新的beanFactory,销毁原有beanFactory、为每个bean生成BeanDefinition等 注意,此处是获取新的,销毁旧的,这就是刷新的意义 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //配置标准的beanFactory,设置ClassLoader,设置SpEL表达式解析器等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //模板方法,允许在子类中对beanFactory进行后置处理。 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //实例化并调用所有注册的beanFactory后置处理器(实现接口BeanFactoryPostProcessor的bean)。 //在beanFactory标准初始化之后执行 例如:PropertyPlaceholderConfigurer(处理占位符) invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //实例化和注册beanFactory中扩展了BeanPostProcessor的bean。 //例如: //AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入) //RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法) //CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等。 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //初始化国际化工具类MessageSource initMessageSource(); // Initialize event multicaster for this context. //初始化事件广播器 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //模板方法,在容器刷新的时候可以自定义逻辑(子类自己去实现逻辑),不同的Spring容器做不同的事情 onRefresh(); // Check for listener beans and register them. //注册监听器,并且广播early application events,也就是早期的事件 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //非常重要。。。实例化所有剩余的(非懒加载)单例Bean。(也就是我们自己定义的那些Bean们) //比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化 扫描的 @Bean之类的 //实例化的过程各种BeanPostProcessor开始起作用~~~~~~~~~~~~~~ finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //refresh做完之后需要做的其他事情 //清除上下文资源缓存(如扫描中的ASM元数据) //初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。 //发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. //如果刷新失败那么就会将已经创建好的单例Bean销毁掉 destroyBeans(); // Reset 'active' flag. //重置context的活动状态 告知是失败的 cancelRefresh(ex); // Propagate exception to caller. //抛出异常 throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... // 失败与否,都会重置Spring内核的缓存。因为可能不再需要metadata给单例Bean了。 resetCommonCaches(); } } }
通过上面的注释,已经能够比较宏观的了解到容器的一个初始化过程了,那么接下来,将针对每一个步骤,进行微观源码级别的解释说明。
refresh() 具体源码解析
refresh() 第一步:prepareRefresh()
protected void prepareRefresh() { //记录容器启动时间,然后设立对应的标志位 this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); // 打印info日志:开始刷新this此容器了 if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment // 这是扩展方法,由子类去实现,可以在验证之前为系统属性设置一些值可以在子类中实现此方法 // 因为我们这边是AnnotationConfigApplicationContext,可以看到不管父类还是自己,都什么都没做,所以此处先忽略 initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties //这里有两步,getEnvironment(),然后是是验证是否系统环境中有RequiredProperties参数值 如下详情 // 然后管理Environment#validateRequiredProperties 后面在讲到环境的时候再专门讲解吧 // 这里其实就干了一件事,验证是否存在需要的属性 getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... // 初始化容器,用于装载早期的一些事件 this.earlyApplicationEvents = new LinkedHashSet<>(); }
AbstractApplicationContext#getEnvironment()
关于getEnvironment()的顶层接口位于:EnvironmentCapable,有如下实现(注意ConfigurableApplicationContext是接口,所以其实上容器的实现只有AbstractApplicationContext):
Demo代码如下:
@Autowired private BeanFactory beanFactory; @Autowired private ApplicationContext applicationContext; @Autowired private ApplicationEventPublisher applicationEventPublisher; //web环境下 @Autowired private HttpServletRequest request; @Autowired private HttpServletResponse response; @Autowired private HttpSession session; @Autowired private WebRequest webRequest; @Override public Object hello() { System.out.println(beanFactory); //org.springframework.beans.factory.support.DefaultListableBeanFactory@3ff27f35 System.out.println(applicationContext); //Root WebApplicationContext: startup date [T ... System.out.println(applicationEventPublisher); //Root WebApplicationContext: startup date [T ... // 我们发现的是同一个Bean System.out.println(System.identityHashCode(applicationEventPublisher) == System.identityHashCode(applicationContext)); //true //web环境 // =================必须说明一点:这里注入的所有web对象,都是线程安全的================= // 请求N次,每次输出的HashCode都是一样的,那怎么还没有线程安全问题呢?具体看下面分解原因 System.out.println(System.identityHashCode(request)); System.out.println(request.getClass()); //class com.sun.proxy.$Proxy22 这是个代理对象哟~~~~ System.out.println(request); //Current HttpServletRequest System.out.println(response); //Current HttpServletResponse System.out.println(session); //Current HttpSession System.out.println(webRequest); //Current ServletWebRequest return "service hello"; }
@Autowire注入HttpServletRequest为何是线程安全的?
现状:我看到很多同事,还有小伙伴们在Controller层想要使用Servlet源生API比如HttpServletRequest的时候,让方法入参了去写,当你的Controller方法多了后(这是必然的),会让代码看起来十分的不优雅(重复工作太多)。
原因:把request放在方法入参里也不无道理。因为我们常识性的认为:由于Controller是单例的,所以直接放在全局属性上,理论上肯定是有线程安全问题的。
迷惑点:为了证明这一点,然后每次请求都输出System.identityHashCode(request),发现HashCode是不变,因此可以确定的是,注入的request,肯定是同一个实例
线程安全的原因分析解释:
其实从上面一句request.getClass()或许能看出端倪的,我们发现注入的是JDK的动态代理对象class com.sun.proxy.$Proxy22(至于为何注入的是代理对象,请参考上面博文:细说Spring IOC容器的自动装配这一篇有详细的分析讲解,代理类的处理器为:ObjectFactoryDelegatingInvocationHandler),因此我们主要看看invoke方法如下:
(图片所示为该处理器的调用地方和基本源码:)
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) { // 如果注入到的值为ObjectFactory类型(并且不是requiredType实例),就猪呢比下面的代理吧~~~ if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) { ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue; //autowiringValue 显示是实现了Serializable 接口的 // 并且requiredType是个接口(HttpServletRequest是接口,继承自ServletRequest) // 所以此处注意了,只能根据接口进行注入才是线程安全的,如果注入实现类,线程就是不安全的(因为无法创建代理了) 但是显然我们不可能注入实现类的 if (autowiringValue instanceof Serializable && requiredType.isInterface()) { // 创建出来的代理对象,才是最终要被注入进去的值==== autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(), new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory)); } else { return factory.getObject(); } } return autowiringValue; }
ObjectFactoryDelegatingInvocationHandler#invoke
:调用request的方法,都被代理到此处
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0]); } else if (methodName.equals("hashCode")) { // Use hashCode of proxy. return System.identityHashCode(proxy); } else if (methodName.equals("toString")) { return this.objectFactory.toString(); } try { // 核心在这里,每次调用的方法,实际上调用的是objectFactory.getObject()这个对象的对应方法,那么这个对象源码呢? // beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); // 可以看出他是一个RequestObjectFactory类型,所以看下面getObject方法 return method.invoke(this.objectFactory.getObject(), args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } //RequestObjectFactory private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { // 它每次返回的是和当前线程上下文绑定的一个request副本。至于怎么和上下文绑定的,下面贴出参考链接 // 有了这些解释,那肯定的这样注入的是线程安全的,不用再担心了 @Override public ServletRequest getObject() { return currentRequestAttributes().getRequest(); } @Override public String toString() { return "Current HttpServletRequest"; } }