mini-spring 学习笔记—扩展篇
最近在学习 mini-spring 项目,记录笔记以总结心得
IoC篇:mini-spring 学习笔记—IoC
AOP篇:mini-spring 学习笔记—AOP
PropertyPlaceholderConfigurer
将 bean 的属性信息统一写在 properties 文件中,本章实现解析 xml 文件中的占位符,通过占位符从 properties 文件中获取对应的信息。
本章的内容都在 PropertyPlaceholderConfigurer
类中,该类实现了 BeanFactoryPostProcessor
接口,可以看出来占位符的处理是在 bean 实例化之前完成。
包含三个成员变量
public static final String PLACEHOLDER_PREFIX = "${";
public static final String PLACEHOLDER_SUFFIX = "}";
private String location;
实现了 postProcessBeanFactory
方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//加载属性配置文件
Properties properties = loadProperties();
//属性值替换占位符
processProperties(beanFactory, properties);
}
loadProperties 方法
读取 properties 文件的方式与读取 xml 文件类似,也是获取资源的输入流,使用 java 本身提供的工具读取输入流
processProperties 方法和 resolvePropertyValues 方法
processProperties
方法用于获取和遍历 bean 定义
private void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties) throws BeansException {
// 获取所有的 bean 定义
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
// 遍历 bean 定义
for (String beanName : beanDefinitionNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 修改 bean 定义
resolvePropertyValues(beanDefinition, properties);
}
}
resolvePropertyValues
方法用于将读取出的 properties 文件中的内容修改 bean 定义,使用的方式与 xml 文档的处理类似
包扫描
@Component
关于自定义注解可以看看这篇博客
Componet
注解用于标记 bean,它又被三个注解所标记:
- @Target(ElementType.TYPE):用于说明该注解所修饰对象的范围,
ElementType.TYPE
表示用于描述类、接口或 Enum - @Retention(RetentionPolicy.RUNTIME):用于声明生命周期,
RetentionPolicy.RUNTIME
表示在运行时注解仍然存在 - @Documented:表示这个注解应该被 javadoc 记录
关于三个注解的详细解释可以看这篇博客
Component
注解仅有一个参数,表示 bean 的名称
String value() default "";
@Scope
Scope
注解被三个元注解修饰,表明它作用于方法、类、接口或 Enum,在运行时依然存在,并且可以被 javadoc 记录
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
仅有一个参数,表示该 bean 的作用域,默认为单例 bean
String value() default "singleton";
ClassPathScanningCandidateComponentProvider 和 ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner
类用于扫描指定路径下带有 @Component
注解的类,并读取为 bean 定义。findCandidateComponents
为具体实现
public class ClassPathScanningCandidateComponentProvider {
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
// 使用 LinkedHashSet 实现
Set<BeanDefinition> candidates = new LinkedHashSet<>();
// 扫描有 org.springframework.stereotype.Component 注解的类, 组成一个集合
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);
for (Class<?> clazz : classes) {
// 为带有 Component 注解的 bean 进行定义
BeanDefinition beanDefinition = new BeanDefinition(clazz);
candidates.add(beanDefinition);
}
return candidates;
}
}
ClassPathBeanDefinitionScanner
继承自 ClassPathScanningCandidateComponentProvider
,完成 bean 定义扫描完成后的解析定义域、改名和注册工作,仅有一个成员变量表示 bean 定义注册表
private BeanDefinitionRegistry registry;
doScan 方法
该方法接收一系列需要扫描的包路径,调用 findCandidateComponents
方法获得所有的 bean 定义之后解析 bean 的作用域和名称,最后向注册表内添加 bean 定义
public void doScan(String... basePackages) {
for(String basePackage : basePackages) {
// 获取这个包下所有的 bean 定义
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for(BeanDefinition candidate : candidates) {
// 解析bean的作用域
String beanScope = resolveBeanScope(candidate);
if(StrUtil.isNotEmpty(beanScope)) {
candidate.setScope(beanScope);
}
//生成bean的名称
String beanName = determineBeanName(candidate);
//注册BeanDefinition
registry.registerBeanDefinition(beanName, candidate);
}
}
}
resolveBeanScope 方法
该方法用于获取 bean 的作用域,通过读取 @Scope
注解的值实现
private String resolveBeanScope(BeanDefinition beanDefinition) {
Class<?> beanClass = beanDefinition.getBeanClass();
Scope scope = beanClass.getAnnotation(Scope.class);
if(scope != null) {
return scope.value();
}
return StrUtil.EMPTY;
}
determineBeanName 方法
该方法读取类的名称,之后将首字母小写作为 bean 的名称
private String determineBeanName(BeanDefinition beanDefinition) {
Class<?> beanClass = beanDefinition.getBeanClass();
Component component = beanClass.getAnnotation(Component.class);
String value = component.value();
if(StrUtil.isEmpty(value)) {
value = StrUtil.lowerFirst(beanClass.getSimpleName());
System.out.println("value = " + value);
}
return value;
}
XmlBeanDefinitionReader
新增两个静态成员变量,用于判断 xml 文件中的标签
public static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
public static final String COMPONENT_SCAN_ELEMENT = "component-scan";
新增 scanPackage
方法,用于调用 doScan
方法扫描包
private void scanPackage(String scanPath) {
String[] basePackages = StrUtil.splitToArray(scanPath, ',');
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
scanner.doScan(basePackages);
}
doLoadBeanDefinitions
方法中新增对 context:component-scan 标签的判断
Element componentScan = root.element(COMPONENT_SCAN_ELEMENT);
if (componentScan != null) {
String scanPath = componentScan.attributeValue(BASE_PACKAGE_ATTRIBUTE);
if (StrUtil.isEmpty(scanPath)) {
throw new BeansException("The value of base-package attribute can not be empty or null");
}
scanPackage(scanPath);
}
BeanDefinition
重写了 equals
方法和 hashCode
方法,重写后根据 beanClass
来判断 true
或者 false
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeanDefinition that = (BeanDefinition) o;
return beanClass.equals(that.beanClass);
}
@Value 注解
@Value
@Value
被两个个元注解修饰,表明它作用于字段、方法、参数,且在运行时依然存在
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
只有一个参数,代表要赋予的值
String value();
InstantiationAwareBeanPostProcessor 和 AutowiredAnnotationBeanPostProcessor
InstantiationAwareBeanPostProcessor
接口增加方法 postProcessPropertyValues
在 bean 实例化之后,设置属性之前执行
PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException;
AutowiredAnnotationBeanPostProcessor
类实现了 InstantiationAwareBeanPostProcessor
接口和 BeanFactoryAware
接口,能够在 bean 实例化之后,设置属性之前为 bean 填入 @Value
注解中的值,同时它包含一个成员变量用于感知 bean 工厂
private ConfigurableListableBeanFactory beanFactory;
主要方法为 postProcessPropertyValues
public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
// 处理@Value注解
Class<?> clazz = bean.getClass();
// 获取所有字段
Field[] fields = clazz.getDeclaredFields();
// 遍历字段数组
for (Field field : fields) {
// 获取每个字段的 Value 注解
Value valueAnnotation = field.getAnnotation(Value.class);
if (valueAnnotation != null) {
String value = valueAnnotation.value();
value = beanFactory.resolveEmbeddedValue(value);
BeanUtil.setFieldValue(bean, field.getName(), value);
}
}
//处理@Autowired注解(下一节实现)
return pvs;
}
其中
value = beanFactory.resolveEmbeddedValue(value)
resolveEmbeddedValue
函数用于对 @Value
注解中的值做预处理(比如注解的值为 "#{name}"
这种变量,需要替换为真实值),该函数位于 AbstractBeanFactory
类中
public String resolveEmbeddedValue(String value) {
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
result = resolver.resolveStringValue(result);
}
return result;
}
AbstractBeanFactory
类新增了变量 embeddedValueResolvers
存储所有的解析器
private final List<StringValueResolver> embeddedValueResolvers = new ArrayList<>();
resolveStringValue
函数由 StringValueResolver
接口规定,用于实现字符串值的解析
String resolveStringValue(String strVal);
一个实现位于 PropertyPlaceholderConfigurer
类中,直接传入占位符解析替换方法进行处理
public String resolveStringValue(String strVal) throws BeansException {
return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);
}
AbstractAutowireCapableBeanFactory
在 doCreateBean
方法中新增 applyBeanPostprocessorsBeforeApplyingPropertyValues
方法调用,在设置 bean 属性之前修改属性值
// doCreateBean 方法
bean = createBeanInstance(beanDefinition);
//在设置bean属性之前,允许BeanPostProcessor修改属性值
applyBeanPostprocessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
//为bean填充属性
applyPropertyValues(beanName, bean, beanDefinition);
//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
initializeBean(beanName, bean, beanDefinition);
applyBeanPostprocessorsBeforeApplyingPropertyValues
方法的实现与其他 BeanPostProcessor
的方法实现大同小异
protected void applyBeanPostprocessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
// Value 注解注入
PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
if (pvs != null) {
for (PropertyValue propertyValue : pvs.getPropertyValues()) {
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
}
}
ClassPathBeanDefinitionScanner
新增静态成员常量 AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME
,记录注入注解处理类的名称
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
doScan
方法中新增对注入注解处理类的注册
// doScan 方法
registry.registerBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));
@Autowired 注解
@Autowired 和 @Qualifier
@Autowired
被两个元注解修饰,表明它作用于字段、方法、构造器,且在运行时依然存在
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {}
@Qualifier
被四个元注解修饰,仅有一个参数
@Retention(RetentionPolicy.RUNTIME)
// 作用范围为字段、方法、参数、类、注解类型声明
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
// 子类可以继承被 Inherited 修饰的注解
@Inherited
// 这个注解会被 javadoc 显示
@Documented
public @interface Qualifier {
String value() default "";
}
AutowiredAnnotationBeanPostProcessor
新增处理 @Autowired
注解代码,在 postProcessPropertyValues
方法中
// postProcessPropertyValues 方法
for (Field field : fields) {
// 获取 Autowired 注解
Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
// 如果这个字段被 Autowired 修饰
if (autowiredAnnotation != null) {
// 获取字段类型
Class<?> fieldType = field.getType();
// 所依赖的 bean 的名称
String dependentBeanName;
// 获取 Qualifier 注解
Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
Object dependentBean;
if (qualifierAnnotation != null) {
dependentBeanName = qualifierAnnotation.value();
dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
} else {
dependentBean = beanFactory.getBean(fieldType);
}
BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
}
}
此处可以看到 @Qualifier
注解的作用:判断该字段是不是 bean
BeanFactory 和 DefaultListableBeanFactory
为了实现 postProcessPropertyValues
中根据字段类型获取字段值的调用,BeanFactory
接口重载方法 getBean
<T> T getBean(Class<T> requiredType) throws BeansException;
DefaultListableBeanFactory
类中实现了该方法
public <T> T getBean(Class<T> requiredType) throws BeansException {
List<String> beanNames = new ArrayList<>();
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
Class beanClass = entry.getValue().getBeanClass();
if (requiredType.isAssignableFrom(beanClass)) {
beanNames.add(entry.getKey());
}
}
if (beanNames.size() == 1) {
return getBean(beanNames.get(0), requiredType);
}
throw new BeansException(requiredType + "expected single bean but found " + beanNames.size() + ": " + beanNames);
}
AbstractApplicationContext
该类中同样新增重载方法 getBean
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(requiredType);
}
bug fix:没有为代理bean设置属性
这章我也没大看懂
查看更改代码需要查看“为AOP代理对象注入属性”提交,修订号为 9e2b1e451e9ea465aa48b0e0e5f026c4d5185a56
DefaultAdvisorAutoProxyCreator
主要改动为将 postProcessBeforeInstantiation
方法中的内容放入 postProcessAfterInitialization
中
本章小结
目前 bean 的生命周期
类型转换(一)
Converter
Converter
接口规范了类型转换的行为规范,它是泛型接口,将 S
类型转化为 T
类型
public interface Converter<S, T> {
T convert(S source);
}
ConverterFactory 和 ConverterRegistry
ConverterFactory
接口规范了类型转换工厂,用于生产出指定的类型转换器
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
ConverterRegistry
接口规范了注册转换器和转换工厂的行为,此处所有出现泛型通配符的地方,都用 ?
,用于代表任何类型
public interface ConverterRegistry {
void addConverter(Converter<?, ?> converter);
void addConverterFactory(ConverterFactory<?, ?> converterFactory);
void addConverter(GenericConverter converter);
}
GenericConverter
GenericConverter
接口中定义了静态类 ConvertiblePair
ConvertiblePair
类有两个成员变量,分别表示源类型和目标类型
private final Class<?> sourceType;
private final Class<?> targetType;
而 GenericConverter
接口中可以获得一系列的 ConvertiblePair
类
Set<ConvertiblePair> getConvertibleTypes();
ConversionService
ConversionService
接口是类型转换体系的核心,规范了转换服务的行为,有两个方法
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
GenericConversionService
GenericConversionService
类实现了 ConversionService
和 ConverterRegistry
两个接口,具有提供类型转换服务和注册转换器的功能,具有一个 Map
类型的成员变量 converters
,用于记录 <转换类型,转换器对象>
private Map<ConvertiblePair, GenericConverter> converters = new HashMap<>();
这里挑部分方法和内部类进行讲解
getClassHierarchy 方法
英文单词 hierarchy 直译为“等级制度”,在这里可以理解为“层次结构”,所以该方法用于获取传入类型变量 clazz
的所有父类,方法内部使用类似于遍历链表的方法,遍历 clazz
的所有父类并放入列表 hierarchy
中
private List<Class<?>> getClassHierarchy(Class<?> clazz) {
List<Class<?>> hierarchy = new ArrayList<>();
while (clazz != null) {
hierarchy.add(clazz);
clazz = clazz.getSuperclass();
}
return hierarchy;
}
getConverter 方法
该方法用于获取从 sourceType
到 targetType
之间的转换器。具体实现为,先获取两个类型的所有父类集合,遍历两个集合的笛卡尔积,看看有没有对应两个类型的转换器,如果有的话则返回该转换器;遍历结束都没有的话,返回 null
protected GenericConverter getConverter(Class<?> sourceType, Class<?> targetType) {
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType);
List<Class<?>> targetCandidates = getClassHierarchy(targetType);
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = converters.get(convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
getRequiredTypeInfo 方法
getGenericInterfaces
方法与 getInterfaces
相似,都是用于返回类型的接口,但是前者同时也会返回接口中的泛型信息,并且前者返回类型为 Type
数组,后者为 Class
数组。之后使用 ParameterizedType
类型包裹泛型接口,调用 getActualTypeArguments
方法获取其中的泛型类信息(比如 Response<Person>
中返回 Person.class
)关于ParameterizedType
类型的更多信息,可以看这篇博客。
最后通过两个泛型类信息,构建一个 ConvertiblePair
对象。
这个方法我也不大懂
private ConvertiblePair getRequiredTypeInfo(Object object) {
// 获取带有泛型的实现的接口信息
Type[] types = object.getClass().getGenericInterfaces();
// 使用 ParameterizedType 包裹
ParameterizedType parameterized = (ParameterizedType) types[0];
// 获取包装类中的泛型类(比如 Response<Person> 中返回 Person.class)
Type[] actualTypeArguments = parameterized.getActualTypeArguments();
Class sourceType = (Class) actualTypeArguments[0];
Class targetType = (Class) actualTypeArguments[1];
return new ConvertiblePair(sourceType, targetType);
}
类型转换(二)
本章按 log 中两个类型转换的时机来讲解:为 bean 填充属性时、处理 @Value
注解时
为 bean 填充属性时
AbstractAutowireCapableBeanFactory
在 applyPropertyValues
方法中增加如下代码
// applyPropertyValues 方法
Class<?> sourceType = value.getClass();
Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);
ConversionService conversionService = getConversionService();
if (conversionService != null) {
if (conversionService.canConvert(sourceType, targetType)) {
value = conversionService.convert(value, targetType);
}
}
处理 @Value
注解时
AutowiredAnnotationBeanPostProcessor
在 postProcessPropertyValues
方法中增加如下代码
// postProcessPropertyValues 方法
Object value = valueAnnotation.value();
value = beanFactory.resolveEmbeddedValue((String) value);
//类型转换
Class<?> sourceType = value.getClass();
Class<?> targetType = (Class<?>) TypeUtil.getType(field);
ConversionService conversionService = beanFactory.getConversionService();
if (conversionService != null) {
if (conversionService.canConvert(sourceType, targetType)) {
value = conversionService.convert(value, targetType);
}
}
注册类型转换器
同样,要使用类型转换器就要先注册类型转换器,这项功能在 ConversionServiceFactoryBean
类中提供。该类包含两个成员变量
private Set<?> converters;
private GenericConversionService conversionService;
使用 registerConverters
方法注册转换器
private void registerConverters(Set<?> converters, ConverterRegistry registry) {
if (converters != null) {
for (Object converter : converters) {
if (converter instanceof GenericConverter) {
registry.addConverter((GenericConverter) converter);
} else if (converter instanceof Converter<?, ?>) {
registry.addConverter((Converter<?, ?>) converter);
} else if (converter instanceof ConverterFactory<?, ?>) {
registry.addConverterFactory((ConverterFactory<?, ?>) converter);
} else {
throw new IllegalArgumentException("Each converter object must implement one of the " +
"Converter, ConverterFactory, or GenericConverter interfaces");
}
}
}
}
然后再 xml 文件中写入该工厂 bean
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" ref="converters"/>
</bean>
<bean id="converters" class="org.springframework.test.common.ConvertersFactoryBean"/>
融入生命周期
最后在 AbstractApplicationContext
类中加入类型转换服务的使用,融入 bean 的生命周期
在 refresh
方法中,将 beanFactory.preInstantiateSingletons();
放入 finishBeanFactoryInitialization(beanFactory);
方法中,即先设置类型转换服务,再进行 bean 的实例化
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//设置类型转换服务
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)) {
Object conversionService = beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME);
if (conversionService instanceof ConversionService) {
beanFactory.setConversionService((ConversionService) conversionService);
}
}
//提前实例化单例bean
beanFactory.preInstantiateSingletons();
}
而类型转换器的注册,发生在 bean 的初始化阶段,具体看 AbstractAutowireCapableBeanFactory
的 invokeInitMethods
方法
// invokeInitMethods 方法
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
通过调用 bean 的 afterPropertiesSet
方法进入到转换器注册方法中
public void afterPropertiesSet() throws Exception {
conversionService = new DefaultConversionService();
registerConverters(converters, conversionService);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
2022-12-09 2022秋 矩阵论考试准备