• Springcloud学习笔记47@Configuration与@Bean注解的原理以及@Configuration是如何被处理的;@Component和@Bean的区别


    1.@Configuration是如何被处理的

    1.1 从SpringApplication应用角度

    一般情况下启动SpringBoot都是新建一个类包含main方法,然后使用SpringApplication.run来启动程序,例如下面代码:

    @SpringBootApplication
    public class AutoConfigApplication {
    
        public static void main(String[] args){
            ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(AutoConfigApplication.class,args);
        }
    }

    SpringApplication.run接收两个参数分别为:primarySource、运行参数(args),上面的代码使用AutoConfigApplication.class作为primarySource。SpringApplication还有一个实例方法也叫run,SpringBoot的大部分启动都由实例run方法来完成的,其中构造ApplicationContext由createApplicationContext方法完成:

    protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    switch(this.webApplicationType) {
                    case SERVLET:
                        contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                        break;
                    case REACTIVE:
                        contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                        break;
                    default:
                        contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                    }
                } catch (ClassNotFoundException var3) {
                    throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
                }
            }
    
            return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
        }

    创建AnnotationConfigApplicationContext的时候会调用默认构造方法

        public AnnotationConfigApplicationContext() {
            this.reader = new AnnotatedBeanDefinitionReader(this);
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }

    AnnotationConfigApplicationContext默认构造函数创建两个对象:

    • reader(AnnotatedBeanDefinitionReader):用于手动注册bean
    • scanner(ClassPathBeanDefinitionScanner): 用于扫描Component、Repository、Service等注解

    AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner会注册一些注解处理器,注册的方式都是使用AnnotationConfigUtils的registerAnnotationConfigProcessors方法

    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
                BeanDefinitionRegistry registry, @Nullable Object source) {
    
            ...
            
            if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
                RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
                def.setSource(source);
                beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
            }
            ...
            return beanDefs;
        }

    构造完ApplicationContext后SpringApplicaiton紧接着会加载primarySource,上面提到 过primarySource是在运行的时候传递进来的(AutoConfigApplication.class),加载过程中不贴代码了,只要知道最终ApplicaitonContext中会多一个AutoConfigApplication的BeanDefinition:

    总的来说SpringApplicaiton主要干了这些事:

    • 创建AnnotationConfigApplicationContext
    • 加载一些处理注解的后处理器如:ConfigurationClassPostProcessor
    • primarySource加载进ApplicationContext

    最重要的一点是,现在是有一个AnnotationConfigApplicationContext里面包含了primarySource(AutoConfigApplication)以及ConfigurationClassPostProcessor。打个断点在ApplicaitonContext刷新之前打印下context中的bean的名称,可以确定这样说没毛病!

    1.2  @Configuration啥时候被解析?

    虽说有了primarySource和ConfigurationClassPostProcessor后处理器,还是需要有个执行的入口。ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,BeanDefinitionRegistryPostProcessor会在ApplicationContext的refresh操作时被处理:

    点进去refreshContext的源码可见如下:

    public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                ...
                invokeBeanFactoryPostProcessors(beanFactory);
                ...
            }
    }
        
    public static void invokeBeanFactoryPostProcessors(
                ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
            
            ...
            //找出所有类型为BeanDefinitionRegistryPostProcessor的bean的名称
             String[] postProcessorNames =
                        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            //执行BeanDefinitionRegistryPostProcessor
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            ...
    }
    
    private static void invokeBeanDefinitionRegistryPostProcessors(
            Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
    
        for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
            //调用postProcessBeanDefinitionRegistry方法
            postProcessor.postProcessBeanDefinitionRegistry(registry);
        }
    }    

    总结:@Configuration是在springboot应用启动时就已经解析处理了;

    2.@Configuration与@Bean注解的原理

    2.1 @Configuration与@Bean结合使用

    @Configuration与@Bean结合使用。@Configuration可理解为用spring的时候xml里面的<beans>标签,@Bean可理解为用spring的时候xml里面的<bean>标签。Spring Boot不是spring的加强版,所以@Configuration和@Bean同样可以用在普通的spring项目中,而不是Spring Boot特有的,只是在spring用的时候,注意加上扫包配置。

    @SpringBootApplication注解相当于使用@Configuration、@EnableAutoConfiguration和@ComponentScan的默认属性@ComponentScan默认为当前包与其子包。SpringBoot中的Spring容器在启动的时候,会扫描当前包与子包中所有实现@Component注解或者其子类如@Configuration(本质上还是@Component)标记的类,认为这些类是bean, 并且把这些bean对应的beanDefinition放到容器中进行管理。BeanDefinition是对bean的描述,里边存有bean的名称,Class等基本信息。

    在获取到所有的bean defenition之后,Spring会有一些post process执行,其中一个就是ConfigurationClassPostProcessor, 在这里,Spring会遍历所有的bean definition, 如果发现其中有标记了@Configuration注解的,会对这个类进行CGLIB代码,生成一个代理的类,并且把这个类设置到BeanDefenition的Class属性中。当需要拿到这个bean的实例的时候,会从这个class属性中拿到的Class对象进行反射,那么最终反射出来的是代理增强后的类。

    2.2 @Configuration 与@Component区别

    虽然Component注解也会当做配置类,但是并不会为其生成CGLIB代理Class。而@Configuration标注的配置类,会通过CGLIB代理Class(详见ConfigurationClassPostProcessor#enhanceConfigurationClasses),所以每次都是获取IOC容器中的固定的bean。

    @Bean就得看所在类是@Component标注,还是@Configuration标注了。

    在配置类的方法间调用时,如果类是@component标注的,每次调用获取的都是新的实体;而如果是@configuration标注的话,每次调用返回的是同一个实体Bean。其他方面都是相同,可以无差别使用(装配注入等)。

    这是因为@Configuration标注下的@Bean调用函数使用都是代理对象,获取的都是从IOC容器里获取的bean,因此都是同一个。而@Component标注下的@Bean下只是普通的函数方法调用,因此每次调用后,都不是同一个。

    @component 的作用:把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>。@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。

    泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。


    2.3 @Component和@Bean的区别

    两者的目的是一样的,都是注册bean到Spring容器中
    1、@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
    2、@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。

    区别:
    1、@Component(@Controller、@Service、@Repository)通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
    2、而@Bean注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
    3、@Component 作用于类,@Bean作用于方法

    Spring帮助我们管理Bean分为两个部分

    • 一个是注册Bean(@Component , @Repository , @ Controller , @Service , @Configration),
    • 一个装配Bean(@Autowired , @Resource,可以通过byTYPE(@Autowired)、byNAME(@Resource)的方式获取Bean)。
      完成这两个动作有三种方式,一种是使用自动配置的方式、一种是使用JavaConfig的方式,一种就是使用XML配置的方式。

    @Compent 作用就相当于 XML配置

    @Component
    public class Student {
    
        private String name = "lkm";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    @Bean 需要在配置类中使用,即类上需要加上@Configuration注解

    @Configuration
    public class WebSocketConfig {
        @Bean
        public Student student(){
            return new Student();
        }
    
    }

    两者都可以通过@Autowired装配

    @Autowired
    Student student;

    那为什么有了@Compent,还需要@Bean呢?
    如果你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component注解的,因此就不能使用自动化装配的方案了,但是我们可以使用@Bean,当然也可以使用XML配置。

    参考文献:

    https://www.cnblogs.com/xwgblog/p/11885790.html--推荐

    https://www.cnblogs.com/daimzh/p/12886161.html---推荐

  • 相关阅读:
    Thinkphp6笔记十八:默认分页2
    Thinkphp6笔记十八:默认分页1
    Thinkphp6笔记十七:模板标签使用
    Thinkphp6笔记十六:IP黑名单
    Thinkphp6笔记十五:模板路径自定义配置
    Thinkphp6笔记十四:Redis配置
    Thinkphp6笔记十三:验证器(模型验证)
    Thinkphp6笔记十三:验证器(场景验证)
    Thinkphp6笔记十三:验证器(控制器验证)
    Thinkphp6笔记十二:多数据库配置
  • 原文地址:https://www.cnblogs.com/luckyplj/p/16424892.html
Copyright © 2020-2023  润新知