《Spring 高手系列》(@Configuration )(@Bean)笔记

前言

参考链接1

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-java-basic-concepts

相关问题

注册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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值