前言
相关问题
注册bean的类上加不加@Configuration 有什么区别?
@Configuration
Configuration 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
//给这个注解的value赋值相当于给Component的value赋值
@AliasFor(annotation = Component.class)
String value() default "";
//是否代理bean方法
boolean proxyBeanMethods() default true;
}
Component 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
Indexed 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Indexed {
}
@Bean
//作用在方法上和注解上
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
//value跟name互为别名
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
@Deprecated
Autowire autowire() default Autowire.NO;
//候选bean
boolean autowireCandidate() default true;
//初始化方法
String initMethod() default "";
//销毁方法
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
案例1 同时使用@Configuration和@Bean
User
public class User {
}
ConfigBean
@Configuration
public class ConfigBean {
@Bean
public User user1() {
return new User();
}
@Bean("user2Bean")
public User user2() {
return new User();
}
@Bean({"user3Bean", "user3BeanAlias1", "user3BeanAlias2"})
public User user3() {
return new User();
}
}
启动类
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean.class);
for (String beanName : context.getBeanDefinitionNames()) {
String[] aliases = context.getAliases(beanName);
System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
beanName,
Arrays.asList(aliases),
context.getBean(beanName)));
}
}
}
案例2 仅使用@Bean
去掉上面案例1中的@Configuration
public class ConfigBean1 {
@Bean
public User user1() {
return new User();
}
@Bean("user2Bean")
public User user2() {
return new User();
}
@Bean({"user3Bean", "user3BeanAlias1", "user3BeanAlias2"})
public User user3() {
return new User();
}
}
对比一下区别
@Configuration
bean名称:configBean,别名:[],bean对象:com.example.lurenjia.spring.c17.ConfigBean$$EnhancerBySpringCGLIB$$99985e8b@5ccddd20
bean名称:user1,别名:[],bean对象:com.example.lurenjia.spring.c17.User@1ed1993a
bean名称:user2Bean,别名:[],bean对象:com.example.lurenjia.spring.c17.User@1f3f4916
bean名称:user3Bean,别名:[user3BeanAlias2, user3BeanAlias1],bean对象:com.example.lurenjia.spring.c17.User@794cb805
没有@Configuration
bean名称:configBean1,别名:[],bean对象:com.example.lurenjia.spring.c17.ConfigBean1@460d0a57
bean名称:user1,别名:[],bean对象:com.example.lurenjia.spring.c17.User@47d90b9e
bean名称:user2Bean,别名:[],bean对象:com.example.lurenjia.spring.c17.User@1184ab05
bean名称:user3Bean,别名:[user3BeanAlias2, user3BeanAlias1],bean对象:com.example.lurenjia.spring.c17.User@3aefe5e5
结果对比
ConfigBean$$EnhancerBySpringCGLIB$$99985e8b@5ccddd20
ConfigBean1@460d0a57
由于 @Configuration 里面proxyBeanMethods默认为true,我们将其改为false后测试。
@Configuration(proxyBeanMethods = false)
bean名称:configBean,别名:[],bean对象:com.example.lurenjia.spring.c17.ConfigBean@5a4aa2f2
结论: proxyBeanMethods true和false会影响对象是否被cglib所代理。
案例3 依赖关系的bean
ServiceA
public class ServiceA {
}
ServiceB,依赖ServiceA
public class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
@Override
public String toString() {
return "ServiceB{" +
"serviceA=" + serviceA +
'}';
}
}
配置类
@Configuration()
public class ConfigBean2 {
@Bean
public ServiceA serviceA() {
System.out.println("调用serviceA()方法");
return new ServiceA();
}
@Bean
ServiceB serviceB1() {
System.out.println("调用serviceB1()方法");
ServiceA serviceA = this.serviceA();
return new ServiceB(serviceA);
}
@Bean
ServiceB serviceB2() {
System.out.println("调用serviceB2()方法");
ServiceA serviceA = this.serviceA();
return new ServiceB(serviceA);
}
}
测试代码
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean2.class);
for (String beanName : context.getBeanDefinitionNames()) {
//别名
String[] aliases = context.getAliases(beanName);
System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
beanName,
Arrays.asList(aliases),
context.getBean(beanName)));
}
}
@Configuration存在的输出
@Configuration(proxyBeanMethods = false) 或者 去掉 @Configuration 效果图
总结:@Configuration() 最关键的作用就是使用cglib来生成代理对象,在某些单例场景下只会创建一次对象。
proxyBeanMethods = false 和 去掉注解都会使得cglib失效,从而创建多次bean。
原理
org.springframework.context.annotation.ConfigurationClassPostProcessor
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory)
enhanceConfigurationClasses
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty() || IN_NATIVE_IMAGE) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}
ConfigurationClassEnhancer
官方示例
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
Annotating a class with @Configuration indicates that its primary purpose is as a source of bean definitions.
@Configuration的作用是作为 bean definitions的一个来源。
Full @Configuration vs “lite” @Bean mode?
根据@Configuration的有无可以分为 Full和lite mode,在lite mode下无法使用CGLIB增强。
AnnotationConfigApplicationContext
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
不仅仅是@Configuration 注解,JSR-330其他注解修饰的类也可以。
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class,
Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
无参构造,使用编程的方式来注册
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}