springIOC原理——自己实现一下不就懂了嘛
Posted 小王子jvm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springIOC原理——自己实现一下不就懂了嘛相关的知识,希望对你有一定的参考价值。
使用Spring
只需要引入对应的依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
maven自动依赖几个核心包:
测试
//DAO 层
public interface UserDao {
void add();
}
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("UserDao...");
}
}
//service 层
public interface UserService {
void add();
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
userDao.add();
System.out.println("UserService实现类"+username+"=="+password);
}
}
使用xml配置文件方式,配置如下:
<beans >
<!--创建userService,依赖注入dao-->
<bean id="userService" class="com.sheep.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="username" value="sheep"/>
<property name="password" value="123456"/>
</bean>
<!--dao相关的bean-->
<bean id="userDao" class="com.sheep.dao.impl.UserDaoImpl"/>
</beans>
然后就是controller,用一个main模拟:
public class MainController {
public static void main(String[] args) throws Exception {
//创建IOC容器,加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
//获取相应的bean
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
经过测试是可以正常输出内容的。
相关IOC接口源码
Spring中Bean的创建是典型的工厂模式,这一系列的Bean工厂,即IoC容器,为开发者管理对象之间 的依赖关系提供了很多便利和基础服务,在Spring中有许多IoC容器的实现供用户选择。
最顶层接口是一个BeanFactory,定义了IoC容器的基本功能规范。它的实现有很多。每个接口都有它的使用场合,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的 数据访问所做的限制。
在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心你的Bean是如何定义及怎样加载 的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。 BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中 的bean对象是非延时加载,即在创建容器对象的时候就对象bean进行初始化,并存储到一个容器中。
要知道工厂是如何产生对象的,我们需要看具体的IoC容器实现,Spring提供了许多IoC容器实现,比如:
- ClasspathXmlApplicationContext : 根据类路径加载xml配置文件,并创建IOC容器对象。
FileSystemXmlApplicationContext:根据系统路径加载xml配置文件,并创建IOC容器对象。- AnnotationConfigApplicationContext :加载注解类配置,并创建IOC容器。
BeanDefinition解析
Spring IoC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以 BeanDefinition来描述的,如下面配置文件:
<bean id="userDao" class="com.sheep.dao.impl.UserDaoImpl"></bean>
bean标签还有很多属性:
scope、init-method、destory-method等。
解析id,class这些东西然后封装成一个BeanDefinition,被没有什么特别。
BeanDefinitionReader解析
Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活 性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过 BeanDefinitionReader来完成,看看Spring中BeanDefinitionReader的类结构图,如下图所示。
总之这个接口就是规范解析配置的规范,比如像解析xml,就有对应的实现做这个事情。
BeanDefinitionRegistry解析
BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文 件中定义了很多bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?答案就是 BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry。
也没有什么特别的,这个接口只不过定义的怎么注册,实现类都会有一个map集合来保存解析出来的东西。
从上面类图可以看到BeanDefinitionRegistry接口的子实现类主要有以下几个: (所谓的注册就是保存解析的东西,这里就是解析出来的东西就是BeanDefinition)
-
DefaultListableBeanFactory 在该类中定义了如下代码,就是用来注册bean
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
-
SimpleBeanDefinitionRegistry 在该类中定义了如下代码,就是用来注册bean:
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
创建容器的过程
ClassPathXmlApplicationContext
对Bean配置资源的载入是从refresh()方法开始的。
refresh()方法是一个模板方法,规定了 IoC 容器的启动流程,有些逻辑要交给其子类实现。它对 Bean 配置资源进行载入,ClassPathXmlApplicationContext通过调用其父类 AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程。
自定义SpringIOC
整个工程的结构如下:
定义bean相关的pojo类
PropertyValue类
用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。
/**
* 每一bean可能会引用其他bean,这个用来封装被引用的bean解析后的属性
* @Description: 封装被引用的bean解析后的属性
* name : 引用的名字,也就是放在容器中的key
* ref : 引用类型
* value : 基本类型的引用
*/
public class PropertyValue {
private String name;
private String ref;
private String value;
public PropertyValue() {
}
public PropertyValue(String name, String ref, String value) {
this.name = name;
this.ref = ref;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
MutablePropertyValues类
一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存 储并管理多个PropertyValue对象。
/**
* 一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存
* 储并管理多个PropertyValue对象。
*/
public class MutablePropertyValues implements Iterable<PropertyValue> {
//用一个list集合保存
private final List<PropertyValue> propertyValueList;
public MutablePropertyValues() {
propertyValueList = new ArrayList<PropertyValue>();
}
public MutablePropertyValues(List<PropertyValue> propertyValueList) {
this.propertyValueList = (propertyValueList != null ?
propertyValueList : new ArrayList<PropertyValue>());
}
//获取所有的PropertyValue,并以数组的形式返回
public PropertyValue[] getPropertyValues(){
return (PropertyValue[]) propertyValueList.toArray();
}
//通过propertyName获取对应的PropertyValue
public PropertyValue getPropertyValue(String propertyName){
for (PropertyValue propertyValue : propertyValueList) {
if (propertyName.equals(propertyValue.getName())){
return propertyValue;
}
}
return null;
}
//迭代器模式,用于遍历
public Iterator<PropertyValue> iterator() {
return propertyValueList.iterator();
}
//判断是否为空
public boolean isEmpty(){
return this.propertyValueList.isEmpty();
}
//是否已有这个封装的对象
public boolean contains(String propertyName) {
return getPropertyValue(propertyName) != null;
}
//添加一个propertyValue到容器中,存在的化进行覆盖
public MutablePropertyValues addPropertyValue(PropertyValue propertyValue){
for (int i = 0; i < propertyValueList.size(); i++) {
PropertyValue tmp = this.propertyValueList.get(i);
if (tmp.getName().equals(propertyValue.getName())){
//存在就覆盖
propertyValueList.set(i,new PropertyValue(propertyValue.getName(),
propertyValue.getRef(),propertyValue.getValue()));
}
}
this.propertyValueList.add(propertyValue);
return this;
}
}
BeanDefinition类
BeanDefinition类用来封装bean信息的,主要包含id(即bean对象的名称)、class(需要交由 spring管理的类的全类名)及子标签property数据。
/**
* 解析出来的Bean的配置属性
*/
public class BeanDefinition {
private String id;
private String className;
private MutablePropertyValues propertyValues;
public BeanDefinition() {
propertyValues = new MutablePropertyValues();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public MutablePropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(MutablePropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}
定义注册表相关类
BeanDefinitionRegistry接口
BeanDefinitionRegistry接口定义了注册表的相关操作,定义如下功能:
- 注册BeanDefinition对象到注册表中
- 从注册表中删除指定名称的BeanDefinition对象
- 根据名称从注册表中获取BeanDefinition对象
- 判断注册表中是否包含指定名称的BeanDefinition对象
- 获取注册表中BeanDefinition对象的个数
- 获取注册表中所有的BeanDefinition的名称
/**
* 用于注册保存BeanDefinition
*/
public interface BeanDefinitionRegistry {
void registryBeanDefinition(String beanName, BeanDefinition beanDefinition);
void removeBeanDefinition(String beanName) throws Exception;
BeanDefinition getBeanDefinition(String beanName) throws Exception;
boolean containsBeanDefinition(String beanName);
int getBeanDefinitionCount();
String[] getBeanDefinitionsName();
}
SimpleBeanDefinitionRegistry类
该类实现了BeanDefinitionRegistry接口,定义了Map集合作为注册表容器。
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
//既然是需要保存,就需要用到map
private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//注册这个BeanDefinition到表中
public void registryBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName,beanDefinition);
}
//删除
public void removeBeanDefinition(String beanName) throws Exception {
beanDefinitionMap.remove(beanName);
}
//获取这个beanDefinition
public BeanDefinition getBeanDefinition(String beanName) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
return beanDefinition;
}
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
public String[] getBeanDefinitionsName() {
return beanDefinitionMap.keySet().toArray(new String[0]);
}
}
定义解析器相关类
BeanDefinitionReader接口
BeanDefinitionReader是用来解析配置文件并在注册表中注册bean的信息。定义了两个规范:
- 获取注册表的功能,让外界可以通过该对象获取注册表对象。
- 加载配置文件,并注册bean数据。
/**
* 解析xml并在封装成BeanDefinition的规范接口
*/
public interface BeanDefinitionReader {
BeanDefinitionRegistry getBeanDefinitionRegistry();
void loadBeanDefinitions(String classPath) throws Exception;
}
XmlBeanDefinitionReader类
既然要解析xml文件,解析的包是必不可少的:
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
XmlBeanDefinitionReader类是专门用来解析xml配置文件的。该类实现 BeanDefinitionReader接口并实现接口中的两个功能。
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
private BeanDefinitionRegistry registry;
public XmlBeanDefinitionReader() {
this.registry = new SimpleBeanDefinitionRegistry();
}
public BeanDefinitionRegistry getBeanDefinitionRegistry() {
return registry;
}
/**
* 加载配置文件,并解析成对应的BeanDefinition
* @param classPath
* @throws Exception
*/
public void loadBeanDefinitions(String classPath) throws Exception {
//从类路径下加载
InputStream is = this.getClass().getClassLoader().getResourceAsStream(classPath);
//利用dom4j解析配置文件
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(is);
Element rootElement = document.getRootElement(); //也就是Beans标签
parseBean(rootElement); //解析这个根节点并封装
}
/**
* 利用dom4j进行解析
* @param rootElement
*/
private void parseBean(Element rootElement) {
//获取所有的标签,这里只有bean标签
List<Element> elements = rootElement.elements();
for (Element element : elements) {
//遍历这些标签,逐个解析
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//封装这些参数
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//然后继续解析可能会有的引用依赖,也就是property
List<Element> list = element.elements("property");
MutablePropertyValues propertyValues = new MutablePropertyValues();
//逐个解析
for (Element element1 : list) {
String name = element1.attributeValue("name");
String ref = element1.attributeValue("ref");
String value = element1.attributeValue("value");
PropertyValue propertyValue = new PropertyValue(name, ref, value);
propertyValues.addPropertyValue(propertyValue);
}
//将所有的依赖全部注入相应的bean
beanDefinition.setPropertyValues(propertyValues);
registry.registryBeanDefinition(id,beanDefinition);
}
}
}
IOC容器相关类
BeanFactory接口
在该接口中定义IOC容器的统一规范即获取bean对象。
/**
* IOC容器的顶级接口,规范怎么获取生产获取Bean,这就是一个典型的工厂模式
*/
public interface BeanFactory {
Object getBean(String name) throws Exception;
<T> T getBean(String name,Class<? extends T> clazz) throws Exception;
}
ApplicationContext接口
该接口的所以的子实现类对bean对象的创建都是非延时的,所以在该接口中定义 refresh() 方法, 该方法主要完成以下两个功能:
- 加载配置文件。
- 根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
public interface ApplicationContext extends BeanFactory {
void refresh() throws ExceptionspringIOC原理——自己实现一下不就懂了嘛