2022-01-21

Java

spring

Spring 注解支持源码流程图

注解实例加载入口

基于注解是: org.springframework.context.annotation.AnnotationConfigApplicationContext

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
	
 
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("org.yang.learnspring");
System.out.println("context 启动成功");
User user = context.getBean(User.class);
System.out.println(user);
 

创建 BeanFactory

org.springframework.context.annotation.AnnotationConfigApplicationContext.AnnotationConfigApplicationContext(String... basePackages)

public AnnotationConfigApplicationContext(String... basePackages) {
 
	// 见下: 创建了 AnnotatedBeanDefinitionReader   和 ClassPathBeanDefinitionScanner, 这有继承 GenericApplicationContext, 还会调用其构造函数创建了 BeanFactory
	this();
	//2. 扫描
	scan(basePackages);
	//3. 刷新 refresh
	refresh();
}
//其父 org.springframework.context.support.GenericApplicationContext() 构造方法 创建了 DefaultListableBeanFactory
public GenericApplicationContext() {
	this.beanFactory = new DefaultListableBeanFactory();
}
 
 
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
	/**
	AnnotatedBeanDefinitionReader 是一个读取注解的Bean读取器
	**/
	this.reader = new AnnotatedBeanDefinitionReader(this);
	/**
	* BeanDefinition扫描器 用来扫描包或者类, 继而转换为bd
 
	* spring默认的扫描器其实不是这个scanner对象, 而是在后面自己又重新new了一个ClassPathBeanDefinitionScanner
	* spring在执行工程后置处理器ConfigurationClassPostProcessor时, 去扫描包时会new一个ClassPathBeanDefinitionScanner
	*
	* 这里的scanner仅仅是为了程序员可以手动调用AnnotationConfigApplicationContext对象的scan方法
	*
	*/
	this.scanner = new ClassPathBeanDefinitionScanner(this);
	//这两个家伙, 参考 https://blog.csdn.net/f641385712/article/details/88059145
}
 

扫描 scan

 
public void scan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	this.scanner.scan(basePackages);
}
 

org.springframework.context.annotation.ClassPathBeanDefinitionScanner.scan(String... basePackages)> org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(String... basePackages)

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) {
		// [2.1. 解析包Components findCandidateComponents] 找到这个包的所有 BeanDefinition
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate: candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			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);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				//注册到 BeanFactory [2.20.10.1] ; registry 是创建 Reader时传过来的
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

2.1. 解析包Components findCandidateComponents

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage) > org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String basePackage)

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		//这个包的所有.class文件 //classpath*:org/yang/learnspring/**/*.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 {
					//拿到注解元数据 MetadataReader [2.1.10 class元数据读取 MetadataReader]
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
					//过滤?
					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);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}
 
 

2.1.10 class元数据读取 MetadataReader

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.getMetadataReaderFactory()

if (this.metadataReaderFactory == null) {
	this.metadataReaderFactory = new CachingMetadataReaderFactory();
}
return this.metadataReaderFactory;
 

org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(Resource resource)

public MetadataReader getMetadataReader(Resource resource) throws IOException {
 
	if (this.metadataReaderCache instanceof ConcurrentMap) {
		// No synchronization necessary...
		MetadataReader metadataReader = this.metadataReaderCache.get(resource);
		if (metadataReader == null) {
			//下
			metadataReader = super.getMetadataReader(resource);
			//缓存,resource = file [F:\Work\project\learnspring\target\classes\org\yang\learnspring\bean\Book.class]
			this.metadataReaderCache.put(resource, metadataReader);
		}
		return metadataReader;
	}
	//这个分支读缓存
	else if (this.metadataReaderCache != null) {
		synchronized (this.metadataReaderCache) {
			MetadataReader metadataReader = this.metadataReaderCache.get(resource);
			if (metadataReader == null) {
				//下
				metadataReader = super.getMetadataReader(resource);
				this.metadataReaderCache.put(resource, metadataReader);
			}
			return metadataReader;
		}
	}
	else {
		//下
		return super.getMetadataReader(resource);
	}
}

org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(Resource resource)

	public MetadataReader getMetadataReader(Resource resource) throws IOException {
		return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
	}

org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(Resource resource)

SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
	//很牛逼, 以二进制的方式读取 .class 
	SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
	//典型策略模式 
	//最终使用的是 org.springframework.asm.ClassReader 去读取, 
	getClassReader(resource).accept(visitor, PARSING_OPTIONS);
	this.resource = resource;
	this.annotationMetadata = visitor.getMetadata();
}
 
private static ClassReader getClassReader(Resource resource) throws IOException {
	try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
		try {
			return new ClassReader(is);
		}
		catch (IllegalArgumentException ex) {
			throw new NestedIOException("ASM ClassReader failed to parse class file - " +
					"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
		}
	}
}

关于 org.springframework.asm.ClassReader是ASM体系里面的内容了 ASM官网

关键对象/接口(Annotation)

ClassMetadata

class的元数据抽象

public interface ClassMetadata {
 
	// 返回类名(注意返回的是最原始的那个className)
	String getClassName();
 
	//是否接口 抽象
	boolean isInterface();
	boolean isAbstract();
	/**
	 *是否注解
	 */
	boolean isAnnotation();
	
	// 是否允许创建, 不是接口且不是抽象类
	default boolean isConcrete() {
		return !(isInterface() || isAbstract());
	}
	boolean isFinal();
 
	 // 是否是独立的(能够创建对象的)  比如是Class, 或者内部类, 静态内部类
	boolean isIndependent();
 
	//是否封闭类
	default boolean hasEnclosingClass() {
		return (getEnclosingClassName() != null);
	}
	@Nullable
	String getEnclosingClassName();
	default boolean hasSuperClass() {
		return (getSuperClassName() != null);
	}
	@Nullable
	String getSuperClassName();
 
	// 会把实现的所有接口名称都返回  具体依赖于Class#getSuperclass
	String[] getInterfaceNames();
 
	// 基于: Class#getDeclaredClasses  返回类中定义的公共, 私有, 保护的内部类
	String[] getMemberClassNames();
 
}
 

Spring IOC 常用注解

@Configuration

@Configuration 通常配合@Bean 注解一起使用 作为配置类

Spring3.0 注解, 用于定义配置类, 可替换xml配置文件,初始化Spring容器,约等于<Beans>...</Beans> 注解的配置类有如下要求:

不可以是final类型; 不可以是匿名类; 嵌套的configuration必须是静态类;

  • @Bean: 就是在spring配置文件中声明了一个bean
    • 赋值为hello world,
    • 方法返回类型就是bean的类型,
    • 方法名是bean的id.

    类似XML<bean id="hello" class="String" />
    (默认为单例, 可通过@Scope(“prototype”)指定,其他类同同几乎XML的所有方式都可以被注解替换 )
    既然@Bean的作用是注册bean对象, 那么完全可以使用@Component, @Controller, @Service, @Repository等注解注册bean(在需要注册的类上加注解), 当然需要配置@ComponentScan注解进行自动扫描;

@Value注解

@Value的作用是通过注解将常量, 配置文件中的值, 其他bean的属性值注入到变量中, 作为变量的初始值;

支持如下方式的注入:

  1. 普通注入
@Value("张三")
private String name; // 注入普通字符串
  1. bean属性, 系统属性, 表达式注入,使用@Value(”#{}”); bean属性注入需要注入者和被注入者属于同一个IOC容器, 或者父子IOC容器关系, 在同一个作用域内;
// 注入其他Bean属性: 注入beanInject对象的属性another, 类具体定义见下面
@Value("#{beanInject.another}")
private String fromAnotherBean; 
// 注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName; 
//注入表达式结果
@Value("#{T(java.lang.Math).random() * 100.0 }")
private double randomNumber;
  1. 配置文件属性注入@Value(”${}”)

@Value("#{}") 读取配置文件中的值, 注入到变量中去; 配置文件分为默认配置文件application.properties和自定义配置文件

@Resource

@Resource: @Resource是JSR250规范的实现, 需要导入javax.annotation实现注入; @Resource可以作用在变量, setter方法上; @Resource是根据名称进行自动装配的, 一般会指定一个name属性;

参考 [JSR330 Spring ]https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-standard-annotations

@Qualifier

@Autowired 默认是根据类型进行注入的, 因此如果有多个类型一样的Bean候选者, 则需要限定其中一个候选者, 否则将抛出异常

@Qualifier("restTemplate2")
@Autowired 
RestTemplate rest

@Primary

当存在多个相同类型的 Bean, 例如, 多个DataSource, 多个JdbcTemplate时, 强烈建议总是使用 @Primary 把其中某一个Bean标识为”主要的”, 使用 @Autowired 注入时会首先使用被标记为 @Primary 的Bean;

相同类型的其他Bean, 每一个都需要用@Bean(name=“xxx”)标识名字, 并且, 在使用@Autowired注入时配合@Qualifier("xxx")指定注入的Bean的名字

Spring Framework 与 JDK 支持情况

  • Spring Framework 7.x: JDK 17-25+
  • Spring Framework 6.2: JDK 17-25
  • Spring Framework 6.1: JDK 17-23
  • Spring Framework 6.0: JDK 17-21
  • Spring Framework 5.3: JDK 8-21 (as of 5.3.26)
  • Spring Framework 5.2.x: JDK 8-15
  • Spring Framework 5.1.x: JDK 8-12
  • Spring Framework 5.0.x: JDK 8-10
  • Spring Framework 4.3.x: JDK 6-8

Supported Versions