spring源码------@Configuration跟@Component及其派生注解@Service等的区别以及spring对其代理增强的原理

@[TOC]

1.常用的注解,以及@Configuration的特殊性

 Spring中提供了很多将对象注入到容器中的注解,这里将这些注解列举出来。@Component@Indexed@Controller@Repository@Service@Configuration。另外还有一个需要跟@Configuration@Component以及派生注解一起使用的注解@Bean
 虽然我们经常使用这些注解中的,但是其实@Configuration跟其他的注解有点不一样,这个注解在默认情况下Spring会通过CGLIB的方式对贴有这个注解的对象进行增强。而这个决定是否进行增强的关键,在于@Configuration注解中的proxyBeanMethods属性。当这个属性为true的时候会进行增强,而默认情况这个属性值就是true

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(annotation = Component.class)
    String value() default "";
    
    boolean proxyBeanMethods() default true;
}

&esmp;从上面其实能看到@Configuration注解本身也是@Component注解的一个派生注解,但是为什么Spring会对其进行增强呢,接下来进行分析。

2. @Configuration特殊性的源码解析

2.1 贴有@Configuration的bean的获取

 spring对于@Configuration注解的解析,跟@Conditional注解的解析大部分过程一样都是在ConfigurationClassPostProcessor中进行的,可以通过@Conditional注解解析的文章看看前面的过程。Spring扩展------基于@Conditional注解以及Condition接口的扩展
。这里天过前面的步骤,直接进入到此类中进行分析。

2.1.1 bean注册前进行分类的方法checkConfigurationClassCandidate

 在这个类中有一个方法checkConfigurationClassCandidate用来检查当前注册的类是不是候选configuration class也就是需要进行代理的类。这个方法的调用的位置都是在,将对应的候选bean放入到需要注册的候选bean的集合这个步骤前。这个贴代码看看

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    ......
    for (String beanName : candidateNames) {
            //获取对应的BeanDefinition
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
                }
            }
            //检查BeanDefinition是不是Configuration类型的候选bean,
            else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }
    ......
        //检查BeanDefinition是不是Configuration类型的候选bean,并且这没有被解析过
        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                            candidates.add(new BeanDefinitionHolder(bd, candidateName));
        }
        ......              
}

 可以看到在将候选bean放入到需要注册的候选bean集合之前都会对bean进行的检查,这也可以看成是一个过滤 的过程,将那些需要进行代理的bean进行分类。分类的过程就在checkConfigurationClassCandidate方法中。这个方法在ConfigurationClassUtils类中,现在看看这个方法的主要步骤

    public static boolean checkConfigurationClassCandidate(
            BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
        ......
        //获取源数据中包含Configuration注解的信息
        Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
        //如果存在Configuration注解,并且proxyBeanMethods是true,则表示需要进行代理
        if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
        }
        //如果存在Configuration注解,但是proxyBeanMethods是false,则表示不需要进行代理
        //如果不存在Configuration注解,但是如果bean中包含Component,ComponentScan,Import,ImportResource或者Bean注解,则也不需要代理,但是是候选bean
        else if (config != null || isConfigurationCandidate(metadata)) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
        }
        //如果满足上面判断条件则表示不是候选bean
        else {
            return false;
        }
        ......
    }

 这里做的就是获取传进来的候选bean上面的@Configuration注解,然后进行判断,主要有以下几种判断。

贴有@Configuration注解 @Configuration注解的proxyBeanMethodstrue 是否需要代理
满足 满足 需要
满足 不满足 不需要
不满足 - 不需要

 上面满足条件需要代理的bean,会在封装其信息的BeanDefinition对象中加上一个属性CONFIGURATION_CLASS_ATTRIBUTE值为CONFIGURATION_CLASS_FULL。这里需要注意这个属性有两种值,需要进行区分。

说明 情况
CONFIGURATION_CLASS_FULL 需要对bean进行代理 贴有@Configuration注解的对象的内部所有的注入到容器的对象(贴有@Bean注解的对象)
CONFIGURATION_CLASS_LITE 不需要对bean进行代理 没有贴有@Configuration注解的对象,即使内部存在注入到容器的对象

 在上面将bean进行分类之后,后面就是对bean进行代理增强的阶段了。对bean进行代理增强的过程的起始调用也是在ConfigurationClassPostProcessor类中。

2.2 对bean进行增强
2.2.1 获取需要代理增强的bean

 增强的初始过程在ConfigurationClassPostProcessor类的enhanceConfigurationClasses方法中。这个方法的调用在spring上下文初始化之后会调用。

    public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
        Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            //从容器中获取BeanDefinition
            BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
            //获取BeanDefinition中的CONFIGURATION_CLASS_ATTRIBUTE属性
            Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
            MethodMetadata methodMetadata = null;
            if (beanDef instanceof AnnotatedBeanDefinition) {
                methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
            }
            //从加载了@Configuration注解的BeanDefinition的beanClassLoader中获取对应的beanClass,因为是这个类加载的@Configuration注解的BeanDefinition
            if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
                // Configuration class (full or lite) or a configuration-derived @Bean method
                // -> resolve bean class at this point...
                AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
                if (!abd.hasBeanClass()) {
                    try {
                        //从beanClassLoader中获取到真正加载的bean的Classes对象
                        abd.resolveBeanClass(this.beanClassLoader);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException(
                                "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
                    }
                }
            }
            //如果CONFIGURATION_CLASS_ATTRIBUTE属性是CONFIGURATION_CLASS_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");
                }
                //如果是AbstractBeanDefinition类型的BeanDefinition需要检查这个bean是不是已经被实例化了,如果是的则不能进行代理
                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);
            }
        }
        if (configBeanDefs.isEmpty()) {
            // nothing to enhance -> return immediately
            return;
        }
        ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
        for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
            //获取AbstractBeanDefinition
            AbstractBeanDefinition beanDef = entry.getValue();
            // If a @Configuration class gets proxied, always proxy the target class
            //如果一个bean已经代理过了,则设置这个状态未true
            beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            // Set enhanced subclass of the user-specified bean class
            //进行代理
            Class<?> configClass = beanDef.getBeanClass();
            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);
            }
        }
    }

 总结以下上面的步骤:

  1. 从当前的容器中获取所有的需要注册的bean集合,然后循环开始2-3步骤,直到结束
  2. 获取BeanDefinition中的CONFIGURATION_CLASS_ATTRIBUTE属性,然后获取需要代理的bean的真实的Class对象
  3. 检查CONFIGURATION_CLASS_ATTRIBUTE属性是不是CONFIGURATION_CLASS_FULL,不是则跳过进入下一个bean然后到步骤2。是的则将bean放到需要代理的bean的集合中,然后进入2步骤
  4. 迭代需要代理的bean的集合进行代理增强。
2.2.2 对bean进行代理增强

&esmp;代理增强的逻辑在ConfigurationClassEnhancer类的enhance方法中。

    public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
        //如果当前的configClass已经是EnhancedConfiguration的子类,说明这个类已经被代理了,因为后面代理的类会设置接口是EnhancedConfiguration
        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;
        }
        //创建一个Enhancer然后进行代理
        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;
    }

 可以看到上面的就是用CGILB进行代理的,在其创建Enhancer的时候,会设置一些参数,这些参数就是用来与普通的CGLIB进行区分的。

    private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
        Enhancer enhancer = new Enhancer();
        //设置父类或者接口类为需要代理的目标类
        enhancer.setSuperclass(configSuperClass);
        //设置实现的接口EnhancedConfiguration
        enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
        //是否需要实现Factory接口
        enhancer.setUseFactory(false);
        //设置命名规则为了避免Spring的CGLIB版本与常规的CGLIB之间的冲突
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        //设置生成策略
        enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
        //设置回调
        enhancer.setCallbackFilter(CALLBACK_FILTER);
        //设置回调类型
        enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
        return enhancer;
    }

 在这里需要关注一下setCallbackFilter这个方法设置的CALLBACK_FILTERCALLBACK_FILTER是一个对象的数组。

    private static final Callback[] CALLBACKS = new Callback[] {
            new BeanMethodInterceptor(),
            new BeanFactoryAwareMethodInterceptor(),
            NoOp.INSTANCE
    };

    private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

 这里包含四个类BeanMethodInterceptorBeanFactoryAwareMethodInterceptorNoOp的空实现类SerializableNoOpConditionalCallbackFilter,其中第三个NoOp在spring中是空实现不用管。主要看ConditionalCallbackFilterBeanFactoryAwareMethodInterceptorBeanMethodInterceptor这三个类。

2.2.3 方法回调的过滤处理ConditionalCallbackFilter

CallbackFilter在CGLIB 中起到回调的作用,就是一个方法的执行时候的拦截过滤的作用。spring实现了自己的ConditionalCallbackFilter。在这个里面会对方法进行过滤。

    private static class ConditionalCallbackFilter implements CallbackFilter {

        private final Callback[] callbacks;

        private final Class<?>[] callbackTypes;

        public ConditionalCallbackFilter(Callback[] callbacks) {
            //设置Callback,这里就是BeanMethodInterceptor, BeanFactoryAwareMethodInterceptor, NoOp的空实现类SerializableNoOp这三个类
            this.callbacks = callbacks;
            this.callbackTypes = new Class<?>[callbacks.length];
            for (int i = 0; i < callbacks.length; i++) {
                this.callbackTypes[i] = callbacks[i].getClass();
            }
        }

        @Override
        public int accept(Method method) {
            //进行循环处理
            for (int i = 0; i < this.callbacks.length; i++) {
                Callback callback = this.callbacks[i];
                //如果是ConditionalCallback的实现类,并且isMatch方法通过则返回对应的callback所在的顺序
                if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
                    return i;
                }
            }
            throw new IllegalStateException("No callback available for method " + method.getName());
        }

 可以看到这里最主要的作用就是设置自己的Callback,也就是BeanMethodInterceptorBeanFactoryAwareMethodInterceptorNoOp。然后进行过滤这些Callback,其中ConditionalCallback也是ConfigurationClassEnhancer类的内部类。只定义了一个方法isMatch,作用是检查对应的拦截方法是否需要进行拦截。

    private interface ConditionalCallback extends Callback {

        boolean isMatch(Method candidateMethod);
    }

 其中isMatch方法由BeanFactoryAwareMethodInterceptorBeanMethodInterceptor实现。接下来就是这两个类

2.2.4 将容器设置到代理对象中的BeanFactoryAwareGeneratorStrategy

BeanFactoryAwareGeneratorStrategy实现了CGLIB的DefaultGeneratorStrategy用来在生成代理对象的时候,设置额外的信息到代理对象中。也是在这里将,容器对象BeanFactory放到了代理对象中,这里只截取部分的代码。

    private static class BeanFactoryAwareGeneratorStrategy extends DefaultGeneratorStrategy {
        ......
        @Override
        protected ClassGenerator transform(ClassGenerator cg) throws Exception {
            //实现ClassEmitterTransformer,为代理对象增加额外的字段BEAN_FACTORY_FIELD,访问级别是PUBLIC,类型为BeanFactory
            ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
                @Override
                public void end_class() {
                    declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
                    super.end_class();
                }
            };
            return new TransformingClassGenerator(cg, transformer);
        }
        ......
    }

 设置的这个值,在后面的两个拦截其中都会用到

2.2.5 增加容器到代理对象中的BeanFactoryAwareMethodInterceptor

BeanFactoryAwareMethodInterceptor主要用来处理实现了BeanFactoryAware类的setBeanFactory方法类。

    private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {

        @Override
        @Nullable
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            //从代理的对象中获取BEAN_FACTORY_FIELD字段
            Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
            Assert.state(field != null, "Unable to find generated BeanFactory field");
            //将BEAN_FACTORY_FIELD字段设置到obj对象中
            field.set(obj, args[0]);

            //如果当前代理类的父类实现了BeanFactoryAware,则调用setBeanFactory方法,如果不是就推出
            if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
                return proxy.invokeSuper(obj, args);
            }
            return null;
        }

        @Override
        public boolean isMatch(Method candidateMethod) {
            //检查是否是设置BeanFactory的方法
            return isSetBeanFactory(candidateMethod);
        }

        public static boolean isSetBeanFactory(Method candidateMethod) {
            //方法名是setBeanFactory,参数个数为1个,平且参数类型是BeanFactory类型,方法是BeanFactoryAware的实现类
            return (candidateMethod.getName().equals("setBeanFactory") &&
                    candidateMethod.getParameterCount() == 1 &&
                    BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
                    BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
        }
    }
2.2.6 处理@Configuration注解中@Bean注解的BeanMethodInterceptor

BeanMethodInterceptor主要处理@Configuration注解中贴有@Bean注解的方法。也正是这个方法,让@Configuration有了区别于@Component注解中贴有@Bean注解的方法的原因之一。主要逻辑集中在这个类的
intercept方法中,现在来看看:
 这里要先看看实现了ConditionalCallback接口的isMatch方法,因为这个会先被调用

        public boolean isMatch(Method candidateMethod) {
            //定义当前方法的对象不是Object,并且不是方法不是setBeanFactory方法,并且方法上面包含@Bean注解
            return (candidateMethod.getDeclaringClass() != Object.class &&
                    !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
                    BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
        }

 到这里就知道,只要是声明方法的类Object类,方法名不是setBeanFactory贴了@Bean注解就会被拦截。
 接下来就是拦截之后的处理逻辑了。

        public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                    MethodProxy cglibMethodProxy) throws Throwable {
            //从代理的实例中获取beanFactory,根据BEAN_FACTORY_FIELD字段获取,而这个字段在enhanced对象创建设置stratege的BeanFactoryAwareGeneratorStrategy类中设置的
            ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
            //获取到方法上@Bean注解的name属性或者方法名来决定bean的名称
            String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
            // Determine whether this bean is a scoped-proxy
            //检查缓存中是否有方法的@Scope注解信息缓存,如果没有则检查是否有@Scope注解并且proxyMode不是NO
            if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
                //生成作用域代理内用于引用目标bean的bean名称
                String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
                if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
                    beanName = scopedBeanName;
                }
            }

            /**
             * 为了处理bean间方法引用的情况,我们必须显式地检查容器中已经缓存的实例。
             * 检查当前的bean是不是一个FactoryBean,如果是这样创建一个子类roxy,拦截对getObject()的调用并返回所有缓存的bean实例
             * 这确保了从@Bean方法中调用FactoryBean的语义与在XML中引用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) {
                    //作用域代理工厂bean是一种特殊情况,不应该进一步代理
                    // 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);
                }
            }
            //检查当前的方法是不是当前调用的工厂方法,就是当前拦截的方法是不是当前贴有@Configuration注解类里面贴有@Bean注解的方法
            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.
                //工厂调用bean方法是为了实例化和注册bean(即通过getBean()调用)->调用父类方法来实际创建bean实例。
                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()));
                }
                //调用父类方法
                return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
            }
            //从容器获取bean
            return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
        }

 这里需要说明的以下几点

  1. BeanMethodInterceptorConfigurationClassEnhancer的一个内部类
    enhancedConfigInstance对象中的
  2. 当贴有@Bean注解的方法返回的是BeanFactory的子类的时候,会先获取到实际的bean,也就是beanName前面加上“&”符号,然后进行代理
  3. isCurrentlyInvokedFactoryMethod用来验证当前拦截的方法是不是贴有@Bean注解的方法,是的话会调用代理的方法。如果不是则直接从容器中获取bean。
3.@Configuration@Component例证

如果在@Configuration注解的类的中有@Bean注解在方法上来注入对象,当调用这个方法的时候,返回的是容器里面已经创建好的对象。举个例子

@Configuration
public class ConfigurationTest {
    @Bean
    public BeanB beanB(){
        BeanB beanB = new BeanB();
        System.out.println("beanB one---"+beanB);
        return beanB;
    }

    @Bean
    @DependsOn(value = "beanB")
    public BeanA beanA(){
        System.out.println("beanA");
        BeanA beanA = new BeanA();
        beanA.setBeanB(beanB());
        System.out.println("beanB two---"+beanA.getBeanB());
        return beanA;
    }
}

 当从容器中获取beanA的时候。打印的结果如下

beanB one---springstudy.configuration.BeanB@2611b9a3
beanA
beanB two---springstudy.configuration.BeanB@2611b9a3

 当我们把ConfigurationTest类上面的@Configuration注解换成@Component的时候,看结果如下

beanB one---springstudy.configuration.BeanB@6d24ffa1
beanA
beanB one---springstudy.configuration.BeanB@65a4798f
beanB two---springstudy.configuration.BeanB@65a4798f

 发现会创建两次BeanB对象。
 会发生这种区别的原因就是,当时存在@Configuration注解的时候,beanBbeanA方法会被代理增强,在beanA方法中调用beanB方法的时候会被拦截,并返回容器中已经创建的BeanB对象。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容