看源码标注是从 3.0 开始添加了这个注解,引入了 @Component,是 @Component 的升级。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see AnnotationBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
/**
* Specify whether {@code @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even
* in case of direct {@code @Bean} method calls in user code. This feature
* requires method interception, implemented through a runtime-generated CGLIB
* subclass which comes with limitations such as the configuration class and
* its methods not being allowed to declare {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
proxyBeanMethods() 从 spring 5.2 中开始添加,之前的版本没有此属性。
看代码 proxyBeanMethods() 的默认值是 true,看注释的意思是默认使用 cglib 生成代理类,如果设置为 false,禁止使用 cglib,返回的是一个普通 java 对象。
在 ConfigurationClassUtils 中发现 @Component、@ComponentScan、@Import、@ImportResource 是按照 @Configuration 的逻辑来处理的。即,无 proxyBeanMethods() 方法按照普通对象进行生成。
abstract class ConfigurationClassUtils {
public static final String CONFIGURATION_CLASS_FULL = "full";
public static final String CONFIGURATION_CLASS_LITE = "lite";
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
private static final String ORDER_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "order");
private static final Log logger = LogFactory.getLog(ConfigurationClassUtils.class);
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
/**
* Check whether the given bean definition is a candidate for a configuration class
* (or a nested component class declared within a configuration/component class,
* to be auto-registered as well), and mark it accordingly.
* @param beanDef the bean definition to check
* @param metadataReaderFactory the current factory in use by the caller
* @return whether the candidate qualifies as (any kind of) configuration class
*/
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
/**
* Check the given metadata for a configuration class candidate
* (or nested component class declared within a configuration/component class).
* @param metadata the metadata of the annotated class
* @return {@code true} if the given class is to be registered for
* configuration class processing; {@code false} otherwise
*/
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
static boolean hasBeanMethods(AnnotationMetadata metadata) {
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
/**
* Determine the order for the given configuration class metadata.
* @param metadata the metadata of the annotated class
* @return the {@code @Order} annotation value on the configuration class,
* or {@code Ordered.LOWEST_PRECEDENCE} if none declared
* @since 5.0
*/
@Nullable
public static Integer getOrder(AnnotationMetadata metadata) {
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
return (orderAttributes != null ? ((Integer) orderAttributes.get(AnnotationUtils.VALUE)) : null);
}
/**
* Determine the order for the given configuration class bean definition,
* as set by {@link #checkConfigurationClassCandidate}.
* @param beanDef the bean definition to check
* @return the {@link Order @Order} annotation value on the configuration class,
* or {@link Ordered#LOWEST_PRECEDENCE} if none declared
* @since 4.2
*/
public static int getOrder(BeanDefinition beanDef) {
Integer order = (Integer) beanDef.getAttribute(ORDER_ATTRIBUTE);
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
}
}
通过代码得知,proxyBeanMethods 设置为 true 时,采用的是 full 模式,通过 cglib 生成代理对象。
通过 ApplicationContext 获取到的对象里,使用注解 @Import 也发现了这个现象。
@Slf4j
@SpringBootApplication
@Import({
FeignConfig.class,
GlobalCorsConfig.class,
FlyWayConfig.class,
RedisConfig.class,
MyBatisConfig.class,
SwaggerConfig.class,
ValidatorConfig.class
})
public class Application {
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
System.out.println(beanDefinitionCount);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
String name = applicationContext.getBean(beanDefinitionName).getClass().getName();
if (StringUtils.containsIgnoreCase(beanDefinitionName, "mapper")) {
// System.out.println(beanDefinitionName);
// System.out.println(name);
System.out.println(String.format("%s %s", beanDefinitionName, name));
}
// System.out.println(String.format("%s %s", beanDefinitionName, name));
}
Environment env = applicationContext.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
String url = "http://" + ip + ":" + port + path;
log.info("----------------------------------------------------------");
log.info("Application ICan is running! Access URLs:");
log.info("Local : http://localhost:" + port + path);
log.info("External : " + url);
log.info("Swagger文档: http://" + ip + ":" + port + path + "doc.html");
log.info("----------------------------------------------------------");
}
}
其中一个配置类的名称和类的全路径名
com.xxx.cql.common.bean.mybatis.MyBatisConfig com.xxx.cql.common.bean.mybatis.MyBatisConfig
通过链接得知
https://blog.csdn.net/andybegin/article/details/116148537
full 模式,proxyBeanMethods 默认是true。
使用代理,也就是说该配置类会被代理,直接从IOC容器之中取得bean对象,不会创建新的对象。SpringBoot总会检查这个组件是否在容器中是否存在,保持组件的单实例。
lite 模式,proxyBeanMethods 设置为false。
每次调用@Bean标注的方法获取到的对象是一个新的bean对象,和之前从IOC容器中获取的不一样,SpringBoot会跳过检查这个组件是否在容器中是否存在,保持组件的多实例。
测试类如下
public class People {
}
public class Room {
private People people;
public People getPeople() {
return people;
}
public void setPeople(People people) {
this.people = people;
}
}
@Component
public class Test3 {
@Resource
private People people;
@Resource
private Room room;
@PostConstruct
public void test() {
System.out.println("测试功能"+(room.getPeople() == people));
}
}
// @Configuration
// @Configuration(proxyBeanMethods = false)
@Component
public class Config {
@Bean
public People people() {
return new People();
}
@Bean
public Room room() {
Room room = new Room();
room.setPeople(people());
return room;
}
}
@Configuration(proxyBeanMethods = false)
测试功能false
@Configuration
测试功能true
@Component
测试功能false
个人理解如下
@Configuration 注解里的 bean通过 cglib 生成子类进行字节码增强,注入的 bean 默认是单例的。
@Component 注解里的 bean是多例的,是普通的 java 对象。
如果设置 @Configuration 的 proxyBeanMethods 为 false,则与 @Component 一致。