你有没有一种需求,就是当某个外部条件成立的时候,就让spring创建bean,并让它管理,一定有的对吧,那么你一定要了解spring的@Conditional这个注解。你有时候有没有觉得@Configuration与@Component都可以把一个类让spring创建并管理,那么它们到底有什么区别呢。今天我就来和你聊聊这两个话题。
你一定在一些与spring整合的框架源码上,看到大量的@Conditional的注解,这个注解有个Class类型的数组属性,当你在这个注解上加上某个类的时候,其实意思就是你加的这个类必须实现Condition这个接口,这个接口上有个matchs方法,当方法的值返回为true的时候,则这个被标注Condition注解的类,才会正常解析并且变成beanDefinition对象。如果返回为false则直接退出当前类的解析,就不会被spring创建并管理。我们通过查找代码路径ConfigurationClassPostProcessor ->processConfigBeanDefinitions()->parser.parse(candidates) 来看下具体源码我们可以看如下截图:
仔细看红线的地方,第一个地方判断有没有Conditional注解,第二地方是调用mactchs方法,如果macths返回true,最终结果是false,则外面的if就不会走,也就不会走return,反之则会走。所以看到这个地方是不是很神奇,接着我们再看看看一个例子,就是根据外部的一个属性决定是否创建bean并被spring管理:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Conditional(value = {PropertityOnClass.class}) public @interface PropertityAnnotation { Class[] value() default {}; String[] name() default {}; }
@Component@PropertityAnnotation(name = "com.hrao") public class PropertityBean { }
public class PropertityOnClass implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { if (metadata.isAnnotated(PropertityAnnotation.class.getName())) { AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(PropertityAnnotation.class.getName(), false)); String[] names = annotationAttributes.getStringArray("name"); try { Properties properties = PropertiesLoaderUtils.loadAllProperties("application.properties", ClassUtils.getDefaultClassLoader()); for (String name : names) { if (properties.getProperty(name).equals("true")) { return true; } } } catch (IOException e) { return false; } } return false; } }
PropertityAnnotation这注解上有Conditional注解,注解的注解,你可以理解成继承的关系把。我们可以看到,可以根据配置文件的的com.hrao这个属性决定释放创建bean。好了关于@Condition 这个注解我就讲到这里了,接下来我们再看看 以上代码有个地方要说明下,就是注解的注解,在注解上加上注解与其在一个注解中的作用是一样的。你可以理解成继承的关系把。我们可以看到,可以根据配置文件的的com.hrao这个属性决定释放创建bean。接下来我们就来聊聊@Configuration这个注解的作用。
关于@Configuration这注解我就先入为主的给大家灌输一个概念,那就是代理,只要是被这个注解标注的类,spring会为这个类生成一个增强的.class文件,当对这个类的有@bean注解的方法进行调用的时候,实际上是对这个增强的.class类进行调用,就是代理。我们知道,jdk原生的动态代理,只能对实现了接口的类进行代理,不能对未实现接口的类进行代理,所以spring使用了cglib动态代理方式,这种方式可以对未实现接口的类进行代理增强。有了这个概念以后,我们通过代码路径先来到以下代码: ConfigurationClassPostProcessor->postProcessBeanFactory()这个方法:
我们看到上面的截图有标记,我来解释下这些标记的意思
1 找到这个类上的configurationClass类注解(@configuration),如果有将这个beanDefinition放入到几乎中
2 如果集合为空,则直接退出,表示没有任何被@configuration标注的类
3 这个地方是核心,创建一个cglib的代理的.class类,具体的你可以看下这个方法的里面怎么创建代理,这cglib创建代理类的标准api是一样的没有什么特别之处
4 将原先的beanDefinition的beanClass属性设置为cglib创建的.class类的属性,以后创建实例的时候,其实是创建这个代理的实例。
看了上面的截图,是不是感觉很简单啊。其实一般类里面@configuration标有这个注解的和方法上有@bean注解配合使用的比较多,也就是说通过代理执行的方法创建对象,是放到缓存里面的,无论你调用多少次,都是获得缓存的里面的bean,都是一个bean,还有就是@bean的创建方式,和我们之前在xml里面的factoryBean和factoryMethod的创建方式是一样的,其实就是@bean使用了现有的xml的factoryBean和factoryMethod方式创建的,只是之前使用的是xml现在使用的是注解,后台的方法是一模一样的,变得只是入口存在的形式而已。
好了关于今天的这两个话题,我已经讲完了,期待后面的分享,谢谢观看。