解析Spring中Configuration和Component区别
我们在往spring中添加组件时,会用到@Configuration,@Component 等注解。这些注解有什么区别,下面我们来一探究竟。
还是老样子,我们源码研究加例子。
@Configuration和@Component的表面区别
Configuration和Component注解的定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
我们通过对比发现@Configuration
定义的时候竟然用到了@Component
注解
@Configuration和@Component本质区别
我们这里先讲结论,不想看的小伙伴可以直接记住结论即可
,接着我们将带着结论来验证是否正确。
首先我们应该明白上面提到的注解都时只能用于类的,而不能修饰方法或者字段。结论
:@Configuration和@Component修饰的类都是配置,但是使用@Configuration修饰的类最后产生的类是一个CGlib代理类,这个类里面的方法都会得到增强
代码验证
这里省略了spring环境的搭建
在Config,java中分别使用 @Configuration
和@Component
注解来看现象
People.java
public class People {
private String name;
private Car car ;
//省略getter/setter
}
Car.java
public class Car {
}
Config.java
@Configuration//第一次使用@Configuration,第二次我们将这里换成@Component注解
public class Config {
@Bean
public Car car(){
System.out.println("Config.car");
return new Car();
}
@Bean
public People people(){
System.out.println("Config.people");
People people = new People();
people.setCar(car());
return people;
}
}
Main.java
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Config.class);
People people = app.getBean(People.class);
Car peopleCar = people.getCar();
Car car = app.getBean(Car.class);
System.out.println(peopleCar == car ? "同一个car":"不同的car");
}
}
运行结果:第一次使用@Configuration
Config.car
Config.people
同一个car
第二次使用@Component
Config.car
Config.people
Config.car
不同的car
通过对比我们发现使用@Configuration注解两次的car是同一个,而使用@Component注解时,使用的竟然不是同一个car.
结论我已经告诉大家了,使用@Configuration注解时,,生成的代理类会对里面的方法进行拦截,因此第二次取car时,直接是在容器中取出来的,而不是new的。延伸思考
:当配置类使用@Controller,@Service,@Repository时是什么情况?
和@Component是一样的
在Spring2.5引入了更多典型化注解(stereotype annotations):
@Component、@Service和@Controller,
其中@Component是所有受Spring管理组件的通用形式:而@Repository、@Service和@Controller则是@Component的细化。用来表示更具体的用例(例如,分别对应了持久化息、服务运和表现层)也就是说。你能用@Component来注解你的组件类。
但如果用@Repositary、@Service或QController来注解它们。也许你的类也许能更好地被工具处理,或与切面进行关联
例如,这些典型化注解可以成为理想的切入点目标。
当然,在SoringFrameWork以后的版本中。
@Repository@Service程Controller也许还能携带更多语义!
如此一来有如果你正在考虑服务层中是该用@Component还是@Service时那@Service显然是更好的选择。
同样的,就像前面所说的那样。@Repository已经能在持久化是中进行异常终换时被作为标记使用了.
解密
在ConfigurationClassPostProcessor#enhanceConfigurationClasses
中
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//判断是否加了Configuration的类,如果是就加到configBeanDefs中,
//加了这个注解的类就叫做完整配置类
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
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.isWarnEnabled() && beanFactory.containsSingleton(beanName))
logger.warn("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);
}
}
// nothing to enhance -> return immediately
if (configBeanDefs.isEmpty()) 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);
try {
// Set enhanced subclass of the user-specified bean class
// 设置用户指定bean类的增强子类
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
if (configClass != null) {
// enhance增强,生成代理类,具体怎样增强就在enhance方法中
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isDebugEnabled())
logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
//将代理类放到BeanDefinition中,我们用Class来描述java类,用Spring中的bean用什么描述呢?
//答案就是BeanDefinition
beanDef.setBeanClass(enhancedClass);
}
}
}
catch (Throwable ex) {
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
在ConfigurationClassUtils
中用来判断是否是增强配置类的方法
/**
* Determine whether the given bean definition indicates a full {@code @Configuration}
* class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
*/
//判断是否是增强配置类
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
/**
* Determine whether the given bean definition indicates a lite {@code @Configuration}
* class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
*/
//判断是否是精简配置类
public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
在该类中我们发现,标注了 Component
ComponentScan
Import
ImportResource
注解的类为精简配置类
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) return false;
// Any of the typical annotations found?
//遍历candidateIndicators看看是否存在里面的注解
for (String indicator : candidateIndicators) if (metadata.isAnnotated(indicator)) return true;
// Finally, let's look for @Bean methods...
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;
}
}
查看具体是怎样增强的
我们回到 Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
这行代码前,来看看它是怎么做的
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
if (logger.isDebugEnabled()) logger.debug(String.format("Ignoring request to enhance %s as it has " +
"already been enhanced. This usually indicates that more than one " +
"ConfigurationClassPostProcessor has been registered (e.g. via " +
"<context:annotation-config>). This is harmless, but you may " +
"want check your configuration and remove one CCPP if possible",
configClass.getName()));
return configClass;
}
//这里创建了代理类
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isDebugEnabled()) logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
return enhancedClass;
}
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass); //将配置类设置成父类,cglib基于继承
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); //将其的接口设置为EnhancedConfiguration加强功能
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 加强点 BeanMethodInterceptor BeanFactoryAwareMethodInterceptor 内部执行 intercept(),在方法前后进行拦截
enhancer.setCallbackFilter(CALLBACK_FILTER);//这里是重点,也就是方法拦截器的回调方法
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// Registering callbacks statically (as opposed to thread-local)
// is critical for usage in an OSGi environment (SPR-5932)...
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
return subclass;
}
//CALLBACK_FILTER主要为 BeanMethodInterceptor 和 BeanFactoryAwareMethodInterceptor,都是私有静态内部类。
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),//逻辑
new BeanFactoryAwareMethodInterceptor(),//设置属性 $$beanFactory
NoOp.INSTANCE
};
我们点到BeanMethodInterceptor中,查看具体的实现
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) beanName = scopedBeanName;
}
// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
// It is a candidate FactoryBean - go ahead with enhancement
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
//检查给定的方法是否对应于容器当前调用的工厂方法。比较方法名和参数类型只是为了解决协变返回类型的潜在问题(目前只知道发生在Groovy类上)。--来自有道翻译。。。
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isWarnEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType()))
logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
//invoke方法调用的对象没有增强过,invokeSuper方法调用的对象已经是增强了的,
// 所以会再走一遍 MyMethodInterceptor的 interceptor方法,
// 如果是个拦截器链条,就会重新在走一次拦截器链
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
//在容器中获取bean
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
总结
1.当一个类上面加了注解 @Configuration
时候,初始化ApplicationContext
的时候, invokeBeanDefinitionRegistryPostProcessors
方法解析此类,则从map里面遍历 BeanDefinition
,判断当前BeanDefinition
的map里面的属性key -configurationClass 值是full
还是lite
,如果为null,则继续解析,如果加了@Configuration 的值设置为full;
2.接下来 调用 invokeBeanFactoryPostPorcessors
,此方法遍历所有的BeanFactoryPostPorcessors
(包括自定义的可以修改spring工厂) 在遍历中会调用到enhanceConfigurationClass
,此方法中获取regist里面的所有类,遍历判断 isfullConfigurationClass
是否为full,若为full,存到一个LinkedHashMap 中,为lite则不存,若map为null ,则返回之后直接new 对象;若map 不为null,则去完成cglib代理的实现。
3.cglib代理的实现:调用enhance()
,判断该类是否实现EnhancedConfiguration
,完成代理则会让该类去实现此接口;若没有被代理则去实现代理 enhancer.create()
创建代码对象。
4.拦截器方法中执行增强逻辑,判断当前线程调用的方法和动态代理调用的方法名称是否一致,一致的说明是初次实例化调用,invoke调用父类方法进行实例化,不一致说明不是初次示例化调用,从BeanFactory中获取。
因此使用@Configuration修饰的类最后产生的类是一个CGlib代理类,这个类里面的方法会得到增强。这就是区别