配置类为何要添加@Configuration注解呢?html
本系列文章:java
推荐阅读:web
Spring官网阅读 | 总结篇spring
Spring杂谈数组
本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础!缓存
咱们先来看看若是不在配置类上添加@Configuration注解会有什么问题,代码示例以下:app
@ComponentScan("com.dmz.source.code") //@Configuration public class Config{ @Bean public A a(){ return new A(dmzService()); } @Bean public DmzService dmzService(){ return new DmzService(); } } public class A { public A(DmzService dmzService){ System.out.println("create A by dmzService"); } } @Component public class DmzService { public DmzService(){ System.out.println("create dmzService"); } }
不添加@Configuration注解运行结果:ide
create dmzService create A by dmzService create dmzService
添加@Configuration注解运行结果:
create dmzService create A by dmzService
在上面的例子中,咱们会发现没有添加@Configuraion注解时dmzService
被建立了两次, 这是由于第一次建立是被Spring容器所建立的,Spring调用这个dmzService()建立了一个Bean被放入了单例池中(没有添加其它配置默认是单例的),第二次建立是Spring容器在建立a时调用了a(),而a()又调用了dmzService()方法。
这样的话,就出现问题了。
第一,对于dmzService而言,它被建立了两次,单例被打破了
第二,对于a而言,它所依赖的dmzService不是Spring所管理的,而是直接调用的一个普通的java method建立的普通对象。这个对象不被Spring所管理意味着,首先它的域(Scope)定义失效了,其次它没有通过一个完整的生命周期,那么咱们所定义全部的Bean的后置处理器都没有做用到它身上,其中就包括了完成AOP的后置处理器,因此AOP也失效了。
上面的分析不能说服你的话,咱们能够看看官方在@Bean上给出的这一段注释
首先,Spring就在注释中指出了,一般来讲,BeanMethod通常都申明在一个被@Configuration注解标注的类中,在这种状况下,BeanMethod可能直接引用了在同一个类中申明的beanMethod,就像本文给出的例子那样,a()直接引用了dmzService(),咱们重点再看看划红线的部分,经过调用另一个beanMethod进入的Bean的引用会被保证是听从域定义以及AOP语义的,就像getBean所作的那样。这是怎么实现的呢?在最后被红线标注的地方也有说明,是经过在运行时期为没有被@Configuration注解标注的配置类生成一个CGLIB的子类。
Spring是在何时建立的代理呢?到目前为止咱们应该没有落掉Spring整个启动流程的任何关键代码,那么咱们不妨带着这个问题继续往下看。目前来讲咱们已经阅读到了Spring执行流程图中的3-5
步,也就是org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
方法,在以前的分析中咱们已经知道了,这个方法的主要做用就是执行BeanFactoryPostProcessor中的方法,首先执行的是BeanDefinitionRegistryPostProcessor(继承了BeanFactoryPostProcessor)的postProcessBeanDefinitionRegistry
方法,而后执行postProcessBeanFactory
方法。而到目前为止咱们并无向容器中注册bean工厂的后置处理器(BeanFactoryPostProcessor),这就意味着当前容器中只有一个ConfigurationClassPostProcessor会被执行,在前文中咱们已经分析过了它的postProcessBeanDefinitionRegistry
方法,紧接着咱们就来看看它的postProcessBeanFactory
方法作了什么。其源码以下:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); // 防止重复处理 if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); // 在执行postProcessBeanDefinitionRegistry方法的时就已经将这个id添加到registriesPostProcessed集合中了 if (!this.registriesPostProcessed.contains(factoryId)) { processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } // 看起来这个方法就是完成了代理 enhanceConfigurationClasses(beanFactory); // 添加了一个后置处理器 beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { // map中放置的是全部须要被代理的类 Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>(); for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) { // 省略异常跟日志代码.... // 这个代码的含义就是若是是一个被@Configuration注解标注的类,那么将其放入到configBeanDefs这个集合中 configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } if (configBeanDefs.isEmpty()) { // nothing to enhance -> return immediately return; } // 对配置类进行代理的核心类 ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // 对于配置类永远使用cglib代理 beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); try { // cglib代理是基于类实现的,因此在这以前要明确代理的类是什么 Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader); if (configClass != null) { // 经过ConfigurationClassEnhancer获取到一个通过代理的class Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); // 省略日志.... // 将原有的配置类的bd中的beanClass属性替换成代理后的class beanDef.setBeanClass(enhancedClass); } } } catch (Throwable ex) { throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex); } } }
这段代码很是简单,核心的代码在ConfigurationClassEnhancer
中,因此咱们要分析下ConfigurationClassEnhancer
的源码,在分析它的源码前,咱们须要对cglib有必定的了解。
public class Target{ public void f(){ System.out.println("Target f()"); } public void g(){ System.out.println("Target g()"); } } public class Interceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("I am intercept begin"); //Note: 此处必定要使用proxy的invokeSuper方法来调用目标类的方法 proxy.invokeSuper(obj, args); System.out.println("I am intercept end"); return null; } } public class Test { public static void main(String[] args) { // 设置这个属性,将代理类的字节码文件生成到F盘的code目录下 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code"); //实例化一个加强器,也就是cglib中的一个class generator Enhancer eh = new Enhancer(); //设置目标类 eh.setSuperclass(Target.class); // 设置拦截对象 eh.setCallback(new Interceptor()); // 生成代理类并返回一个实例 Target t = (Target) eh.create(); t.f(); t.g(); } }
运行结果为:
I am intercept begin Target f() I am intercept end I am intercept begin Target g() I am intercept end
查看F盘的code目录,会发现多了如下几个文件
其中第二个文件就是咱们的代理类字节码,将其直接用IDEA打开
// 省略多余的方法,咱们就关注g方法 public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory { final void CGLIB$g$0() { super.g(); } // 通过代理过的g方法 public final void g() { // 查看是否有拦截器存在 MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { CGLIB$BIND_CALLBACKS(this); tmp4_1 = this.CGLIB$CALLBACK_0; } // 若是有拦截器的存在的话,直接调用拦截器的方法 if (this.CGLIB$CALLBACK_0 != null) { tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy); } // 若是没有拦截器,说明不须要代理,直接调用父类方法,也就是目标类的方法 else{ super.g(); } } }
能够看到,代理类继承了目标类(Target),代理类为每一个目标类的方法生成两个方法,例如针对目标类中的每一个非private方法,代理类会生成两个方法,以g方法为例:一个是@Override的g方法,一个是CGLIB$g$0(CGLIB$g$0至关于目标类的g方法)。咱们在示例代码中调用目标类的方法t.g()时,实际上调用的是代理类中的g()方法。 从这里就能看出,跟JDK动态代理不一样的是,cglib代理采用的是继承的方式生成的代理对象。
在上面的例子中,咱们实现了对cglib中方法的拦截,可是就目前而言咱们没有办法选择性的拦截目标类中的某一个方法,假设如今咱们只想拦截Target中的g方法而不拦截f方法有什么方法呢?咱们看下面这个例子
public class Main { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code"); //实例化一个加强器,也就是cglib中的一个class generator Enhancer eh = new Enhancer(); //设置目标类 eh.setSuperclass(Target.class); // 设置拦截对象 eh.setCallbacks(new Callback[]{new Interceptor(), NoOp.INSTANCE}); eh.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { if(method.getName().equals("g")) // 这里返回的是上面定义的callback数组的下标,0就是咱们的Interceptor对象,1是内置的NoOp对象,表明不作任何操做 return 0; else return 1; } }); // 生成代理类并返回一个实例 Target t = (Target) eh.create(); t.f(); t.g(); } }
运行结果:
Target f() I am intercept begin Target g() I am intercept end
此时f方法已经不会被代理了
在对cglib的原理有了必定了解后,咱们再来看ConfigurationClassEnhancer
的源码就轻松多了
咱们就关注其中核心的几个方法,代码以下:
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) { // 若是已经实现了EnhancedConfiguration接口,说明被代理过了,直接返回 if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { return configClass; } // 不然调用newEnhancer方法先建立一个加强器,而后直接使用这个加强器生成代理类的字节码对象 Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader)); if (logger.isDebugEnabled()) { logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s", configClass.getName(), enhancedClass.getName())); } return enhancedClass; } private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) { Enhancer enhancer = new Enhancer(); // 设置目标类 enhancer.setSuperclass(configSuperClass); // 让代理类实现EnhancedConfiguration接口,这个接口继承了BeanFactoryAware接口 // 主要两个做用:1.起到标记做用,若是实现了,表明已经被代理过了 // 2.代理类须要访问BeanFactory,全部实现了BeanFactoryAware接口 enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); // 设置生成的代理类不实现factory接口 enhancer.setUseFactory(false); // 设置代理类名称的生成策略 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); // 代理类中引入一个BeanFactory字段 enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); // 设置过滤器,CALLBACK_FILTER中也同时设置了拦截器 enhancer.setCallbackFilter(CALLBACK_FILTER); enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); return enhancer; } // 使用加强器生成代理类的字节码对象 private Class<?> createClass(Enhancer enhancer) { Class<?> subclass = enhancer.createClass(); Enhancer.registerStaticCallbacks(subclass, CALLBACKS); return subclass; }
而且咱们会发现,在最开始这个类就申明了三个拦截器
// 声明的三个拦截器 private static final Callback[] CALLBACKS = new Callback[] { new BeanMethodInterceptor(), new BeanFactoryAwareMethodInterceptor(), NoOp.INSTANCE };
基于咱们以前对cglib的学习,确定能知道,代理的核心逻辑就是依赖于拦截器实现的。其中NoOp.INSTANCE
表明什么都没作,咱们就关注前面两个。
之因此把这个拦截器放到前面分析是由于这个拦截器的执行时机是在建立配置类的时候,其源码以下:
private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback { @Override @Nullable public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在生成代理类的字节码时,使用了BeanFactoryAwareGeneratorStrategy策略 // 这个策略会在代理类中添加一个字段,BEAN_FACTORY_FIELD = "$$beanFactory" Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD); Assert.state(field != null, "Unable to find generated BeanFactory field"); // 此时调用的方法是setBeanFactory方法, // 直接经过反射将beanFactory赋值给BEAN_FACTORY_FIELD字段 field.set(obj, args[0]); // Does the actual (non-CGLIB) superclass implement BeanFactoryAware? // If so, call its setBeanFactory() method. If not, just exit. // 若是目标配置类直接实现了BeanFactoryAware接口,那么直接调用目标类的setBeanFactory方法 if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) { return proxy.invokeSuper(obj, args); } return null; } @Override // 在调用setBeanFactory方法时才会拦截 // 从前文咱们知道,代理类是实现了实现EnhancedConfiguration接口的, // 这就意味着它也实现了BeanFactoryAware接口,那么在建立配置类时, // setBeanFactory方法就会被调用,以后会就进入到这个拦截器的intercept方法逻辑中 public boolean isMatch(Method candidateMethod) { return isSetBeanFactory(candidateMethod); } public static boolean isSetBeanFactory(Method candidateMethod) { return (candidateMethod.getName().equals("setBeanFactory") && candidateMethod.getParameterCount() == 1 && BeanFactory.class == candidateMethod.getParameterTypes()[0] && BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass())); } }
相比于上面一个拦截器,这个拦截器的逻辑就要复杂多了,咱们先来看看它的执行时机,也就是isMatch方法
public boolean isMatch(Method candidateMethod) { // 第一个条件,不能是Object,这个一定是知足的 // 第二个条件,不能是setBeanFactory方法,显而易见的嘛,咱们要拦截的方法实际只应该是添加了@Bean注解的方法 // 第三个条件,添加了@Bean注解 return (candidateMethod.getDeclaringClass() != Object.class && !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) && BeanAnnotationHelper.isBeanAnnotated(candidateMethod)); }
简而言之,就是拦截@Bean标注的方法,知道了执行时机后,咱们再来看看它的拦截逻辑,代码其实不是很长,可是理解起来确很不容易,牵涉到AOP以及Bean的建立了,不过放心,我会结合实例给你讲明白这段代码,下面咱们先看源码:
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable { // 以前不是给BEAN_FACTORY_FIELD这个字段赋值了BeanFactory吗,这里就是反射获取以前赋的值 ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); // 肯定Bean的名称 String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // Determine whether this bean is a scoped-proxy // 判断这个Bean是不是一个域代理的类 Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class); // 存在@Scope注解,而且开启了域代理模式 if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); // 域代理对象的目标对象正在被建立,何时会被建立?固然是使用的时候嘛 if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { // 使用的时候调用@Bean方法来建立这个域代理的目标对象,因此@Bean方法代理的时候针对的是域代理的目标对象,目标对象须要经过getBean的方式建立 beanName = scopedBeanName; } } // 判断这个bean是不是一个factoryBean if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) && factoryContainsBean(beanFactory, beanName)) { Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName); if (factoryBean instanceof ScopedProxyFactoryBean) { // ScopedProxyFactoryBean还记得吗?在进行域代理时使用的就是这个对象 // 对于这个FactoryBean咱们是不须要进行代理的,由于这个factoryBean的getObject方法 // 只是为了获得一个相似于占位符的Bean,这个Bean只是为了让依赖它的Bean在建立的过程当中不会报错 // 因此对于这个FactoryBean咱们是不须要进行代理的 // 咱们只须要保证这个FactoryBean所生成的代理对象的目标对象是经过getBean的方式建立的便可 } else { // 而对于普通的FactoryBean咱们须要代理其getObject方法,确保getObject方法产生的Bean是经过getBean的方式建立的 // It is a candidate FactoryBean - go ahead with enhancement return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName); } } // 举个例子,假设咱们被@Bean标注的是A方法,当前建立的BeanName也是a,这样就符合了这个条件 // 可是若是是这种请求,a(){b()},a方法中调用的b方法,那么此时调用b方法建立b对象时正在执行的就是a方法 // 此时就不知足这个条件,会调用这个resolveBeanReference方法来解决方法引用 if (isCurrentlyInvokedFactoryMethod(beanMethod)) { // 若是当前执行的方法就是这个被拦截的方法,(说明是在建立这个Bean的过程当中) // 那么直接执行目标类中的方法,也就是咱们在配置类中用@Bean标注的方法 return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); } // 说明不是在建立中了,而是别的地方直接调用了这个方法,这时候就须要代理了,实际调用getBean方法 return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, ConfigurableBeanFactory beanFactory, String beanName) { // The user (i.e. not the factory) is requesting this bean through a call to // the bean method, direct or indirect. The bean may have already been marked // as 'in creation' in certain autowiring scenarios; if so, temporarily set // the in-creation status to false in order to avoid an exception. // 何时会是alreadyInCreation?就是正在建立中,当Spring完成扫描后获得了全部的BeanDefinition // 那么以后就会遍历全部的BeanDefinition,根据BeanDefinition一个个的建立Bean,在建立Bean前会将这个Bean // 标记为正在建立的,若是是正在建立的Bean,先将其标记为非正在建立,也就是这行代码beanFactory.setCurrentlyInCreation(beanName, false) // 这是由于以后又会调用getBean方法,若是已经被标记为建立中了,那么在调用getBean时会报错 boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName); try { // 若是是正在建立的Bean,先将其标记为非正在建立,避免后续调用getBean时报错 if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); } // 在调用beanMthod的时候,也就是被@Bean注解标注的方法的时候若是使用了参数,只要有一个参数为null,就直接调用getBean(beanName),不然带参数调用getBean(beanName,args),后面经过例子解释这段代码 boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs); if (useArgs && beanFactory.isSingleton(beanName)) { for (Object arg : beanMethodArgs) { if (arg == null) { useArgs = false; break; } } } Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); // 这里发现getBean返回的类型不是咱们方法返回的类型,这意味着什么呢? // 在《你知道Spring是怎么解析配置类的吗?》我有提到过BeanDefinition的覆盖 // 这个地方说明beanMethod所定义的bd被覆盖了 if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) { if (beanInstance.equals(null)) { beanInstance = null; } else { // 省略日志 throw new IllegalStateException(msg); } } // 注册Bean之间的依赖关系 // 这个method是当前执行的一个建立bean的方法 Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); // 不等于null意味着currentlyInvoked这个方法建立的bean依赖了beanName所表明的Bean // 在开头的例子中,currentlyInvoked就是a(),beanName就是dmzService,outBeanName就是a if (currentlyInvoked != null) { String outerBeanName = BeanAnnotationHelper.determineBeanNafanhr(currentlyInvoked); // 注册的就是a跟dmzService的依赖关系,注册到容器中的dependentBeanMap中 // key为依赖,value为依赖所在的bean beanFactory.registerDependentBean(beanName, outerBeanName); } return beanInstance; } finally { if (alreadyInCreation) { // 实际还在建立中,要走完整个生命周期流程 beanFactory.setCurrentlyInCreation(beanName, true); } } }
这部份内容很是细节,不感兴趣能够跳过,主要是BeanMethodInterceptor中的方法。
@Configuration @EnableAspectJAutoProxy public class Config { @Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS) public DmzService dmzService() { return new DmzService(); } } @RestController @RequestMapping("/test") public class Controller { DmzService dmzService; @Autowired public void setDmzService(DmzService dmzService) { this.dmzService = dmzService; } @GetMapping("/get") public ResponseEntity<?> get() { System.out.println(dmzService); return ResponseEntity.ok().build(); } }
咱们须要调试两种状况
端点调试,也确实如咱们所料,这个地方注入的确实是一个代理对象,由于咱们在配置类上申明了proxyMode = ScopedProxyMode.TARGET_CLASS
,因此这里是一个cglib的代理对象。
咱们直接在intecept方法中进行断点,会发现此时的调用栈以下
这种状况下就会进入到下面这段代码的逻辑中
// 判断这个Bean是不是一个域代理的类 Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class); // 存在@Scope注解,而且开启了域代理模式 if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); // 域代理对象的目标对象正在被建立,何时会被建立?固然是使用的时候嘛 if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { // 使用的时候调用@Bean方法来建立这个域代理的目标对象,因此@Bean方法代理的时候针对的是域代理的目标对象 beanName = scopedBeanName; } }
也就是下面这段代码何时会成立
if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); }
@ComponentScan(value = "com.dmz.spring.first") @Configuration public class Config { @Bean public A a(){ return new A(); } @Bean public B b(){ a(); return new B(); } } class A{ B b; @Autowired public void setB(B b) { this.b = b; } } class B{ }
上面这种配置,在启动的时候就会进入到if条件中,在建立a的时候发现须要注入b,那么Spring此时就会去建立b,b在建立的过程当中又调用了a方法,此时a方法在执行时又被拦截了,而后就会进入到if判断中去。对Spring有必定了解的同窗应该能感受到,这个其实跟循环依赖的原理是同样的。关于循环依赖,在后面我单独写一篇文章进行说明。
这个代码我初看时也很不明白,为何只要有一个参数为null就直接标记成不使用参数呢?我说说本身的理解。
beanMethodArgs表明了调用beanMethod时传入的参数,正常Spring自身是不会传入这个参数的,由于没有必要,建立Bean时其依赖早就经过BeanDefinition肯定了,可是可能出现下面这种状况
@Configuration public class AnotherConfig { @Bean public DmzService dmzService(IndexService indexService) { return new DmzService(indexService); } @Bean public OrderService orderService() { DmzService dmzService = dmzService(null); return dmzService.createOrder(); } } @Component public class IndexService { } public class DmzService { public DmzService(IndexService indexService) { } public OrderService createOrder() { return new OrderService(); } } public class OrderService { }
这种状况下,咱们在orderService()为了获得当前容器中的dmzService调用了对应的BeanMethod,可是按照方法的定义咱们不得不传入一个参数,可是实际上咱们知道BeanMethod等价于getBean,因此上面这段代码能够等价于
@Configuration public class AnotherConfig { @Autowired ApplicationContext applicationContext; @Bean public DmzService dmzService(IndexService indexService) { return new DmzService(indexService); } @Bean public OrderService orderService() { DmzService dmzService = (DmzService) applicationContext.getBean("dmzService"); return dmzService.createOrder(); } }
对于getBean而言,传入参数跟不传参数在建立Bean时是有区别的,可是建立后从容器中获取Bean时跟传入的参数没有一毛钱关系(单例状况),由于这是从缓存中获取嘛。也就是说单例下,传入的参数只会影响第一次建立。正由于如此,getBean在单纯的作获取的时候不须要参数,那就意味着beanMthod在获取Bean的时候也能够不传入参数嘛,可是beanMthod做为一个方法又定义了形参,Spring就说,这种状况你就传个null吧,反正我知道要去getBean,固然,这只是笔者的我的理解。
修改bd的整个过程都发生在Bean工厂后置处理器的执行逻辑中
在上文中咱们已经知道了,在执行bean工厂后置处理器前,Spring容器的状态以下:
那么执行完成Bean工厂后置处理器后(不考虑程序员自定义的后置处理器),容器的状态应该是这样的
在容器中的bd就绪后,Spring会经过bd来建立Bean了,会先建立配置类,而后建立配置类中beanMethod定义的bean。在建立配置类的过程当中在初始化Bean时,若是实现了Aware接口,会调用对于的setXxx方法,具体代码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
在调用setBeanFactory
方法时,会被拦截,进入到拦截器的逻辑中
如下面这段代码为例:
@Configuration public class AnotherConfig { @Bean public DmzService dmzService(){ return new DmzService(); } @Bean public OrderService orderService(){ return new OrderService(dmzService()); } }
Spring会根据beanMethod
在配置类中定义顺序来建立Bean,因此上面这段配置会先建立dmzServcice
,以后在建立orderService
。
那么BeanMethodInterceptor
的拦截将会发生在两个地方
dmzService
的过程当中,拦截的是dmzService()
方法orderService
过程当中,第一次拦截的是orderService()
方法orderService()
方法调用了dmzService()
方法,dmzService()
方法又被拦截在直接建立dmzService
时,因为isCurrentlyInvokedFactoryMethod(beanMethod)
这句代码会成立,因此会直接调用目标类的方法,也就是cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)
,就是咱们在配置类中定义的dmzService()
方法,经过这个方法返回一个dmzService
而建立orderService
时,方法的调用就略显复杂,首先它相似于上面的直接建立dmzService
的流程,orderService()
方法会被拦截,可是因为正在执行的方法就是orderService()
方法,因此orderService()
也会被直接调用。可是orderService()
中又调用了dmzService()
方法,dmzService()
方法又被拦截了,此时orderService()
还没被执行完成,也就是说正在执行的方法是orderService()
方法,因此isCurrentlyInvokedFactoryMethod(beanMethod)
这句代码就不成立了,那么就会进入org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#resolveBeanReference
这个方法的逻辑中,在这个方法中,最终又经过getBean
方法来获取dmzService
,由于dmzService
以前已经被建立过了,因此在单例模式下,就直接从单例池中返回了,而不会再次调用咱们在配置类中定义的dmzService()
方法。
这里就在上篇文章的基础上对流程图再作一次完善吧,由于图片太大了,就放个连接~
码字不易,要是以为对你有帮助的话,记得点个赞吧~!