@Configration和@Compnent注解区别

前言

一、案例

二、源码解析

前言

 在Spring Boot中基本是@Configration注解使用较多,例如,配置类的启动加载,和@Component相比,两者都是为了实例化,那么它们有什么区别?


一、案例

用一个例子来予以说明,在下面的代码中,Spring启动中lison() 方法会被调用两次,但是同一个实例吗?先用@Component注解:

@Component
public class AnnoBean {

    @Bean
    public Lison lison() {
        return new Lison();
    }

    @Bean
    public LisonFactory lisonFactory() throws Exception {
        LisonFactory lisonFactory = new LisonFactory();
        lisonFactory.setLison(this.lison());
        return lisonFactory;
    }
}

单元测试看一下,被调用了几次:

    @Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
        Lison lison = applicationContext.getBean("lison",Lison.class);
        System.out.println(lison.hashCode());
        LisonFactory lisonFactory = applicationContext.getBean(LisonFactory.class);
        System.out.println(lisonFactory.getLison().hashCode());
    }


单元测试看一下,被调用了几次:

image.png

测试结果显示被调用了两次,每次调用生成的实例不一样,违背了Spring单例的思想。
如果上面的类用@Configuration注解,再进行单元测试,打印结果:

image.png

        将上面示例中的代码修改一下,在lisonFactory() 方法中实例化LiLi这个类,我们手动调用它的getObject()方法:

@Component
public class AnnoBean {

    @Bean
    public LisonFactory lisonFactory() {
        LiLi liLi = this.liLi();
        // 手动调用getObject()方法
        Object object = liLi.getObject();
        // 通过beanFactory拿实例
        // beanFactory.getBean()
        System.out.println("lili getObject : " + object.hashCode());
        return lisonFactory;
    }

    @Bean
    public LiLi liLi() {
        return new LiLi();
    }
}
public class LiLi implements FactoryBean {

    // 如果是getObject()方法,就不会调到这个方法本身,会通过代理直接返回beanFactory.getBean(beanName)方法
    @Override
    public Object getObject() {
        return new Docker();
    }

    @Override
    public Class<?> getObjectType() {
        return Docker.class;
    }

}

        LiLi 类实现了FactoryBean 接口,在它的getObject() 方法中new了一个Docker实例,我们进行单元测试,看两次调用返回的Docker 是否为同一个:

@Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
        Docker bean = applicationContext.getBean(Docker.class);
        System.out.println(bean.hashCode());
    }

调用的实例不是同一个:

image.png

 再用@Configration注解进行测试,两次调用的实例为同一个:

image.png

        @Configration注解进行实例化,不管调用多少次,返回的实例都是同一个,是单例的一种实现,上面获取实例的过程就是走的beanFactory.getBean(id) 方法,最终调到LiLi类中getObjectff返回的对象,并放到缓存中,所以拿到的为同一个。

二、源码解析


        如何实现的,进入源码,看ConfigurationClassPostProcessor类,这个类很重要,在Springboot中绝大多数的注解,如:@Configration、@Component、@ComponentScan、@ComponentScans、@Conditional、@PropertySource、@Import、@Bean等都是被该类负责扫描、收集、解析,最终封装成我们熟悉的BeanDefiniiton,注册到BeanDefinitionRegistry,我们看它的postProcessBeanDefinitionRegistry()方法:
所属类:org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

/**
     * Derive further bean definitions from the configuration classes in the registry.
     * 这是该类顶层接口BeanFactoryPostProcessor的方法,进行了重写,(先执行这个方法)
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        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);
        }
        this.registriesPostProcessed.add(registryId);
        // TODO 核心逻辑,重点看---->
        processConfigBeanDefinitions(registry);
    }

        在实例化过程中,先调用BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry()方法,进行beanDefiniiton的收集、注册,然后再执行postProcessBeanFactory接口的postProcessBeanFactory()方法,生成代理:

/**
     * 这是BeanFactoryPostProcessor接口的方法,(再执行这个方法)
     */
    @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);
        if (!this.registriesPostProcessed.contains(factoryId)) {
            // BeanDefinitionRegistryPostProcessor hook apparently not supported...
            // Simply call processConfigurationClasses lazily at this point then.
            processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
        }

        // TODO CGLIB代理,字节码增强,动态生成了代理类,并加载到了JVM中
        enhanceConfigurationClasses(beanFactory);
        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }

        上面代码中的enhanceConfigurationClasses(beanFactory)是生成代理的方法,它会先找到类上面有@Configuration注解的BeanDefinition:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
        StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
        Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
        // 拿到所有的BeanDefinition
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
            // beanDef.getAttribute()拿到对应的值
            Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
            MethodMetadata methodMetadata = null;
            if (beanDef instanceof AnnotatedBeanDefinition) {
                methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
            }
            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 {
                        abd.resolveBeanClass(this.beanClassLoader);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException(
                                "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
                    }
                }
            }
            // 只有beanDefinition为"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);
            }
        }
        // 为空表示没有需要生成代理的BeanDefinition,直接返回
        if (configBeanDefs.isEmpty()) {
            // nothing to enhance -> return immediately
            enhanceConfigClasses.end();
            return;
        }
        if (IN_NATIVE_IMAGE) {
            throw new BeanDefinitionStoreException("@Configuration classes need to be marked as proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
        }
        // new一个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
            beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            // Set enhanced subclass of the user-specified bean class
            Class<?> configClass = beanDef.getBeanClass();
            // TODO 增强的核心方法
            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()));
                }
                // 通过完整限定名获取到类的反射对象,并设置到BeanDefinition中
                beanDef.setBeanClass(enhancedClass);
            }
        }
        enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
    }

进入增强的核心方法enhance() :
所属类:org.springframework.context.annotation.ConfigurationClassEnhancer

    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;
        }
        // createClass()方法返回类名,而不是实例;newEnhancer(configClass, classLoader)方法为CGLIB的增强过程
        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()方法,它会创建一个CGLIB的增强器:

 private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
        Enhancer enhancer = new Enhancer();
         // configSuperClass代表有@Configration注解的类
        enhancer.setSuperclass(configSuperClass);
        enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
        enhancer.setUseFactory(false);
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
        // 对应的CallbackFilter为new BeanMethodInterceptor()、new BeanFactoryAwareMethodInterceptor()、NoOp.INSTANCE
        enhancer.setCallbackFilter(CALLBACK_FILTER);
        enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
        return enhancer;
    }
/**
     * 这里返回了类名,而不是类的实例,最后的实例化是Spring做的
     */
    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;
    }

        看registerStaticCallbacks()方法的入参CALLBACKS:

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

         会走到BeanMethodInterceptor 拦截器,实现了MethodInterceptor接口,重写了intercept()方法:

private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {

        /**
         * 会进入intercept()方法
         */
        @Override
        @Nullable
        public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                    MethodProxy cglibMethodProxy) throws Throwable {

            // enhancedConfigInstance是代理对象
            ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
            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;
                }
            }

                // 判断是否为FactoryBean类型的实例,如果一个对象加上”&“前缀和对象名称,拿到的是实现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) {
                    // Scoped proxy factory beans are a special case and should not be further proxied
                }
                else {
                    // It is a candidate FactoryBean - go ahead with enhancement
                    // 如果是FactoryBean对象,在这进行代理增强
                    return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
                }
            }

            // 只有@Bean注解的方法是被Spring调用的时候,才会走下来
            // 之前反射调用FactoryMethod的时候把FactoryMethod的值放到ThreadLocal中,当用代理对象调的时候,ThreadLocal中才有值
            if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
                
                ----省略无关代码---- 

                // 调用被代理方法
                return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
            }
            // 用代理对象手动调用@Bean注解方法是才会执行到这里
            return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
        }

        如果是代理对象手动调用,会走下面的方法:

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
                ConfigurableBeanFactory beanFactory, String beanName) {

          ----省略无关代码---- 

                // TODO 核心代码就是这beanFactory.getBean(beanName),拿到的是同一个对象,因为有缓存
                Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
                        beanFactory.getBean(beanName));
                if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
                    // Detect package-protected NullBean instance through equals(null) check
                    if (beanInstance.equals(null)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug(String.format("@Bean method %s.%s called as bean reference " +
                                    "for type [%s] returned null bean; resolving to null value.",
                                    beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
                                    beanMethod.getReturnType().getName()));
                        }
                        beanInstance = null;
                    }
                    
            ----省略无关代码---- 

                // 将拿到的实例返回
                return beanInstance;

        如果是FactoryBean类型的实例,即&+beanName的形式,进入enhanceFactoryBean() 方法,同样也是CGLIB代理:

private Object createCglibProxyForFactoryBean(final Object factoryBean,
                final ConfigurableBeanFactory beanFactory, final String beanName) {

            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(factoryBean.getClass());
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setCallbackType(MethodInterceptor.class);

            // Ideally create enhanced FactoryBean proxy without constructor side effects,
            // analogous to AOP proxy creation in ObjenesisCglibAopProxy...
            Class<?> fbClass = enhancer.createClass();
            Object fbProxy = null;

            if (objenesis.isWorthTrying()) {
                try {
                    fbProxy = objenesis.newInstance(fbClass, enhancer.getUseCache());
                }
                catch (ObjenesisException ex) {
                    logger.debug("Unable to instantiate enhanced FactoryBean using Objenesis, " +
                            "falling back to regular construction", ex);
                }
            }

            if (fbProxy == null) {
                try {
                    fbProxy = ReflectionUtils.accessibleConstructor(fbClass).newInstance();
                }
                catch (Throwable ex) {
                    throw new IllegalStateException("Unable to instantiate enhanced FactoryBean using Objenesis, " +
                            "and regular FactoryBean instantiation via default constructor fails as well", ex);
                }
            }
            // CGLIB的Callback赋值给代理对象,代理对象调方法,走的是切面,会进入这里
            ((Factory) fbProxy).setCallback(0, (MethodInterceptor) (obj, method, args, proxy) -> {
                // 如果是getObject()方法,调用getBean(beanName)
                if (method.getName().equals("getObject") && args.length == 0) {
                    return beanFactory.getBean(beanName);
                }
                // 如果调用的是非getObject方法,会调对象本身(原始实例)
                return proxy.invoke(factoryBean, args);
            });

            return fbProxy;
        }

        总结,如果一个类加@Configration注解,CGLIB会生成字节码代理对象 lisonFactory.getLison()是通过代理对象this.lison() 调用的。然后用切面赋值给了 lisonFactory.setLison(this.lison()),两个走的都是beanFactory.getBean()方法,从缓存中拿,所以实例是同一个。
       同样, lili会生成一个代理,因为代理对象调getObject()方法要走切面,advice生成同一个实例,然后通过beanFactory.getBean()方法返回实例。不管调用有@Bean注解的方法几次,生成的代理都是同一个。
        而@Component注解却是对象本身,调用一次生成一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值