@Configuration public class UserConfig { @Value("${user.name}") private String userName; @Bean public UserManager userManager() { UserManager userManager = new UserManager(userName); return userManager; } }@Configuration 加 @Bean 会创建一个 userName 不为 null 的 UserManager 对象
@Component public class UserManager { private String userName; public UserManager() { System.out.println("User 空构造方法 this = " + this); } public UserManager(String userName) { this.userName = userName; System.out.println("User 非空构造方法 this = " + this); } public String getUserName(){ return userName; } }@Component 也会创建一个 userName 为 null 的 UserManager 对象
验证方式有很多,可以 debug 跟源码,看看 Spring 容器中到底有几个 UserManager 对象,也可以直接从 UserManager 构造方法下手,看看哪几个构造方法被调用等等。我们从构造方法下手,看看 UserManager 到底实例化了几次?
备注:spring-boot-starter-parent版本2.0.3.RELEASE
2024-04-13 12:43:12.682 INFO 8184 --- [ main] org.example.TestApplication : Starting TestApplication on weijsh with PID 8184 (E:\IdeaProjects\self\target\classes started by yian in E:\IdeaProjects\self) 2024-04-13 12:43:12.687 INFO 8184 --- [ main] org.example.TestApplication : No active profile set, falling back to default profiles: default 2024-04-13 12:43:12.777 INFO 8184 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5ad851c9: startup date [Sat Apr 13 12:43:12 CST 2024]; root of context hierarchy 2024-04-13 12:43:13.627 INFO 8184 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'userManager' with a different definition: replacing [Generic bean: class [org.example.entity.UserManager]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:\IdeaProjects\self\target\classes\org\example\entity\UserManager.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=userConfig; factoryMethodName=userManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/example/config/UserConfig.class]] 2024-04-13 12:43:14.624 INFO 8184 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2024-04-13 12:43:14.661 INFO 8184 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2024-04-13 12:43:14.661 INFO 8184 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.31 2024-04-13 12:43:14.667 INFO 8184 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [D:\software\jdk\jdk1.8.0_181\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;D:\software\vmware\bin\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;D:\software\jdk\jdk1.8.0_181\bin;D:\software\maven\apache-maven-3.6.0\bin;D:\software\git\cmd;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;;.] 2024-04-13 12:43:14.767 INFO 8184 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2024-04-13 12:43:14.767 INFO 8184 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1991 ms 2024-04-13 12:43:14.982 INFO 8184 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/] 2024-04-13 12:43:14.988 INFO 8184 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] 2024-04-13 12:43:14.988 INFO 8184 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2024-04-13 12:43:14.989 INFO 8184 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*] 2024-04-13 12:43:14.989 INFO 8184 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*] User 非空构造方法 this = org.example.entity.UserManager@14b030a0 2024-04-13 12:43:15.348 INFO 8184 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2024-04-13 12:43:15.686 INFO 8184 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5ad851c9: startup date [Sat Apr 13 12:43:12 CST 2024]; root of context hierarchy 2024-04-13 12:43:15.749 INFO 8184 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2024-04-13 12:43:15.750 INFO 8184 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2024-04-13 12:43:15.780 INFO 8184 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2024-04-13 12:43:15.781 INFO 8184 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2024-04-13 12:43:16.164 INFO 8184 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2024-04-13 12:43:16.224 INFO 8184 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2024-04-13 12:43:16.229 INFO 8184 --- [ main] org.example.TestApplication : Started TestApplication in 4.242 seconds (JVM running for 6.265) 2024-04-13 12:43:22.723 INFO 8184 --- [ Thread-10] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5ad851c9: startup date [Sat Apr 13 12:43:12 CST 2024]; root of context hierarchy 2024-04-13 12:43:22.733 INFO 8184 --- [ Thread-10] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown只有有参构造方法被调用了,无参构造方法岿然不动(根本没被调用),答案也就清晰了,没得选了呀,只能是 @Configuration加 @Bean 创建的 的 UserManager 对象
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { /** * Explicitly specify the name of the Spring bean definition associated * with this Configuration class. If left unspecified (the common case), * a bean name will be automatically generated. * <p>The custom name applies only if the Configuration class is picked up via * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}. * If the Configuration class is registered as a traditional XML bean definition, * the name/id of the bean element will take precedence. * @return the suggested component name, if any (or empty String otherwise) * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator */ @AliasFor(annotation = Component.class) String value() default ""; }@Configuration能够修饰Class、interface和enum,用的最多的还是标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,用于配置spring容器,@Configuration往往会结合@Bean来使用,组成了基于Java类的配置,是spring的推荐配置方式。
其中 ConfigurationClassPostProcessor 与 @Configuration 息息相关,其类继承结构图如下:
它实现了BeanFactoryPostProcessor接口和PriorityOrdered接口,关于BeanFactoryPostProcessor,那么我们从AbstractApplicationContext的refresh方法调用的invokeBeanFactoryPostProcessors(beanFactory)开始,来跟下源码:
for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition( ).get0riginatingBeanDefinition( ); if (bdCand == null) { bdCand = holder.getBeanDefinition( ); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName( ),holder.getBeanName()); } } }循环递归处理 UserConfig 、 UserController 和 UserManager ,把它们都封装成 ConfigurationClass ,递归扫描 BeanDefinition 循环完之后,我们来看看 configClasses
#堆代码 duidaima.com 2024-04-13 14:29:36.185 INFO 8956 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'userManager' with a different definition: replacing [Generic bean: class [org.example.entity.UserManager]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:\IdeaProjects\self\target\classes\org\example\entity\UserManager.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=userConfig; factoryMethodName=userManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/example/config/UserConfig.class]]Spring 升级优化
Description: The bean 'userManager', defined in class path resource [org/example/config/UserConfig.class], could not be registered. A bean with that name has already been defined in file [E:\IdeaProjects\self\target\classes\org\example\entity\UserManager.class] and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true我们来跟下源码,主要看看与 Spring 5.0.7.RELEASE 的区别