网站建设哪里找广州市数商云网络科技有限公司
web/
2025/10/2 14:00:18/
文章来源:
网站建设哪里找,广州市数商云网络科技有限公司,wordpress怎么进登录界面,深圳网站建设网站制作哪家好文章目录 ⭐⭐⭐Spring核心源码分析自定义Spring框架⭐⭐⭐一、Spring使用回顾二、Spring核心功能结构1、Spring核心功能2、bean概述 三、Spring IOC相关接口分析1、BeanFactory解析2、BeanDefinition解析3、BeanDefinitionReader解析4、BeanDefinitionRegistry解析5、创建容器… 文章目录 ⭐⭐⭐Spring核心源码分析自定义Spring框架⭐⭐⭐一、Spring使用回顾二、Spring核心功能结构1、Spring核心功能2、bean概述 三、Spring IOC相关接口分析1、BeanFactory解析2、BeanDefinition解析3、BeanDefinitionReader解析4、BeanDefinitionRegistry解析5、创建容器 四、自定义SpringIOC1、定义bean相关的pojo类1PropertyValue类2MutablePropertyValues类3BeanDefinition类 2、定义注册表相关类1BeanDefinitionRegistry接口2SimpleBeanDefinitionRegistry类 3、定义解析器相关类1BeanDefinitionReader接口2XmlBeanDefinitionReader类 4、IOC容器相关类1BeanFactory接口2ApplicationContext接口3AbstractApplicationContext类4ClassPathXmlApplicationContext类 5、测试运行自定义Spring IOC框架6、自定义Spring IOC总结1使用到的设计模式2符合大部分设计原则3整个设计和Spring的设计还是有一定的出入 五、设计模式常见相关面试问题 ⭐⭐⭐Spring核心源码分析自定义Spring框架⭐⭐⭐
学习完这个篇章以后【23种设计模式·全精解析】至此完结。相信手写完这个自定义Spring框架后你可以对Spring的核心底层有更深的认识并学会设计模式在具体实际开发中的如何去灵活应用 一、Spring使用回顾
自定义spring框架前先回顾一下spring框架的使用从而分析spring的核心并对核心功能进行模拟。
数据访问层。定义UserDao接口及其子实现类
// 数据访问层接口
public interface UserDao {void add();
}// 数据访问层实现类
public class UserDaoImpl implements UserDao {Overridepublic void add() {System.out.println(UserDao...);}
}业务逻辑层。定义UserService接口及其子实现类
// 业务逻辑层接口
public interface UserService {void add();
}// 业务逻辑层实现类
public class UserServiceImpl implements UserService {// 声明一个UserDao类型的变量private UserDao userDao;// 通过Spring依赖注入进行赋值public void setUserDao(UserDao userDao) {this.userDao userDao;}Overridepublic void add() {System.out.println(UserService...);userDao.add();}
}定义UserController类使用main方法模拟controller层
import com.aizen.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;// 控制层
public class UserController {public static void main(String[] args) {// 创建Spring的容器对象非延时加载ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml);// 从ioc容器对象中获取userService对象UserService userService applicationContext.getBean(userService, UserService.class);//调用userService方法进行业务逻辑处理userService.add();}
}编写配置文件。在类路径下编写一个名为applicationContext.xml的配置文件
?xml version1.0 encodingUTF-8?
beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlnshttp://www.springframework.org/schema/beansxmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdbean iduserDao classcom.aizen.dao.impl.UserDaoImpl/!-- IOC容器管理对象 --bean iduserService classcom.aizen.service.impl.UserServiceImplproperty nameuserDao refuserDao/!-- 依赖注入DI --/bean
/beans代码运行结果如下
通过上面代码及结果可以看出
userService对象是从applicationContext容器对象获取到的也就是userService对象交由spring进行管理。上面结果可以看到调用了UserDao对象中的add方法也就是说UserDao子实现类对象也交由spring管理了。UserService中的userDao变量我们并没有进行赋值但是可以正常使用说明spring已经将UserDao对象赋值给了userDao变量。
上面三点体现了Spring框架的IOCInversion of Control和DIDependency Injection, DI 二、Spring核心功能结构 1、Spring核心功能
Spring大约有20个模块由1300多个不同的文件构成。这些模块可以分为核心容器、AOP和设备支持、数据访问与集成、Web组件、通信报文和集成测试等下面是 Spring 框架的总体架构图
核心容器由 beans、core、context 和 expressionSpring Expression LanguageSpEL4个模块组成。
spring-beans和spring-core模块是Spring框架的核心模块包含了控制反转Inversion of ControlIOC和依赖注入Dependency InjectionDI。BeanFactory使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。BeanFactory属于延时加载也就是说在实例化容器对象后并不会自动实例化Bean只有当Bean被使用时BeanFactory才会对该 Bean 进行实例化与依赖关系的装配。
// 延时加载示例
BeanFactory beanFactory new XmlBeanFactory(new ClassPathResource(applicationContext.xml));
// 执行完getBean方法ioc容器才会创建service和dao对象并绑定依赖关系
UserService userService beanFactory.getBean(userService, UserService.class);
userService.add();spring-context模块构架于核心模块之上扩展了BeanFactory为它添加了Bean生命周期控制、框架事件体系及资源加载透明化等功能。此外该模块还提供了许多企业级支持如邮件访问、远程访问、任务调度等ApplicationContext 是该模块的核心接口它的超类是 BeanFactory。与BeanFactory不同ApplicationContext实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配使之处于待用状态。spring-context-support模块是对Spring IoC容器及IoC子容器的扩展支持。spring-context-indexer模块是Spring的类管理组件和Classpath扫描组件。spring-expression 模块是统一表达式语言EL的扩展模块可以查询、管理运行中的对象同时也可以方便地调用对象方法以及操作数组、集合等。它的语法类似于传统EL但提供了额外的功能最出色的要数函数调用和简单字符串的模板函数。EL的特性是基于Spring产品的需求而设计的可以非常方便地同Spring IoC进行交互。 2、bean概述
Spring 就是面向 Bean 的编程BOP ,Bean Oriented ProgrammingBean 在 Spring 中处于核心地位。Bean对于Spring的意义就像Object对于OOP的意义一样Spring中没有Bean也就没有Spring存在的意义。Spring IoC容器通过配置文件或者注解的方式来管理bean对象之间的依赖关系。
spring中bean用于对一个类进行封装。如下面的配置
bean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean
bean iduserService classcom.itheima.service.impl.UserServiceImplproperty nameuserDao refuserDao/property
/bean为什么Bean如此重要呢
spring将bean对象交由一个叫IOC容器进行管理。bean对象之间的依赖关系在配置文件中体现并由spring完成。 三、Spring IOC相关接口分析 1、BeanFactory解析
Spring中Bean的创建是典型的工厂模式这一系列的Bean工厂即IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务在Spring中有许多IoC容器的实现供用户选择其相互关系如下图所示。
其中BeanFactory作为最顶层的一个接口定义了IoC容器的基本功能规范BeanFactory有三个重要的子接口ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是DefaultListableBeanFactory它实现了所有的接口。
那么为何要定义这么多层次的接口呢每个接口都有它的使用场合主要是为了区分在Spring内部操作过程中对象的传递和转化对对象的数据访问所做的限制。例如 ListableBeanFactory接口表示这些Bean可列表化。HierarchicalBeanFactory表示这些Bean是有继承关系的也就是每个Bean可能有父BeanAutowireCapableBeanFactory接口定义Bean的自动装配规则。 这三个接口共同定义了Bean的集合、Bean之间的关系及Bean行为。最基本的IoC容器接口是BeanFactory来看一下它的源码
public interface BeanFactory {String FACTORY_BEAN_PREFIX ;// 根据bean的名称获取IOC容器中的的bean对象Object getBean(String name) throws BeansException;// 根据bean的名称获取IOC容器中的的bean对象并指定获取到的bean对象的类型这样我们使用时就不需要进行类型强转了T T getBean(String name, ClassT requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;T T getBean(ClassT requiredType) throws BeansException;T T getBean(ClassT requiredType, Object... args) throws BeansException;T ObjectProviderT getBeanProvider(ClassT requiredType);T ObjectProviderT getBeanProvider(ResolvableType requiredType);// 判断容器中是否包含指定名称的bean对象boolean containsBean(String name);// 根据bean的名称判断是否是单例boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class? typeToMatch) throws NoSuchBeanDefinitionException;NullableClass? getType(String name) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}在BeanFactory里只对IoC容器的基本行为做了定义根本不关心Bean是如何定义及怎样加载的。正如我们只关心能从工厂里得到什么产品不关心工厂是怎么生产这些产品的。具体如何创建交给该接口的具体子实现类去实现。
BeanFactory有一个很重要的子接口就是ApplicationContext接口该接口主要来规范容器中的bean对象是非延时加载即在创建容器对象的时候就对象bean进行初始化并存储到一个容器中。
要知道工厂是如何产生对象的我们需要看具体的IoC容器实现Spring提供了许多IoC容器实现比如 ClasspathXmlApplicationContext : 根据类路径加载xml配置文件并创建IOC容器对象。FileSystemXmlApplicationContext 根据系统路径加载xml配置文件并创建IOC容器对象。AnnotationConfigApplicationContext 加载注解类配置并创建IOC容器。 2、BeanDefinition解析
Spring IoC容器管理我们定义的各种Bean对象及其相互关系而Bean对象在Spring实现中是以BeanDefinition来描述的如下面配置文件
bean iduserDao classcom.itheima.dao.impl.UserDaoImpl/beanbean标签还有很多属性scope、init-method、destory-method等。Spring要解析这些bean标签必然要把这些bean标签对应属性的值进行一个封装而封装成的对象就是BeanDefinition对象而BeanDefinition是一个接口所以Spring封装的是BeanDefinition的子实现类对象。
其继承体系如下图所示 3、BeanDefinitionReader解析
Bean的解析过程非常复杂功能被分得很细因为这里需要被扩展的地方很多必须保证足够的灵活性以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过BeanDefinitionReader来完成看看Spring中BeanDefinitionReader的类结构图部分如下图所示。
看看BeanDefinitionReader接口定义的功能来理解它具体的作用
public interface BeanDefinitionReader {// 获取BeanDefinitionRegistry注册器对象BeanDefinitionRegistry getRegistry();NullableResourceLoader getResourceLoader();NullableClassLoader getBeanClassLoader();BeanNameGenerator getBeanNameGenerator();/*下面的loadBeanDefinitions都是加载bean定义从指定的资源中获取如String类型的字符冲路径Resource对象类型*/int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}4、BeanDefinitionRegistry解析
BeanDefinitionReader用来解析bean定义并封装BeanDefinition对象而我们定义的配置文件中定义了很多bean标签所以就有一个问题解析的多个BeanDefinition对象存储到哪儿答案就是BeanDefinition的注册中心而该注册中心顶层接口就是BeanDefinitionRegistry。
public interface BeanDefinitionRegistry extends AliasRegistry {// 往注册表中注册beanvoid registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;// 从注册表中删除指定名称的beanvoid removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;// 获取注册表中指定名称的beanBeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;// 判断注册表中是否已经注册了指定名称的beanboolean containsBeanDefinition(String beanName);// 获取注册表中所有的bean的名称String[] getBeanDefinitionNames();int getBeanDefinitionCount();boolean isBeanNameInUse(String beanName);
}继承结构图如下部分
从上面类图可以看到BeanDefinitionRegistry接口的子实现类主要有以下几个
DefaultListableBeanFactory在该类中定义了如下代码就是用来注册beanMapString, BeanDefinition键是BeanDefinition的名称值是BeanDefinition对象
private final MapString, BeanDefinition beanDefinitionMap new ConcurrentHashMap(256);SimpleBeanDefinitionRegistry在该类中定义了如下代码就是用来注册beanMapString, BeanDefinition键是BeanDefinition的名称值是BeanDefinition对象
private final MapString, BeanDefinition beanDefinitionMap new ConcurrentHashMap(64);5、创建容器 ClassPathXmlApplicationContextApplicationContext接口下的子实现类对Bean配置资源的载入非延迟加载是从refresh()方法开始的。 通过查看ClassPathXmlApplicationContext的源码找到refresh()方法。 refresh()方法是一个模板方法规定了 IoC 容器的启动流程有些逻辑要交给其子类实现。 它对 Bean 配置资源进行载入ClassPathXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程。 简单来说refresh()的功能启动IoC容器加载配置文件并初始化Bean对象将Bean对象存储在容器里面。 四、自定义SpringIOC
现要对下面的配置文件进行解析并自定义Spring框架的IOC对涉及到的对象进行管理。
?xml version1.0 encodingUTF-8?
beansbean iduserService classcom.itheima.service.impl.UserServiceImplproperty nameuserDao refuserDao/property/beanbean iduserDao classcom.itheima.dao.impl.UserDaoImpl/bean
/beans总体项目文件及包结构设计 1、定义bean相关的pojo类 1PropertyValue类
用于封装bean的属性体现到上面的配置文件就是封装bean标签的子标签property标签数据。
// 用来封装bean标签下的property标签的属性
public class PropertyValue {private String name;private String ref;private String value; // 赋值的属性为基本数据类型及String类型数据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;}
}2MutablePropertyValues类
一个bean标签可以有多个property子标签所以再定义一个MutablePropertyValues类用来存储并管理多个PropertyValue对象。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;// 用户存储和管理多个PropertyValue对象
public class MutablePropertyValues implements IterablePropertyValue {// 定义List集合对象用来存储PropertyValue对象private final ListPropertyValue propertyValueList;public MutablePropertyValues() {this.propertyValueList new ArrayListPropertyValue();}public MutablePropertyValues(ListPropertyValue propertyValueList) {this.propertyValueList (propertyValueList ! null ? propertyValueList : new ArrayListPropertyValue());}// 获取所有的PropertyValue对象返回以数组的形式public PropertyValue[] getPropertyValues() {// 将集合转换为数组并返回return propertyValueList.toArray(new PropertyValue[0]); // 参数指定返回数组的类型}// 根据name属性值名称获取对应的PropertyValue对象public PropertyValue getPropertyValue(String propertyName) {// 遍历集合对象for (PropertyValue pv : propertyValueList) {if (pv.getName().equals(propertyName)) {return pv;}}return null;}// 判断集合是否为空public boolean isEmpty() {return propertyValueList.isEmpty();}// 添加PropertyValue对象public MutablePropertyValues addPropertyValue(PropertyValue pv) {// 遍历并判断集合中存储的PropertyValue对象是否和传递进来的pv重复了for (int i 0; i propertyValueList.size(); i) {// 获取集合中每一个PropertyValue对象PropertyValue currentPv propertyValueList.get(i);// 如果重复了进行覆盖if (currentPv.getName().equals(pv.getName())) {propertyValueList.set(i, pv);//this.propertyValueList.set(i, new PropertyValue(pv.getName(), pv.getRef(), pv.getValue()));return this; // 目的是实现链式编程}}// 遍历完说明没有重复的this.propertyValueList.add(pv);return this; // 目的是实现链式编程}// 判断是否有指定name属性值的对象public boolean contains(String propertyName) {// 如果不等于null说明包含该name的PropertyValue返回true如果等于null说明不包含返回falsereturn getPropertyValue(propertyName) ! null;}// 获取迭代器对象Overridepublic IteratorPropertyValue iterator() {return propertyValueList.iterator();}
}3BeanDefinition类
BeanDefinition类用来封装bean信息的主要包含id即bean对象的名称、class需要交由spring管理的类的全类名及子标签property数据。
// 用来封装bean标签数据
public class BeanDefinition {private String id;private String className;private MutablePropertyValues propertyValues; // property子标签的数据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;}
}2、定义注册表相关类 1BeanDefinitionRegistry接口
BeanDefinitionRegistry接口定义了注册表的相关操作定义如下功能
注册BeanDefinition对象到注册表中从注册表中删除指定名称的BeanDefinition对象根据名称从注册表中获取BeanDefinition对象判断注册表中是否包含指定名称的BeanDefinition对象获取注册表中BeanDefinition对象的个数获取注册表中所有的BeanDefinition的名称
import com.aizen.framework.beans.BeanDefinition;// 注册表接口
public interface BeanDefinitionRegistry {// 注册BeanDefinition对象到注册表中void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);// 从注册表中删除指定名称的BeanDefinition对象void removeBeanDefinition(String beanName) throws Exception;// 根据名称从注册表中获取BeanDefinition对象BeanDefinition getBeanDefinition(String beanName) throws Exception;boolean containsBeanDefinition(String beanName);int getBeanDefinitionCount();String[] getBeanDefinitionNames();
}2SimpleBeanDefinitionRegistry类
该类实现了BeanDefinitionRegistry接口定义了Map集合作为注册表容器。
import com.aizen.framework.beans.BeanDefinition;import java.util.HashMap;
import java.util.Map;// 注册表接口的子实现类
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {// 定义一个容器用来存储BeanDefinition对象private MapString, BeanDefinition beanDefinitionMap new HashMapString, BeanDefinition();Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName,beanDefinition);}Overridepublic void removeBeanDefinition(String beanName) throws Exception {beanDefinitionMap.remove(beanName);}Overridepublic BeanDefinition getBeanDefinition(String beanName) throws Exception {return beanDefinitionMap.get(beanName);}Overridepublic boolean containsBeanDefinition(String beanName) {return beanDefinitionMap.containsKey(beanName);}Overridepublic int getBeanDefinitionCount() {return beanDefinitionMap.size();}Overridepublic String[] getBeanDefinitionNames() {return beanDefinitionMap.keySet().toArray(new String[0]);}
}3、定义解析器相关类 1BeanDefinitionReader接口
BeanDefinitionReader是用来解析配置文件并在注册表中注册bean的信息。定义了两个规范
获取注册表的功能让外界可以通过该对象获取注册表对象。加载配置文件并注册bean数据。
// 用来解析配置文件的而该接口只是定义了规范
public interface BeanDefinitionReader {// 获取注册表对象BeanDefinitionRegistry getRegistry();// 加载配置文件并在注册表中进行注册void loadBeanDefinitions(String configLocation) throws Exception;
}2XmlBeanDefinitionReader类
XmlBeanDefinitionReader类是专门用来解析xml配置文件的。该类实现BeanDefinitionReader接口并实现接口中的两个功能。引入dom4j依赖坐标
!-- dom4j --
dependencygroupIddom4j/groupIdartifactIddom4j/artifactIdversion1.6.1/version
/dependencyimport com.aizen.framework.beans.BeanDefinition;
import com.aizen.framework.beans.MutablePropertyValues;
import com.aizen.framework.beans.PropertyValue;
import com.aizen.framework.beans.factory.support.BeanDefinitionReader;
import com.aizen.framework.beans.factory.support.BeanDefinitionRegistry;
import com.aizen.framework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.util.List;// XML解析器针对XML配置文件进行解析的类把Bean解析封装为BeanDefinition对象并注册到注册表中
public class XmlBeanDefinitionReader implements BeanDefinitionReader {// 声明注册表对象private BeanDefinitionRegistry registry;public XmlBeanDefinitionReader() {registry new SimpleBeanDefinitionRegistry();}Overridepublic BeanDefinitionRegistry getRegistry() {return registry;}Overridepublic void loadBeanDefinitions(String configLocation) throws Exception {// 使用dom4j进行xml配置文件的解析SAXReader reader new SAXReader();// 通过输入流获取类路径下的配置文件InputStream is XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);Document document reader.read(is);// 根据Document对象获取根标签对象beansElement rootElement document.getRootElement();// 获取根标签下的所有的bean标签对象ListElement beanElements rootElement.elements(bean);// 遍历所有bean标签集合对象获取每一个beanfor (Element beanElement : beanElements) {// 获取bean标签下的id属性class属性property属性String id beanElement.attributeValue(id);String className beanElement.attributeValue(class);// 将id属性和cLass属性封装到BeanDefinition对家中BeanDefinition beanDefinition new BeanDefinition();beanDefinition.setId(id);beanDefinition.setClassName(className);// 获取bean标签下所有的property标签对象ListElement propertyElements beanElement.elements(property);// 创建MutablePropertyValues对象MutablePropertyValues propertyValues new MutablePropertyValues();for (Element propertyElement : propertyElements) {// 获取property标签下的name属性ref属性value属性String name propertyElement.attributeValue(name);String ref propertyElement.attributeValue(ref);String value propertyElement.attributeValue(value);// 封装为PropertyValue对象PropertyValue propertyValue new PropertyValue(name, ref, value);propertyValues.addPropertyValue(propertyValue);}// 将propertyValues对象封装到beanDefinition对象中beanDefinition.setPropertyValues(propertyValues);// 将beanDefinition对象注册到注册表中registry.registerBeanDefinition(id, beanDefinition);}}
}4、IOC容器相关类 1BeanFactory接口
在该接口中定义IOC容器的统一规范即获取bean对象。
// IOC容器父接口
public interface BeanFactory {// 根据bean对象的名称获取bean对象Object getBean(String name) throws Exception;// 根据bean对象的名称获取bean对象并进行类型转换T T getBean(String name, Class? extends T clazz) throws Exception;
}2ApplicationContext接口
该接口的所以的子实现类对bean对象的创建都是非延时的所以在该接口中定义 refresh() 方法该方法主要完成以下两个功能
加载配置文件。根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
import com.aizen.framework.beans.factory.BeanFactory;// 定义非延时加载功能
public interface ApplicationContext extends BeanFactory {// 进行配置文件加载并进行对象创建void refresh() throws Exception;
}3AbstractApplicationContext类
作为ApplicationContext接口的子类所以该类也是非延时加载所以需要在该类中定义一个Map集合作为bean对象存储的容器。声明BeanDefinitionReader类型的变量用来进行xml配置文件的解析符合单一职责原则。BeanDefinitionReader类型的对象创建交由子类实现因为只有子类明确到底创建BeanDefinitionReader哪儿个子实现类对象如解析xml配置文件、properties配置文件。
import com.aizen.framework.beans.factory.support.BeanDefinitionReader;
import com.aizen.framework.beans.factory.support.BeanDefinitionRegistry;
import com.aizen.framework.context.ApplicationContext;import java.util.HashMap;
import java.util.Map;// ApplicationContext接口的子实现类抽象类用于立即加载非延时加载
public abstract class AbstractApplicationContext implements ApplicationContext {// 声明解析器变量protected BeanDefinitionReader beanDefinitionReader;// 定义用于存储bean对象的map容器key存储的是bean的id值value存储的是bean对象protected MapString, Object singletonObjects new HashMapString, Object();// 声明配置文件路径的变量protected String configLocation;Overridepublic void refresh() throws Exception {// 加载BeanDefinition对象beanDefinitionReader.loadBeanDefinitions(configLocation);// 初始化beanfinishBeanInitialization();}// bean的初始化private void finishBeanInitialization() throws Exception {// 从注册表里获取BeanDefinition对象BeanDefinitionRegistry registry beanDefinitionReader.getRegistry();String[] beanNames registry.getBeanDefinitionNames();// 进行每一个bean的初始化for (String beanName : beanNames) {// 调用具体子实现类的getBean方法进行bean的初始化getBean(beanName);}}
}注意该类finishBeanInitialization()方法中调用getBean()方法使用到了模板方法模式。 4ClassPathXmlApplicationContext类
该类主要是加载类路径下的配置文件并进行bean对象的创建主要完成以下功能
在构造方法中创建BeanDefinitionReader对象因为在父类中声明了在不同子类中具体实现。在构造方法中调用refresh()方法用于进行配置文件加载、创建bean对象并存储到容器中。重写父接口中的getBean()方法并实现依赖注入DI操作。
import com.aizen.framework.beans.BeanDefinition;
import com.aizen.framework.beans.MutablePropertyValues;
import com.aizen.framework.beans.PropertyValue;
import com.aizen.framework.beans.factory.support.BeanDefinitionRegistry;
import com.aizen.framework.beans.factory.xml.XmlBeanDefinitionReader;
import com.aizen.framework.utils.StringUtils;import java.lang.reflect.Method;// IOC容器的具体子实现类用于加载类路径下的XML格式的配置文件
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {public ClassPathXmlApplicationContext(String configLocation) {// 通过构造方法设置类路径下的配置文件路径this.configLocation configLocation;// 构建解析器对象beanDefinitionReader new XmlBeanDefinitionReader();// 调用refresh方法try {this.refresh();} catch (Exception e) {throw new RuntimeException(e);}}// 根据bean对象的名称id属性值获取bean对象Overridepublic Object getBean(String name) throws Exception {// 判断对象容器中是否包含指定名称的bean对象Object obj singletonObjects.get(name);// 如果包含直接返回即可if (obj ! null) return obj;// 如果不包含需要自行创建获取BeanDefinition对象里面有bean的信息BeanDefinitionRegistry registry beanDefinitionReader.getRegistry();BeanDefinition beanDefinition registry.getBeanDefinition(name);// 不存在与该id属性值一致的bean返回nullif (beanDefinition null) return null;// 若存在该beanDefinition对象则获取bean信息中的className有了全类名通过反射就可以创建对象String className beanDefinition.getClassName(); // com.aizen.service.impl.UserServiceImpl// 通过反射创建对象Class? clazz Class.forName(className);Object beanObj clazz.newInstance(); // UserServiceImpl// 进行依赖注入操作MutablePropertyValues propertyValues beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues) {// 获取property的属性String propertyName propertyValue.getName();String ref propertyValue.getRef();String value propertyValue.getValue();// ref和value属性只能存在一个判断使用了哪个属性if (ref ! null !ref.isEmpty()) {// 注意property nameuserDao其中name的值应该和属性名setUserDao方法保持一致// 递归获取依赖的bean对象Object bean getBean(ref); // getBean(UserDao)返回userDao对象// 使用自定义工具类StringUtils拼接构造set方法名String methodName StringUtils.getSetterMethodByFieldName(propertyName); // userDao -- setUserDao// 获取所有的方法对象Method[] methods clazz.getMethods();for (Method method : methods) {// 如果该方法是set方法if (method.getName().equals(methodName)) {// 执行setter方法进行依赖注入method.invoke(beanObj, bean); // userServiceImpl.setUserDao(userDao);}}}if (value ! null !value.isEmpty()) {// 使用自定义工具类StringUtils拼接构造set方法名String methodName StringUtils.getSetterMethodByFieldName(propertyName); // 例username - setUsername// 获取method方法Method method clazz.getMethod(methodName, String.class);method.invoke(beanObj, value);}}// 在返回beanObj对象之前将该对象存储到Map容器中singletonObjects.put(name, beanObj);return beanObj;}Overridepublic T T getBean(String name, Class? extends T clazz) throws Exception {Object bean getBean(name);if (bean null) return null;T obj clazz.cast(bean);return obj;}
}StringUtils工具类
public class StringUtils {// 私有构造方法private StringUtils() {}// refuserDao -- setUserDao | valueusername - setUsernamepublic static String getSetterMethodByFieldName(String fieldName) {return set fieldName.substring(0, 1).toUpperCase() fieldName.substring(1);}
}bean配置中属性ref与value的区别 refvalueref引用已经存在的对象value创建新的对象ref可以引用其他的bean对象value可以赋一些简单类型的值使用ref的时候Spring容器会在引用后进行验证验证当前的xml是否存在引用的bean使用value的时候spring会在容器启动实例化bean的时候进行验证 5、测试运行自定义Spring IOC框架
至此我们已经完成了自定义SpringIoC的所有功能下面来运行测试一下。
先把我们写的项目安装在本地仓库mvn install。 在我们之前的spring_demo项目中引用该自定义的custom_spring依赖并且注释掉之前的Spring的依赖。 将UserController中导入的Spring包删掉改成导入我们自己的包其他的地方不需要修改。
import com.aizen.framework.context.ApplicationContext;
import com.aizen.framework.context.support.ClassPathXmlApplicationContext;
import com.aizen.service.UserService;// 控制层
public class UserController {public static void main(String[] args) throws Exception {// 创建Spring的容器对象非延时加载ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml);// 从ioc容器对象中获取userService对象UserService userService applicationContext.getBean(userService, UserService.class);//调用userService方法进行业务逻辑处理userService.add();}
}在此处打断点Debug模式运行可以看到执行创建IOC容器后初始化加载并创建了bean对象实现了非延时加载。 运行结果可以看到userDao和userService对象都被创建了说明IOC容器创建成功Bean对象初始化并创建成功并且userService调用add方法可以调用userDao的add方法说明依赖注入成功 测试完ref属性注入下面再接着测试一下value属性注入添加如下代码后测试运行。
import com.aizen.dao.UserDao;// 数据访问层实现类
public class UserDaoImpl implements UserDao {private String username;private String password;// 提供set方法进行注入public void setUsername(String username) {this.username username;}public void setPassword(String password) {this.password password;}public UserDaoImpl() {System.out.println(userDao被创建了);}Overridepublic void add() {System.out.println(UserDao... username password);}
}?xml version1.0 encodingUTF-8?
beans!-- IOC容器管理对象 --bean iduserDao classcom.aizen.dao.impl.UserDaoImplproperty nameusername valuezhangsan/property namepassword value123456//beanbean iduserService classcom.aizen.service.impl.UserServiceImplproperty nameuserDao refuserDao/!-- 依赖注入DI --/bean
/beansvalue属性注入String类型成功实现了自定义依赖管理。 6、自定义Spring IOC总结 1使用到的设计模式
工厂模式。这个使用工厂模式 配置文件的方式。单例模式。Spring IOC管理的bean对象都是单例的此处的单例不是通过构造器进行单例的控制的而是spring框架对每一个bean只创建了一个对象。模板方法模式。AbstractApplicationContext类中的finishBeanInitialization()方法调用了子类的getBean()方法因为getBean()的实现和环境息息相关。迭代器模式。对于MutablePropertyValues类定义使用到了迭代器模式因为此类存储并管理PropertyValue对象也属于一个容器所以给该容器提供一个遍历方式。
Spring框架其实使用到了很多设计模式如AOP使用到了代理模式选择JDK代理或者CGLIB代理使用到了策略模式还有适配器模式装饰者模式观察者模式等。
2符合大部分设计原则
自行体会~~(▽) 3整个设计和Spring的设计还是有一定的出入
Spring框架底层是很复杂的进行了很深入的封装并对外提供了很好的扩展性。而我们自定义SpringIOC有以下几个目的 了解Spring底层对对象的大体管理机制。了解设计模式在具体的开发中的使用。以后学习Spring源码通过该案例的实现可以降低Spring学习的成本。 五、设计模式常见相关面试问题
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/85644.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!