数据访问层,定义 UserDao 接口及其子实现类:
public interface UserDao {
public void add();
}
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("userDaoImpl ....");
}
}
业务逻辑层,定义 UserService 接口及其子实现类
public interface UserService {
public void add();
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("userServiceImpl ...");
userDao.add();
}
}
定义 UserController 类,使用 main 方法模拟 controller 层
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对象的add方法
userService.add();
}
}
编写配置文件。在类路径下编写一个名为 ApplicationContext.xml 的配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.lsc.service.UserServiceImpl">
<property name="userDao" ref="userDao">property>
bean>
<bean id="userDao" class="com.lsc.dao.UserDaoImpl">bean>
beans>
代码运行结果如下:
userServiceImpl ...
userDaoImpl ....
通过上面代码及结果可以看出:
userService 对象从 applicationContext 容器对象中获取,也就是 userService 对象交由 Spring 进行管理
结果中调用了 UserDao 对象中的 add 方法,也就是说 UserDao 子实现类对象也交由 Spring 管理
UserService 中的 userDao 变量没有手动赋值却可以正常使用,说明由 Spring 进行了赋值
上面三点体现了 Spring 框架的 IOC 和 DI
IOC:Inversion of Control,控制反转
DI:Dependency Injection,依赖注入
Spring 是面向 Bean 的编程 (BOP,Bean Oriented Programming),Bean 在 Spring 中处于核心地位。
Spring IoC 容器通过配置文件或者注解的方式来管理 Bean 对象之间的依赖关系。
Bean 对于 Spring 的意义就像 Object 对于 OOP 的意义一样。
Spring 中 Bean 用于对一个类进行封装,如下面的配置:
为什么 Bean 如此重要呢?
Spring 中 Bean 的创建是典型的工厂模式(简单工厂 + 配置文件),这一系列的 Bean 工厂,即 IOC 容器。
在 Spring 中有许多 IOC 容器的实现供用户选择,其相互关系如下图所示:
其中,BeanFactory 作为最顶层的一个接口,定义了 IOC 容器的基本功能规范,它有三个重要接口:
ListableBeanFactory 接口表示这些 Bean 可列表化。
HierarchicalBeanFactory 接口表示这些 Bean 有继承关系,也就是每个 Bean 可能有父 Bean。
AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。
定义这么多接口是为了区分在 Spring 内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。
从类图中可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口。
最基本的 IOC 容器接口是 BeanFactory,看一下它的源码:
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;
String[] getAliases(String name);
}
在 BeanFactory 里只对 IOC 容器的基本行为做了定义,根本不关心 Bean 是如何定义及怎样加载的。
正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。
BeanFactory 有一个很重要的子接口 ApplicationContext 接口,该接口主要来规范容器中的 bean 对象是非延时加载,即在创建容器对象的时候就对象 bean 进行初始化,并存储到一个容器中。
要知道工厂是如何产生对象的,我们需要看具体的 IoC 容器实现,Spring 提供了许多 IOC 容器实现:
Spring IOC 容器管理定义的各种 Bean 对象及其相互关系,而 Bean 对象在 Spring 实现中是以 BeanDefinition 来描述的,如下面配置文件:
即 BeanDefinition 定义了 Spring 中的 Bean 对象与配置文件中元素属性的映射。
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">bean>
其继承体系如下图:
Bean 的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过 BeanDefinitionReader 来完成。Spring 中 BeanDefinitionReader 的类结构图如下图:
查看 BeanDefinitionReader 接口定义的方法来理解它具体的作用:
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;
}
BeanDefinitionReader 用来解析 bean 定义,并封装 BeanDefinition 对象。
在配置文件中定义了很多 bean 标签,思考一下**,解析的 BeanDefinition 对象存储到哪儿**?
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);
}
继承结构图如下:
完整的类结构图如下:
从上图可看到 BeanDefinitionRegistry 接口的子实现类有以下:
// key - beanDefinition名称,value- beanDefinition对象
private final Map beanDefinitionMap =
new ConcurrentHashMap<>(64);
// key - beanDefinition名称,value- beanDefinition对象
private final Map beanDefinitionMap =
new ConcurrentHashMap<>(256);
ClassPathXmlApplicationContext 对 Bean 配置资源的载入是从 refresh() 方法开始的。
refresh() 方法是一个模板方法,规定了 IOC 容器的启动流程,有些逻辑要交给其子类实现。它对 Bean 配置资源进行载入,ClassPathXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh() 方法启动整个 IOC 容器对 Bean 定义的载入过程。
用于封装 Bean 的属性,体现到配置文件中就是封装
标签的子标签
标签的属性。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PropertyValue {
//name属性
private String name;
//ref属性
private String ref;
//给基本类型和字符串进行赋值
private String value;
}
一个
标签可以有多个
子标签。
定义一个 MutablePropertyValues 类,用来存储并管理多个 PropertyValue 对象。
/**
* @BelongsProject: desigin_pattern
* @BelongsPackage: com.lsc.spring.beans
* @Author: lsc
* @CreateTime: 2023-06-01 12:56
* @Description: 存储和管理多个PropertyValue对象
* @Version: 1.0
*/
public class MutablePropertyValues implements Iterable<PropertyValue> {
private final List<PropertyValue> propertyValueList;
public MutablePropertyValues() {
propertyValueList = new ArrayList<>();
}
public MutablePropertyValues(List<PropertyValue> propertyValueList) {
if (propertyValueList == null) {
this.propertyValueList = new ArrayList<>();
} else {
this.propertyValueList = propertyValueList;
}
}
/**
* @description: 获取所有的PropertyValue对象
* @author: lsc
* @date: 2023/6/1 13:01
* @param:
* @return:
**/
public PropertyValue[] getPropertyValues(){
return propertyValueList.toArray(new PropertyValue[0]);
}
/**
* @description: 根据name属性值获取PropertyValue对象
* @author: lsc
* @date: 2023/6/1 13:07
* @param: [propertyName]
* @return: com.lsc.spring.beans.factory.PropertyValue
**/
public PropertyValue getPropertyValue(String propertyName){
for (PropertyValue propertyValue : propertyValueList) {
if(propertyValue.getName().equals(propertyName)){
return propertyValue;
}
}
return null;
}
/**
* @description: 判断集合是否为空
* @author: lsc
* @date: 2023/6/1 13:14
* @param: []
* @return: boolean
**/
public boolean isEmpty(){
return propertyValueList.isEmpty();
}
/**
* @description: 添加PropertyValue对象
* @author: lsc
* @date: 2023/6/1 13:33
* @param: [pv]
* @return: 返回自身实现链式编程
**/
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
//如果集合中已经有了对应的name的对象,则覆盖它
for (int i = 0; i < propertyValueList.size(); i++) {
if(propertyValueList.get(i).getName().equals(pv.getName())){
propertyValueList.set(i,pv);
}
}
propertyValueList.add(pv);
return this;
}
/**
* @description: 判断是否有指定name属性值的对象
* @author: lsc
* @date: 2023/6/1 13:39
* @param: [propertyName]
* @return: boolean
**/
public boolean contains(String propertyName){
return getPropertyValue(propertyName)!=null;
}
/**
* @description: 获取迭代器对象
* @author: lsc
* @date: 2023/6/1 13:40
* @param: []
* @return: java.util.Iterator
**/
@Override
public Iterator<PropertyValue> iterator() {
return propertyValueList.iterator();
}
}
BeanDefinition 类用来封装
标签的属性,主要包含 id、class 及子标签
数据。
class 属性是需要交由 Spring 管理的类的全类名
@Data
public class BeanDefinition {
private String id;
private String className;
private MutablePropertyValues propertyValues;
public BeanDefinition() {
propertyValues= new MutablePropertyValues();
}
}
BeanDefinitionRegistry 接口定义了注册表的相关操作:
/**
* 注册表对象
*/
public interface BeanDefinitionRegistry {
/**
* 注册 BeanDefinition 对象到注册表中
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
/**
* 从注册表中删除指定名称的 BeanDefinition 对象
*/
void removeBeanDefinition(String beanName) throws Exception;
/**
* 根据名称从注册表中获取 BeanDefinition 对象
*/
BeanDefinition getBeanDefinition(String beanName) throws Exception;
/**
* 判断注册表中是否包含指定名称的 BeanDefinition 对象
*/
boolean containsBeanDefinition(String beanName);
/**
* 获取注册表中 BeanDefinition 对象的个数
*/
int getBeanDefinitionCount();
/**
* 获取注册表中所有的 BeanDefinition 的名称
*/
String[] getBeanDefinitionNames();
}
该类实现了 BeanDefinitionRegistry 接口,定义了 Map 集合作为注册表容器。
/**
* 注册表接口的子实现类
*/
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
// 存储BeanDefinition对象的容器
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) throws Exception {
beanDefinitionMap.remove(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws Exception {
return beanDefinitionMap.get(beanName);
}
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
@Override
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
@Override
public String[] getBeanDefinitionNames() {
return beanDefinitionMap.keySet().toArray(new String[0]);
}
}
BeanDefinitionReader 是用来解析配置文件并在注册表中注册 bean 的信息,定义了两个规范:
public interface BeanDefinitionReader {
/**
* @description:获取注册表对象
* @author: lsc
* @date: 2023/6/1 14:03
* @param: []
* @return: Registry
**/
BeanDefinitionRegistry getRegistry();
/**
* 加载配置文件并在注册表中进行注册
*/
void loadBeanDefinitions(String configLocation) throws Exception;
}
XmlBeanDefinitionReader 类专门用来解析 xml 格式的配置文件,实现 BeanDefinitionReader 接口。
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
//声明注册表对象
private BeanDefinitionRegistry registry;
public XmlBeanDefinitionReader() {
registry=new SimpleBeanDefinitionRegistry();
}
@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}
/**
* @description: 解析xml文件进行注册bean到注册表中
* @author: lsc
* @date: 2023/6/1 14:08
* @param: 类路径下配置文件的路径
* @return: void
**/
@Override
public void loadBeanDefinitions(String configLocation) throws Exception {
//使用demo4j进行xml配置文件的解析
SAXReader saxReader = new SAXReader();
//获取类路径下的配置文件
ClassLoader classLoader = XmlBeanDefinitionReader.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream(configLocation);
Document document = saxReader.read(resourceAsStream);
//根据document对象获取根路径标签对象beans
Element rootElement = document.getRootElement();
//获取根标签下所有的bean标签对象
List<Element> beanElements = rootElement.elements("bean");
//封装BeanDefinition对象,并添加到注册表中
for (Element beanElement : beanElements) {
//1封装BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
String id = beanElement.attributeValue("id");
String className = beanElement.attributeValue("class");
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//创建MutablePropertyValues对象
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
// 获取bean标签下所有的property标签对象
List<Element> propertyElements = beanElement.elements("property");
for (Element propertyElement : propertyElements) {
PropertyValue propertyValue = new PropertyValue(
propertyElement.attributeValue("name"),
propertyElement.attributeValue("ref"),
propertyElement.attributeValue("value")
);
mutablePropertyValues.addPropertyValue(propertyValue);
};
beanDefinition.setPropertyValues(mutablePropertyValues);
registry.registerBeanDefinition(id,beanDefinition);
}
}
}
该接口中定义 IOC 容器的统一规范,即获取 bean 对象。
public interface BeanFactory {
/**
* 根据bean对象的名称获取bean对象
*/
Object getBean(String name) throws Exception;
/**
* 根据bean对象的名称获取bean对象,并进行类型转换
*/
<T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}
该接口是 BeanFactory 的子接口,它的实现类对 bean 对象的创建都是非延时的。
在该接口中定义 refresh() 方法,该方法主要完成以下两个功能:
/**
* 定义非延时加载功能
*/
public interface ApplicationContext extends BeanFactory {
/**
* 加载配置文件并创建对象
*/
void refresh() throws Exception;
}
需要在该类中定义一个 Map 集合,作为 bean 对象存储的容器。
作为 ApplicationContext 接口的子类,所以该类是非延时加载。
声明 BeanDefinitionReader 类型的变量,用来进行 xml 配置文件的解析,符合单一职责原则。BeanDefinitionReader 对象的创建交由子类实现,因为只有子类知道应当创建 BeanDefinitionReader 哪个实现类对象(是 xml 还是 properties)。
public abstract class AbstractApplicationContext implements ApplicationContext{
//声明解析器变量,具体创建交给子类实现
protected BeanDefinitionReader beanDefinitionReader;
//定义用于存储bean对象的map容器
protected Map<String,Object> singletonObjects = new HashMap<>();
//声明配置文件路径的变量
protected String configLocation;
@Override
public void refresh() throws Exception {
//加载BeanDefinition对象
beanDefinitionReader.loadBeanDefinitions(configLocation);
//初始化bean
finishBeanInitialization();
}
private void finishBeanInitialization() throws Exception {
//获取注册表对象
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
//获取BeanDefinition对象
String[] beanNames = registry.getBeanDefinitionNames();
//bean的初始化
for (String beanName : beanNames) {
getBean(beanName);
}
}
}
该类
finishBeanInitialization()
方法中调用getBean()
方法使用了模板方法模式。
该类主要是加载类路径下的配置文件,并进行 bean 对象的创建,主要完成以下功能:
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) {
this.configLocation=configLocation;
//构造解释器对象
beanDefinitionReader = new XmlBeanDefinitionReader();
try {
this.refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String name) throws Exception {
//对象容器中有指定名称的bean的对象,就直接返回,不包含咨询创建
Object o = singletonObjects.get(name);
if(o!=null){
return o;
}
//从注册表中获取BeanDefinition对象, 然后拿到类路径创建对象
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
BeanDefinition beanDefinition = registry.getBeanDefinition(name);
String className = beanDefinition.getClassName();
//通过反射创建对象
Class<?> aClass = Class.forName(className);
Object beanObj = aClass.newInstance();
//进行依赖注入
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues) {
String pName = propertyValue.getName();
String value = propertyValue.getValue();
String ref = propertyValue.getRef();
if(ref!=null && !"".equals(ref)){
//获取依赖的bean对象
Object bean = getBean(ref);
// 获取到setter方法:userDao ==> setUserDao
String setterMethodByFieldName = StringUtils.getSetterMethodByFieldName(pName);
//获取到setter方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
if(setterMethodByFieldName.equals(method.getName())){
method.invoke(beanObj,bean);
}
}
}
if (value != null && !"".equals(value)) {
// 获取到setter方法:age ==> setAge, 并执行
String methodName = StringUtils.getSetterMethodByFieldName(pName);
Method method = aClass.getMethod(methodName, String.class);
method.invoke(beanObj, value);
}
}
// 返回之前将该对象存储到map容器中,下次可以直接取,达到单例效果
singletonObjects.put(name, beanObj);
return null;
}
@Override
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
Object bean = getBean(name);
if (bean == null) {
return null;
}
return clazz.cast(bean);
}
}
public class StringUtils {
private StringUtils() {
}
// userDao ==> setUserDao
public static String getSetterMethodByFieldName(String fieldName) {
return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
}
使用上面自定义的 Spring 框架,可以直接替换前面使用 Spring 的代码(实现 Spring 最简单的功能)。
使用到的设计模式:
工厂模式。这里使用的是简单工厂 + 配置文件。
单例模式。Spring IOC 管理的 bean 对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是 Spring 框架对每一个 bean 只创建了一个对象(利用 Map 容器控制)。
模板方法模式。AbstractApplicationContext 类中的 finishBeanInitialization() 方法调用了子类的 getBean() 方法,因为 getBean() 的实现和环境息息相关(对不同配置文件解析获取 bean 对象的方式是不同的)。
迭代器模式。对于 MutablePropertyValues 类定义使用到了迭代器模式,因为此类存储并管理 PropertyValue 对象,也属于一个容器,所以给该容器提供一个遍历方式。
真正的 Spring 框架其实使用到了很多设计模式,如 AOP 使用到了代理模式,选择 JDK 代理或者 CGLIB 代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等。
注意我们自己写的 Spring 和真实的 Spring 在设计上是有一定的不同的。
Spring 框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性。
自定义 SpringIOC 有以下几个目的: