脚本宝典收集整理的这篇文章主要介绍了设计模式终章----手写IOC容器,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
Spring大约有20个模块,由1300多个不同的文件构成
这些模块可以分为:
核心容器,AOP和设备支持,数据访问和集成,Web组件,通信报文和集成测试,下面是Spring框架的总体架构图:
核心容器由beans,core,context和exPression(Spring Expression Language,SPEL)4个模块组成要点一:
BeanFactory属于延时加载,也就是说在实例化容器对象后并不会自动实例化Bean,只有当Bean被使用时,BeanFatory才会对该Bean进行实例化与依赖关系的装配.
要点二:
要点三F1b;
要点四:
要点五:
最基本的IOC容器接口是BeanFactory,来看一下它的源码
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
//根据Bean的名称,获取IOC容器中的Bean对象
Object getBean(String VAR1) throws BeansException;
//根据Bean的名称,获取IOC容器中的Bean对象,并指定获取到的Bean对象的类型,这样我们使用时,就不需要进行强制类型转换
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
//判断容器中是否包含指定名称的Bean
boolean containsBean(String var1);
//根据Bean的名称判断是否是单例
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
//是否是多实例Bean
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
在BeanFactory里只对IOC容器的基本行为做了定义,根本不关心你的Bean是如何定义及加载的。
正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。
BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中的bean对象是非延时加载的,即在创建容器对象的时候就对Bean进行初始化,并存储到一个容器中
ClasspathXmlApplicationContext :根据类路径加载xml配置文件,并创建IOC容器对象
FileSystemXmlApplicationContext:根据系统路径加载xml配置文件,并创建IOC容器对象
AnnotationconfigApplicationContext:加载注解类配置,并创建IOC容器
Spring IOC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以BeanDefinition来描述的,如下面的配置文件
<bean id="userDAO" class="com.dao.impl.UserDaoImpl"></bean>
bean标签还有很多属性: scope,init-method,destory-method等
@H_663_777@
public interface BeanDefinitionReader {
//获取BeanDefinitionRegistry 注册器对象
BeanDefinitionRegistry getRegistry();
@Nullable
ResourceLoader getResourceLoader();
@Nullable
ClassLoader getBeanClassLoader();
BeanNameGenerator getBeanNameGenerator();
//下面的loadBeanDefinitions都是从指定的资源中加载bean定义信息
int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}
BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文件中定义了很多Bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?
答案就是BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry
public interface BeanDefinitionRegistry extends AliasRegistry {
//往注册表中注册bean,即bean定义加载后被封装成的BeanDefinition对象
void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;
//从注册表删除指定名称的bean
void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//获取注册表中指定名称的Bean
BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//判断注册表中是否已经注册了指定名称的bean,即BeanDefinition对象
boolean containsBeanDefinition(String var1);
//获取注册表中所有bean的名称
String[] getBeanDefinitionnames();
//获取注册表中注册的bean的个数
int getBeanDefinitionCount();
//判断当前的bean名称在注册表中是否已经在使用了
boolean isBeanNameinUse(String var1);
}
public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
//重点是这个beanDefinitionMap ---作为容器存放beanDefinition对象
//将beanDefinition对象放入map集合的过程就称为注册bean
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(64);
public SimpleBeanDefinitionRegistry() {
}
//下面都是重写父类的方法
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
this.beanDefinitionMap.put(beanName, beanDefinition);
}
....
}
该类中也有一个属性是用来注册bean的
private final Map<String, BeanDefinition> beanDefinitionMap;
ClassPathXmlApplicationContext对Bean配置资源的载入是从refresh()方法开始的。
refresh()方法是一个模板方法,规定了IOC容器的启动流程,有些逻辑要交给器其子类实现。
他对Bean配置资源进行载入,ClassPathXmlApplicationContext通过调用父类AbstractApplicationContext的refresh()方法启动整个IOC容器对Bean定义的载入过程.
现在要对下面的配置文件进行解析,并自定义Spring框架的IOC对涉及到的对象进行管理
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userService" class="com.pojo.UserService">
<property name="userDao" ref="userDao"></property>
<bean id="userDao" class="com.pojo.UserDao"></bean>
</beans>
用于封装bean的属性,体现到上面就是封装bean标签的子标签的property标签数据
每个PropertyValue实例对象,封装一条property标签里面的数据
//用来封装bean标签下的property标签下的属性
//name属性,ref属性:给引用类型赋值,value属性:给基本数据类型及String类型属性赋值
@Data
@NoargsConstructor
@AllArgsConstructor
public class PropertyValue
{
private String name;
private String ref;
private String value;
}
一个bean标签可以有多个property子标签,所以再定义一个MultablePropertyValues类,用来存储并管理多个PropertyValue对象
//用来存储和管理多个PropertyValue对象
public class MultablePropertyValues implements
Iterable<PropertyValue> {
//定义list集合对象,用来存储propertyvlaue对象
private final List<PropertyValue> propertyValueList;
//用final修饰的变量只能赋值一次,并且必须在构造方法结束前进行赋值
public MultablePropertyValues()
{
propertyValueList=new ArrayList<>();
}
public MultablePropertyValues(List<PropertyValue> valueList)
{
if(valueList==null)
{
propertyValueList=new ArrayList<>();
}
else
{
propertyValueList=new ArrayList<>();
}
}
//获取所有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 MultablePropertyValues addPropertyValue(PropertyValue pv)
{
//判断传递进来的PropertyValue对象,是否和集合中已有的重复了,如果重复了,就记性覆盖操作
for(int i=0;i<propertyValueList.size();i++)
{
PropertyValue propertyValue = propertyValueList.get(i);
if(propertyValue.getName().equals(pv.getName()))
{
//进行覆盖操作
propertyValueList.set(i,propertyValue);
return this;//实现链式编程
}
}
//没有就直接添加
propertyValueList.add(pv);
return this;
}
//判断是否有指定name属性值的对象
public boolean contains(String propertyName)
{
return getPropertyValue(propertyName)!=null;
}
//获取迭代器对象
@override
public Iterator<PropertyValue> iterator()
{
//调用list集合里面获取迭代器的方法
return propertyValueList.iterator();
}
}
BeanDenfinition用来封装bean信息的,主要包含id(即对象的名称),class(需要交由spring管理类的全类名)及子标签property数据
//用来封装bean标签数据
//id属性,class属性,property子标签数据
@Data
public class BeanDefinition
{
private String id;
private String classname;
private MultablePropertyValues propertyValues;
//利用无参构造对PropertyValues里面的list集合进行初始化
public BeanDefinition()
{
propertyValues=new MultablePropertyValues();
}
}
BeanDefinitionRegistry接口定义了注册表相关操作,定义了如下功能:
public interface BeanDefinitionRegistry
{
//往注册表中注册bean,即bean定义加载后被封装成的BeanDefinition对象
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
//从注册表删除指定名称的bean
void removeBeanDefinition(String beanName);
//获取注册表中指定名称的Bean
BeanDefinition getBeanDefinition(String beanName);
//判断注册表中是否已经注册了指定名称的bean,即BeanDefinition对象
boolean containsBeanDefinition(String beanName);
//获取注册表中所有bean的名称
String[] getBeanDefinitionNames();
//获取注册表中注册的bean的个数
int getBeanDefinitionCount();
//判断当前的bean名称在注册表中是否已经在使用了
boolean isBeanNameInUse(String beanName);
}
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry{
//定义一个容器,用来存储BeanDefinition对象
private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
//往注册表中注册bean,即bean定义加载后被封装成的BeanDefinition对象
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName,beanDefinition);
}
//从注册表删除指定名称的bean
@Override
public void removeBeanDefinition(String beanName) {
beanDefinitionMap.remove(beanName);
}
//获取注册表中指定名称的Bean
@Override
public BeanDefinition getBeanDefinition(String beanName) {
return beanDefinitionMap.get(beanName);
}
//判断注册表中是否已经注册了指定名称的bean,即BeanDefinition对象
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
//获取注册表中所有bean的名称
@Override
public String[] getBeanDefinitionNames()
{
//获取key的set集合,再将set集合,转换为数组,数组的类型为string
return beanDefinitionMap.keySet().toArray(new String[0]);
}
//获取注册表中注册的bean的个数
@Override
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
//判断当前的bean名称在注册表中是否已经在使用了
@Override
public boolean isBeanNameInUse(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
}
BeanDefinitionReader是用来解析配置文件并在注册表中注册bean信息,定义了两个规范:
//用来解析配置文件的,该接口只是定义了规范
public interface BeanDefinitionReader
{
//获取注册表对象
BeanDefinitionRegistry getRegistry();
//加载配置文件,并在注册表中进行注册
void loadBeanDefinitions(String configLocation) throws Exception;
}
XmlBeanDefinitionReader类专门用来解析xml配置文件的,该类实现BeanDefinitionReader接口,并实现接口中的两个功能
DOM4J的依赖
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
需要进行解析的xml格式,参考下面:
具体代码实现:
//针对xml配置文件进行解析的类
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 {
//使用dom4j进行xml配置文件的解析
SAXReader reader=new SAXReader();
//获取类路径下的配置文件
InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);
//传入字节输入流,获取文档对象
Document document = reader.read(is);
//根据Document对象获取根标签对象(beans)
Element rootElement=document.getRootElement();
//获取根标签下所有的bean标签对象
List<Element> beanElements = rootElement.elements("bean");
//遍历集合--获取每个bean标签上的属性值
for (Element beanElement : beanElements) {
//获取id属性
String id = beanElement.attributeValue("id");
//获取class属性
String className = beanElement.attributeValue("class");
//将id属性和class属性封装到BeanDefinition对象中
//1.创建BeanDefinition对象
BeanDefinition beanDefinition=new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//创建MultablePropertyValues对象
MultablePropertyValues multablePropertyValues=new MultablePropertyValues();
//获取bean标签下所有的property标签对象
List<Element> propertyElements = beanElement.elements("property");
for (Element propertyElement : propertyElements) {
//获取name属性
String name = propertyElement.attributeValue("name");
//获取ref属性
String ref = propertyElement.attributeValue("ref");
//获取value属性
String value = propertyElement.attributeValue("value");
//使用PropertyValue对象封装上述属性
PropertyValue propertyValue=new PropertyValue(name,ref,value);
//添加到管理PropertyValue对象的容器中
multablePropertyValues.addPropertyValue(propertyValue);
}
//将multablePropertyValues对象封装到BeanDefinition对象中
beanDefinition.setPropertyValues(multablePropertyValues);
//将beanDefinition对象注册到注册表中
//xml文件中配置的id做为管理BeanDefinition对象的容器中的名字
registry.registerBeanDefinition(id,beanDefinition);
}
}
}
在该接口中定义IOC容器统一规范获取bean对象
//IOC容器父接口
public interface BeanFactory
{
//根据bean对象的名称获取bean对象
Object getBean(String name)throws Exception;
//根据bean对象的名称获取bean对象,并进行类型转换
//方法设置为泛型方法,并且第二个参数为泛型参数,类型必须是T类型的或者其子类
<T> T getBean(String name,Class<? extends T> clazz)throws Exception;
}
该接口的所有子实现类的bean
对象的创建都是非延时的,所以在该接口中定义refresh()
方法,该方法主要完成以下两个功能:
BeanDefinition
对象封装的数据进行bean
对象的创建//定义非延时加载功能---继承BeanFactory接口
public interface ApplicationContext extends BeanFactory
{
void refresh()throws Exception;
}
//ApplicationContext接口的子实现类,用于立即加载
public abstract class AbstractApplicationContext implements ApplicationContext {
//声明解析器变量,具体是xml解析还是什么解析,由子类决定
protected BeanDefinitionReader beanDefinitionReader;
//定义用于存储bean对象的map容器
protected Map<String,Object> signletonObjects=new HashMap<>();
//声明配置文件路径的变量
protected String configLocation;
//重写父类refresh方法
@Override
public void refresh() throws Exception {
//加载BeanDefinition对象
beanDefinitionReader.loadBeanDefinitions(configLocation);
//初始化bean
finishBeanInialLization();
}
//bean的初始化
private void finishBeanInialLization()throws Exception{
//获取注册表对象
BeanDefinitionRegistry registry=beanDefinitionReader.getRegistry();
//获取BeanDefinition对象
String[] beanNames = registry.getBeanDefinitionNames();
//进行bean的初始化方法
//将注册表中已有的BeanDefinition对象,全部通过反射创建对象后,放入IOC容器中
for (String beanName : beanNames) {
//调用父类的getBean方法,获取对应的bean对象
//getBean方法的具体实现,有对应的子类完成
//getBean对应的子类实现方式,会有区别,例如ClassPathXmlApplicationContext重写getBean方法
//里面逻辑主要是读取xml,而如果是其他方式创建子类,那么getBean重写的逻辑也会有区别
getBean(beanName);
}
}
}
该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要完成以下功能:
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{
public ClassPathXmlApplicationContext(String configLocation)
{
//配置文件的路径
this.configLocation=configLocation;
//创建解析器对象
beanDefinitionReader=new XmlBeanDefinitionReader();
//调用父类中的refresh方法
try {
refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
//根据bean对象的名称获取bean对象
@Override
public Object getBean(String name) throws Exception {
//判断对象容器中是否包含指定名称的bean对象,如果包含,直接返回即可
//如果不包含,需要自行创建
//尝试从容器中获取指定bean对象
Object o = signletonObjects.get(name);
//容器中存在该对象
if(o!=null)
{
return o;
}
//获取BeanDefinition对象
//1.先获取注册表对象
BeanDefinitionRegistry beanDefinitionRegistry=beanDefinitionReader.getRegistry();
//2.从注册表中获取对应的BeanDefinition对象
BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name);
//如果注册表中也没有对应的BeanDefinition对象,那么抛出异常
if(beanDefinition==null)
return null;
//3.获取bean信息中的className:对象的全类名
String className = beanDefinition.getClassName();
//4.通过反射创建对象
Class<?> clazz = Class.forName(className);
//获得了实例化完后的对象
Object beanObj=clazz.newInstance();
//进行依赖注入操作---创建完对象后的属性赋值
//先获取所有property对象--每一个里面property对象封装了对象的一个属性的名字和对应的值
MultablePropertyValues propertyValues = beanDefinition.getPropertyValues();
//获取迭代器,遍历存放property对象的list集合
Iterator<PropertyValue> iterator = propertyValues.iterator();
//使用迭代器遍历
while(iterator.hasNext())
{
//获取name属性值
PropertyValue next = iterator.next();
String propertyName = next.getName();
//获取value属性
String propertyValue = next.getValue();
//获取ref属性
String propertyRef = next.getRef();
//ref和value只能出现一个
if(propertyRef!=null&&!"".equals(propertyRef))
{
//ref属性存在
//获取依赖的bean对象---使用递归
Object bean = getBean(propertyRef);
//拼接方法名---传入属性名,获取其对应的set方法的名字
String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
//获取所有的方法对象
Method[] methods=clazz.getMethods();
//找到我们需要执行的对象属性的set方法
for (Method method : methods) {
if(methodName.equals(method.getName()))
{
//执行该setter方法
//这里是对依赖对象进行属性注入
//例如:
// class Person
// {
// Stu stu
// }
method.invoke(beanObj,bean);
}
}
}
//value属性存在
if(propertyValue!=null&&!"".equals(propertyValue))
{
//拼接方法名--传入属性名,获取对应的set方法
String methodName1= StringUtils.getSetterMethodByFieldName(propertyName);
//获取method对象,方法名称,和参数类型,这里参数我们针对的是String类型进行操作
Method method = clazz.getMethod(methodName1, String.class);
//执行哪个对象的方法,实参的值
method.invoke(beanObj,propertyValue);
}
}
//返回beanObj对象之前,将该对象存储到map容器中
signletonObjects.put(name,beanObj);
return beanObj;
}
//根据bean对象的名称获取bean对象,并进行强制类型转换
@Override
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
//首先调用上面的getBean方法得到一个bean对象
Object bean = getBean(name);
//如果为null,说明没有这个对象
if(bean==null)
return null;
//进行类型强转,并返回强转后的对象
return clazz.cast(bean);
}
}
//工具类,负责拼接字符串
public class StringUtils
{
//构造器私有,不能创建实例化对象
private StringUtils(){}
//静态方法---字段名前面拼接set
//userDao ---> setUserDao
//属性名第一个字母大写,前面拼上set
public static String getSetterMethodByFieldName(String fieldName)
{
String methodName="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
return methodName;
}
}
目前写的IOC只能做到对String类型的字段完成依赖注入
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean id="animal" class="com.dao.Animal">
<property name="dog" ref="dog">
</property>
<property name="type" value="狗">
</property>
</bean>
<bean id="dog" class="com.dao.Dog">
<property name="name" value="汤姆">
</property>
<property name="age" value="3">
</property>
</bean>
</beans>
测试
public class main
{
public static void main(String[] args) throws Exception {
//加载配置文件,启动IOC容器
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
//根据名字获取指定bean对象
Dog dog = (Dog)applicationContext.getBean("dog");
System.out.println(dog);
//根据名字和类型获取
Animal animal = applicationContext.getBean("animal", Animal.class);
System.out.println(animal);
//如果传入的名字,没有找到对应的bean对象
Object hhhh = applicationContext.getBean("hhhh");
System.out.println(hhhh);
}
}
码云仓库:
码云地址
后续可能也会继续补充
Spring框架其实使用到了很多设计模式,入AOP使用到了代理模式,选择JDK代理或者CGLIB代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等
Spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性,而我们自定义SpringIOC有一下几个目的:
以上是脚本宝典为你收集整理的设计模式终章----手写IOC容器全部内容,希望文章能够帮你解决设计模式终章----手写IOC容器所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。