IOC
终于学完了设计模式,今天试着写了一下IOC容器,只是简单的模仿了一下,不喜勿喷
spring使用回顾 自定义spring框架前,先回顾一下spring框架的使用,从而分析spring的核心,并对核心功能进行模拟。
数据访问层。定义UserDao接口及其子实现类
1 2 3 4 5 6 7 8 9 10 public interface UserDao { public void add () ; } public class UserDaoImpl implements UserDao { public void add () { System.out.println("userDaoImpl ...." ); } }
业务逻辑层。定义UserService接口及其子实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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层
1 2 3 4 5 6 7 8 9 10 public class UserController { public static void main (String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext ("applicationContext.xml" ); UserService userService = applicationContext.getBean("userService" , UserService.class); userService.add(); } }
编写配置文件。在类路径下编写一个名为ApplicationContext.xml的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <bean id="userService" class="com.xu.service.impl.UserServiceImpl" > <property name="userDao" ref="userDao" ></property> </bean> <bean id="userDao" class="com.xu.dao.impl.UserDaoImpl" ></bean> </beans>
代码运行结果如下:
通过上面代码及结果可以看出:
userService对象是从applicationContext容器对象获取到的,也就是userService对象交由spring进行管理。
上面结果可以看到调用了UserDao对象中的add方法,也就是说UserDao子实现类对象也交由spring管理了。
UserService中的userDao变量我们并没有进行赋值,但是可以正常使用,说明spring已经将UserDao对象赋值给了userDao变量。
上面三点体现了Spring框架的IOC(Inversion of Control)和DI(Dependency Injection, DI)
spring核心功能结构 Spring大约有20个模块,由1300多个不同的文件构成。这些模块可以分为:
核心容器、AOP和设备支持、数据访问与集成、Web组件、通信报文和集成测试等,下面是 Spring 框架的总体架构图:
核心容器由 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进行交互。
bean概述 Spring 就是面向 Bean
的编程(BOP,Bean Oriented Programming),Bean 在 Spring 中处于核心地位。Bean对于Spring的意义就像Object对于OOP的意义一样,Spring中没有Bean也就没有Spring存在的意义。Spring IoC容器通过配置文件或者注解的方式来管理bean对象之间的依赖关系。
spring中bean用于对一个类进行封装。如下面的配置:
1 2 3 4 <bean id ="userService" class ="com.xu.service.impl.UserServiceImpl" > <property name ="userDao" ref ="userDao" > </property > </bean > <bean id ="userDao" class ="com.xu.dao.impl.UserDaoImpl" > </bean >
为什么Bean如此重要呢?
spring 将bean对象交由一个叫IOC容器进行管理。
bean对象之间的依赖关系在配置文件中体现,并由spring完成。
Spring IOC相关接口分析 BeanFactory解析 Spring中Bean的创建是典型的工厂模式,这一系列的Bean工厂,即IoC容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在Spring中有许多IoC容器的实现供用户选择,其相互关系如下图所示。
其中,BeanFactory作为最顶层的一个接口,定义了IoC容器的基本功能规范,BeanFactory有三个重要的子接口:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。
那么为何要定义这么多层次的接口呢?
每个接口都有它的使用场合,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。例如,
ListableBeanFactory接口表示这些Bean可列表化。
HierarchicalBeanFactory表示这些Bean 是有继承关系的,也就是每个 Bean 可能有父 Bean
AutowireCapableBeanFactory 接口定义Bean的自动装配规则。
这三个接口共同定义了Bean的集合、Bean之间的关系及Bean行为。最基本的IoC容器接口是BeanFactory,来看一下它的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&" ; Object getBean (String name) throws BeansException; <T> T getBean (String name, Class<T> requiredType) throws BeansException; Object getBean (String name, Object... args) throws BeansException; <T> T getBean (Class<T> requiredType) throws BeansException; <T> T getBean (Class<T> requiredType, Object... args) throws BeansException; <T> ObjectProvider<T> getBeanProvider (Class<T> requiredType) ; <T> ObjectProvider<T> getBeanProvider (ResolvableType requiredType) ; boolean containsBean (String name) ; 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容器实现,比如:
ClasspathXmlApplicationContext : 根据类路径加载xml配置文件,并创建IOC容器对象。
FileSystemXmlApplicationContext :根据系统路径加载xml配置文件,并创建IOC容器对象。
AnnotationConfigApplicationContext :加载注解类配置,并创建IOC容器。
BeanDefinition解析 Spring IoC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以BeanDefinition来描述的,如下面配置文件
1 2 3 4 <bean id ="userDao" class ="com.xu.dao.impl.UserDaoImpl" > </bean > bean标签还有很多属性: scope、init-method、destory-method等。
其继承体系如下图所示。
BeanDefinitionReader解析 Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过BeanDefinitionReader来完成,看看Spring中BeanDefinitionReader的类结构图,如下图所示。
看看BeanDefinitionReader接口定义的功能来理解它具体的作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry () ; @Nullable ResourceLoader getResourceLoader () ; @Nullable ClassLoader getBeanClassLoader () ; BeanNameGenerator getBeanNameGenerator () ; int loadBeanDefinitions (Resource resource) throws BeanDefinitionStoreException; int loadBeanDefinitions (Resource... resources) throws BeanDefinitionStoreException; int loadBeanDefinitions (String location) throws BeanDefinitionStoreException; int loadBeanDefinitions (String... locations) throws BeanDefinitionStoreException; }
BeanDefinitionRegistry解析 BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文件中定义了很多bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?答案就是BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface BeanDefinitionRegistry extends AliasRegistry { void registerBeanDefinition (String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; void removeBeanDefinition (String beanName) throws NoSuchBeanDefinitionException; BeanDefinition getBeanDefinition (String beanName) throws NoSuchBeanDefinitionException; boolean containsBeanDefinition (String beanName) ; String[] getBeanDefinitionNames(); int getBeanDefinitionCount () ; boolean isBeanNameInUse (String beanName) ; }
继承结构图如下:
从上面类图可以看到BeanDefinitionRegistry接口的子实现类主要有以下几个:
7.3.5 创建容器 ClassPathXmlApplicationContext对Bean配置资源的载入是从refresh()方法开始的。refresh()方法是一个模板方法,规定了 IoC 容器的启动流程,有些逻辑要交给其子类实现。它对 Bean 配置资源进行载入,ClassPathXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程。
自定义IOC 总体架构
PropertyValue类 封装property标签,属性有value,name,ref,三个属性,用于反射创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.xu.framework.beans;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类 用来存储property类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.xu.framework.beans;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class MutablePropertyValues implements Iterable <PropertyValue> { private final List<PropertyValue> propertyValues; public MutablePropertyValues () { this .propertyValues = new ArrayList <>(); } public MutablePropertyValues (List<PropertyValue> propertyValues) { this .propertyValues = (propertyValues == null ? new ArrayList <>() : propertyValues); } public PropertyValue[] getPropertyValues() { return this .propertyValues.toArray(new PropertyValue [0 ]); } public PropertyValue getPropertyValue (String propertyName) { for (PropertyValue pv : this .propertyValues) { if (pv.getName().equals(propertyName)){ return pv; } } return null ; } public boolean isEmpty () { return propertyValues.isEmpty(); } public MutablePropertyValues addProperty (PropertyValue propertyValue) { for (int i = 0 ; i < this .propertyValues.size(); i++) { PropertyValue pv = this .propertyValues.get(i); if (pv.getName().equals(propertyValue)) { this .propertyValues.set(i, propertyValue); return this ; } } this .propertyValues.add(propertyValue); return this ; } public boolean contains (String propertyValueName) { return this .getPropertyValue(propertyValueName) != null ; } @Override public Iterator<PropertyValue> iterator () { return propertyValues.iterator(); } }
BeanDefinition类 该类用来封装Bean 标签数据,便于反射创建对象
该类有三个成员变量
id,bean标签id标识
classname,类的全类名
PropertyValue集合,Bean标签里面包的property标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.xu.framework.beans;public class BeanDefinition { private String id; private String className; private MutablePropertyValues mutablePropertyValues; public BeanDefinition () { mutablePropertyValues = 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 getMutablePropertyValues () { return mutablePropertyValues; } public void setMutablePropertyValues (MutablePropertyValues mutablePropertyValues) { this .mutablePropertyValues = mutablePropertyValues; } }
BeanDefinitionRegisrty接口 这个接口用来用来规范注册Bean的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.xu.framework.beans.factory.support;import com.xu.framework.beans.BeanDefinition;public interface BeanDefinitionRegistry { void registerBeanDefinition (String beanName, BeanDefinition beanDefinition) ; void removeBeanDefinition (String beanName) throws Exception; BeanDefinition getBeanDefinition (String beanName) throws Exception; boolean containsBeanDefinition (String beanName) ; int getBeanDefinitionCount () ; String[] getBeanDefinitionNames(); }
SimpleBeanDefinitionRegistry类 实现了BeanDefinitionRegistry接口,用来注册Bean
拥有一个成员变量,该成员变量是一个Map集合,用来保存Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.xu.framework.beans.factory.support;import com.xu.framework.beans.BeanDefinition;import java.util.HashMap;import java.util.Map;public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry { 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接口 该接口规范了读取配置文件的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.xu.framework.beans.factory.support;public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry () ; void loadBeanDefinitions (String configLocation) throws Exception; }
XMLBeanDefinitionReader类 该类实现了BeanDefinitionReader接口,实现了对Xml配置文件的读取
读取配置文件,这里引入了第三方库dom4j,该库可以实现对xml文件的读取
该类注入了SimpleBeanDefinitionRegistry类,随着遍历DOM文件树,蒋所有文件里的内容存储到注册表里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.xu.framework.beans.factory.xml;import com.xu.framework.beans.BeanDefinition;import com.xu.framework.beans.MutablePropertyValues;import com.xu.framework.beans.PropertyValue;import com.xu.framework.beans.factory.support.BeanDefinitionReader;import com.xu.framework.beans.factory.support.BeanDefinitionRegistry;import com.xu.framework.beans.factory.support.SimpleBeanDefinitionRegistry;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.util.List;public class XMLBeanDefinitionReader implements BeanDefinitionReader { private BeanDefinitionRegistry registry; public XMLBeanDefinitionReader () { registry = new SimpleBeanDefinitionRegistry (); } @Override public BeanDefinitionRegistry getRegistry () { return registry; } @Override public void loadBeanDefinitions (String configLocation) throws Exception { SAXReader saxReader = new SAXReader (); Document document = saxReader.read(XMLBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation)); Element rootElement = document.getRootElement(); List<Element> beanList = rootElement.elements("bean" ); for (Element element : beanList) { String id = element.attributeValue("id" ); String className = element.attributeValue("class" ); BeanDefinition beanDefinition = new BeanDefinition (); beanDefinition.setId(id); beanDefinition.setClassName(className); MutablePropertyValues propertyValues = new MutablePropertyValues (); List<Element> propertyList = element.elements("property" ); for (Element property : propertyList) { String ref = property.attributeValue("ref" ); String name = property.attributeValue("name" ); String value = property.attributeValue("value" ); PropertyValue propertyValue = new PropertyValue (name, ref, value); propertyValues.addProperty(propertyValue); } beanDefinition.setMutablePropertyValues(propertyValues); registry.registerBeanDefinition(id,beanDefinition); } } }
BeanFactory,ApplicationContext接口 规范Bean工厂,并且ApplicationContext接口继承Bean工厂
1 2 3 4 5 6 7 8 9 10 11 package com.xu.framework.beans.factory;public interface BeanFactory { Object getBean (String name) throws Exception; <T> T getBean (String name, Class<? extends T> clazz) throws Exception; }
1 2 3 4 5 6 7 8 9 10 11 12 package com.xu.framework.context;import com.xu.framework.beans.factory.BeanFactory;public interface ApplicationContext extends BeanFactory { void refresh () throws Exception; }
AbstractApplicationContext类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.xu.framework.context.support;import com.xu.framework.beans.BeanDefinition;import com.xu.framework.beans.factory.support.BeanDefinitionReader;import com.xu.framework.beans.factory.support.BeanDefinitionRegistry;import com.xu.framework.context.ApplicationContext;import java.util.HashMap;import java.util.Map;public abstract class AbstractApplicationContext implements ApplicationContext { protected BeanDefinitionReader beanDefinitionReader; protected Map<String, Object> singletonObjects = new HashMap <>(); protected String configLocation; @Override public void refresh () throws Exception{ beanDefinitionReader.loadBeanDefinitions(configLocation); finishBeanInitialization(); } private void finishBeanInitialization () throws Exception { BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); String[] beanNames = registry.getBeanDefinitionNames(); for (String beanName : beanNames) { BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); getBean(beanName); } } }
ClassPathXMLApplicationContext类 该类作为容器,并且在创建容器之时就会立即加载类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 package com.xu.framework.context.support;import com.xu.framework.beans.BeanDefinition;import com.xu.framework.beans.MutablePropertyValues;import com.xu.framework.beans.PropertyValue;import com.xu.framework.beans.factory.support.BeanDefinitionRegistry;import com.xu.framework.beans.factory.xml.XMLBeanDefinitionReader;import com.xu.framework.utils.StringUtils;import java.lang.reflect.Method;public class ClassPathXMLApplicationContext extends AbstractApplicationContext { public ClassPathXMLApplicationContext (String configLocation) { this .configLocation = configLocation; beanDefinitionReader = new XMLBeanDefinitionReader (); try { refresh(); } catch (Exception e) { e.printStackTrace(); } } @Override public Object getBean (String name) throws Exception { Object obj = singletonObjects.get(name); if (obj != null ){ return obj; } BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); BeanDefinition beanDefinition = registry.getBeanDefinition(name); String className = beanDefinition.getClassName(); Class<?> clazz = Class.forName(className); Object beanObj = clazz.newInstance(); MutablePropertyValues mutablePropertyValues = beanDefinition.getMutablePropertyValues(); for (PropertyValue mutablePropertyValue : mutablePropertyValues) { String propertyName = mutablePropertyValue.getName(); String value = mutablePropertyValue.getValue(); String ref = mutablePropertyValue.getRef(); if (ref != null && !"" .equals(ref)){ Object bean = getBean(ref); String methodName = StringUtils.getSetterMethodByFieldName(propertyName); Method[] methods = clazz.getMethods(); for (Method method : methods) { if (methodName.equals(method.getName())){ method.invoke(beanObj,bean); } } } if (value != null && !"" .equals(value)){ String methodName = StringUtils.getSetterMethodByFieldName(propertyName); Method method = clazz.getMethod(methodName, String.class); method.invoke(beanObj,value); } } singletonObjects.put(name,beanObj); return beanObj; } @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); } }
工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.xu.framework.utils;public class StringUtils { public StringUtils () {} public static String getSetterMethodByFieldName (String filedName) { String methodName = "set" + filedName.substring(0 ,1 ).toUpperCase() + filedName.substring(1 ); return methodName; } }
安装到本地仓库 使用IDEA的Maven工具进行安装
安装成功
集成测试 引入依赖
测试类
配置文件
测试