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是自定义bean的添加入口;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); } }
此时,看下ConfigurationClassPostProcessor对该接口的实现;
@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); processConfigBeanDefinitions(registry); }
接下来就是关键部分,通过名称可以看出下面方法的作用:处理配置类的Bean.
processConfigBeanDefinitions(registry);
代码:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
我们一步一步看下做了什么工作:
Parse1:
从当前的Bean 定义文件集合中查找@Configuration修饰的类,如果没有找到,直接返回.
for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; }
Parse2:
对Configuration类进行排序.
// Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
Parse3:
检查是否有配置的Bean 名称生成策略.
SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } }
Parse4:
设置环境上下文.
if (this.environment == null) { this.environment = new StandardEnvironment(); }
Parse5:
解析@Configuration配置类
ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty());
该过程主要分为两步:
- 解析@Configuration,根据配置扫描该路径下的@Configuration,@Controller,@Service,@Component等注解.
- 通过ConfigurationClassBeanDefinitionReader添加到当前registry中.
//扫描 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); parser.parse(candidates); //添加bean definition 到registry. // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses);
处理过程交由ConfigurationClassBeanDefinitionReader,我们接下来看ConfigurationClassBeanDefinitionReader 如何完成@Configuration的解析.
接着看 ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass源码
/** * Read a particular {@link ConfigurationClass}, registering bean definitions * for the class itself and all of its {@link Bean} methods. */ private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); return; } if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
通过注释可以看出该方法是将一个ConfigurationClass和它内部所有被@Bean标记的方法,变成bean definition 注册到registry.
到现在我们终于看到了@Configuration类的解析.
从代码我们可以看出对@Configuration类的解析主要分为四个部分:
- 如果当前类是通过@Import被别的配置类引入,则将当前类转化成bean definition注册.
- 将当前类的所有被@Bean标记方法,转化为bean definition注册到当前registry
- 解析当前类上@ImportResource
- 解析当前类引入的ImportBeanDefinitionRegistrar接口的子类.
我们接下来重点说一下,对@Bean标记的方法的解析
/** * Read the given {@link BeanMethod}, registering bean definitions * with the BeanDefinitionRegistry based on its contents. */ private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { ConfigurationClass configClass = beanMethod.getConfigurationClass(); MethodMetadata metadata = beanMethod.getMetadata(); String methodName = metadata.getMethodName(); // Do we need to mark the bean as skipped by its condition? if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { configClass.skippedBeanMethods.add(methodName); return; } if (configClass.skippedBeanMethods.contains(methodName)) { return; } AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class); Assert.state(bean != null, "No @Bean annotation attributes"); // Consider name and any aliases List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name"))); String beanName = (!names.isEmpty() ? names.remove(0) : methodName); // Register aliases even when overridden for (String alias : names) { this.registry.registerAlias(beanName, alias); } // Has this effectively been overridden before (e.g. via XML)? if (isOverriddenByExistingDefinition(beanMethod, beanName)) { if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) { throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!"); } return; } ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata); beanDef.setResource(configClass.getResource()); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); if (metadata.isStatic()) { // static @Bean method beanDef.setBeanClassName(configClass.getMetadata().getClassName()); beanDef.setFactoryMethodName(methodName); } else { // instance @Bean method beanDef.setFactoryBeanName(configClass.getBeanName()); beanDef.setUniqueFactoryMethodName(methodName); } beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata); Autowire autowire = bean.getEnum("autowire"); if (autowire.isAutowire()) { beanDef.setAutowireMode(autowire.value()); } String initMethodName = bean.getString("initMethod"); if (StringUtils.hasText(initMethodName)) { beanDef.setInitMethodName(initMethodName); } String destroyMethodName = bean.getString("destroyMethod"); beanDef.setDestroyMethodName(destroyMethodName); // Consider scoping ScopedProxyMode proxyMode = ScopedProxyMode.NO; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); if (attributes != null) { beanDef.setScope(attributes.getString("value")); proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; } } // Replace the original bean definition with the target one, if necessary BeanDefinition beanDefToRegister = beanDef; if (proxyMode != ScopedProxyMode.NO) { BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy( new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS); beanDefToRegister = new ConfigurationClassBeanDefinition( (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata); } if (logger.isDebugEnabled()) { logger.debug(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getMetadata().getClassName(), beanName)); } this.registry.registerBeanDefinition(beanName, beanDefToRegister); }
我们从源码可以看出,该方法是生成一个ConfigurationClassBeanDefinition,并将其注册到registry
主要信息设置如下:
- 设置Bean定义来源
- 设置工厂名称/工厂方法
- 设置Autowire方式
- 设置initMethod/destoryMethod
- 设置scope
也就是说,一个@Configuration类的@Bean会作为一个工厂bean的方式添加到registry中,在实例化该bean时,会调用该工厂类对应的方法.
总结:@Configuration是在springboot应用启动时就已经解析处理了;
(1) 发生的时机
在AbstractApplicationContext,refresh()方法,会触发对@Configuration类的解析,并生成对应的BeanDefinition注册到当前BeanDefinitionRegistry中.
invokeBeanFactoryPostProcessors(beanFactory);
(2) 解析内容
@Bean方法
@Import注解,引入的其他@Configuration类或ImportBeanDefinitionRegistrar子类
@ImportResource注解
(3) 当前@Configuration标记的类也会作为一个bean添加到BeanDefinitionRegistry
2.@Configuration与@Bean注解的原理
SpringBoot 推荐使用 java 配置完全代替 XML 配置,java 配置是通过 @Configration 和 @Bean 注解实现的。二者作用如下:
@Configration 注解:声明当前类是一个配置类,相当于 Spring 中的一个 XML 文件
@Bean 注解:作用在方法上,声明当前方法的返回值是一个 Bean
2.1 @Configuration与@Bean结合最基础用法
表示一个类声明了一个或多个@Bean方法,可能会被 Spring 容器处理以在运行时为这些 bean 生成 bean 定义和服务请求,例如:
@Configuration public class AppConfig { @Bean //表示一个方法产生了一个由Spring容器管理的bean public MyBean myBean() { System.out.println("=======Bean1========"); // 实例化、配置和返回 bean ... } }
当我们启动项目时,名字叫myBean的就会装配到Spring 容器管理的bean中。
2.2 @Bean 注解详解
@Bean 注解作用在方法上
@Bean 指示一个方法返回一个 Spring 容器管理的 Bean,也就是说方法返回值就是给Springr容器装配的bean
@Bean 一般和 @Component 或者 @Configuration 一起使用,也可以在 @Service 里使用,没有特定要求,主要看项目的需求。
@Bean 注解默认作用域为单例 singleton 作用域,可通过 @Scope(“prototype”) 设置为原型作用域
(1)默认情况下 Bean 名称就是方法名,比如下面 Bean 名称便是 myBean:
@Bean public MyBean myBean() { return new MyBean(); }
(2)@Bean 注解支持设置别名。比如下面除了主名称 myBean 外,还有个别名 myBean1(两个都可以使用)
@Bean("myBean1") public MyBean myBean() { return new MyBean(); }
2.3 @Configration 注解详解
1,使用说明
@Configration 注解作用在类、接口(包含注解)上
@Configuration 用于定义配置类,可替换 xml 配置文件
@Configration 注解类中可以声明一个或多个 @Bean 方法
2. 例子
在Configuration配置类里面可以声明多个Bean方法。bean和bean之间是可以联系的,比如bean4需要bean1等其它bean。只需要调用其方法就可以
import com.hkz.entity.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @ClassName MyConfig * @Description TODO * @Author zishibushui * @Date 2021/11/2 22:14 */ @Configuration public class MyConfig { /** * 该Bean即为方法的返回值,Bean的名字就是方法的名字。 * 如果@Bean{"b1","b2"},则Bean的名字就是b1,b2。不再是方法的名字了 */ @Bean public String bean1(){ System.out.println("=========Bean1========="); return "bean1"; } @Bean public User bean2(){ System.out.println("=======bean2测试========"); return new User(2L,"子时不睡2号","深圳"); } @Bean public User bean3(){ System.out.println("=======bean3测试========"); return new User(); } /** * bean()方法带有@Bean注解,它将用来在Spring应用上下文中声明String bean和User bean。 * 对于bean的任何调用都会被拦截,并且会返回应用上下文中的bean实例。 * @return */ @Bean public String bean4(){ System.out.println("=======bean4测试========"); /* * 在Configuration配置类里面可以声明多个Bean方法。bean和bean之间是可以联 * 系的,比如bean4需要bean1等其它bean。只需要调用其方法就可以 */ //输出的bean1 就是bean1方法的返回值 System.out.println(bean1()); System.out.println(bean2()); System.out.println(bean3()); return "bean4"; } }
2.4 @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.5 @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.6 @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://blog.csdn.net/qq_28136919/article/details/124714409 非常推荐
https://www.cnblogs.com/xwgblog/p/11885790.html--推荐
https://www.cnblogs.com/daimzh/p/12886161.html---推荐
https://blog.csdn.net/weixin_45755816/article/details/121424751