借助@Component
Spring本身的@Component实现,依赖于ClassPathScanningCandidateComponentProvider在指定的路径下进行扫描,并且完成自动加载。
查看ClassPathScanningCandidateComponentProvider的源码,可以看到Spring默认加载了一个注解过滤器,来过滤@Component的类。
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
} catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
这里只加载了@Component注解的过滤器,那@Service、@Controller这些注解呢?看了下@Service的源码,就知道了@Service其实是借助了@Component来实现加载的。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
/**
* 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
*/
String value() default "";
}
这种方式比较局限,不能做一些定制。
Spring启动之后,再把bean拿出来加工、定制
SpringMVC就是使用这种方式,在Spring启动之后,遍历所有的bean,把带有@Controller的bean拿出来,构造UrlMapping。比如AbstractDetectingUrlHandlerMapping 。
自定义扫描
BeanFactoryPostProcessor 和ApplicationContextAware。
Spring提供了一些的接口使程序可以嵌入Spring的加载过程。这个类中的继承ApplicationContextAware接口,Spring会读取ApplicationContextAware类型的的JavaBean,并调用setApplicationContext(ApplicationContext applicationContext)传入Spring的applicationContext。
同样继承BeanFactoryPostProcessor接口,Spring会在BeanFactory的相关处理完成后调用postProcessBeanFactory方法,进行定制的功能。
ClassPathBeanDefinitionScanner是Spring提供的一个ClassPath扫描器,其实也继承于ClassPathScanningCandidateComponentProvider,只不过ClassPathBeanDefinitionScanner需要一个BeanFactory或者ApplicationContext,在扫描到符合要求的类,就会加入到BeanFactory或者ApplicationContext中。
至此,完成了自定义扫描功能,但是还没有对bean进行加工、定制,还需要使用到FactoryBean,普通的JavaBean是直接使用类的实例,但是如果一个Bean继承了这个借口,就可以通过getObject()方法来自定义实例的内容,在FactoryBeanTest的getObject()就通过代理了原始类的方法,自定义类的方法。
总结一下:
- ApplicationContextAware获取ApplicationContext
- BeanFactoryPostProcessor 获取BeanFactory
- ClassPathBeanDefinitionScanner提供了自定义扫描的入口(使用ApplicationContext和BeanFactory,并且启动自定义扫描)
- FactoryBean对自己感兴趣的Bean进行增强处理