前段时间自己在研究springboot的时候突然发现,springboot的诞生离不开spring注解驱动的支持,于是查询有关资料总结了spring注解驱动的发展史,
众所周知spring的一重要特色就是IOC控制反转,帮助我们管理Bean及生命周期。其实这个重要特性也就是在spring的第一个版本发布的。但是这个时候我们在使用这个特性时是需要在applicationContext.xml中配置
在此版本迭代中比较重要的是Spring2.5这个版本,堪称经典版本,诞生了很多我们所熟悉的注解,比如:@Compontent、@Service、@Controller 。在这个版本中我们已经可以进行springMVC的分层架构了,controller层、service层、其它组件声明等。但是这里还是需要在.xml中配置相关参数,比如:
这个版本在spring的发展史中堪称里程碑,因为在此版本中实现了完全去.xml化,推出了@Configration注解。现在我们配置一个模块或者组件可以这样做
@Configration
public class MybatisConfigration{
@Bean
public MybatisFactory MybatisFactory(){
return new MybatisFactor();
}
}
当然还有其它注解,比如:@Enable模块驱动 @Import 导入其它配置等。这两个注解其实是后来springboot自动装配特性的关键,正常情况我们使用@EnableSchule等注解可以直接启用一个模块,而不在需要配置其它相关的Bean,而在sprinboot中的启动类上有个@SpringBootApplication注解,这个注解是个复合注解,里面有个@EnableAutoConfigration注解,继续往里看,发现它里面有个@Import注解,,而这里正是springboot能够自动装配的关键,
看下源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
看这个类:EnableAutoConfigurationImportSelector.class
源码:
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NO_IMPORTS = {};
private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
通过源码我们发现这个类继承了AutoConfigurationImportSelector类,而这个类最终实现了DeferredImportSelector接口,并且重写了selectImports这个方法,,这里返回的String【】数组就是所有待装配的className。
而如果想知道他是怎样扫描到这些配置类的,那我门可以继续往下看这段代码,getCandidateConfigurations类,获取候选配置的操作
List configurations = getCandidateConfigurations(annotationMetadata,
attributes);
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
然后我们进去看看loader做了啥
/**
* The location to look for factories.
* Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List loadFactoryNames(Class> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List result = new ArrayList();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
至此我们已经全然明白。
现在大家应该彻底搞清楚了吧。。不知不觉扯太多了,,回归正题哈哈
这个版本也有个新的注解出现,就是@Conditional,条件注解,我们找个源码的应用来看它的使用,
* @author Phillip Webb
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
注意条件是一个class,我们进去看下
* @author Phillip Webb
* @author Dave Syer
* @author Jakub Kubrynski
* @author Stephane Nicoll
* @author Andy Wilkinson
*/
@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {
/**
* Bean definition attribute name for factory beans to signal their product type (if
* known and it can't be deduced from the factory bean class).
/**
* Base of all {@link Condition} implementations used with Spring Boot. Provides sensible
* logging to help the user diagnose what classes are loaded.
*
* @author Phillip Webb
* @author Greg Turnquist
*/
public abstract class SpringBootCondition implements Condition {
private final Log logger = LogFactory.getLog(getClass());
@Override
public final boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
try {
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
发现它继承的类SpringBootCondition实现了一个Condition接口,并且重写了matches方法,通过方法返回的boolean值来决定是否加载该Bean。这就是@Condition注解的用法。
这个版本没有太多新的业务功能注解出现,,只有个@Indexed ,,主要在spring扫描多个配置路径时优化其效率的,它可以为Spring的模式注解添加索引,以提升应用启动性能
官网的解释: