Spring自定义注解驱动开发使用及源码分析
目录
前言
- 在我们实际开发中,你是不是也经常使用各种
@Enablexxx
(如@EnableAspectJAutoProxy
、@EnableTransactionManagement
)之类的注解呢?只要使用了它,某个功能(组件)就应用上了。为我们提供了很多便利。 - 下面我们就从自己模仿写一个Enablexxx注解开始,一步步揭开注解驱动开发背后真相。
注解驱动开发使用
需求
- 假设有这样一个需求:有比较多的微服务,接口请求要打印下请求参数、请求地址什么的。
- 我的想法是使用AOP实现,可以把它封装成一个组件,引入项目中然后使用如
@EnableWebLogAspect
这样的注解就可以启用它了
这里方案排除Spring Boot自动装配
spring.factories
的方式 。本章主角是注解驱动开发,其实Spring Boot@EnableAutoConfiguration
的原理也是注解驱动。
代码实现
WebLogAspect.java
拦截controller
打印参数的地方。
注意:下面我使用了
@Component
,但其实它并不会因为这个注解初始化,因为我的扫描包路径是com.zzq.core
。
之所以加@Component
,是老版本要加Component、Bean、Configuration其一注解,或者派生,我用的是Spring 3.2.18的源码 registerBeanDefinitionForImportedConfigurationClass方法中ConfigurationClassUtils.checkConfigurationClassCandidate在检查 ,否则异常
Configuration problem: com.zzq.core.annotationdrivendevelopment.WebLogAspect was @Import’ed but is not annotated with @Configuration nor does it declare any @Bean methods; it does not implement ImportSelector or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements or do not attempt to @Import it.
我看了高版本的代码如 5.1.16.RELEASE没有这个限制,不用加注解
package com.zzq.annotationdrivendevelopment;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.fasterxml.jackson.databind.ObjectMapper;
/***
* 2022年4月13日11:17:09
* @author zzq
* 打印日志 ,需要<aop:aspectj-autoproxy>标签否则 注解方式不生效
*/
/***
* 老版本要加Component、Bean、@Configuration其一注解,我用的是Spring 3.2.18的源码 registerBeanDefinitionForImportedConfigurationClass方法中ConfigurationClassUtils.checkConfigurationClassCandidate在检查 ,否则异常
* Configuration problem: com.zzq.core.annotationdrivendevelopment.WebLogAspect was @Import'ed but is not annotated with @Configuration nor does it declare any @Bean methods; it does not implement ImportSelector or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements or do not attempt to @Import it.
*
*/
@Component
@Aspect
public class WebLogAspect {
private final ObjectMapper objectMapper = new ObjectMapper();
@Pointcut("execution(* com.*.core.controller..*.*(..))")
public void webLog() {
System.out.println("webLog");
}
/**
* 在切点之前织入
*
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
Object [] args = joinPoint.getArgs();
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Map<String, String[]> parameterMap = request.getParameterMap();
System.out.println("========================================== Request start ==========================================");
System.out.println("URL : " + request.getRequestURL().toString());
System.out.println("HTTP Method : " + request.getMethod());
System.out.println("Class Method : "+ joinPoint.getSignature().getDeclaringTypeName() +"."+ joinPoint.getSignature().getName());
System.out.println("IP : " + request.getRemoteAddr());
if (null != args && args.length>0) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < args.length; i++) {
sb.append(objectMapper.writeValueAsString(args[i]) + "\t");
}
System.out.println("Request Args : " +sb.toString());
}else{
System.out.println("Request Args : " );
}
System.out.println("========================================== Request end ==========================================");
}
}
- 创建
WebLogAspectImportSelector
,选择导入刚才的WebLogAspect
package com.zzq.annotationdrivendevelopment;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class WebLogAspectImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println("使用注解驱动 ImportSelector");
return new String[]{WebLogAspect.class.getName()};
}
}
- 创建注解
EnableWebLogAspect
,导入WebLogAspectImportSelector
package com.zzq.annotationdrivendevelopment;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/***
* @author zzq
* 2022年4月13日11:32:18
* 开启日志的注解
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(WebLogAspectImportSelector.class)
public @interface EnableWebLogAspect {
}
前面的代码都可以封装成一个公共模块
- 新建配置类,
ConfigurationTest
使用EnableWebLogAspect
注解
@Configuration
@ComponentScan("com.zzq.core")
@EnableWebLogAspect // 启用
public class ConfigurationTest {
}
- 新建
TestController
,用于测试。
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping(value ="/test8")
@ResponseBody
public String test8(com.zzq.core.dto.TestReq testReq){
System.out.println("test8 controller " + testReq.getUserId());
return "test8 result :" + testReq.getUserId();
}
}
- 请求参数
TestReq
package com.zzq.core.dto;
import java.lang.Integer;
public class TestReq {
private Integer userId;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
测试效果
- 浏览器访问:http://localhost:8080/test_web/test/test8?userId=2,打印如下图所示。
源码分析
- 下面我们就来揭开
@EnableWebLogAspect
使某功能(组件)启用的奥秘。
BeanDefinitionRegistryPostProcessor接口
- 首先我们要定位到
ConfigurationClassPostProcessor
类(这个类一直是Spring实现注解开发的核心之一,到了Spring Boot时代依旧如此),它实现了BeanDefinitionRegistryPostProcessor
接口,项目启动时就会去找BeanDefinitionRegistryPostProcessor
的类,找到后for循环遍历
调用其postProcessBeanDefinitionRegistry
方法。代码片段如下图所示。
具体关于
BeanDefinitionRegistryPostProcessor
扩展接口详细信息,请参考拙作spring扩展 BeanDefinitionRegistryPostProcessor详解
- 定位到
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
//取得registry的id并做判重处理或记录
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called for this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called for this post-processor against " + registry);
}
//保存处理过的registry,避免重复处理
this.registriesPostProcessed.add(registryId);
//处理java配置形式的bean定义
processConfigBeanDefinitions(registry);
}
解析BeanDefinition
- 进入
ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
//加载当前已知所有bean定义
for (String beanName : registry.getBeanDefinitionNames()) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 判断对应bean是否为配置类,如果是,则加入到configCandidates
// Component、Bean、Configuration其一注解,或者派生
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
//如果找不到 Component、Bean、Configuration其一注解,或者派生的类,则立即返回
if (configCandidates.isEmpty()) {
return;
}
// Detect any custom bean name generation strategy supplied through the enclosing application context
// 3. 如果BeanDefinitionRegistry 是SingletonBeanRegistry 子类的话,由于我们当前传入的是DefaultListableBeanFactory,是
// SingletonBeanRegistry 的子类。因此会将registry强转为SingletonBeanRegistry
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry) {
singletonRegistry = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
// 如果localBeanNameGeneratorSet 等于false 并且SingletonBeanRegistry 中有 id 为 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
// 的bean .则将componentScanBeanNameGenerator,importBeanNameGenerator 赋值为 该bean.
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// Parse each @Configuration class
// 实例化ConfigurationClassParser 为了解析 各个配置类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
System.out.println("ConfigurationClassPostProcessor bd " + bd.getBeanClassName());
if (bd.getBeanClassName().equals("com.zzq.core.configuration.ConfigurationTest")) {
System.out.println("ConfigurationClassPostProcessor com.zzq.core.configuration.ConfigurationTest ");
}
try {
if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parser.parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
//ConfigurationTest 会进入这里
parser.parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
}
}
parser.validate();
// Handle any @PropertySource annotations
Stack<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
if (!parsedPropertySources.isEmpty()) {
if (!(this.environment instanceof ConfigurableEnvironment)) {
logger.warn("Ignoring @PropertySource annotations. " +
"Reason: Environment must implement ConfigurableEnvironment");
}
else {
MutablePropertySources envPropertySources = ((ConfigurableEnvironment)this.environment).getPropertySources();
while (!parsedPropertySources.isEmpty()) {
envPropertySources.addLast(parsedPropertySources.pop());
}
}
}
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator);
}
// 加载 BeanDefinition,里面会完成注册逻辑
this.reader.loadBeanDefinitions(parser.getConfigurationClasses());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
- 进入处理配置类
ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
AnnotationMetadata metadata = configClass.getMetadata();
if (this.environment != null && metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
return;
}
}
if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
if (configClass.equals(it.next())) {
it.remove();
}
}
}
// Recursively process the configuration class and its superclass hierarchy.
do {
// 处理Bean
metadata = doProcessConfigurationClass(configClass, metadata);
}
while (metadata != null);
// 添加到集合中
this.configurationClasses.add(configClass);
}
处理Bean上配置的注解
- 进入真正处理配置类的地方,要处理好几种注解
ConfigurationClassParser#doProcessConfigurationClass
protected AnnotationMetadata doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(metadata);
// Process any @PropertySource annotations
//处理@PropertySource 加载外面资源文件
AnnotationAttributes propertySource = MetadataUtils.attributesFor(metadata,
org.springframework.context.annotation.PropertySource.class);
if (propertySource != null) {
processPropertySource(propertySource);
}
// Process any @ComponentScan annotations
//处理 @ComponentScan 扫描包
AnnotationAttributes componentScan = MetadataUtils.attributesFor(metadata, ComponentScan.class);
if (componentScan != null) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, metadata.getClassName());
// scannedBeanDefinitions 有可能是空元素,因为在扫描包时会事先注册好
// Check the set of scanned definitions for any further config classes and parse recursively if necessary
// 如果有配置类,递归解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
//处理@Import注解
// Process any @Import annotations
Set<Object> imports = new LinkedHashSet<Object>();
Set<String> visited = new LinkedHashSet<String>();
collectImports(metadata, imports, visited);
if (!imports.isEmpty()) {
processImport(configClass, metadata, imports, true);
}
// Process any @ImportResource annotations
if (metadata.isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = MetadataUtils.attributesFor(metadata, ImportResource.class);
String[] resources = importResource.getStringArray("value");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
//处理@Bean注解
Set<MethodMetadata> beanMethods = metadata.getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process superclass, if any
if (metadata.hasSuperClass()) {
String superclass = metadata.getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// superclass found, return its annotation metadata and recurse
if (metadata instanceof StandardAnnotationMetadata) {
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
}
else {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
return reader.getAnnotationMetadata();
}
}
}
return null;
}
处理@Import注解
- 我们本章要关注的就是
处理@Import注解
部分 - 先收集导入的类
collectImports
private void collectImports(AnnotationMetadata metadata, Set<Object> imports, Set<String> visited) throws IOException {
String className = metadata.getClassName();
if (visited.add(className)) {
if (metadata instanceof StandardAnnotationMetadata) {
StandardAnnotationMetadata stdMetadata = (StandardAnnotationMetadata) metadata;
for (Annotation ann : stdMetadata.getIntrospectedClass().getAnnotations()) {
if (!ann.annotationType().getName().startsWith("java") && !(ann instanceof Import)) {
collectImports(new StandardAnnotationMetadata(ann.annotationType()), imports, visited);
}
}
//得到Import注解
Map<String, Object> attributes = stdMetadata.getAnnotationAttributes(Import.class.getName(), false);
if (attributes != null) {
Class<?>[] value = (Class<?>[]) attributes.get("value");
if (!ObjectUtils.isEmpty(value)) {
for (Class<?> importedClass : value) {
// Catch duplicate from ASM-based parsing...
imports.remove(importedClass.getName());
//把Import注解的值添加进去
imports.add(importedClass);
}
}
}
}
else {
for (String annotationType : metadata.getAnnotationTypes()) {
if (!className.startsWith("java") && !className.equals(Import.class.getName())) {
try {
collectImports(
new StandardAnnotationMetadata(this.resourceLoader.getClassLoader().loadClass(annotationType)),
imports, visited);
}
catch (ClassNotFoundException ex) {
// Silently ignore...
}
}
}
Map<String, Object> attributes = metadata.getAnnotationAttributes(Import.class.getName(), true);
if (attributes != null) {
String[] value = (String[]) attributes.get("value");
if (!ObjectUtils.isEmpty(value)) {
for (String importedClassName : value) {
// Catch duplicate from reflection-based parsing...
boolean alreadyThereAsClass = false;
for (Object existingImport : imports) {
if (existingImport instanceof Class &&
((Class<?>) existingImport).getName().equals(importedClassName)) {
alreadyThereAsClass = true;
}
}
if (!alreadyThereAsClass) {
imports.add(importedClassName);
}
}
}
}
}
}
}
- 找到要导入的类后,再进行处理
processImport
private void processImport(ConfigurationClass configClass, AnnotationMetadata metadata,
Collection<?> classesToImport, boolean checkForCircularImports) throws IOException {
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
}
else {
this.importStack.push(configClass);
try {
for (Object candidate : classesToImport) {
Object candidateToCheck = (candidate instanceof Class ? (Class) candidate :
this.metadataReaderFactory.getMetadataReader((String) candidate));
//实现ImportSelector接口的处理
if (checkAssignability(ImportSelector.class, candidateToCheck)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
processImport(configClass, metadata, Arrays.asList(selector.selectImports(metadata)), false);
}
//实现ImportBeanDefinitionRegistrar接口的处理
else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
registrar.registerBeanDefinitions(metadata, this.registry);
}
else {
//候选类不是importSelector或importBeanDefinitionRegistrar
//@Configuration类的处理
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as a @Configuration class
this.importStack.registerImport(metadata,
(candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
processConfigurationClass(candidateToCheck instanceof Class ?
new ConfigurationClass((Class) candidateToCheck, true) :
new ConfigurationClass((MetadataReader) candidateToCheck, true));
}
}
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to load import candidate class", ex);
}
finally {
this.importStack.pop();
}
}
}
selector.selectImports
便会执行到自定义的WebLogAspectImportSelector#selectImports
导入我们的Aop类com.zzq.annotationdrivendevelopment.WebLogAspect
,然后再递归调用processImport
。- 这一次会调用,注意
new ConfigurationClass是imported为true
,这个属性在后面会用到。
this.importStack.registerImport(metadata,
(candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
processConfigurationClass(candidateToCheck instanceof Class ?
new ConfigurationClass((Class) candidateToCheck, true) :
new ConfigurationClass((MetadataReader) candidateToCheck, true))
- 又会进入
ConfigurationClassParser#processConfigurationClass
,处理完成后加入configurationClasses
集合。
校验
- 校验逻辑
ConfigurationClassParser#validate
public void validate() {
for (ConfigurationClass configClass : this.configurationClasses) {
if ("configurationTest".equals(configClass.getBeanName())) {
System.out.println("ConfigurationClassParser validate ConfigurationTest");
}
configClass.validate(this.problemReporter);
}
}
- 例如对于配置类的校验,
ConfigurationClass#validate
(如判断是否配置类方法名称重复,因为默认用方法名称作为beanName)
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
if (getMetadata().isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
}
// An @Bean method may only be overloaded through inheritance. No single
// @Configuration class may declare two @Bean methods with the same name.
Map<String, Integer> methodNameCounts = new HashMap<String, Integer>();
for (BeanMethod beanMethod : this.beanMethods) {
String fqMethodName = beanMethod.getFullyQualifiedMethodName();
Integer currentCount = methodNameCounts.get(fqMethodName);
int newCount = (currentCount != null ? currentCount + 1 : 1);
methodNameCounts.put(fqMethodName, newCount);
}
for (String fqMethodName : methodNameCounts.keySet()) {
int count = methodNameCounts.get(fqMethodName);
// 判断是否配置类方法名称重复,因为默认用方法名称作为beanName
if (count > 1) {
String shortMethodName = ConfigurationMethod.getShortMethodName(fqMethodName);
problemReporter.error(new BeanMethodOverloadingProblem(shortMethodName, count));
}
}
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
加载BeanDefinition
- 进入
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
,使用for循环一个个加载。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass);
}
}
- 进入
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
,还记得前面递归调用processImport中最后new ConfigurationClass是imported为true吗?就是在这里应用的
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
// 还记得前面递归调用processImport中最后new ConfigurationClass是imported为true吗?就是在这里应用的
// WebLogAspect ConfigurationClass imported为true
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
//载入、注册@Configuration注解的@Bean注解的每个方法
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
}
- 往下进入
ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass
前面说的Configuration problem: com.zzq.core.annotationdrivendevelopment.WebLogAspect was @Import’ed but is not annotated with @Configuration nor does it declare any @Bean methods; it does not implement ImportSelector or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements or do not attempt to @Import it.异常就是在这个方法产生的
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
//判断是否 Component、Bean、Configuration其一注解,或者派生
if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
this.registry.registerBeanDefinition(configBeanName, configBeanDef);
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
}
}
else {
this.problemReporter.error(
new InvalidConfigurationImportProblem(metadata.getClassName(), configClass.getResource(), metadata));
}
}
注册Bean到Spring容器
- 进入
DefaultListableBeanFactory#registerBeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if("configurationTest".equals(beanName)){
System.out.println(" registerBeanDefinition(String beanName, BeanDefinition beanDefinition) " + beanName);
}
if(null != beanName && beanName.indexOf( "AspectJPointcutAdvisor") > -1){
System.out.println(" registerBeanDefinition AspectJPointcutAdvisor");
}
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// old? 还记得 “允许 bean 覆盖” 这个配置吗?allowBeanDefinitionOverriding
BeanDefinition oldBeanDefinition;
synchronized (this.beanDefinitionMap) {
// 之后会看到,所有的 Bean 注册后会放入这个 beanDefinitionMap 中
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 处理重复名称的 Bean 定义的情况
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
// 如果不允许覆盖的话,抛异常
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
//添加beanDefinitionNames
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// 没有就填加 有就覆盖
this.beanDefinitionMap.put(beanName, beanDefinition);
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
初始化对象
- 注册进去后,又会在
DefaultListableBeanFactory#preInstantiateSingletons
被初始化(初始化非懒加载对象)。
毫无悬念,最终又会调用经典的
getBean
方法初始化对象。
- 这样我们的这个组件就算应用上了。
扩展知识
- 在处理@Import注解时,
processImport
有这样一段代码。
...省略...
if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
registrar.registerBeanDefinitions(metadata, this.registry);
}
...省略...
- 从源码中找到,还可以实现
ImportBeanDefinitionRegistrar
接口,去导入我们自定义的Bean。 - 创建
WebLogAspectImportBeanDefinitionRegistrar
,实现ImportBeanDefinitionRegistrar
接口方式导入
package com.zzq.annotationdrivendevelopment;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class WebLogAspectImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("使用注解驱动 ImportBeanDefinitionRegistrar");
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(WebLogAspect.class.getName());
registry.registerBeanDefinition("webLogAspect", beanDefinition );
}
}
... 省略...
/***
* @author zzq
* 2022年4月13日11:32:18
* 开启日志的注解
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(WebLogAspectImportBeanDefinitionRegistrar.class)
public @interface EnableWebLogAspect {
}
- 这样也绕过了低版本源码中检查我问题,两种方式都可以。有关
ImportBeanDefinitionRegistrar
扩展接口的具体内容,请参考拙作ImportBeanDefinitionRegistrar
小结
- 本文实现了2中自定义注解驱动开发的方式
ImportBeanDefinitionRegistrar
和ImportSelector
。知道我们的组件是如何通过一个简单的注解去启用的。我们回顾下过程。- 1、进入BeanDefinitionRegistryPostProcessor的实现类
ConfigurationClassPostProcessor
,项目启动时会回调其postProcessBeanDefinitionRegistry
方法。 - 2、收集使用了
Import
的类。 - 3、处理Import的类,回调方法,如果实现
ImportSelector
就是selectImports
;如果是实现ImportBeanDefinitionRegistrar
就调用registerBeanDefinitions
。 - 4、向Spring容器注册导入的
BeanDefinition
。 - 5、容器
初始化类
,初始化后就相当于这个组件被启用了。
- 1、进入BeanDefinitionRegistryPostProcessor的实现类
- Spring Boot的
EnableAutoConfiguration
注解的原理很类似的,同样可以找到ImportSelector
的踪迹,只是导入类调用了AutoConfigurationImportSelector#getAutoConfigurationEntry
...省略...
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- 所以理解了本篇文章,对于看Spring Boot自动装配源码还是有一定帮助的。