前言
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的实现类
探索了解ApplicationContext的实现类
接下来展示迷你spring的ClassPathXmlApplicationContext的测试类进行深入了解一下关于这个IOC容器是如何加载bean以及如何动态创建代理对象
java
@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定义的。
xml
<!-- 这是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的时候才会根据记录的关系去引用,所以这里的顺序随意。如何记录对应的关系呢?接着往下看就了解啦。
scala
/**
* 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方法
scss
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(),直接看里面的会调用的核心部分
ini
/**
* 读取配置在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()方法如下。
kotlin
@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使用
scss
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
scss
public void preInstantiateSingletons() throws BeansException {
//只有当bean是单例且不为懒加载才会被创建
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
if(beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){
getBean(beanName);
}
});
}
- 会遍历前面收集的bean定义信息,如果bean是单例且不是懒加载,就会进行实例化getBean(beanName)方法
scss
/**
* 获取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方法
typescript
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方法
scss
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的前置和后置处理方法
typescript
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方法
css
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postPrqie
- 首先,会拿出之前收集的两个processor,其中一个是DefaultAdvisorAutoProxyCreator,它的postProcessAfterInitialization方法如下
typescript
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// earlyProxyReferences用于存储在早期阶段就已经被代理过的 Bean 的名称的集合
if (!earlyProxyReferences.contains(beanName)) {
return wrapIfNecessary(bean, beanName);
}
return bean;
}
- 如果该Bean没有被代理过,就会进入wrapIfNecessary方法,该方法会将Bean创建为代理对象
scss
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()这个方法
kotlin
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,表示该类没有实现任何接口
- 最后我们来看看获取到的增强器,如下
ini
<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方法,就会触发拦截器。
对于使用CGLIB动态代理方向的测试具体可以看看我的另一篇基于代理工厂创建代理对象的文章:🐉CGLIB的动态代理如何实现方法增强 - 掘金 (juejin.cn)
总结
通过以上内容,可以了解到applicationContext是IOC容器的核心,简单总结迷你spring实现的部分
- 它能够创建Bean工厂,并通过多种方式进行加载Bean定义信息
- 在bean实例化之前,执行BeanFactoryPostProcessor(可以修改bean定义信息)
- bean实例化之前提前注册BeanPostProcessor(便于代理创建)
- 初始化事件发布者和注册事件监听器
- 注册类型转换器和提前实例化单例bean
- 发布容器刷新完成事件