IOC:juejin.cn/post/736645…
AOP:juejin.cn/spost/73683…
扩展一下Spring的其他功能
紧接着前两章的IOC和AOP的原理篇,本篇介绍下Spring中的高级功能特性
1 解析占位符
Spring可以使用占位符给属性赋值,数据来自配置文件,也就是根据占位符内容从配置文件中得到真实数据,然后填充到属性
很容易想到,这本质就是修改BeanDefinition,修改BeanDefinition就需要使用BeanFactory后处理器
定义一个BeanFactory后处理器PropertyPlaceholderConfigurer,用于解析BeanDefinition并将其持有的属性值字符串中的占位符替换成真实数据
//赋值解析Bean属性的占位符
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {
//配置文件地址
private String location;
public void setLocation(String location) {
this.location = location;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory) throws BeansException {
Properties properties = loadProperties();
processProperties(beanFactory, properties);
}
//加载配置文件
private Properties loadProperties() {
try {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(location);
Properties properties = new Properties();
properties.load(resource.getInputStream());
return properties;
} catch (IOException e) {
throw new BeansException("加载配置文件失败", e);
}
}
private void processProperties(ConfigurableListableBeanFactory beanFactory,
Properties properties) throws BeansException {
String[] definitionNames = beanFactory.getBeanDefinitionNames();
for (String definitionName : definitionNames) {
BeanDefinition bd = beanFactory.getBeanDefinition(definitionName);
PropertyValue[] pvs = bd.getPropertyValues().getPropertyValues();
for (PropertyValue pv : pvs) {
Object value = pv.getValue();
//只解析字符串
if (value instanceof String strVal) {
//解析字符串中的占位符并返回解析后的结果
String newValue = resolvePlaceholder(strVal, properties);
//用解析后的新增替换就值
bd.getPropertyValues().addPropertyValue(
new PropertyValue(pv.getName(), newValue));
}
}
}
}
//解析字符串中占位符的工具类,自己写的,了解一下
public String resolvePlaceholder(String target, Map<Object, Object> datasource) {
//设置快慢指针,low遇到'${'停下来,high遇到'}'时如果low处于停止状态则high也停下来
//如果low、high指针都停止了,则获取两个指针中间的字符串作为key,从数据源获取值追加到结果字符串
//然后重置low、high指针,解析运行
int low = 0, high = 0;
//low、high指针的停止与否
boolean lowStatus = false, highStatus = false;
//用来保存格式化之后的字符串
StringBuilder sb = new StringBuilder();
//缓存lowStatus = true时,遍历到的字符串
StringBuilder cache = new StringBuilder();
for (int i = 0; i < target.length(); i++) {
char currentChar = target.charAt(i);
char nextChar = i == target.length() - 1 ? target.charAt(i) : target.charAt(i + 1);
if (currentChar == 36 && nextChar == 123) {
lowStatus = true;
//如果遇到新的"${"组合时,上一个"${"还没有闭合,就将缓存写入sb,并清空缓存
if (cache.length() > 0) {
sb.append(cache);
cache = new StringBuilder();
low = i;
}
}
if (lowStatus && currentChar == 125) {
highStatus = true;
}
//遇到"}"时,闭合"${",并根据"${"和"}"中间的字符串,从数据源获取数据追加到sb,并清空缓存
if (lowStatus && highStatus) {
String key = target.substring(low + 2, high);
String value = datasource.getOrDefault(key, "").toString();
sb.append(value);
//reset
lowStatus = false;
highStatus = false;
low = high = i + 1;
//"${"成功闭合后
cache = new StringBuilder();
continue;
}
//low指针没有停下来
if (!lowStatus) {
low++;
sb.append(currentChar);
}
//low指针停下里了,就缓存当前遍历的字符串
else {
cache.append(currentChar);
}
//为了防止缓存还没有写入sb循环就结束了的情况,在最后一次循环时将缓存数据写入sb
if (i == target.length() - 1) {
sb.append(cache);
}
high++;
}
return sb.toString();
}
}
为了方便使用,占位符解析器应该作为一个组件注册给BeanFactory,不过这都是后话了
将该BeanFactory后处理器注册进容器,它就会开始工作,就像下面这样
<!--placeholder.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="person" class="org.springframework.test.bean.Person">
<!--用占位符给属性赋值-->
<property name="name" value="${name}"></property>
</bean>
<!--解析占位符的BeanFactory后处理器-->
<bean class="org.springframework.beans.factory.PropertyPlaceholderConfigurer" >
<property name="location" value="classpath:application.properties"></property>
</bean>
</beans>
测试一下,准备一个配置文件
#application.properties
name=狗剩
然后启动我们前面写的容器
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:placeholder.xml");
Person person = context.getBean("person", Person.class);
assert person != null;
person.sayHello();
2 包扫描
Spring中可以指定一个扫描路径,然后扫描到该路径下所有符合条件的类并注册成BeanDefinition
定义一个注解,只要类上标注了该注解,就会被扫描到
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
String value() default "";
}
再定义一个注解用来指定Bean的作用域范围
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
String value() default "singleton";
}
引入一个组件,负责扫描指定路径下标准了Component的类,并封装为BeanDefinition
public class ClassPathScanningCandidateComponentProvider {
//basePackage是指定包路径
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
//Spring为了优化性能会直接扫描磁盘上的类文件看是否加了指定注解,如果加了才加载该类
//这里为了简单直接使用hutool工具类进行扫描
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(
basePackage, Component.class);
for (Class<?> clazz : classes) {
BeanDefinition bd = new BeanDefinition(clazz);
candidates.add(bd);
}
return candidates;
}
}
在引入一个组件,负责注册扫描到的的BeanDefinition,由此可知,该组件肯定组合了BeanDefinitionRegistry
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
//由于要将扫描到的类注册成BeanDefinition,所以必须持有一个BeanDefinition注册表组件
private BeanDefinitionRegistry registry;
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this.registry = registry;
}
public void doScan(String... basePackages) {
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//设置作用域
String scope = resolveBeanScope(candidate);
if(StrUtil.isNotEmpty(scope)) {
candidate.setScope(scope);
}
//推断Bean名称
String beanName = determineBeanName(candidate);
registry.registerBeanDefinition(beanName, candidate);
}
}
}
//获取Bean作用域
private String resolveBeanScope(BeanDefinition beanDefinition) {
Class<?> beanClass = beanDefinition.getBeanClass();
Scope scope = beanClass.getAnnotation(Scope.class);
if (scope != null) {
return scope.value();
}
return StrUtil.EMPTY;
}
//获取Bean名称
private String determineBeanName(BeanDefinition bd) {
Class<?> beanClass = bd.getBeanClass();
Component component = beanClass.getAnnotation(Component.class);
String value = component.value();
//如果Component指定了名称,就是要指定的,如果没有指定,就使用当前类名首字母小写的字符串作为名称
if (StrUtil.isEmpty(value)) {
value = StrUtil.lowerFirst(beanClass.getSimpleName());
}
return value;
}
}
组件有了,下面就是使用该组件了
在XML配置文件里定义一个新标签,如果加载XML时发现有这个标签就会调用ClassPathBeanDefinitionScanner进行包扫描,这个新标签就是context:component-scan
XmlBeanDefinitionReader修改如下代码
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
//负责扫描指定路径下标注了Component的类,并注册成BeanDefinition
private void scanPackage(String scanPath) {
ClassPathBeanDefinitionScanner scanner =
new ClassPathBeanDefinitionScanner(getRegistry());
//路径可能有多个
scanner.doScan(StrUtil.splitToArray(scanPath, ","));
}
protected void doLoadBeanDefinitions(InputStream inputStream) {
Document document = XmlUtil.readXML(inputStream);
Element root = document.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i) instanceof Element) {
String beanNode = childNodes.item(i).getNodeName();
if (BEAN_ELEMENT.equals(beanNode)){
//....该if分支代码不变
}
//如果存在context:component-scan标签
else if ("context:component-scan".equals(beanNode)) {
Element bean = (Element) childNodes.item(i);
String packages = bean.getAttribute("base-package");
if (StrUtil.isEmpty(packages)) {
throw new BeansException("当context:component-scan存在时,其属性不能为空");
}
scanPackage(packages);
}
}
}
}
}
XML文件如下
<!--package-scan.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context">
<!--开启注解扫描-->
<context:component-scan base-package="org.springframework.test.bean"/>
</beans>
测试一下
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:package-scan.xml");
Target target = context.getBean("target", Target.class);
//XML文件并没有定义Target,但是这里却会得到Target,说明扫描成功了
assert target != null;
target.bar(2);
3 @Value注解
注解开发,在Bean的属性上添加@Value直接,会根据占位符从环境中获取真实值并赋值给该属性
思路大致如下:
- 给BeanFactory注册一个组件用于解析占位符字符串
- 准备一个特殊的Bean后处理器中解析@Value注解,后处理器返回的Bean就是解析之后被赋值的Bean
- 在构造之后,属性填充前,回调这个特殊的Bean后处理器
思路有了,开始编码
首先添加一个新注解@Value
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Value {
//类似"hello,${name}"这种占位符字符串就放在该字段中
String value();
}
定义一个解析器组件用于解析Value注解的占位符字符串
public interface StringValueResolver {
//解析占位符字符串strVal,并返回解析后的真实字符串
String resolveStringValue(String strVal);
}
解析器实现类如下,由于解析占位符的代码在前面PropertyPlaceholderConfigurer中就实现了,肯定不会重新实现而是调用其方法,为了方便调用,下面的解析器实现类应该定义为PropertyPlaceholderConfigurer内部类
//调用PropertyPlaceholderConfigurer中的resolvePlaceholder方法,为了方便,该类是
class PlaceholderResolvingStringValueResolver implements StringValueResolver {
private final Properties properties;
public PlaceholderResolvingStringValueResolver(Properties properties) {
this.properties = properties;
}
public String resolveStringValue(String strVal) throws BeansException {
//调用resolvePlaceholder解析字符串中的占位符
return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);
}
}
ConfigurableBeanFactory中定义了解析器的规范,所以添加两个跟解析器有关的抽象方法
public interface ConfigurableBeanFactory
extends HierarchicalBeanFactory, SingletonBeanRegistry {
//上面两个抽象方法这里省略了
//.....
//注册解析器
void addEmbeddedValueResolver(StringValueResolver valueResolver);
//解析占位符字符串
String resolveEmbeddedValue(String value);
}
AbstractBeanFactory新增一个容器来保存所有的解析器,并实现了上面新增的两个抽象方法
//下面出现的都是添加或者修改的代码,其他代码原封不动
public abstract class AbstractBeanFactory
extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
//保存解析器的集合
private final List<StringValueResolver> embeddedValueResolvers =
new ArrayList<StringValueResolver>();
//注册解析器
@Override
public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
this.embeddedValueResolvers.add(valueResolver);
}
//解析占位符字符串
@Override
public String resolveEmbeddedValue(String value) {
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
result = resolver.resolveStringValue(result);
}
return result;
}
}
在哪注册解析器呢?肯定是在BeanFactory后处理器中,将来容器一启动就会回调所有BeanFactory后处理器的postProcessBeanFactory方法,PropertyPlaceholderConfigurer修改代码如下
//该类的其他代码不动
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//加载属性配置文件
Properties properties = loadProperties();
//属性值替换占位符
processProperties(beanFactory, properties);
//下面两行就是新增的代码
//往容器中添加字符解析器,供解析@Value注解使用
StringValueResolver resolver =
new PlaceholderResolvingStringValueResolver(properties);
beanFactory.addEmbeddedValueResolver(resolver);
}
//PlaceholderResolvingStringValueResolver设置为内部类
private class PlaceholderResolvingStringValueResolver implements StringValueResolver {
//....
}
}
@Value注解的解析应该在Bean构造之后属性填充之前进行,也就是InstantiationAwareBeanPostProcessor的方法postProcessPropertyValues,AOP篇加了该的该抽象方法一直没有真正实现
现在给InstantiationAwareBeanPostProcessor新增一个实现类,实现的postProcessPropertyValues方法专用于解析@Value注解
//用于解析Value和Autowired注解的Bean后处理器
public class AutowiredAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
//构造后
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass,
String beanName) throws BeansException {
return null;
}
//构造后
@Override
public boolean postProcessAfterInstantiation(Object bean,
String beanName) throws BeansException {
return true;
}
//依赖注入
@Override
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 valueAnno = field.getAnnotation(Value.class);
if (valueAnno != null) {
//开始解析
String value = beanFactory.resolveEmbeddedValue(valueAnno.value());
//使用hutool工具类将解析后的真实属性值赋值给Bean
BeanUtil.setFieldValue(bean, field.getName(), value);
}
}
return pvs;
}
//前初始化
@Override
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
return bean;
}
//后初始化
@Override
public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
return bean;
}
}
该Bean后处理器非常常用,这种Bean后处理器的注册不会写在XML文件中,而是硬编码在代码中
ClassPathBeanDefinitionScanner中就硬编码了对AutowiredAnnotationBeanPostProcessor的注册
//该类修改的代码
public class ClassPathBeanDefinitionScanner
extends ClassPathScanningCandidateComponentProvider {
//AutowiredAnnotationBeanPostProcessor的名称是固定的
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME
= "org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this.registry = registry;
}
public void doScan(String... basePackages) {
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//设置作用域
String scope = resolveBeanScope(candidate);
if(StrUtil.isNotEmpty(scope)) {
candidate.setScope(scope);
}
//推断Bean名称
String beanName = determineBeanName(candidate);
registry.registerBeanDefinition(beanName, candidate);
}
}
//注册一个用于解析Value和Autowired注解的后处理器
registry.registerBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class));
}
//其他代码不变.....
}
前面已经实现applyBeanPostprocessorsBeforeApplyingPropertyValues,AbstractAutowireCapableBeanFactory不用修改
至此,@Value的解析功能就实现了
测试前添加一个XML配置文件,application.properties用前面定义的文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context">
<!--开启注解扫描-->
<context:component-scan base-package="org.springframework.test.bean"/>
<!--添加解析占位符的后处理器-->
<bean class="org.springframework.beans.factory.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:application.properties" />
</bean>
</beans>
org.springframework.test.bean这个包下的Person类修改一下
@Component
public class Person {
//给name属性添加Value注解
@Value("${name}")
private String name;
//其他代码不变
}
然后测试
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:anno.xml");
Person person = context.getBean("person", Person.class);
assert person != null;
//发现person对象的name属性值为:zhangsan,正是application.properties中定义的值
person.sayHello();
4 @Autowired注解
@Autowired注解的解析也很简单,该注解和@Value可以一块解析
添加一个@Autowired注解
//该注解没有任何属性,纯粹打标记
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {
}
再添加一个注解@Qualifier用于配合@Autowired
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Inherited
@Documented
public @interface Qualifier {
//根据名称寻找Bean
String value() default "";
}
@Autowired单独使用就是根据类型寻找Bean,配合@Qualifier就是根据名称和类型寻找Bean
所以BeanFacotry需要增加一个抽象方法,用于根据类型得到唯一Bean
public interface BeanFactory {
//根据类型获取Bean
<T> T getBean(Class<T> requiredType) throws BeansException;
}
DefaultListableBeanFactory负责实现这个方法
//该方法实现自BeanFactory
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也要实现该方法
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(requiredType);
}
然后在AutowiredAnnotationBeanPostProcessor类的postProcessPropertyValues方法增加中对@Autowired的支持
public class AutowiredAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
//处理@Value注解的代码不变
//....
//增加处理@Autowired注解的代码
for (Field field : fields) {
Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
if (autowiredAnnotation != null) {
Class<?> fieldType = field.getType();
String dependentBeanName = null;
Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
Object dependentBean = null;
//如果加了@Qualifier,就根据名称和类型获取Bean
if (qualifierAnnotation != null) {
dependentBeanName = qualifierAnnotation.value();
dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
}
//如果没有加@Qualifier,就根据类型获取Bean
else {
dependentBean = beanFactory.getBean(fieldType);
}
BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
}
}
return pvs;
}
}
在Spring5以后,使用@Autowired进行属性注入时,如果该类型的Bean有多个,则会以属性名作为BeanName寻找Bean,而不需使用@Qualifier指定名称,代码实现其实也很简单,有兴趣可以自己实现一下
测试阶段,在上一节的基础上,修改application.properties
name=zhangsan
idNumber=114514
再修改下Person和IDCard类,确保这两个要被扫描到哦
@Component
public class IDCard {
@Value("${idNumber}")
private String idNumber;
//其他代码不变
}
@Component
public class Person {
@Value("${name}")
private String name;
@Autowired
private IDCard idCard;
}
XML配置文件anno.xml不变,测试代码如下
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:anno.xml");
Person person = context.getBean("person", Person.class);
assert person != null;
//发现person的name、idCard都被正常填充了
person.sayHello();
5 循环依赖(没有代理对象)
IOC篇留了个坑,那就是循环依赖
class A{
@Autowired
private B b;
public B getB() {
return b;
}
}
class B{
@Autowired
private A a;
public A getA() {
return a;
}
}
产生循环依赖的本质就是如下
- 调用getBean(A.class),发现A在单例缓存中不存在,就走A的实例化流程
- A的实例化过程中,发现依赖B,就会getBean(B.class),但是B在单例缓存中不存在,就走B的实例化流程
- B的实例化过程中,发现依赖A,就会getBean(A.class),又会循环到第一步
就这样一直递归,直到爆栈
产生循环依赖的原因很简单,那就是在初始化Bean完成后才将Bean放入单例缓存,但是在依赖注入和属性填充阶段就开始产生依赖了
解决的方式也很简单,那就是将Bean放入单例缓存的时间提前到构造之后,提前将Bean的引用暴露出来,依赖时不就能从缓存中获取到了吗
将原来的初始化Bean后放入的缓存singletonObjects称为一级缓存,这里构造后放入的缓存称为二级缓存
修改单例Bean注册表DefaultSingletonBeanRegistry
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
//一级缓存
public Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二级缓存
public Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(256);
//修改获取单例的逻辑
@Override
public Object getSingleton(String beanName) {
//先从一级缓存获取Bean
Object bean = singletonObjects.get(beanName);
//一级缓存没有,就从二级缓存获取Bean
if (bean == null) {
bean = earlySingletonObjects.get(beanName);
}
return bean;
}
//修改添加单例的逻辑
public void addSingleton(String beanName, Object singletonObject) {
this.singletonObjects.put(beanName, singletonObject);
//Bean放入一级缓存后,就从二级缓存移除
this.earlySingletonObjects.remove(beanName);
}
}
然后修改一下doCreateBean的逻辑
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
Object bean = null;
try {
//构造
bean = createBeanInstance(beanDefinition);
//构造后,放入二级缓存,将Bean的引用提前暴露出来,供依赖注入和属性填充时使用
if (beanDefinition.isSingleton()) {
earlySingletonObjects.put(beanName, bean);
}
//其他代码不变.....
} catch (Exception e) {
throw new BeansException("实例化bean失败", e);
}
//其他代码不变.....
}
测试一下
//使用二级缓存的方案
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:circular-reference.xml");
A a = context.getBean(A.class);
B b = context.getBean(B.class);
assert a == b.getA();
6 循环依赖(有代理对象)
此时有个问题,如果A被代理了,那么放入二级缓存的依然是原始A对象而非A的代理对象
实例化B时,发现B依赖A,就从二级缓存找到A,也就是原始A对象赋值给B的属性
B持有的A就是原始A对象,而不是A的代理对象
验证一下上面结论,给A添加切面
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context">
<context:component-scan base-package="org.springframework.test.bean"/>
<!--Bean后处理器,这里应该使用CGLIB代理-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!--添加前置通知、前置通知的适配器,以及advisor切面-->
<bean id="advisor" class
="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression"
value="execution(* org.springframework.test.bean.A.getB())"/>
<property name="advice" ref="beforeAdviceMethodInterceptor"/>
</bean>
<bean id="beforeAdviceMethodInterceptor" class
="org.springframework.aop.framework.advice.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice" />
</bean>
<bean id="beforeAdvice" class="org.springframework.test.service.MyBeforeAdvice"/>
</beans>
判断B持有的A,是否和容器中的A是同一个对象,发现并不是同一个对象
//使用二级缓存的方案
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:circular-reference.xml");
A a = context.getBean(A.class);
B b = context.getBean(B.class);
//看A有没有被增强
a.getB();
//看A和B持有的A是否是同一个对象
assert a == b.getA();
在Spring中,如果A被代理了,那么B依赖的A依然是代理对象,这是怎么做到的呢?
其实也很简单,再像上面一样提前创建代理对象存入三级缓存,并将其引用暴露出来就行了
三级缓存需要延迟获取,故需要用到Lambda的特性,引入了一个函数式接口
@FunctionalInterface
public interface ObjectFactory<T> {
//该方法返回值就是被放入三级缓存的Bean
T getObject() throws BeansException;
}
DefaultSingletonBeanRegistry修改成如下代码
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
//一级缓存
public Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二级缓存
public Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(256);
//三级缓存
private Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(256);
@Override
public Object getSingleton(String beanName) {
//先从一级缓存获取Bean
Object bean = singletonObjects.get(beanName);
//一级缓存没有,就从二级缓存获取Bean
if (bean == null) {
bean = earlySingletonObjects.get(beanName);
//如果二级缓存也是空,就从三级缓存获取
if (bean == null) {
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
bean = singletonFactory.getObject();
//将单例Bean从三级缓存放入二级缓存
earlySingletonObjects.put(beanName, bean);
singletonFactories.remove(beanName);
}
}
}
return bean;
}
public void addSingleton(String beanName, Object singletonObject) {
this.singletonObjects.put(beanName, singletonObject);
//Bean放入一级缓存后,从二级缓存移除
this.earlySingletonObjects.remove(beanName);
//再从三级缓存移除
this.singletonFactories.remove(beanName);
}
//向三级缓存添加数据
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
singletonFactories.put(beanName, singletonFactory);
}
}
InstantiationAwareBeanPostProcessor增加一个方法,用于提前创建并暴露代理类型
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
//getEarlyBeanReference意为提前暴露bean
//该方法并不需要所有实现类实现,所以设计成默认方法
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
}
只有DefaultAdvisorAutoProxyCreator才需要实现该方法
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
//增加一个缓存用来记录哪些Bean在getEarlyBeanReference中提前被处理了
private Set<Object> earlyProxyReferences = new HashSet<>();
//相当于将代理类的创建从后初始化阶段提前到了该方法中
//如果该方法执行过了,那么该类的后初始化方法也就没有必要执行了
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
//该方法已经处理过beanName了,在后初始化阶段就不用重复处理了
earlyProxyReferences.add(beanName);
return wrapIfNecessary(bean, beanName);
}
//后初始化
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//如果getEarlyBeanReference中已经处理过该Bean,那么这里就不用重复处理了
if (!earlyProxyReferences.contains(beanName)) {
return wrapIfNecessary(bean, beanName);
}
return bean;
}
private Object wrapIfNecessary(Object bean, String beanName) {
//将避免死循环的代码移到wrapIfNecessary中
if (isInfrastructureClass(bean.getClass())) {
return bean;
}
//其他代码不变.....
}
}
最后修改AbstractAutowireCapableBeanFactory类的代码
public abstract class AbstractAutowireCapableBeanFactory
extends AbstractBeanFactory implements AutowireCapableBeanFactory {
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition);
//构造后,放入三级缓存,可以同时解决有代理对象的循环依赖
if (beanDefinition.isSingleton()) {
Object finalBean = bean;
//这里设计成Lambda表达式的好处是,getEarlyBeanReference不会立即执行
//只有实例化B时,从缓存获取A,才会执行getEarlyBeanReference并创建A的代理对象
addSingletonFactory(
beanName,
() -> getEarlyBeanReference(beanName, beanDefinition, finalBean)
);
}
//调用bean后处理器执行构造后的逻辑,如果返回false,就不走后面实例化流程
boolean continueWithPropertyPopulation
= applyBeanPostProcessorsAfterInstantiation(beanName, bean);
if (!continueWithPropertyPopulation) {
return bean;
}
//依赖注入
applyBeanPostprocessorsBeforeApplyingPropertyValues(
beanName, bean, beanDefinition);
//属性填充
applyPropertyValues(beanName, bean, beanDefinition);
//前初始化、初始化、后初始化
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("实例化bean失败", e);
}
//将成品Bean注册到单例Bean注册表之前,先注册拥有销毁方法的Bean
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
//实例化完成后,将Bean注册进单例Bean注册表
Object exposedObject = bean;
if (beanDefinition.isSingleton()) {
//如果有代理对象,此处获取代理对象
exposedObject = getSingleton(beanName);
//注册单例Bean的同时,移除二级、三级缓存的Bean
addSingleton(beanName, exposedObject);
}
return bean;
}
//回调DefaultAdvisorAutoProxyCreator后处理器的getEarlyBeanReference方法
protected Object getEarlyBeanReference(String beanName,
BeanDefinition beanDefinition, Object bean) {
Object exposedObject = bean;
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
exposedObject = ((InstantiationAwareBeanPostProcessor) bp)
.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return exposedObject;
}
}
}
return exposedObject;
}
}
测试一下
//使用三级缓存的方案解决有代理时的循环依赖
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:circular-reference.xml");
A a = context.getBean(A.class);
B b = context.getBean(B.class);
assert b.getA() == a;
7 类型转换
Spring提供了完善的类型转换机制,底层是基于Converter进行特定的A类型转B类型,然后注册了非常多的Converter,用起来就好像可以转换任意两种类型,原理如下
首先定义一个最简单的类型转换器组件Converter,将S类型转为T类型
//属于一对一的转换,S是源类型,T是被目标类型,粒度较细
public interface Converter<S, T> {
//将S类型转为T类型
T convert(S source);
}
该转换器作用有限,只能用于将S类型转为T类型,比如String类型转为Integer类型的转换器
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
引入一个新的转换器组件ConverterFactory,依赖了Converter,可以将S类型转成T类型及其子类型
//属性一对多的转换,S是源类型,R是目标类型,T是R类型或者R的子类型,也就是将S转为T
public interface ConverterFactory<S, R> {
//将S转为R类型或者R的子类型
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
比如一个转换器可以将String类型转为Number类型及其子类型
public class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
@Override
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToNumber<T>(targetType);
}
static class StringToNumber<T extends Number> implements Converter<String, T> {
//要转化成的目标类型
private final Class<T> targetType;
public StringToNumber(Class<T> targetType) {
this.targetType = targetType;
}
@Override
public T convert(String source) {
if (source.length() == 0) {
return null;
}
if (targetType.equals(Byte.class)) {
return (T) Byte.valueOf(source);
} else if (targetType.equals(Short.class)) {
return (T) Short.valueOf(source);
} else if (targetType.equals(Integer.class)) {
return (T) Integer.valueOf(source);
} else if (targetType.equals(Long.class)) {
return (T) Long.valueOf(source);
} else if (targetType.equals(Float.class)) {
return (T) Float.valueOf(source);
} else if (targetType.equals(Double.class)) {
return (T) Double.valueOf(source);
} else {
throw new IllegalArgumentException(
"["+source+"]到类型["+targetType.getName()+"]的转换失败"
);
}
}
}
}
可以看出ConverterFactory本质是转换器工厂,专门生产Converter转换器
上面两种转换器功能还不够强大,我们希望将进行多对多的转换
多对多,无非就是聚合了若干了一对一或者一对多的转换器,并且要记录下每个转换器的源类型和目标类型映射关系,所有定义一个类ConvertiblePair,记录下源类型和目标类
//保存sourceType源类型和targetType目标类映射
record ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || obj.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair other = (ConvertiblePair) obj;
return this.sourceType.equals(other.sourceType)
&& this.targetType.equals(other.targetType);
}
@Override
public int hashCode() {
return this.sourceType.hashCode() * 31 + this.targetType.hashCode();
}
}
一个Converter对应一个ConvertiblePair,那一个ConverterFactory是不是对应多个ConvertiblePair呢?
并不是,因为对于ConverterFactory来说,只要知道了sourceType和targetType,它就能自动进行类型转换,所有只提供一个ConvertiblePair给ConverterFactory就足矣!
然后再引入一个通用的类型转换器GenericConverter,将数据从源类型转为目标类型,这的数据是源类型及其子类型,转换后的数据也是目标类型及其子类型,这不就是实现多对多的基础吗
public interface GenericConverter {
//返回该通用转换器支持的所有ConvertiblePair
Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, Class sourceType, Class targetType);
}
最后定义一个复合的类型转换器,该转换器聚合了上面三种类型转换器,并且会根据数据和目标类型选择一个合适的转换器进行真正的转换操作
但是有个问题Converter、ConverterFactory、GenericConverter是三个不同的类型,不好进行聚合,于是可以使用适配器将Converter、ConverterFactory统一适配为GenericConverter
适配器类如下
//将Converter适配为GenericConverter
class ConverterAdapter implements GenericConverter {
private final ConvertiblePair typeInfo;
private final Converter<Object, Object> converter;
public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {
this.typeInfo = typeInfo;
this.converter = (Converter<Object, Object>) converter;
}
//返回该通用类型转换器支持的ConvertiblePair
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(typeInfo);
}
//使用Converter转换器进行转换
@Override
public Object convert(Object source, Class sourceType, Class targetType) {
return converter.convert(source);
}
}
//将ConverterFactory适配为GenericConverter
class ConverterFactoryAdapter implements GenericConverter {
private final ConvertiblePair typeInfo;
private final ConverterFactory<Object, Object> converterFactory;
public ConverterFactoryAdapter(ConvertiblePair typeInfo,
ConverterFactory<?, ?> converterFactory) {
this.typeInfo = typeInfo;
this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(typeInfo);
}
@Override
public Object convert(Object source, Class sourceType, Class targetType) {
//ConverterFactory本质也是使用Converter进行的转换
return converterFactory.getConverter(targetType).convert(source);
}
}
然后就是复合的类型转换器ConversionService,长这样
public interface ConversionService {
//判断是否支持从sourceType类型转换到targetType类型
GenericConverter canConvert(Class<?> sourceType, Class<?> targetType);
//如果支持就进行转换
<T> T convert(Object source, Class<T> targetType);
}
其实现类必然聚合了若干个类型转换器,所以定义一个类型转换器注册表接口
public interface ConverterRegistry {
//注册Converter类型的转换器
void addConverter(Converter<?, ?> converter);
//注册ConverterFactory类型转换器
void addConverterFactory(ConverterFactory<?, ?> converterFactory);
//注册GenericConverter类型转换器
void addConverter(GenericConverter converter);
}
实现类如下
public class GenericConversionService implements ConversionService, ConverterRegistry {
//保存所有的类型转换器,根据源类型和目标类型的ConvertiblePair能够得到对应的GenericConverter
private Map<ConvertiblePair, GenericConverter> converters = new HashMap<>();
//下面两个方法自ConversionService
@Override
public GenericConverter canConvert(Class<?> sourceType, Class<?> targetType) {
//根据源类型和目标类型,判断converters中有没有合适的转换器,步骤如下
//1.由于要支持多对多转换,也就是将源类型及其子类型的数据转为目标类型及其子类型,所以这里得到源类型和目标类型的所有祖先类型
//2.将源和目标类型的祖先类型一一对应组合,得到m*n种组合,也就是m*n个ConvertiblePair
//3.然后根据这些ConvertiblePair试图得到转换器,只要可以得到一个转换器就说明支持转换并返回该转换器
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;
}
@Override
public <T> T convert(Object source, Class<T> targetType) {
//如果source本来就是targetType类型的,则不用转换
if (targetType.isAssignableFrom(source.getClass())) {
return (T)source;
}
Class<?> sourceType = source.getClass();
GenericConverter converter = canConvert(sourceType, targetType);
//如果converter不为空,则说明支持转换
if (converter != null) {
return (T) converter.convert(source, sourceType, targetType);
}
throw new ClassCastException(
"从["+sourceType.getName()+ "]到["+targetType.getName()+"]没有合适的类型转换器");
}
//下面两个方法自ConverterRegistry
@Override
public void addConverter(Converter<?, ?> converter) {
//得到该转换器的源类型和目标类型
ConvertiblePair pair = getRequiredTypeInfo(converter);
ConverterAdapter adapter = new ConverterAdapter(pair, converter);
//ConverterAdapter支持多个ConvertiblePair,那么这多个ConvertiblePair对应同一个转换器
for (ConvertiblePair convertibleType : adapter.getConvertibleTypes()) {
converters.put(convertibleType, adapter);
}
}
@Override
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
ConvertiblePair pair = getRequiredTypeInfo(converterFactory);
ConverterFactoryAdapter adapter = new ConverterFactoryAdapter(pair, converterFactory);
for (ConvertiblePair convertibleType : adapter.getConvertibleTypes()) {
converters.put(convertibleType, adapter);
}
}
@Override
public void addConverter(GenericConverter converter) {
for (ConvertiblePair convertibleType : converter.getConvertibleTypes()) {
converters.put(convertibleType, converter);
}
}
//得到clazz类型以及clazz类型的所有祖先类型
private List<Class<?>> getClassHierarchy(Class<?> clazz) {
List<Class<?>> hierarchy = new ArrayList<>();
while (clazz != null) {
hierarchy.add(clazz);
clazz = clazz.getSuperclass();
}
return hierarchy;
}
//得到转换器对象的源类型和目标类型,并封装为ConvertiblePair
private ConvertiblePair getRequiredTypeInfo(Object object) {
//得到转换器对象object实现的接口
Type[] types = object.getClass().getGenericInterfaces();
ParameterizedType parameterized = (ParameterizedType) types[0];
//得到接口的泛型,也就是源类型和目标类型
Type[] actualTypeArguments = parameterized.getActualTypeArguments();
Class sourceType = (Class) actualTypeArguments[0];
Class targetType = (Class) actualTypeArguments[1];
//封装成ConvertiblePair
return new ConvertiblePair(sourceType, targetType);
}
}
至此复合的类型转换器就实现了,不过该复合的类型转换器想要工作,就必须手动添加转换器对象
为了方便,再定义一个内置了转换器对象的实现类
public class DefaultConversionService extends GenericConversionService {
public DefaultConversionService() {
addDefaultConverters(this);
}
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
//添加其他转换器...
}
}
只需要给GenericConversionService不断注册具体的类型转换器,其类型转换的能力就会不断增强
测试一下
ConversionService conversion = new DefaultConversionService();
assert conversion.convert("3", Integer.class).equals(3);
assert conversion.convert("3", Double.class).equals(3.0);
最后将类型转换整合到Spring
Spring中,Bean的实例化流程中使用到类型转换的地方无非就是依赖注入和属性填充阶段,而是是非Bean类型的属性才属性类型转换,因为对于Bean类型的属性直接从容器中getBean,得到的Bean肯定可以赋值给该属性
所以总结一下:
- 依赖注入阶段,@Value注解修饰的属性需要类型转换
- 属性填充阶段,没有被BeanReference封装的属性需要类型转换
进入编码环节,由于引入了一个新组件——ConversionService,所以BeanFactory要维护该组件
这里为了跟Spring一致,BeanFacotry对应一个抽象方法
public interface BeanFactory {
//上面三个方法不变.....
//新增一个containsBean,判断容器中是否包含指定BeanName
boolean containsBean(String name);
}
然后ConfigurableBeanFactory新增两个方法,用于注册和获取ConversionService
public interface ConfigurableBeanFactory
extends HierarchicalBeanFactory, SingletonBeanRegistry {
void setConversionService(ConversionService conversionService);
ConversionService getConversionService();
}
实现类AbstractBeanFactory持有该组件,新增如下代码
public abstract class AbstractBeanFactory
extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
//组合类型转换器
private ConversionService conversionService;
@Override
public ConversionService getConversionService() {
return conversionService;
}
@Override
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public boolean containsBean(String name) {
return containsBeanDefinition(name);
}
//该方法一开始在子类DefaultListableBeanFactory中实现了,这里出现只是为了方便containsBean使用
protected abstract boolean containsBeanDefinition(String beanName);
}
在容器刷新时,如果容器中存在ConversionService,就注册给BeanFactory
public abstract class AbstractApplicationContext
extends DefaultResourceLoader implements ConfigurableApplicationContext {
//ConversionService的beanName
public static final String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
@Override
public void refresh() throws BeansException {
//上面代码不变....
//提前实例化单例bean,这行旧代码注释调
//beanFactory.preInstantiateSingletons();
//将实例化单例bean的代码,抽取成一个方法
finishBeanFactoryInitialization(beanFactory);
//下面代码不变.....
}
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();
}
//实现BeanFactory的方法
@Override
public boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
}
然后就是在哪使用ConversionService进行类型转换
依赖注入阶段,在AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法中进行类型转换
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
Class<?> clazz = bean.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Value valueAnno = field.getAnnotation(Value.class);
if (valueAnno != null) {
Object value = beanFactory.resolveEmbeddedValue(valueAnno.value());
//新增如下类型转换代码
Class<?> sourceType = value.getClass();
Class<?> targetType = (Class<?>) TypeUtil.getType(field);
ConversionService conversionService = beanFactory.getConversionService();
if (conversionService != null) {
//没有对应的类型转换器会报错
value = conversionService.convert(value, targetType);
}
BeanUtil.setFieldValue(bean, field.getName(), value);
}
}
//解析@Autowired代码不变
//.....
return pvs;
}
属性填充阶段,在AbstractAutowireCapableBeanFactory的applyPropertyValues方法中类型转换
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
PropertyValues pvs = beanDefinition.getPropertyValues();
try {
for (PropertyValue pv : pvs.getPropertyValues()) {
String name = pv.getName();
Object value = pv.getValue();
if (value instanceof BeanReference beanReference) {
value = getBean(beanReference.getBeanName());
}
//新增一个if分支
else {
//进行类型转换
Class<?> sourceType = value.getClass();
Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);
ConversionService conversionService = getConversionService();
if (conversionService != null) {
//尝试进行类型转换,没有对应的类型转换器会报错
value = conversionService.convert(value, targetType);
}
}
String setMethod = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
ReflectUtil.invoke(bean, setMethod, value);
}
} catch (Exception e) {
throw new BeansException("属性填充发生异常,在Bean:" + beanName, e);
}
}
最后定义一个FactoryBean用于创建ConversionService,然后将该FactoryBean注册进容器
public class ConversionServiceFactoryBean
implements FactoryBean<ConversionService>, InitializingBean {
private Set<?> converters;
private GenericConversionService conversionService;
@Override
public ConversionService getObject() throws Exception {
return conversionService;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public void afterPropertiesSet() throws Exception {
conversionService = new DefaultConversionService();
registerConverters(converters, conversionService);
}
//注册转换器
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("emm,你在逗我吗,你传的上面类型");
}
}
}
}
public void setConverters(Set<?> converters) {
this.converters = converters;
}
}
测试阶段,先添加一个类型转换器用于测试,这里也用到了FactoryBean
public class ConvertersFactoryBean implements FactoryBean<Set<?>> {
@Override
public Set<?> getObject() throws Exception {
HashSet<Object> converters = new HashSet<>();
StringToLocalDateConverter stringToLocalDateConverter =
new StringToLocalDateConverter("yyyy-MM-dd");
converters.add(stringToLocalDateConverter);
return converters;
}
@Override
public boolean isSingleton() {
return true;
}
//定义一个字符串转日期的转换器
class StringToLocalDateConverter implements Converter<String, LocalDate> {
private final DateTimeFormatter DATE_TIME_FORMATTER;
public StringToLocalDateConverter(String pattern) {
DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(pattern);
}
@Override
public LocalDate convert(String source) {
return LocalDate.parse(source, DATE_TIME_FORMATTER);
}
}
}
然后给Persn增加一个属性birthday用于测试字符串转日期的转换器
public class Person {
//...其他代码不变
//新增一个属性,并添加其get、set方法
private LocalDate birthday;
}
XML文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" ref="converters"/>
</bean>
<bean id="converters" class="org.springframework.test.service.ConvertersFactoryBean"/>
<bean id="person" class="org.springframework.test.bean.Person">
<property name="birthday" value="2024-01-01" />
</bean>
</beans>
测试代码
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:type-convert.xml");
Person person = context.getBean(Person.class);
assert person != null;
assert person.getBirthday() != null;
//发现字符串"2024-01-01"被转为LocalDate
System.out.println(person.getBirthday());
属性填充阶段的类型转换测试成功,有兴趣的可以测试下依赖注入阶段的类型转换
8 懒加载
当容器上下文对象启动时,并不希望一次性实例化全部单例Bean,可以引入懒加载机制
给BeanDefinition增加一个属性,表示Bean是否需要懒加载,默认false
public class BeanDefinition {
//是否需要懒加载
private boolean lazyInit = false;
public void setLazyInit(boolean b){
lazyInit=b;
}
public boolean isLazyInit(){
return lazyInit;
}
//其他代码不变
}
然后XmlBeanDefinitionReader解析lazyInit标签并赋值给BeanDefinition的lazyInit属性,很简单,这就省略了
然后DefaultListableBeanFactory#preInstantiateSingletons中,判断一下,如果是lazyInit = true,就不立刻实例化
@Override
public void preInstantiateSingletons() throws BeansException {
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
if (beanDefinition.isSingleton() && !beanDefinition.isLazyInit()) {
getBean(beanName);
}
});
}
只有将来主动getBean时,才实例化对应的Bean