likes
comments
collection
share

🐾Spring的ApplicationContext加载Bean定义信息,创建代理对象过程

作者站长头像
站长
· 阅读数 1

前言

🐾Spring的ApplicationContext加载Bean定义信息,创建代理对象过程

SpringIOC容器实际上指的就是ApplicationContext这个接口

  • 首先,它继承BeanFactory对Bean基本功能的规范(这也是基本的IOC实现)
  • 其次,除了对Bean的管理外,还包含应用事件(ApplicationEventPublisher),以及对Bean配置进行加载(ResourceLoader),此外还有国际化(MessageSource)
  • 对于不同Bean的配置方式(比如注解,xml文件等)有着不同的资源加载方式,所以applicationContext的实现类也有几种

ApplicationContext的抽象类AbstractApplicationContext的抽象实现类

  • GenericApplicationContext: 是初始化的时候就创建容器,往后的每次refresh都不会更改
  • AbstractRefreshableApplicationContext:它本身及子类的每次refresh都是先清除已有(如果不存在就创建)的容器,然后再重新创建,同时它及子类无法做到GenericApplicationContext混合搭配从不同源头获取bean的定义信息(GenericApplicationContext可以从annotation,properties等源头获取,它可以混合搭配也是跟每次refresh都不会改变的性质有关)。

这里我们主要看AbstractRefreshableApplicationContext的实现类🐾Spring的ApplicationContext加载Bean定义信息,创建代理对象过程

探索了解ApplicationContext的实现类

接下来展示迷你spring的ClassPathXmlApplicationContext的测试类进行深入了解一下关于这个IOC容器是如何加载bean以及如何动态创建代理对象

@Test
public void testAutoProxy() throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:auto-proxy.xml");

//获取代理对象
WorldService worldService = applicationContext.getBean("worldService", WorldService.class);
worldService.explode();
}
  • new ClassPathXmlApplicationContext时会把参数传入的xml文件进行加载,而xml文件是写入Bean定义的。
<!-- 这是auto-proxy.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<bean id="worldService" class="org.springframework.test.service.WorldServiceImpl"/>

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!-- 切面表达式的切点增强器 -->
<bean id="pointcutAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* org.springframework.test.service.WorldService.explode(..))"/>
<!-- 将"methodInterceptor"作为切点增强器的通知对象-->
<property name="advice" ref="methodInterceptor"/>
</bean>
<!-- 切面表达式的切点增强器 -->
<bean id="pointcutAdvisor2" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* org.springframework.test.service.WorldService.explode(..))"/>
<property name="advice" ref="methodInterceptor2"/>
</bean>
<bean id="methodInterceptor2" class="org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor">
<property name="advice" ref="afterAdvice"/>
</bean>
<!-- 后置通知 -->
<bean id="afterAdvice" class="org.springframework.test.common.WorldServiceAfterReturnAdvice"/>
<bean id="methodInterceptor" class="org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<!-- 前置通知 -->
<bean id="beforeAdvice" class="org.springframework.test.common.WorldServiceBeforeAdvice"/>
  • 注意:ref是引用的意思,在加载Bean定义信息的时候,是不会直接引用的,因为都只是通过比如beanName记录对应的关系,只有getBean的时候才会根据记录的关系去引用,所以这里的顺序随意。如何记录对应的关系呢?接着往下看就了解啦。
/**
 * xml文件的应用上下文
 *
 */
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {

    private String[] configLocations;

    /**
    * 从xml文件加载BeanDefinition,并且自动刷新上下文
    *
    * @param configLocation xml配置文件
    * @throws BeansException 应用上下文创建失败
    */
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation});
    }

    /**
    * 从xml文件加载BeanDefinition,并且自动刷新上下文
    *
    * @param configLocations xml配置文件
    * @throws BeansException 应用上下文创建失败
    */
    public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
        this.configLocations = configLocations;
        refresh();
    }

    protected String[] getConfigLocations() {
        return this.configLocations;
    }
}
  • 可以看见构造函数调用重载,会进行refresh()方法,也就是刷新和重新加载ApplicationContext,将配置文件或注解中定义的Bean加载到应用上下文中,进行实例化和依赖注入,从而准备好应用中的各个Bean供其他组件调用和使用。接下来我们看看refresh方法
public void refresh() throws BeansException {
//创建BeanFactory,并加载BeanDefinition
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();

//添加ApplicationContextAwareProcessor,让继承自ApplicationContextAware的bean能感知bean
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));


//在bean实例化之前,执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);

//BeanPostProcessor需要提前与其他bean实例化之前注册
registerBeanPostProcessors(beanFactory);

//初始化事件发布者
initApplicationEventMulticaster();

//注册事件监听器
registerListeners();

//注册类型转换器和提前实例化单例bean
finishBeanFactoryInitialization(beanFactory);

//发布容器刷新完成事件
finishRefresh();
}
  • 我们主要了解关于如何加载Bean定义信息,以及如何getBean,如何实现前后置通知,所以我们就看refreshBeanFactory()即可。
  • 先看refreshBeanFactory(),直接看里面的会调用的核心部分
/**
 * 读取配置在xml文件中的bean定义信息
 *
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public static final String BEAN_ELEMENT = "bean";
    public static final String PROPERTY_ELEMENT = "property";
    public static final String ID_ATTRIBUTE = "id";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String CLASS_ATTRIBUTE = "class";
    public static final String VALUE_ATTRIBUTE = "value";

    /**
    * 属性的引用
    */
    public static final String REF_ATTRIBUTE = "ref";
    public static final String INIT_METHOD_ATTRIBUTE = "init-method";
    public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
    public static final String SCOPE_ATTRIBUTE = "scope";

    public static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
    public static final String COMPONENT_SCAN_ELEMENT = "component-scan";

    public static final String LAZYINIT_ATTRIBUTE = "lazyInit";
    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            InputStream inputStream = resource.getInputStream();
            try {
                doLoadBeanDefinitions(inputStream);
            } finally {
                inputStream.close();
            }
        } catch (IOException | DocumentException ex) {
            throw new BeansException("IOException parsing XML document from " + resource, ex);
        }
    }

    protected void doLoadBeanDefinitions(InputStream inputStream) throws DocumentException {

        SAXReader reader = new SAXReader();
        //代表整个XML文档的根节点。
        Document document = reader.read(inputStream);
        //获取到XML文档的根元素
        Element root = document.getRootElement();
        //解析context:component-scan标签并扫描指定包中的类,提取类信息,组装成BeanDefinition
        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);
        }

        List<Element> beanList = root.elements(BEAN_ELEMENT);
        for (org.dom4j.Element bean : beanList) {
            String beanId = bean.attributeValue(ID_ATTRIBUTE);
            String beanName = bean.attributeValue(NAME_ATTRIBUTE);
            String className = bean.attributeValue(CLASS_ATTRIBUTE);
            String initMethodName = bean.attributeValue(INIT_METHOD_ATTRIBUTE);
            String destroyMethodName = bean.attributeValue(DESTROY_METHOD_ATTRIBUTE);
         String beanScope = bean.attributeValue(SCOPE_ATTRIBUTE);
         String lazyInit=bean.attributeValue(LAZYINIT_ATTRIBUTE);
         Class<?> clazz;
         try {
            clazz = Class.forName(className);
         } catch (ClassNotFoundException e) {
            throw new BeansException("Cannot find class [" + className + "]");
         }
         //id优先于name
         beanName = StrUtil.isNotEmpty(beanId) ? beanId : beanName;
         if (StrUtil.isEmpty(beanName)) {
            //如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
            beanName = StrUtil.lowerFirst(clazz.getSimpleName());
         }

         BeanDefinition beanDefinition = new BeanDefinition(clazz);
         beanDefinition.setInitMethodName(initMethodName);
         beanDefinition.setDestroyMethodName(destroyMethodName);
         beanDefinition.setLazyInit("true".equals(lazyInit));
         if (StrUtil.isNotEmpty(beanScope)) {
            beanDefinition.setScope(beanScope);
         }

         List<org.dom4j.Element> propertyList = bean.elements(PROPERTY_ELEMENT);
         for (Element property : propertyList) {
            String propertyNameAttribute = property.attributeValue(NAME_ATTRIBUTE);
            String propertyValueAttribute = property.attributeValue(VALUE_ATTRIBUTE);
            String propertyRefAttribute = property.attributeValue(REF_ATTRIBUTE);

            if (StrUtil.isEmpty(propertyNameAttribute)) {
               throw new BeansException("The name attribute cannot be null or empty");
            }

            Object value = propertyValueAttribute;
            if (StrUtil.isNotEmpty(propertyRefAttribute)) {
               value = new BeanReference(propertyRefAttribute);
            }
            PropertyValue propertyValue = new PropertyValue(propertyNameAttribute, value);
            beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
         }
         if (getRegistry().containsBeanDefinition(beanName)) {
            //beanName不能重名
            throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
         }
         //注册BeanDefinition
         getRegistry().registerBeanDefinition(beanName, beanDefinition);
      }
      }
   /**
    * 扫描注解Component的类,提取信息,组装成BeanDefinition
    *
    * @param scanPath
    */
   private void scanPackage(String scanPath) {
      String[] basePackages = StrUtil.splitToArray(scanPath, ',');
      ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
      scanner.doScan(basePackages);
   }
}
  • 通过refreshBeanFactory()方法,会调用XmlBeanDefinitionReader的loadBeanDefinitions方法,而该方法的参数resource则是"auto-proxy.xml"这个xml文件,因为这个resource.getInputStream()方法如下。
@Override
public InputStream getInputStream() throws IOException {
//使用当前类的类加载器,通过指定的路径获取资源文件的输入流。
//getResourceAsStream() 它可用于从类路径中获取指定路径的资源文件,并返回该资源文件的输入流。
InputStream is = this.getClass().getClassLoader().getResourceAsStream(this.path);
if (is == null) {
    throw new FileNotFoundException(this.path + " cannot be opened because it does not exist");
}
return is;
}
  • 接着我们看回去 doLoadBeanDefinitions方法,这个就是核心,可以看见该方法主要作一些收集bean定义信息,并没有实例化bean。这里就弄清上面引用的问题啦。
  • 然后后面到finishBeanFactoryInitialization(beanFactory)这个方法的时候就会把bean提前实例化,供getBean使用
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();
//发布容器刷新完成事件
finishRefresh();
}
  • preInstantiateSingletons这个方法用来提前实例化单例bean
public void preInstantiateSingletons() throws BeansException {
//只有当bean是单例且不为懒加载才会被创建
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
    if(beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){
        getBean(beanName);
    }
});
}
  • 会遍历前面收集的bean定义信息,如果bean是单例且不是懒加载,就会进行实例化getBean(beanName)方法
/**
 * 获取bean
 *
 * @param name
 * @return
 * @throws BeansException bean不存在时
 */
@Override
public Object getBean(String name) throws BeansException {
    //尝试获取已经存在的单例bean实例
    Object sharedInstance = getSingleton(name);
    if (sharedInstance != null) {
        //如果是FactoryBean,从FactoryBean#getObject中创建bean
        return getObjectForBeanInstance(sharedInstance, name);
    }
    //先根据name拿到之前收集的bean定义信息,然后再进行createBean
    BeanDefinition beanDefinition = getBeanDefinition(name);
    Object bean = createBean(name, beanDefinition);
    return getObjectForBeanInstance(bean, name);
}
  • 首先会尝试获取已经存在的单例bean实例,会用到getSingleton方法
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    //一级缓存
    private Map<String, Object> singletonObjects = new HashMap<>();

    //二级缓存
    private Map<String, Object> earlySingletonObjects = new HashMap<>();

    //三级缓存
    private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>();

    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    @Override
    public Object getSingleton(String beanName) {
        Object singletonObject = singletonObjects.get(beanName);
        if (singletonObject == null) {
            singletonObject = earlySingletonObjects.get(beanName);
            if (singletonObject == null) {
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    //从三级缓存放进二级缓存
                    earlySingletonObjects.put(beanName, singletonObject);
                    singletonFactories.remove(beanName);
                }
            }
        }
        return singletonObject;
    }

    @Override
    public void addSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        singletonFactories.put(beanName, singletonFactory);
    }
  • 这里涉及三级缓存,三级缓存的目的是解决单例bean的循环依赖问题,这里先跳过该问题,后续再细说。
  • 直接看到createBean方法,里面又会跳到 doCreateBean方法
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
    Object bean;
    try {
        bean = createBeanInstance(beanDefinition);

        //为解决循环依赖问题,将实例化后的bean放进缓存中提前暴露
        if (beanDefinition.isSingleton()) {
            Object finalBean = bean;
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, beanDefinition, finalBean);
                }
            });
        }

        //实例化bean之后执行
        boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
        if (!continueWithPropertyPopulation) {
            return bean;
        }
        //在设置bean属性之前,允许BeanPostProcessor修改属性值
        applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);
        //为bean填充属性
        applyPropertyValues(beanName, bean, beanDefinition);
        //执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
        bean = initializeBean(beanName, bean, beanDefinition);
    } catch (Exception e) {
        throw new BeansException("Instantiation of bean failed", e);
    }

    //注册有销毁方法的bean
    registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

    Object exposedObject = bean;
    if (beanDefinition.isSingleton()) {
        //如果有代理对象,此处获取代理对象
        exposedObject = getSingleton(beanName);
        addSingleton(beanName, exposedObject);
    }
    return exposedObject;
}
  • 会经过创建bean实例,设置bean属性等,当执行到initializenBean方法时,会执行BeanPostProcessor的前置和后置处理方法
protected Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
    if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(this);
    }

    //执行BeanPostProcessor的前置处理
    Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

    try {
        invokeInitMethods(beanName, wrappedBean, beanDefinition);
    } catch (Throwable ex) {
        throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", ex);
    }

    //执行BeanPostProcessor的后置处理
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    return wrappedBean;
}
  • 迷你spring的主要代理创建放在后置处理,所以我们看applyBeanPostProcessorsAfterInitialization方法
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postPrqie
  • 首先,会拿出之前收集的两个processor,其中一个是DefaultAdvisorAutoProxyCreator,它的postProcessAfterInitialization方法如下
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   // earlyProxyReferences用于存储在早期阶段就已经被代理过的 Bean 的名称的集合
    if (!earlyProxyReferences.contains(beanName)) {
        return wrapIfNecessary(bean, beanName);
    }

    return bean;
}
  • 如果该Bean没有被代理过,就会进入wrapIfNecessary方法,该方法会将Bean创建为代理对象
protected Object wrapIfNecessary(Object bean, String beanName) {
    //避免死循环
    if (isInfrastructureClass(bean.getClass())) {
        return bean;
    }
    //获取切面表达式的增强器集合
    Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class)
    .values();
    try {
        ProxyFactory proxyFactory = new ProxyFactory();
        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (classFilter.matches(bean.getClass())) {
                TargetSource targetSource = new TargetSource(bean);
                proxyFactory.setTargetSource(targetSource);
                proxyFactory.addAdvisor(advisor);
                proxyFactory.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            }
        }
        if (!proxyFactory.getAdvisors().isEmpty()) {
            return proxyFactory.getProxy();
        }
    } catch (Exception ex) {
        throw new BeansException("Error create proxy bean for: " + beanName, ex);
    }
    return bean;
}
  • 首先获取切面表达器的增强器集合,然后创建代理工厂,遍历集合,往代理工厂设置目标对象,以及添加获取到的两个增强器,还有就是设置要增强的方法。最后返回proxyFactory.getProxy()这个方法
public Object getProxy() {
    return createAopProxy().getProxy();
}

private AopProxy createAopProxy() {
    if (this.isProxyTargetClass()||this.getTargetSource().getTargetClass().length==0) {
        return new CglibAopProxy(this);
    }
    return new JdkDynamicAopProxy(this);
}
  • createAopProxy主要判断用Cglib动态代理还是JDK动态代理
    • this.isProxyTargetClass(),表示是否使用CGLIB动态代理
    • this.getTargetSource().getTargetClass().length==0,表示该类没有实现任何接口
  • 最后我们来看看获取到的增强器,如下
<bean id="pointcutAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* org.springframework.test.service.WorldService.explode(..))"/>
<property name="advice" ref="methodInterceptor"/>
</bean>

<bean id="pointcutAdvisor2" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* org.springframework.test.service.WorldService.explode(..))"/>
<property name="advice" ref="methodInterceptor2"/>
</bean>
  • 可以看到表达式,都是对explode方法进行增强。所以当测试类getBean之后,执行explode方法,就会触发拦截器。

总结

通过以上内容,可以了解到applicationContext是IOC容器的核心,简单总结迷你spring实现的部分

  1. 它能够创建Bean工厂,并通过多种方式进行加载Bean定义信息
  2. 在bean实例化之前,执行BeanFactoryPostProcessor(可以修改bean定义信息)
  3. bean实例化之前提前注册BeanPostProcessor(便于代理创建)
  4. 初始化事件发布者和注册事件监听器
  5. 注册类型转换器和提前实例化单例bean
  6. 发布容器刷新完成事件