极简Spring之高级

IOC:https://juejin.cn/post/7366459005881778202
AOP:https://juejin.cn/spost/7368355894708584488

扩展一下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 &lt; target.length(); i++) {
        char currentChar = target.charAt(i);
        char nextChar = i == target.length() - 1 ? target.charAt(i) : target.charAt(i + 1);

        if (currentChar == 36 &amp;&amp; nextChar == 123) {
            lowStatus = true;
            //如果遇到新的"${"组合时,上一个"${"还没有闭合,就将缓存写入sb,并清空缓存
            if (cache.length() &gt; 0) {
                sb.append(cache);
                cache = new StringBuilder();
                low = i;
            }
        }
        if (lowStatus &amp;&amp; currentChar == 125) {
            highStatus = true;
        }

        //遇到"}"时,闭合"${",并根据"${"和"}"中间的字符串,从数据源获取数据追加到sb,并清空缓存
        if (lowStatus &amp;&amp; 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();
}

}
</object,>

为了方便使用,占位符解析器应该作为一个组件注册给BeanFactory,不过这都是后话了

将该BeanFactory后处理器注册进容器,它就会开始工作,就像下面这样




    
        
        
    
    
    
        
    

测试一下,准备一个配置文件

#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 findCandidateComponents(String basePackage) {
        Set candidates = new LinkedHashSet();
        //Spring为了优化性能会直接扫描磁盘上的类文件看是否加了指定注解,如果加了才加载该类
        //这里为了简单直接使用hutool工具类进行扫描
        Set> 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 &lt; 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文件如下




    
    

测试一下

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直接,会根据占位符从环境中获取真实值并赋值给该属性

思路大致如下:

  1. 给BeanFactory注册一个组件用于解析占位符字符串
  2. 准备一个特殊的Bean后处理器中解析@Value注解,后处理器返回的Bean就是解析之后被赋值的Bean
  3. 在构造之后,属性填充前,回调这个特殊的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 embeddedValueResolvers = 
        new ArrayList();
//注册解析器
@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用前面定义的文件



    
    
<!--添加解析占位符的后处理器-->
<bean class="org.springframework.beans.factory.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:application.properties">
</property></bean>

</context:component-scan>

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 getBean(Class requiredType) throws BeansException;
}

DefaultListableBeanFactory负责实现这个方法

//该方法实现自BeanFactory
public  T getBean(Class requiredType) throws BeansException {
    List beanNames = new ArrayList<>();
    for (Map.Entry 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 getBean(Class 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;
}

}

产生循环依赖的本质就是如下

  1. 调用getBean(A.class),发现A在单例缓存中不存在,就走A的实例化流程

  2. A的实例化过程中,发现依赖B,就会getBean(B.class),但是B在单例缓存中不存在,就走B的实例化流程

  3. 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&lt;&gt;(256);
//二级缓存
public Map<string, object=""> earlySingletonObjects = new ConcurrentHashMap&lt;&gt;(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);
}

}
</string,></string,>

然后修改一下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添加切面



    
<!--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">
</property></property></bean>
<bean id="beforeAdviceMethodInterceptor" class="org.springframework.aop.framework.advice.MethodBeforeAdviceInterceptor">
    <property name="advice" ref="beforeAdvice">
</property></bean>
<bean id="beforeAdvice" class="org.springframework.test.service.MyBeforeAdvice">

</context:component-scan>

判断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 {
    //该方法返回值就是被放入三级缓存的Bean
    T getObject() throws BeansException;
}

DefaultSingletonBeanRegistry修改成如下代码

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    //一级缓存
    public Map singletonObjects = new ConcurrentHashMap<>(256);
    //二级缓存
    public Map earlySingletonObjects = new ConcurrentHashMap<>(256);
    //三级缓存
    private Map> 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);
}

}
</string,></string,></string,>

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&lt;&gt;();

//相当于将代理类的创建从后初始化阶段提前到了该方法中
//如果该方法执行过了,那么该类的后初始化方法也就没有必要执行了
@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, 
                () -&gt; 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 类型转换

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


这是一个从 https://juejin.cn/post/7369052013151780916 下的原始话题分离的讨论话题