1. 前言
最近看代码的时候,发现AnnotationConfigApplicationContext中的构造方法中,初始化了两个类:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。
这两个类我觉的是构成JavaConfig以及注解方式的核心,所以想写一下对两个类的解析。
2. AnnotatedBeanDefinitionReader(注解类型读取器)
这个类其实比较简单,这个类主要是注册了一些内置的BeanFactoryPostProcessor和BeanPostProcessor。
他的代码实现比较简单,基本上先判断容器内是否有这些bean,如果没有bean就去注册。
其中最主要的就是ConfigurationClassPostProcessor和AutowiredAnnotationBeanPostProcessor以及CommonAnnotationBeanPostProcessor。
其中ConfigurationClassPostProcessor在context.refresh中首先被调用,这个类的主要是用来解析诸如@Configuration、@Import、@Component以及ImportSelector接口以及ImportBeanDefinitionRegistrar接口的注册和调用。这个类之后我会专门去分析。
AutowiredAnnotationBeanPostProcessor这个类主要是用于解析@Autowaired这个注解的解析。之后也会重点分析。
CommonAnnotationBeanPostProcessor这个类主要是对@PostConstruct和@PreDestory注解的解析。
2.1 下面分析一下,AnnotatedBeanDefinitionReader的registerBean方法。
//直接将配置类放入,当前类可以不使用@Configuration注解。但是基于规范,应该使用 @Configuration
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//this(); 这里将构造器直接放到下面了,便于查看
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
register(annotatedClasses);
refresh();
}
//实际就是AnnotatedBeanDefinitionReader去将传进来的Configuration类做解析。
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
public void registerBean(Class<?> annotatedClass) {
doRegisterBean(annotatedClass, null, null, null);
}
/**
* @param annotatedClass-javaConfig配置
* 其他参数为null,应该是别的类做扩展用,这里不再讨论
**/
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
//AnnotatedBeanDefinition跟RootBeanDefinition属于同一层次。就是定义的Bean的一个在Spring容器内的原始封装对象
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
//这里看到,这个class被封装成为一个对象,这个对象里面包括了一个所谓的标准注解元数据。
public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
setBeanClass(beanClass);
this.metadata = new StandardAnnotationMetadata(beanClass, true);
//通过这里可以看到这个标准注解元数据将配置类的所有注解获取了。
public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
super(introspectedClass);
this.annotations = introspectedClass.getAnnotations();
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}
}
//这个方法我不再分析了,但是这里有一个很有意思的东西。如何判断一个类是否有某个注解
//AnnotatedElementUtils可以使用这个类的hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) 来判断。
//AnnotatedElement这个东西很有意思,他的实现类是Class类。所以get的一个新技能
//AnnotatedElement element = Object.class
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(instanceSupplier);
//这行代码就是将Scope注解解析出来封装成一个ScopeMetadata(scope元数据)对象
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
//将Scope的作用域设置到当前的这个配置类的作用域下
abd.setScope(scopeMetadata.getScopeName());
//生成beanName。有value值用value,没有的话类名首字母小写(这里逻辑比较简单也不分析了)
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
//解析一些基本注解
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
//具体实现。将一些基本元素添加到AnnotatedBeanDefinition中,例如是否懒加载、角色等等
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(), Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
}
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
abd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
abd.setDescription(description.getString("value"));
}
}
//qualifiers==null
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
//definitionCustomizers==null
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}
//将bean对象(配置类)再次封装BeanDefinitionHolder==bean定义类+名称+别称
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//将该对象注册到spring容器中
//实际上还是以registry.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)实现
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
2.2 总结
这个AnnotatedBeanDefinitionReader类做了以下几件事:
1.初始化了内部一个BeanFactoryPostProcessor和BeanPostProcessor。
2.将初始化传进来的的配置类注册到Spring容器中。
3. ClassPathBeanDefinitionScanner(类路径扫描器)
这个类是在AnnotationConfigApplicationContext构造方法中初始化的,看上面代码。
这里为什么重点想说一下这个类,这个类其实是在xml里面出现而使用的。通过代码注释可以看到它最早在Spring2.5就已经在使用了。而AnnotationConfigApplicationContext这个类在Spring3.0的时候才有。这里可以看到代码的复用是很重要的。
3.1 分析方法
/**
* 1.Bean注册类-->就是AnnotationConfigApplicationContext
* 2.是否使用默认过滤器(这个过滤器是用来过滤如 @Component、@Service、@Controller、@Repository以及自定义Bean注解类) 这里我觉得可以把 @Component当做Spring的元注解。
* 3.系统环境 --这里的环境指的是什么? 我现在的看法指的是系统参数,例如我们在加载main方法指定的一些参数。这里我看到的代码是从System.getProperties()获取一些参数。
* 4.类加载器
*/
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
this.registry = registry;
if (useDefaultFilters) {
registerDefaultFilters();
//由此可以看到是添加一些过滤注解
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.trace("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.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
//再来看下AnnotationConfigApplicationContext的另一种构造方法,扫描指定路径下的文件注册为Bean
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//遍历
for (String basePackage : basePackages) {
//通过方法名称可知找到Component注解标注的类作为Bean(candidate--候选人)
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//拿到Scope元素
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//beanName生成
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//加载公共参数
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//校验名称是否存在
if (checkCandidate(beanName, candidate)) {
//生成一个包装类
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//生产scope代理类
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//注册bean
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
//这个if其实没搞太清楚,但是从方法名字可以知道应该是带有编号或者索引的查找
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//这里拼接了一个完整路径 classpath*:xxxx/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//转换成资源类
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//getMetadataReaderFactory获取的是CachingMetadataReaderFactory
//MetadataReader获取的是SimpleMetadataReader
//这里知道获取的是那个对象即可,不再往下追踪代码,因为底层代码用了大量的字节码技术,这块我自己不清楚。
//这块代码就是获取了一个元数据读取器,这个元数据读取器包括了Resource(原始资源 字节流)、ClassMetadata(跟类相关的元数据,例如类名称、是否是接口、是否是final类型等等)、AnnotationMetadata(该类的注解元数据,就是这个类注解上所有的信息)
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//判断是否为 @Component注解标注的类(这里不只是Component,只要是在过滤器里面的都会返回true)
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
//下面都是关于日志打印的,去掉了
....
3.2 总结
其实从上面的分析可以看出来,ClassPathBeanDefinitionScanner主要是对指定路径下 @Component注解的类 注册为Bean。
通过添加新的过滤器,用于自定义注解的类把他注册为一个bean。
还有解析注解的时候可以使用MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
4.代码实践
4.1 ClassPathBeanDefinitionScanner代码实践
主类
public class ClassPathScannerTest {
public static void main(String[] args) {
//定义了一个初始的BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//扫描器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory);
//添加自定义过滤器
scanner.addIncludeFilter(new AnnotationTypeFilter(ModifyComponent.class));
//扫描包路径
scanner.scan("com.study.spring.scanner");
String[] names = factory.getBeanDefinitionNames();
Stream.of(names).forEach(System.out::println);
}
}
自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModifyComponent {
String value() default "";
}
@Component
public class ScanTestBean1 {
}
@ModifyComponent
public class ScanTestBean2 {
}
4.2 MetadataReader代码实践
public class MetaReaderTest {
public static void main(String[] args) throws IOException {
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
//获取某个类的元数据类型
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader("com.study.spring.scanner"
+ ".ScanTestBean2");
//注解元数据类型
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//类元数据类型
ClassMetadata classMetadata = metadataReader.getClassMetadata();
}
}
通过这个类可以很快的去判断类的一些特征数据,是一个很方便的工具类
5.源码总结
AnnotationConfigApplicationContext跟ClassPathXmlApplicationContext解析方式完全不同,毕竟一个依赖于xml配置文件去解析,一个依靠注解去解析。
AnnotationConfigApplicationContext相当于提供了两种方式:
1.注册配置类:
需要手动指定一个配置类,然后通过AnnotatedBeanDefinitionReader把配置类注册成为一个bean。
2.扫描指定路径
则通过ClassPathBeanDefinitionScanner去将 @Component 注解的类(包括父注解有@Component的类)注册成为一个bean。