本文节选自《Spring 5外围原理》
Spring IoC容器还有一些高级个性,如应用lazy-init属性对Bean预初始化、应用FactoryBean产生或者润饰Bean对象的生成、IoC容器在初始化Bean过程中应用BeanPostProcessor后置处理器对Bean申明周期事件进行治理等。
1 对于延时加载
咱们曾经晓得,IoC容器的初始化过程就是对Bean定义资源的定位、载入和注册,此时容器对Bean的依赖注入并没有产生,依赖注入是在应用程序第一次向容器索取Bean时通过getBean()办法实现的。
当Bean定义资源的< bean>元素中配置了lazy-init=false属性时,容器将会在初始化时对所配置的Bean进行预实例化,Bean的依赖注入在容器初始化时就曾经实现。这样,当应用程序第一次向容器索取被治理的Bean时,就不必再初始化和对Bean进行依赖注入了,而是间接从容器中获取曾经实现依赖注入的Bean,进步了应用程序第一次向容器获取Bean的性能。
1.1. refresh()办法
IoC容器读入曾经定位的Bean定义资源是从refresh()办法开始的,咱们从AbstractApplicationContext类的refresh()办法动手剖析,回顾一下源码:
@Overridepublic void refresh() throws BeansException, IllegalStateException { ... //子类的refreshBeanFactory()办法启动 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ...}
在refresh()办法中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();启动了Bean定义资源的载入、注册过程。finishBeanFactoryInitialization()办法是对注册后的Bean定义中的预实例化(lazy-init=false,Spring默认进行预实例化,即为true)的Bean进行解决的中央。
1.2. 应用finishBeanFactoryInitialization()解决预实例化的Bean
当Bean定义资源被载入IoC容器之后,容器将Bean定义资源解析为容器外部的数据结构BeanDefinition,并注册到容器中,AbstractApplicationContext类中的finishBeanFactoryInitialization()办法对配置了预实例化属性的Bean进行预初始化,源码如下:
//对配置了lazy-init属性的Bean进行预实例化解决protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { //这是Spring 3新加的代码,为容器指定一个转换服务(ConversionService) //在对某些Bean属性进行转换时应用 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders (strVal)); } String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } //为了使类型匹配,停止使用长期的类加载器 beanFactory.setTempClassLoader(null); //缓存容器中所有注册的BeanDefinition元数据,以防被批改 beanFactory.freezeConfiguration(); //对配置了lazy-init属性的单例模式的Bean进行预实例化解决 beanFactory.preInstantiateSingletons();}
其中ConfigurableListableBeanFactory是一个接口,preInstantiateSingletons()办法由其子类DefaultListableBeanFactory提供。
1.3. 对配置了lazy-init属性的单例模式的Bean的预实例化
对配置了lazy-init属性的单例模式的Bean的预实例化相干源码如下:
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) { //获取指定名称的Bean定义 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); //Bean不是形象的,是单例模式的,且lazy-init属性配置为false if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果指定名称的Bean是创立容器的Bean if (isFactoryBean(beanName)) { //FACTORY_BEAN_PREFIX="&",当Bean名称后面加"&"符号 //时,获取的是容器对象自身,而不是容器产生的Bean //调用getBean办法,触发Bean实例化和依赖注入 final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); //标识是否须要预实例化 boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { //一个匿名外部类 isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> ((SmartFactoryBean<?>) factory).isEagerInit(), getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { //调用getBean()办法,触发Bean实例化和依赖注入 getBean(beanName); } } else { getBean(beanName); } } }
通过对lazy-init解决源码的剖析能够看出,如果设置了lazy-init属性,则容器在实现Bean定义的注册之后,会通过getBean()办法触发指定Bean的初始化和依赖注入。如前所述,这样当应用程序第一次向容器索取所需的Bean时,容器不再须要对Bean进行初始化和依赖注入,可间接从曾经实现实例化和依赖注入的Bean中取一个现成的Bean,进步了第一次获取Bean的性能。
2 对于FactoryBean和BeanFactory
Spring中,有两个很容易混同的类:BeanFactory和FactoryBean。
BeanFactory:Bean工厂,是一个工厂(Factory),Spring IoC容器的最高层接口就是BeanFactory,它的作用是治理Bean,即实例化、定位、配置应用程序中的对象及建设这些对象之间的依赖。
FactoryBean:工厂Bean,是一个Bean,作用是产生其余Bean实例。这种Bean没有什么特地的要求,仅须要提供一个工厂办法,该办法用来返回其余Bean实例。在通常状况下,Bean毋庸本人实现工厂模式,Spring容器负责工厂的角色;在多数状况下,容器中的Bean自身就是工厂,其作用是产生其余Bean实例。
当用户应用容器时,能够应用转义字符“&”来失去FactoryBean自身,以区别通过FactoryBean产生的实例对象和FactoryBean对象自身。在BeanFactory中通过如下代码定义了该转义字符:
String FACTORY_BEAN_PREFIX = "&";
如果myJndiObject是一个FactoryBean,则应用&myJndiObject失去的是myJndiObject对象,而不是myJndiObject产生的对象。
2.1. FactoryBean源码
//工厂Bean,用于产生其余对象public interface FactoryBean<T> { //获取容器治理的对象实例 @Nullable T getObject() throws Exception; //获取Bean工厂创立的对象的类型 @Nullable Class<?> getObjectType(); //Bean工厂创立的对象是否是单例模式的,如果是, //则整个容器中只有一个实例对象,每次申请都返回同一个实例对象 default boolean isSingleton() { return true; }}
2.2. AbstractBeanFactory的getBean()办法
在剖析Spring IoC容器实例化Bean并进行依赖注入的源码时,提到在getBean()办法触发容器实例化Bean时会调用AbstractBeanFactory的doGetBean()办法,其重要源码如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ... BeanFactory parentBeanFactory = getParentBeanFactory(); //以后容器的父容器存在,且以后容器中不存在指定名称的Bean if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { //解析指定Bean名称的原始名称 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { //委派父容器依据指定名称和显式的参数查找 return (T) parentBeanFactory.getBean(nameToLookup, args); } else { //委派父容器依据指定名称和类型查找 return parentBeanFactory.getBean(nameToLookup, requiredType); } } ... return (T) bean;}//获取给定Bean的实例对象,次要实现FactoryBean的相干解决protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { //容器曾经失去了Bean实例对象,这个实例对象可能是一个一般的Bean, //也可能是一个工厂Bean,如果是一个工厂Bean,则应用它创立一个Bean实例对象, //如果调用自身就想取得一个容器的援用,则返回这个工厂Bean实例对象 //如果指定的名称是容器的解援用(dereference,即对象自身而非内存地址) //且Bean实例也不是创立Bean实例对象的工厂Bean if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } //如果Bean实例不是工厂Bean,或者指定名称是容器的解援用 //调用者获取对容器的援用时,间接返回以后的Bean实例 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } //解决指定名称不是容器的解援用,或者依据名称获取的Bean实例对象是一个工厂Bean //应用工厂Bean创立一个Bean的实例对象 Object object = null; if (mbd == null) { //从Bean工厂缓存中获取指定名称的Bean实例对象 object = getCachedObjectForFactoryBean(beanName); } //让Bean工厂生产指定名称的Bean实例对象 if (object == null) { FactoryBean<?> factory = (FactoryBean<?>) beanInstance; //如果从Bean工厂生产的Bean是单例模式的,则缓存 if (mbd == null && containsBeanDefinition(beanName)) { //从容器中获取指定名称的Bean定义,如果继承了基类,则合并基类的相干属性 mbd = getMergedLocalBeanDefinition(beanName); } //如果从容器失去了Bean定义信息,并且Bean定义信息不是虚构的, //则让工厂Bean生产Bean实例对象 boolean synthetic = (mbd != null && mbd.isSynthetic()); //调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean()办法 //实现工厂Bean生产Bean实例对象的过程 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object;}
在下面获取给定Bean的实例对象的getObjectForBeanInstance()办法中,会调用FactoryBean- RegistrySupport类的getObjectFromFactoryBean()办法,该办法实现了Bean工厂生产Bean实例对象。
2.3. AbstractBeanFactory生产Bean实例对象
AbstractBeanFactory类中生产Bean实例对象的次要源码如下:
//Bean工厂生产Bean实例对象protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { //Bean工厂是单例模式,并且Bean工厂缓存中存在指定名称的Bean实例对象 if (factory.isSingleton() && containsSingleton(beanName)) { //多线程同步,以避免数据不统一 synchronized (getSingletonMutex()) { //间接从Bean工厂的缓存中获取指定名称的Bean实例对象 Object object = this.factoryBeanObjectCache.get(beanName); //如果Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象 if (object == null) { //调用Bean工厂的获取对象的办法生产指定Bean的实例对象 object = doGetObjectFromFactoryBean(factory, beanName); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { if (shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } } //将生产的实例对象增加到Bean工厂的缓存中 this.factoryBeanObjectCache.put(beanName, object); } } return object; } } //调用Bean工厂的获取对象的办法生产指定Bean的实例对象 else { Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; }}//调用Bean工厂的办法生产指定Bean的实例对象private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { //实现PrivilegedExceptionAction接口的匿名外部类 object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> factory.getObject(), acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //调用BeanFactory接口实现类的创建对象办法 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); } //创立进去的实例对象为null,或者因为单例对象正在创立而返回null if (object == null) { if (isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } object = new NullBean(); } return object;}
从下面的源码剖析中能够看出,BeanFactory接口调用其实现类的获取对象的办法来实现创立Bean实例对象的性能。
2.4. FactoryBean实现类的获取对象的办法
FactoryBean接口的实现类十分多,比方Proxy、RMI、JNDI、ServletContextFactoryBean等。FactoryBean接口为Spring容器提供了一个很好的封装机制,具体的获取对象的办法由不同的实现类依据不同的实现策略来提供,咱们剖析一下最简略的AnnotationTestFactoryBean类的源码:
public class AnnotationTestBeanFactory implements FactoryBean<FactoryCreatedAnnotationTestBean> { private final FactoryCreatedAnnotationTestBean instance = new FactoryCreatedAnnotationTestBean(); public AnnotationTestBeanFactory() { this.instance.setName("FACTORY"); } @Override public FactoryCreatedAnnotationTestBean getObject() throws Exception { return this.instance; } //AnnotationTestBeanFactory产生Bean实例对象的实现 @Override public Class<? extends IJmxTestBean> getObjectType() { return FactoryCreatedAnnotationTestBean.class; } @Override public boolean isSingleton() { return true; }}
Proxy、RMI、JNDI等其余实现类都依据相应的策略提供办法,这里不做一一剖析,这曾经不是Spring的外围性能,感兴趣的“小伙伴”能够自行深入研究。
3 再述autowiring
Spring IoC容器提供了两种治理Bean依赖关系的形式:
(1)显式治理:通过BeanDefinition的属性值和构造方法实现Bean依赖关系治理。
(2)autowiring:Spring IoC容器有依赖主动拆卸性能,不须要对Bean属性的依赖关系做显式的申明,只须要配置好autowiring属性,IoC容器会主动应用反射查找属性的类型和名称,而后基于属性的类型或者名称来主动匹配容器中的Bean,从而主动实现依赖注入。
容器对Bean的主动拆卸产生在容器对Bean依赖注入的过程中。在对Spring IoC容器的依赖注入源码进行剖析时,咱们曾经晓得容器对Bean实例对象的依赖属性注入产生在AbstractAutoWireCapableBeanFactory类的populateBean()办法中,上面通过程序流程剖析autowiring的实现原理。
3.1. AbstractAutoWireCapableBeanFactory对Bean实例对象进行属性依赖注入
应用程序第一次通过getBean()办法(配置了lazy-init预实例化属性的除外)向IoC容器索取Bean时,容器创立Bean实例对象,并且对Bean实例对象进行属性依赖注入,AbstractAutoWire- CapableBeanFactory的populateBean()办法就实现了属性依赖注入的性能,其次要源码如下:
//将Bean属性设置到生成的实例对象上protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {… //获取容器在解析Bean定义时为BeanDefinition设置的属性值 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); //解决依赖注入,首先解决autowiring主动拆卸的依赖注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); //依据Bean名称进行autowiring主动拆卸解决 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } //依据Bean类型进行autowiring主动拆卸解决 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } //对非autowiring的属性进行依赖注入解决 ...}
3.2. Spring IoC容器依据Bean名称或者类型进行autowiring主动属性依赖注入
Spring IoC容器依据Bean名称或者类型进行autowiring主动属性依赖注入的重要代码如下:
//依据类型对属性进行主动依赖注入protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //获取用户定义的类型转换器 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //寄存解析的要注入的属性 Set<String> autowiredBeanNames = new LinkedHashSet<>(4); //对Bean对象中非简略属性(不是简略继承的对象,如8种原始类型、字符、URL等都是简略属性)进行解决 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { //获取指定属性名称的属性形容器 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); //不对Object类型的属性进行autowiring主动依赖注入 if (Object.class != pd.getPropertyType()) { //获取属性的赋值办法 MethodParameter MethodParam = BeanUtils.getWriteMethodParameter(pd); //查看指定类型是否能够被转换为指标对象的类型 boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance()); //创立一个要被注入的依赖形容 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(MethodParam, eager); //依据容器的Bean定义解析依赖关系,返回所有要被注入的Bean对象 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { //将属性赋值为所援用的对象 pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { //为指定名称属性注册依赖Bean名称,进行属性的依赖注入 registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + autowiredBeanName + "'"); } } //开释已主动注入的属性 autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } }}
通过下面的源码剖析能够看出,通过属性名进行主动依赖注入相比通过属性类型进行主动依赖注入要略微简略一些。然而真正实现属性注入的是DefaultSingletonBeanRegistry类的registerDependentBean()办法。
3.3. DefaultSingletonBeanRegistry的registerDependentBean()办法实现属性依赖注入
DefaultSingletonBeanRegistry的registerDependentBean()办法实现属性依赖注入的重要代码如下:
//为指定的Bean注入依赖的Beanpublic void registerDependentBean(String beanName, String dependentBeanName) { //解决Bean名称,将别名转换为标准的Bean名称 String canonicalName = canonicalName(beanName); Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) { return; } //多线程同步,保障容器内数据的一致性 //在容器中通过“Bean名称→全副依赖Bean名称汇合”查找指定名称Bean的依赖Bean synchronized (this.dependentBeanMap) { //获取指定名称Bean的所有依赖Bean名称 dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { //为Bean设置依赖Bean信息 dependentBeans = new LinkedHashSet<>(8); this.dependentBeanMap.put(canonicalName, dependentBeans); } //在向容器中通过“Bean名称→全副依赖Bean名称汇合”增加Bean的依赖信息 //即,将Bean所依赖的Bean增加到容器的汇合中 dependentBeans.add(dependentBeanName); } //在容器中通过“Bean名称→指定名称Bean的依赖Bean汇合”查找指定名称Bean的依赖Bean synchronized (this.dependenciesForBeanMap) { Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName); if (dependenciesForBean == null) { dependenciesForBean = new LinkedHashSet<>(8); this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean); } //在容器中通过“Bean名称→指定Bean的依赖Bean名称汇合”增加Bean的依赖信息 //即,将Bean所依赖的Bean增加到容器的汇合中 dependenciesForBean.add(canonicalName); }}
能够看出,autowiring的实现过程如下:
(1)对Bean的属性调用getBean()办法,实现依赖Bean的初始化和依赖注入。
(2)将依赖Bean的属性援用设置到被依赖的Bean属性上。
(3)将依赖Bean的名称和被依赖Bean的名称存储在IoC容器的汇合中。
Spring IoC容器的autowiring主动属性依赖注入是一个很不便的个性,能够简化开发配置,然而凡事都有两面性,主动属性依赖注入也有有余:首先,Bean的依赖关系在配置文件中无奈很分明地看进去,会给保护造成肯定的艰难;其次,因为主动属性依赖注入是Spring容器主动执行的,容器是不会智能判断的,如果配置不当,将会带来无奈意料的结果。所以在应用主动属性依赖注入时须要综合思考。
关注微信公众号『 Tom弹架构 』回复“Spring”可获取残缺源码。
本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『 Tom弹架构 』可获取更多技术干货!
原创不易,保持很酷,都看到这里了,小伙伴记得点赞、珍藏、在看,一键三连加关注!如果你感觉内容太干,能够分享转发给敌人滋润滋润!