Spring的XML解析原理
-
Spring的作用:
- 定位: 寻找XML配置文件
- 加载: 将解析的XML配置加载到内存
-
实例化: 根据加载的XML配置进行实例化
-
IOC体系图:
- 所有的IOC都有继承关系,这样设计使得任何一个子类IOC都可以直接使用父类IOC加载的Bean
- 其中重要的类是BeanFactory和ApplicationContext,这是所有IOC的父接口
- BeanFactory中提供了对于Bean最基本的操作
- ApplicationContext继承BeanFactory, 同时继承MessageSource, ResourceLoader, ApplicationEventPublisher接口用来提供国际化,资源加载,时间发布高级功能 .Spring加载的XML的类是ApplicationContext的子类
- 其余重要的类:
-
BeanFactory
- HierarchicalBeanFactory
-
ListableBeanFactory
- DefaultListableBeanFactory
-
AliasRegistry
- SimpleAliasRegistry
-
ResourceLoader
-
ApplicationContext
-
AbstractApplicationContext
-
AbstractRefreshableApplicationContext
-
AbstractRefreshableConfigApplicationContext
-
AbstractXmlApplicationContext
- ClassPathXmlApplicationContext
-
AbstractXmlApplicationContext
-
AbstractRefreshableConfigApplicationContext
-
AbstractRefreshableApplicationContext
-
AbstractApplicationContext
-
ApplicationContext
-
BeanFactory
-
Spring中的两种标签: 标签在使用前都需要在Spring的xml配置文件里声明Namespace URI, 这样才能在解析标签时通过Namespace URI找到对应的NamespaceHandler
- Spring原生标签: 不带前缀的标签是Spring中的原生标签
- 自定义标签: 带前缀的标签是自定义标签
<bean class="com.oxford.bean.Bean" id="bean" c:id="beanId" p:name="beanName" />
- c: 通过构造器给属性赋值,是constructor-arg的简化写法
- p: 通过属性的setter方法给属性赋值,是property的简化写法
-
Spring自定义扩展标签:
- 创建一个自定义标签和解析类
- 指定命名空间和NamespaceHandler
- 在META-INF的spring.handler文件中指定命名空间和NamespaceHandler的映射关系. 类似Spring的c标签和p标签一样
-
Spring的XML解析流程:
Spring的Bean实例化原理
-
BeanFactoryPostProcessor的类图:
- ConfigurationClassPostProcessor类完成对 @Configuration, @Bean等注解的解析注册
Beans
接口实例化
基本概念
-
JavaBean:
-
JavaBean是遵循Sun制定的JavaBean标准的特定编码规范的类:
- 包含一个默认的公有无参构造器
- 允许通过getter和setter访问器访问类的成员属性,或者其余遵循特定命名规范的方法访问
- 实现了java.io.Serializable接口,可序列化
-
JavaBean是遵循Sun制定的JavaBean标准的特定编码规范的类:
-
POJO:
- POJO即简单Java对象
- POJO类的对象不需要通过框架来使用,也不需要在特定的应用服务环境下运行
- POJO主要用来区分简单Java对象,轻量级Java对象和重量级Java对象
-
SpringBean:
- SpringBean表示由Spring框架管理的对象,也就是由Spring框架的容器进行初始化,配置和管理的Java对象
- Spring Bean是通过Spring配置文件或者通过注解定义的,在Spring容器中初始化,然后注入到应用程序中
Spring实例化Bean
-
Spring寻找并解析Bean属性的方式:
-
注解的方式:
- Spring在启动后进行初始化时会扫描classpath包下符合要求的class
- 然后进行解析并注册到BeanFactory中
-
xml的方式:
- 注解通过XmlBeanDefinitionReader解析Bean
- 一般情况下,主流的方式是使用注解的配置,尤其是在SpringBoot框架中
-
注解的方式:
构造器实例化
- SpringBean类中的默认构造器创建Bean实例:
- Spring容器根据Spring配置文件的路径加载配置文件
- 在加载的同时 ,Spring容器会通过实现Bean类中默认的无参构造器对Bean进行实例化
- SpringBean构造器实例化示例
静态工厂实例化
- 工厂实现类中提供一个静态工厂方法创建Bean实例:
- Spring容器根据Spring配置文件的路径加载配置文件
- 在加载的同时 ,Spring容器会通过工厂实现类StaticFactoryBean类中的静态方法getBean() 对Bean进行实例化
- SpringBean静态工厂实例化示例
实例工厂实例化
- 工厂实现类直接使用成员方法创建Bean实例,同时在配置文件中:
- 需要实例化的Bean不是通过class属性直接指向实例化的类
- 而是通过class属性配置实例工厂
- 然后通过factory-bean属性指定一个实例工厂
- 最后使用factory-method属性指定使用factory-bean配置的实例工厂中的哪一个方法
- Spring容器根据Spring配置文件的路径加载配置文件
- 在加载的同时 ,Spring容器会通过配置的实例工厂类InstanceBeanFactory中的成员getBean() 对Bean进行实例化
- SpringBean实例工厂实例化示例
代理Bean操作
- 实现一个动态代理接口,并且在Spring容器初始化完成之前将该代理对象注册到Spring容器中.实现可以通过 @Autowired等方法从Spring中获取该代理对象
- Spring中代理Bean操作示例
Spring中动态注入和删除Bean
-
动态注入和删除Bean:
- 使用getBean() 获取对象的操作,这些对象都是程序中事先定义好的
- 有时候需要动态地加入和删除对象. 尽管可以采用配置文件或者注解的方式,但是每次都需要重启服务
- 为了避免以上问题,需要在Spring中对Bean进行动态地管理,包括Bean对象的注入和删除
-
Spring中动态管理Bean实现思路:
-
Spring中管理Bean对象的是BeanFactory, 具体的是DefaultListableBeanFactory
- 通过获取到Bean类的ApplicationContext对象获取到BeanFactory
- 在类DefaultListableBeanFactory中有registerBeanDefinition() 方法使用一个BeanDefinition参数注入Bean
- 通过BeanDefinitionBuilder可以构建一个Bean类的BeanDefinition
-
Spring中管理Bean对象的是BeanFactory, 具体的是DefaultListableBeanFactory
-
Spring中动态注入Bean步骤:
- 获取Bean类的ApplicationContext
- 通过ApplicationContext获取BeanFactory
- 通过BeanDefinitionBuilder获取Bean类的BeanDefinition
- 通过BeanFactory的registerBeanDefinition() 注入Bean类的BeanDefinition
- 注入完成后,可以通过ApplicationContext的getBean() 获取Bean对象
// 1. 获取Bean类的ApplicationContext
ApplicationContext ctx = SpringApplication.run(Bean.class, args);
// 2. 通过ApplicationContext获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
// 3. 获取并创建BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Bean.class);
beanDefinitionBuilder.addPropertyValue("name", "Oxford");
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// 4. 通过BeanFactory的registerBeanDefinition()注入Bean类的BeanDefinition
defaultListableBeanFactory.registerBeanDefinition("beanInterface", beanDefinition);
// 获取动态注册的Bean
Bean beanInterface = ctx.getBean(Bean.class);
-
Spring中动态删除Bean步骤:
- 获取Bean类的ApplicationContext
- 通过ApplicationContext获取BeanFactory
- 通过BeanFactory的removeBeanDefinition() 删除已经注入的Bean类的BeanDefinition
// 1. 获取Bean类的ApplicationContext
ApplicationContext ctx = SpringApplication.run(Bean.class, args);
// 2. 通过ApplicationContext获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
// 通过BeanFactory的removeBeanDefinition()删除已经注入的BeanDefinition
defaultListableBeanFactory.removeBeanDefinition("beanInterface");
-
动态注入Bean出现的问题:
- 多次注入同一个Bean类时,如果BeanName一样,后注入的Bean会覆盖先注入的Bean
- 多次注入同一个Bean类时,如果BeanName不一样,就会产生两个Bean
- 此时不能使用ApplicationContext.getBean(Bean.class) 方法通过ByType方式获取注入的Bean, 会产生报错
- 此时可以使用ApplicationContext.getBean("beanInterface") 方法通过ByName的方式获取重复注入的Bean
- 可以使用以下单例方式解决重复注入Bean的问题:
// 使得Bean对象完成Spring初始化过程中的所有增强器检验,但是不重新创建Bean对象 applicationContext.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(beanClass, beanClass.getClass().getName()); // 将Bean对象以单例的形式注入到容器中. 此时通过 beanClass.getClass() 和 beanClass.getClass().getName() 都可以获取到Spring容器中注入的Bean对象 defaultListableBeanFactory.registerSingleton(beanClass.getClass().getName(), beanClass);