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---推荐