1 前言
首先说下为什么会产生这样的疑惑哈,最近在看Spring-retry的时候,发现:
其次我们再来看个现象,@Component 声明了一个Bean,内部有个单例AService,当我们调用两次 aService() 发现得到的对象不一样:
@Component public class Demo { public class AService { public AService() { System.out.println("初始化AService"); } } @Bean public AService aService() { return new AService(); } @PostConstruct public void init() { AService a1 = aService(); AService a2 = aService(); System.out.println(a1); System.out.println(a2); } }
那我们把@Component 换成 @Configuration 再来看看:
看见没构造器执行了一次,并且获得的都是同一个对象,其实就是因为生成了代理,方法被增强处理了,那么接下来我们就来看看是怎么回事。
2 源码分析
2.1 入口分析
我们说了是会生成代理的,生成代理的类就是 ConfigurationClassPostProcessor,那么它是什么时候入场的呢,我们来看看:
看类图发现它实现了 BeanDefinitionRegistryPostProcessor,而该接口又继承了 BeanFactoryPostProcessor 说明这个类是个BeanFactoyry的后置处理器哇,工厂后置处理是什么时候执行的呢,是不是就是在刷新的时候执行的?
那么是如何进到创建代理的,我画个图来理解一下:
如上图所示,就是 BeanFactory的后置处理,进入到我们的 ConfigurationClassPostProcessor类里,给@Configuration修饰的类创建代理的,那么具体是如何创建的,怎么创建我们细细往下看。
2.2 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry
在看之前我们先看下 ConfigurationClassPostProcessor 中的 postProcessBeanDefinitionRegistry这个方法,这个方法是优先于 postProcessBeanFactory,它主要做什么呢?就是从注册进来的配置类(可能是Full模式,可能是Lite模式)里进一步派生bean定义。简而言之:收集到所有的BeanDefinition(后简称为bd)存储起来,包括@Import、@Component等等组件。并且做出标注:是Full模式的还是Lite模式的配置类(若非配置组件就不标注哦)。也是为postProcessBeanFactory做准备的。
/** * Derive further bean definitions from the configuration classes in the registry. */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 生成一个id,防止重复执行 int registryId = System.identityHashCode(registry); // 若重复就抛出异常 if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } // 表示此registry里的bd收集动作,已经做了 避免再重复收集此registry this.registriesPostProcessed.add(registryId); // 根据配置类,收集到所有的bd信息 // 并且做出mark标注:是Full模式还是Lite模式,和很重要很重要 processConfigBeanDefinitions(registry); }
我们就不进去细看了哈,大概总结下什么情况下是Full模式、Lite模式。
- Full模式:@Configuration注解并且proxyBeanMethods属性等于true
- Lite模式:@Component,@ComponentScan,@Import,@ImportResource这几个注解的类(或者加了@Configuration注解但是proxyBeanMethods属性等于false)
2.3 ConfigurationClassPostProcessor 的 postProcessBeanFactory
完成了bd的收集和标记,那接下来就是我们要看的 postProcessBeanFactory,此方法的作用用一句话可概括为:为Full模式的Bean使用CGLIB做字节码提升,确保最终生成的是代理类实例放进容器内。
/** * Prepare the Configuration classes for servicing bean requests at runtime * by replacing them with CGLIB-enhanced subclasses. */ @Override 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集合中了 // 所以到这里就不会再重复执行配置类的解析了(解析@Import、@Bean等) if (!this.registriesPostProcessed.contains(factoryId)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } // 这个方法就是为配置类创建代理 enhance是cglib代理创建的核心类 enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }
达到这一步之前,已经完成了bd的收集和标记。对bd进行实例化之前,针对于Full模式的配置类这步骤里会做增强处理,那就是enhanceConfigurationClasses(beanFactory)这个方法,我们继续看。
2.4 enhanceConfigurationClasses 方法
对一个BeanFactory进行增强,先查找配置类BeanDefinition,再根据Bean定义信息(元数据信息)来决定配置类是否应该被ConfigurationClassEnhancer增强。具体我们看看:
/** * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}. * Candidate status is determined by BeanDefinition attribute metadata. * @see ConfigurationClassEnhancer */ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance"); // 收集哪些Bean 需要增强 Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>(); // 遍历当前BeanFactory中的 Bean定义,也就是要找@Configuration注解修饰的Bean for (String beanName : beanFactory.getBeanDefinitionNames()) { // 获取到Bean的定义 BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);...// 如果是Full模式,才会放进来 if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) { if (!(beanDef instanceof AbstractBeanDefinition)) { throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass"); } else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) { logger.info("Cannot enhance @Configuration bean definition '" + beanName + "' since its singleton instance has been created too early. The typical cause " + "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " + "return type: Consider declaring such methods as 'static'."); } configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } // 如果发现没有 bean需要增强就直接返回 if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) { // nothing to enhance -> return immediately enhanceConfigClasses.end(); return; } // ConfigurationClassEnhancer 应该就是这个类创建增强的类 ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // If a @Configuration class gets proxied, always proxy the target class // 如果代理了@Configuration类,则始终代理目标类 // 该属性和自动代理时是相关的 beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // Set enhanced subclass of the user-specified bean class // CGLIB是给继承的方式实现代理,所以这里指定“父类”类型 Class<?> configClass = beanDef.getBeanClass(); // 做增强处理,返回enhancedClass就是一个增强过的子类 Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); // 不相等,证明代理成功,那就把实际类型设置进去 // 这样后面实例化配置类的实例时,实际实例化的就是代理后的类 if (configClass != enhancedClass) { if (logger.isTraceEnabled()) { logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " + "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName())); } beanDef.setBeanClass(enhancedClass); } } enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end(); }
大致的执行步骤就是:
(1)从BeanFactory拿出所有的bd信息,一个个判断如果是配置类并且是Full模式,就先存储起来,后面会对它做字节码提升。最终如果一个Full模式的配置类都没有的话,那直接return,此方法结束。否则继续
(2)对收集到的每一个 Full模式的配置类,使用ConfigurationClassEnhancer增强器进行字节码提升,生成一个CGLIB子类型并此处显示标注了AOP自动代理为:始终代理目标类
(3)把CGLIB生成的子类型设置到元数据里去:beanDef.setBeanClass(enhancedClass)。这样Spring在最后实例化Bean时,实际生成的是该代理类型的实例,从而达到代理/增强的目的
2.5 ConfigurationClassEnhancer 创建代理
通过生成一个CGLIB子类来增强@Configuration类与Spring容器进行交互,每个这样的@Bean方法都会被生成的子类所复写。这样子当遇到方法调用时,从而保证单例特性,获得相应的Bean。我们继续看代理的创建:
/** * Loads the specified class and generates a CGLIB subclass of it equipped with * container-aware callbacks capable of respecting scoping and other bean semantics. * @return the enhanced subclass */ public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) { // 如果已经实现了该接口,证明已经被代理过了,直接返回 if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { if (logger.isDebugEnabled()) { logger.debug(String.format("Ignoring request to enhance %s as it has " + "already been enhanced. This usually indicates that more than one " + "ConfigurationClassPostProcessor has been registered (e.g. via " + "<context:annotation-config>). This is harmless, but you may " + "want check your configuration and remove one CCPP if possible", configClass.getName())); } return configClass; } // 若没被代理过,就先调用newEnhancer()方法创建一个增强器Enhancer // 然后使用这个增强器,生成代理类字节码Class对象 Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader)); if (logger.isTraceEnabled()) { logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s", configClass.getName(), enhancedClass.getName())); } return enhancedClass; }
可以看到是先给我们的熬代理的类,创建一个增强器newEnhancer(),然后调用createClass创建其代理,那我们来看下这两个方法。
2.5.1 newEnhancer 方法
创建一个新的CGLIB Enhancer实例,并且做好相应配置。
/** * Creates a new CGLIB {@link Enhancer} instance. */ private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) { Enhancer enhancer = new Enhancer(); // 设置被代理的类为其父类型 cglib就是继承的方式实现代理的 enhancer.setSuperclass(configSuperClass); // 让代理类实现EnhancedConfiguration接口,这个接口继承了BeanFactoryAware接口 enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); // 设置生成的代理类不实现org.springframework.cglib.proxy.Factory接口 enhancer.setUseFactory(false); // 设置代理类名称的生成策略:Spring定义的一个生成策略 // 即你代理的名称中会有“BySpringCGLIB”字样 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); // 设置拦截器/过滤器 enhancer.setCallbackFilter(CALLBACK_FILTER); enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); return enhancer; }
2.5.2 createClass 方法
/** * Uses enhancer to generate a subclass of superclass, * ensuring that callbacks are registered for the new subclass. */ private Class<?> createClass(Enhancer enhancer) { // 创建代理 Class<?> subclass = enhancer.createClass(); // Registering callbacks statically (as opposed to thread-local) // is critical for usage in an OSGi environment (SPR-5932)... Enhancer.registerStaticCallbacks(subclass, CALLBACKS); return subclass; }
Enhancer是CGLIB的最核心API,而这里好像是spring内的,这是因为CGLIB在Spring内太常用了(强依赖),因此Spring索性就自己fork了一份代码过来。之前我们也讲过cglib的代理哈,大家可以去看看。通过这两个方法我们能看到:
- 通过它增强的每个类都实现了EnhancedConfiguration接口,并且它还是BeanFactoryAware的子接口,这样Spring在创建代理类实例的时候会给注入BeanFactory
- 使用SpringNamingPolicy策略来生成类名称。这就是解释了为何代理类的名你都能看到BySpringCGLIB字样
- 那么对于代理最为重要的当属过滤器/拦截器org.springframework.cglib.proxy.Callback,它们是实现功能的核心
配置此增强器时设置了CALLBACK_FILTER共三个拦截器:
private static final Callback[] CALLBACKS = new Callback[] { new BeanMethodInterceptor(), new BeanFactoryAwareMethodInterceptor(), NoOp.INSTANCE };
2.5.3 拦截器分析
上面的三个拦截器中,NoOp.INSTANCE代表什么都没做,因此我们只需要关注前两个。他俩都是MethodInterceptor接口的实现类,均实现了intercept()方法来做具体的拦截操作,我们就来看看。
2.5.3.1 BeanFactoryAwareMethodInterceptor 拦截器
看名字,表示的是BeanFactoryAware方法的拦截器,所以应该能猜到它拦截的是setBeanFactory(beanFactory)方法,我们看是不是:
@Override // 当执行到setBeanFactory(xxx)方法时匹配成功 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())); }
可以看到去匹配我们的setBeanFactory方法,isSetBeanFactory()判断方法做这么“复杂”主要是为了容错,“担心”你自己定义了一个名为setBeanFactory的方法而“搞错了”。那它到底做了什么事情呢,我们继续看:
@Override @Nullable public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 找到类里名为`$$beanFactory`的字段 注意这里的类是代理类 Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD); // 若没找到直接报错。若找到了此字段,就给此字段赋值 Assert.state(field != null, "Unable to find generated BeanFactory field"); field.set(obj, args[0]); // Does the actual (non-CGLIB) superclass implement BeanFactoryAware? // If so, call its setBeanFactory() method. If not, just exit. // 如果用户类(也就是你自己定义的类)自己实现了该接口,那么别担心,也会给你赋值上 if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) { return proxy.invokeSuper(obj, args); } return null; }
拦截的是setBeanFactory()方法的执行。所以这里的Method就代表的是setBeanFactory()方法,Object[] args的值是当前容器的BeanFactory工厂(注意理解这句话)实例。说白了其实就是给代理类设置 BeanFactory。
2.5.3.2 BeanMethodInterceptor 拦截器
我们看下它拦截的匹配规则:
/** * 匹配的规则: * 1、不是Object类的方法,看过cglib生成的代理类源码的知道 生成后会有toString等Object的方法 * 2、不是setBeanFactory方法 这个理解的吧 * 3、@Bean注解的方法 * 其实也就是匹配@Bean注解标注的方法,快跟我们上边的困惑对上了哈 */ public boolean isMatch(Method candidateMethod) { return (candidateMethod.getDeclaringClass() != Object.class && !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) && BeanAnnotationHelper.isBeanAnnotated(candidateMethod)); }
其实就是拦截@Bean标注的方法哈,我们继续看匹配到方法后的处理:
/** * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the * existence of this bean object. * @throws Throwable as a catch-all for any exception that may be thrown when invoking the * super implementation of the proxied method i.e., the actual {@code @Bean} method */ @Override @Nullable public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable { // 获取到 BeanFactory 这里别有疑问哈,会不会获得的是空呢?不会的哈,BeanFactoryAware赋值的优先级还是比较高的 ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); // 根据当前方法获得一个bean的名字 默认就是我们的方法名字哈 String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // Determine whether this bean is a scoped-proxy if (BeanAnnotationHelper.isScopedProxy(beanMethod)) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { beanName = scopedBeanName; } } // To handle the case of an inter-bean method reference, we must explicitly check the // container for already cached instances. // First, check to see if the requested bean is a FactoryBean. If so, create a subclass // proxy that intercepts calls to getObject() and returns any cached bean instance. // This ensures that the semantics of calling a FactoryBean from within @Bean methods // is the same as that of referring to a FactoryBean within XML. See SPR-6602. if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) && factoryContainsBean(beanFactory, beanName)) { Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName); if (factoryBean instanceof ScopedProxyFactoryBean) { // Scoped proxy factory beans are a special case and should not be further proxied } else { // It is a candidate FactoryBean - go ahead with enhancement return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName); } } // 如果当前bean 是Spring人家正在创建的,就调用父类即被代理类实际方法生成bean实例,并注册到Spring容器中 if (isCurrentlyInvokedFactoryMethod(beanMethod)) { // The factory is calling the bean method in order to instantiate and register the bean // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually // create the bean instance. if (logger.isInfoEnabled() && BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) { logger.info(String.format("@Bean method %s.%s is non-static and returns an object " + "assignable to Spring's BeanFactoryPostProcessor interface. This will " + "result in a failure to process annotations such as @Autowired, " + "@Resource and @PostConstruct within the method's declaring " + "@Configuration class. Add the 'static' modifier to this method to avoid " + "these container lifecycle issues; see @Bean javadoc for complete details.", beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName())); } // 执行父类的方法生成Bean实例 return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); } // 走到这里说明此时的@Bean方法并不是由spring调用的,而是用户自身的代码显示调用的 // 从BeanFactory中获取bean return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); }
上边的这个方法暂时没看全,先放着哈,设计的东西比较多主要是bean的实例化过程以及bean注入的先后顺序都会形成不同的场景,就要根据不同的场景去做判断处理,就先看到这里哈。
3 小结
本节我们主要看了下@Configuration注解的类的代理创建时机和过程,要知道生成的代理类实现了BeanFactoryAware接口以及两个增强方法,分别是针对setBeanFactory的增强和@Bean注解方法的增强处理哈,有理解不对的地方欢迎指正哈。
标签:return,beanFactory,Spring,bean,代理,Bean,Configuration,class From: https://www.cnblogs.com/kukuxjx/p/17339022.html