- 5.自定义Spring框架
- 5.1.Spring介绍
- 5.2.Spring核心功能结构
- 5.3.bean概述
- 5.4.Spring IoC相关接口分析
- 5.4.1.BeanFactory
- 5.4.2.BeanDefinition
- 5.4.3.BeanDefinitionReader
- 5.4.4.BeanDefinitionRegistry
- 5.4.5.创建容器
- 5.5.自定义Spring IoC
- 5.5.1.定义bean相关的pojo类
- 5.5.2.定义注册表相关类
- 5.5.3.定义解析器相关类
- 5.5.4.IOC容器相关类
- 5.5.5.测试
- 5.6.总结
本文章笔记整理来自黑马视频https://www.bilibili.com/video/BV1Np4y1z7BU,相关资料可在评论区获取。
详解23种设计模式(基于Java)—— 设计模式相关内容介绍(一 / 五) 详解23种设计模式(基于Java)—— 创建者模式(二 / 五) 详解23种设计模式(基于Java)—— 结构型模式(三 / 五) 详解23种设计模式(基于Java)—— 行为型模式(四 / 五)
5.自定义Spring框架下面将根据前面所学的设计模式的相关知识,来实现Spring中的核心功能IoC中的一小部分。
5.1.Spring介绍(1)有关Spring的介绍可以查看Spring——入门介绍这篇文章,此处就不再赘述。 (2)简单使用 ① 在IDEA中创建一个Maven工程,并在pom.xml中引入Spring的相关依赖
4.0.0
com.itheima
design_pattern_spring_demo
1.0-SNAPSHOT
org.springframework
spring-context
5.2.0.RELEASE
② 数据访问层,定义UserDao接口及其子实现类 UserDao.java
public interface UserDao {
public void add();
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("userDaoImpl ....");
}
}
③ 业务逻辑层,定义UserService接口及其子实现类 UserService.java
public interface UserService {
public void add();
}
UserServiceImpl.java
public class UserServiceImpl implements UserService {
//声明一个UserDao类型的变量
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void add() {
System.out.println("UserService...");
userDao.add();
}
}
④ 定义UserController类,使用main方法模拟controller层 UserController.java
public class UserController {
public static void main(String[] args) throws Exception {
//1.创建spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器对象中获取userService对象
UserService userService = applicationContext.getBean("userService", UserService.class);
//3.调用userService中的方法进行业务逻辑处理
userService.add();
}
}
⑤ 编写配置文件,在类路径下编写一个名为ApplicationContext.xml的配置文件
⑥ 测试,即运行UserController.java中的main方法,得到的结果如下:
UserService...
userDaoImpl ....
(3)通过上面代码及结果可以看出: ① userService对象是从applicationContext容器对象获取到的,也就是userService对象交由spring进行管理。 ② 上面结果可以看到调用了UserDao对象中的add方法,也就是说UserDao子实现类对象也交由spring管理了。 ③ UserService中的userDao变量我们并没有进行赋值,但是可以正常使用,说明spring已经将 UserDao对象赋值给了userDao变量。 上面三点体现了Spring框架的IOC(Inversion of Control)和DI(Dependency Injection, DI)
5.2.Spring核心功能结构(1)Spring大约有20个模块,由1300多个不同的文件构成。这些模块可以分为:核心容器、AOP和设备支持、数据访问与集成、Web组件、通信报文和集成测试等,Spring框架的总体架构图如下: (2)核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4个模块组成。 ① spring-beans和spring-core模块是Spring框架的核心模块,包含了控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)。BeanFactory使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。BeanFactory属于延时加载,也就是说在实例化容器对象后并不会自动实例化Bean,只有当Bean被使用时,BeanFactory才会对该 Bean 进行实例化与依赖关系的装配。 ② 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进行交互。
(1)什么是bean? ① 先来看看Spring官方文档中对bean的解释:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are
called beans.A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
② 翻译过来就是:
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是由SpringIOC容器实例化、组装和管理的对象。
(2)Spring 就是面向 Bean 的编程(BOP, Bean Oriented Programming),Bean 在 Spring 中处于核心地位。Bean对于Spring的意义就像Object对于OOP的意义一样,Spring中没有Bean也就没有Spring存在的意义。Spring IoC容器通过配置文件或者注解的方式来管理bean对象之间的依赖关系。 (3)Spring中的bean用于对一个类进行封装,如下面的配置:
(4)bean的重要性:
spring 将bean对象交由一个叫IOC容器进行管理。
bean对象之间的依赖关系在配置文件中体现,并由spring完成。
5.4.Spring IoC相关接口分析
5.4.1.BeanFactory
(1)Spring中Bean的创建是典型的工厂模式,这一系列的Bean工厂,即 IoC 容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在Spring中有许多 IoC 容器的实现供用户选择,其相互关系如下图所示。 (2)其中,BeanFactory作为最顶层的一个接口,定义了IoC容器的基本功能规范,BeanFactory有三个重要的子接口:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。
(3)定义这么多层次的接口的目的在于:区分在Spring内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。 例如:
ListableBeanFactory表示这些Bean可列表化HierarchicalBeanFactory表示这些Bean 是有继承关系的,也就是每个Bean可能有父BeanAutowireCapableBeanFactory表示接口定义Bean的自动装配规则(4)这三个接口共同定义了Bean的集合、Bean之间的关系及Bean行为。最基本的IoC容器接口是BeanFactory,来看一下它的源码: BeanFactory.java
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名称获取IOC容器中的的bean对象
Object getBean(String name) throws BeansException;
//根据bean的名称获取IOC容器中的的bean对象,并指定获取到的bean对象的类型,这样使用时就不需要进行类型强转
T getBean(String name, Class requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
T getBean(Class requiredType) throws BeansException;
T getBean(Class requiredType, Object... args) throws BeansException;
ObjectProvider getBeanProvider(Class requiredType);
ObjectProvider 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;
@Nullable
Class getType(String name) throws NoSuchBeanDefinitionException;
@Nullable
Class getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心你的Bean是如何定义及怎样加载的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。
(5)BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中的bean对象是非延时加载,即在创建容器对象的时候就对象bean进行初始化,并存储到一个容器中。 要知道工厂是如何产生对象的,我们需要看具体的IoC容器实现,Spring提供了许多IoC容器实现,例如:
(1)Spring IoC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以BeanDefinition来描述的,如下面配置文件:
(2)其继承体系如下图所示:
(1)Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。该过程主要通过BeanDefinitionReader来完成,Spring中BeanDefinitionReader的类结构图如下所示: (2)看看BeanDefinitionReader接口定义的功能来理解它具体的作用: BeanDefinitionReader.java
package org.springframework.beans.factory.support;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;
public interface BeanDefinitionReader {
//获取BeanDefinitionRegistry注册器对象
BeanDefinitionRegistry getRegistry();
@Nullable
ResourceLoader getResourceLoader();
@Nullable
ClassLoader getBeanClassLoader();
BeanNameGenerator getBeanNameGenerator();
//下面的loadBeanDefinitions都是从指定的资源中加载bean定义,
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}
5.4.4.BeanDefinitionRegistry
(1)BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文件中定义了很多bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?答案就是BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry。 BeanDefinitionRegistry.java
package org.springframework.beans.factory.support;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.AliasRegistry;
public interface BeanDefinitionRegistry extends AliasRegistry {
//往注册表中注册bean
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
//从注册表中删除指定名称的bean
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//获取注册表中指定名称的bean
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//判断注册表中是否已经注册了指定名称的bean
boolean containsBeanDefinition(String beanName);
//获取注册表中所有的bean的名称
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
}
(2)其继承结构图如下: 从上面类图可以看到 BeanDefinitionRegistry 接口的子实现类主要有以下两个: ① DefaultListableBeanFactory 在该类中定义了如下代码,就是用来注册bean
private final Map beanDefinitionMap = new ConcurrentHashMap(256);
② SimpleBeanDefinitionRegistry 在该类中定义了如下代码,就是用来注册bean
private final Map beanDefinitionMap = new ConcurrentHashMap(64);
5.4.5.创建容器
(1)ClassPathXmlApplicationContext对Bean配置资源的载入是从 refresh() 方法开始的。 (2)refresh() 方法是一个模板方法,规定了 IoC 容器的启动流程,有些逻辑要交给其子类实现。它对Bean配置资源进行载入,ClassPathXmlApplicationContext通过调用其父类AbstractApplicationContext的 refresh() 方法启动整个IoC容器对Bean定义的载入过程。
5.5.自定义Spring IoC先创建一个Maven工程,自定义完成后的完整的项目结构如下图所示: pom.xml
4.0.0
com.itheima
customized_spring
1.0-SNAPSHOT
dom4j
dom4j
1.6.1
5.5.1.定义bean相关的pojo类
(1)PropertyValue类 用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。 PropertyValue.java
package com.itheima.framework.beans;
/*
* 用来封装bean标签下的property标签的属性
* name属性
* ref属性
* value属性: 给基本数据类型及String类型数据赋的值
* */
public class PropertyValue {
private String name;
private String ref;
private String 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;
}
public PropertyValue() {
}
public PropertyValue(String name, String ref, String value) {
this.name = name;
this.ref = ref;
this.value = value;
}
}
(2)MutablePropertyValues类 一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存储并管理多个PropertyValue对象。 MutablePropertyValues.java
package com.itheima.framework.beans;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//用户存储和管理多个PropertyValue对象(迭代器模式)
public class MutablePropertyValues implements Iterable{
//定义list集合对象,用来存储PropertyValue对象
private final List propertyValueList;
public MutablePropertyValues() {
this.propertyValueList = new ArrayList();
}
public MutablePropertyValues(List propertyValueList) {
if(propertyValueList == null){
this.propertyValueList = new ArrayList();
}else{
this.propertyValueList = propertyValueList;
}
}
//获取所有的PropertyValue对象,返回以数组的形式
public PropertyValue[] getPropertyValues() {
//将集合转换为数组并返回
return propertyValueList.toArray(new PropertyValue[0]);
}
//根据name属性值获取PropertyValue对象
public PropertyValue getPropertyValue(String propertyName) {
//遍历集合对象
for (PropertyValue propertyValue : propertyValueList) {
if (propertyValue.getName().equals(propertyName)) {
return propertyValue;
}
}
return null;
}
//判断集合是否为空
public boolean isEmpty(){
return propertyValueList.isEmpty();
}
//添加PropertyValue对象
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
//判断集合中存储的PropertyValue对象是否和传递进行的重复了,如果重复了,则进行覆盖
for (int i = 0; i setUserDao
public static String getSetterMethodByFieldName(String fieldName) {
String methodName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
return methodName;
}
}
ClassPathXmlApplicationContext.java
package com.itheima.framework.context.support;
import com.itheima.framework.beans.BeanDefinition;
import com.itheima.framework.beans.MutablePropertyValues;
import com.itheima.framework.beans.PropertyValue;
import com.itheima.framework.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.framework.beans.factory.xml.XmlBeanDefinitionReader;
import com.itheima.framework.utils.StringUtils;
import java.lang.reflect.Method;
//IOC容器具体的子实现类
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) {
this.configLocation = configLocation;
//构建解析器对象
beanDefinitionReader = new XmlBeanDefinitionReader();
try{
this.refresh();
} catch (Exception e) {
}
}
//根据bean对象的名称获取bean对象
public Object getBean(String name) throws Exception {
//判断对象容器中是否包含指定名称的bean对象,如果包含,直接返回即可,如果不包含,需要自行创建
Object obj = singletonObjects.get(name);
if (obj != null) {
return obj;
}
//获取BeanDefinition对象
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
BeanDefinition beanDefinition = registry.getBeanDefinition(name);
//获取bean信息中的className
String className = beanDefinition.getClassName();
//通过反射创建对象
Class clazz = Class.forName(className);
Object beanObj = clazz.newInstance();
//进行依赖注入操作
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues) {
//获取name属性值
String propertyName = propertyValue.getName();
//获取value属性
String value = propertyValue.getValue();
//获取ref属性
String ref = propertyValue.getRef();
if(ref != null && !"".equals(ref)) {
//获取依赖的bean对象
Object bean = getBean(ref);
//拼接方法名
String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
//获取所有的方法对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (methodName.equals(method.getName())) {
//执行该setter方法
method.invoke(beanObj,bean);
}
}
}
if(value != null && !"".equals(value)) {
//拼接方法名
String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
//获取method对象
Method method = clazz.getMethod(methodName, String.class);
method.invoke(beanObj,value);
}
}
//在返回beanObj对象之前,将该对象存储到map容器中
singletonObjects.put(name,beanObj);
return beanObj;
}
public T getBean(String name, Class clazz) throws Exception {
Object bean = getBean(name);
if(bean == null) {
return null;
}
return clazz.cast(bean);
}
}
5.5.5.测试
(1)上述代码编写完成后,先将该项目安装到本地仓库
(2)在5.1中的已有项目中进行测试 ① 修改pom.xml文件
4.0.0
com.itheima
design_pattern_spring_demo
1.0-SNAPSHOT
com.itheima
customized_spring
1.0-SNAPSHOT
② 修改UserController.java中相关类的包
package com.itheima.controller;
import com.itheima.framework.context.ApplicationContext;
import com.itheima.framework.context.support.ClassPathXmlApplicationContext;
import com.itheima.service.UserService;
public class UserController {
public static void main(String[] args) throws Exception {
//1.创建spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器对象中获取userService对象
UserService userService = applicationContext.getBean("userService", UserService.class);
//3.调用userService中的方法进行业务逻辑处理
userService.add();
}
}
结果与使用Spring框架的一样
UserService...
userDaoImpl ....
至此,一个简单的自定义的Spring IoC就实现了。
5.6.总结(1)上面自定义Spring IoC的过程中使用到的设计模式有: ① 工厂模式。 这个使用工厂模式 + 配置文件的方式。 ② 单例模式。 Spring IoC管理的bean对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是spring框架对每一个bean只创建了一个对象。 ③ 模板方法模式。 AbstractApplicationContext类中的finishBeanInitialization()方法调用了子类的getBean()方法,因为getBean()的实现和环境息息相关。 ④ 迭代器模式。 对于MutablePropertyValues类定义使用到了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器,所以给该容器提供一个遍历方式。
(2)Spring框架其实使用到了很多设计模式,如AOP使用到了代理模式,选择JDK代理或者CGLIB代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等。有关Spring中用到了哪些设计模式的问题,可以查看面试官:说说Spring用到了哪些设计模式?这篇文章。