何时为Full模式
标注有@Configuration注解的类被称为full模式的配置类。自Spring5.2后这句话改为下面这样我觉得更为精确些:
- 标注有@Configuration或者@Configuration(proxyBeanMethods = true)的类被称为Full模式的配置类
- (当然喽,proxyBeanMethods属性的默认值是true,所以一般需要Full模式我们只需要标个注解即可)
优缺点
优点:
- 可以支持通过常规Java调用相同类的@Bean方法而保证是容器内的Bean,这有效规避了在“Lite模式”下操作时难以跟踪的细微错误。特别对于萌新程序员,这个特点很有意义
缺点:
- 运行时会给该类生成一个CGLIB子类放进容器,有一定的性能、时间开销(这个开销在Spring Boot这种拥有大量配置类的情况下是不容忽视的,这也是为何Spring 5.2新增了proxyBeanMethods属性的最直接原因)
- 正因为被代理了,所以@Bean方法 不可以是private、不可以是final
代码示例
主配置:
@ComponentScan("com.yourbatman.fullliteconfig.fullconfig") @Configuration public class AppConfig { }
准备一个Full模式的配置:
@Configuration public class FullConfig { @Bean public User user() { User user = new User(); user.setName("A哥-lite"); user.setAge(18); return user; } @Bean protected User user2() { User user = new User(); user.setName("A哥-lite2"); user.setAge(18); // 模拟依赖于user实例 看看是否是同一实例 System.out.println(System.identityHashCode(user())); System.out.println(System.identityHashCode(user())); return user; } public static class InnerConfig { @Bean // private final User userInner() { // 只在lite模式下才好使 public User userInner() { User user = new User(); user.setName("A哥-lite-inner"); user.setAge(18); return user; } } }
测试用例:
public class Application { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 配置类情况 System.out.println(context.getBean(FullConfig.class).getClass()); System.out.println(context.getBean(FullConfig.InnerConfig.class).getClass()); String[] beanNames = context.getBeanNamesForType(User.class); for (String beanName : beanNames) { User user = context.getBean(beanName, User.class); System.out.println("beanName:" + beanName); System.out.println(user.getClass()); System.out.println(user); System.out.println("------------------------"); } } }
结果输出:
550668305 550668305 class com.yourbatman.fullliteconfig.fullconfig.FullConfig$$EnhancerBySpringCGLIB$$70a94a63 class com.yourbatman.fullliteconfig.fullconfig.FullConfig$InnerConfig beanName:userInner class com.yourbatman.fullliteconfig.User User{name='A哥-lite-inner', age=18} ------------------------ beanName:user class com.yourbatman.fullliteconfig.User User{name='A哥-lite', age=18} ------------------------ beanName:user2 class com.yourbatman.fullliteconfig.User User{name='A哥-lite2', age=18} ------------------------
小总结
- 该模式下,配置类会被CGLIB增强(生成代理对象),放进IoC容器内的是代理
- 该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
- 该模式下,配置类内部可以通过方法调用来处理依赖,并且能够保证是同一个实例,都指向IoC内的那个单例
- 该模式下,@Bean方法不能被private/final等进行修饰(很简单,因为方法需要被复写嘛,所以不能私有和final。defualt/protected/public都可以哦),否则启动报错(其实IDEA编译器在编译器就提示可以提示你了):
Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'user2' must not be private or final; change the method's modifiers to continue Offending resource: class path resource [com/yourbatman/fullliteconfig/fullconfig/FullConfig.class] at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72) at org.springframework.context.annotation.BeanMethod.validate(BeanMethod.java:50) at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:220) at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:211) at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:326) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:242) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89) at com.yourbatman.fullliteconfig.Application.main(Application.java:11)
使用建议
了解了Spring配置类的Full模式和Lite模式,那么在工作中我该如何使用呢?这里A哥给出使用建议,仅供参考:
- 如果是在公司的业务功能/服务上做开发,使用Full模式
- 如果你是个容器开发者,或者你在开发中间件、通用组件等,那么使用Lite模式是一种更被推荐的方式,它对Cloud Native更为友好
思考题?
通过new AnnotationConfigApplicationContext(AppConfig.class)直接放进去的类,它会成为一个IoC的组件吗?若会,那么它是Full模式 or Lite模式呢?是个固定的结果还是也和其标注的注解有关呢?
本思考题不难,自己试验一把便知,建议多动手~
总结
本文结合代码示例阐述了Spring配置中Full模式和Lite模式,以及各自的定义和优缺点。对于一般的小伙伴,掌握本文就够用了,并且足够你面试中吹x。但A哥系列文章一般不止于“表面”嘛,下篇文章将从原理层面告诉你Spring是如何来巧妙的处理这两种模式的,特别是会结合Spring 5.2.0新特性,以及对比Spring 5.2.0的实现和之前版本有何不同,你课订阅我的公众号保持关注。