手写Spring Ioc框架
一、Spring体系结构
1、Spring是一个轻量级一站式企业开发解决方案。
Spring成员 | 描述 |
Spring framework | spring的基础,包括ioc、aop及spring mvc、spring template等 |
Spring boot | 尽量减少框架本身的学习成本以及开发成本,让开发人员更多的关注和开发业务代码;主要特点就是 纯注解、零配置(无Spring配置文件)、自动装配、起步依赖 |
Spring Cloud | 分布式/微服务一站式开发框架 |
Spring data | 解决的是数据交互(mysql、redis、elasticsearch、mongodb、orm等) |
Spring security | 主要是针对登陆安全、权限认证等 |
Spring oauth2 | 解决单点登录、用户鉴权等问题 |
2、Spring framework的体系结构入下图所示。
对每一个模块的描述如下表格:
模块 | 子模块 | 描述 |
核心容器 | Spring-Beans、Spring-Core |
1、这两个模块是Spring的核心模块,包含了控制反转了依赖注入; 2、其中BeanFactory接口是Spring框架中的核心接口,他是工厂模式的具体实现 3、BeanFactory使用控制反转和依赖注入解耦了配置、依赖性规范与业务代码 4、BeanFactory容器实例化后并不会自动实例化Bean,而是等到Bean被使用时,BeanFactory才会对Bean进行实例化和依赖关系装配 |
Spring-Context |
1、SpringContext模块架构于核心模块之上,他扩展了BeanFactory,为他添加了Bean生命周期控制、框架事件体系以及资源加载透明化等功能 2、ApplicationContext是该模块的核心接口,他是BeanFactory的超类 3、ApplicationContext容器实例化后会对所有的单例Bean进行实例化和依赖关系的装配,使其处于可用状态 |
|
Expression | Expression是统一表达式语言的扩展模块,可以查询和管理运行中的对象 | |
AOP和设备支持 | Spring-AOP | Spring-AOP是Spring的另一个核心模块,是AOP主要的实现模块 |
Spring-Aspects | Spring-Aspects模块集成自AspectJ,主要是为Spring-AOP提供多种AOP实现方案 | |
Spring-Instrument | Spring-Instrument模块应该是AOP的支援模块,主要作用是在JVM启动时,生成一个代理类,开发人员可以通过代理类在运行时改变类的字节,从而改变一个类的功能,从而实现AOP功能 | |
数据访问与集成 | Spring-JDBC |
1、Spring-JDBC模块是Spring提供的JDBC抽象框架的主要实现模块,用于简化SpringJDBC 2、Spring-JDBC主要提供模板方式、关系数据库对象化方式、SimpleJdbc方式、事务管理来简化JDBC编程 3、Spring-JDBC的主要实现类是JdbcTemplate、SimpleJdbcTemplate、NamedParameterJdbcTemplater |
数据访问及集成 | Spring-TX | Spring-TX是SpringJDBC的事务控制实现模块。 |
Spring-ORM | Spring-ORM模块是ORM框架支持模块,主要集成Hibernate、JPA等用于资源管理、数据访问的实现和事务策略 | |
Spring-JMS | Spring-JMS能够发送和接口信息,从Spring4开始,还提供了Spring-Messaging模块的支撑 | |
Spring-OXM5 |
Spring-OXM模块主要提供一个抽象层以支撑OXM:将JAVA对象映射成XML数据,或者将XML数据映射成JAVA对象 |
|
Web | Spring-Web | Spring-Web为Spring提供了最基础的Web支持,主要建立在核心容器之上,通过Servlet或者Listeners来初始化IOC容器,也包含一些与Web相关的支持 |
Spring-WebMVC | Spring-WebMVC是一个Web-Servlet模块,实现了SpringMVC的Web应用 | |
Spring-WebSocket | Spring-WebSocket主要是与Web前端的全双工通讯的协议 | |
Spring-WebFlux | Spring-WebFlux是一个新的非阻塞函数式ReactiveWeb框架,可以用来建立异步的,非阻塞,事件驱动的服务,并且扩展性非常好 | |
报文发送 | Spring-Messaging | Spring-Messaging是从Spring4开始加入的新模块,主要职责是为Spring框架集成一些基础的报文传送应用 |
Test | Spring-Test | Spring-Test主要为测试提供支持。 |
二、手写Spring IOC框架
手写框架可以由简入繁,一步步深入。
以 apis--->service--->dao为例,进行数据查询,dao中需要注入datasource,service需要注入dao,apis需要注入service。
V1为直接手动赋值写法,V2为使用了配置文件加载的写法,V3是借鉴了Spring的类体系后的写法
1、V1版本(手动注入)
IOC主要是使用了构造函数或者是setter方法进行了依赖注入,那么V1版本就先模拟依赖注入部分。
dao:
@Slf4j public class UserDaoImpl implements UserDao { private BasicDataSource dataSource; public UserDaoImpl(BasicDataSource dataSource){ this.dataSource = dataSource; } @Override public UserDo findUserById(String id) throws Exception { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; UserDo userDo = new UserDo(); try { //加载数据库驱动 Class.forName(dataSource.getDriverClassName()); //获取数据库连接 connection = (Connection) DriverManager.getConnection(dataSource.getUrl(),dataSource.getUsername(),dataSource.getPassword()); //sql预处理 String sql = "select * from user where id = ?"; preparedStatement = (PreparedStatement) connection.prepareStatement(sql); //参数设置 preparedStatement.setString(1,id); //执行sql resultSet = preparedStatement.executeQuery(); //循环结果集 while (resultSet.next()){ userDo.setId(resultSet.getInt("id")); userDo.setUsername(resultSet.getString("username")); userDo.setAddress(resultSet.getString("address")); userDo.setSex(resultSet.getString("sex")); userDo.setBirthday(resultSet.getDate("birthday")); log.info("查询到用户信息,id=【{}】,username=【{}】", resultSet.getString("id"), resultSet.getString("username")); } }catch (Exception e){ e.printStackTrace(); }finally { //释放资源 if (resultSet != null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (preparedStatement != null){ try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return userDo; } }
service:
public class UserServiceImpl implements UserService { private UserDao userDao; @Override public UserDo findUserById(String id) throws Exception{ return userDao.findUserById(id); } public UserServiceImpl(UserDao userDao){ this.userDao = userDao; } }
apis:
@Slf4j public class UserApis { private UserService userService; public UserApis(UserService userService){ this.userService = userService; } public void findUserById(String id) throws Exception{ UserDo userDo = userService.findUserById(id); log.info("=================={}=================", userDo); } }
V1版本测试类:
@Test public void writeFrameV1() throws Exception { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://*********:3306/ins_buying_0"); dataSource.setUsername("*******"); dataSource.setPassword("********"); UserApis userApis = new UserApis(new UserServiceImpl(new UserDaoImpl(dataSource))); userApis.findUserById("2"); }
可以看到,直接将datasource注入到了UserDao,然后将UserDao注入到了UserService,将Userservice注入到了Userapis,最终完成调用,并在UserDao中使用datasource做了数据库查询操作。
2、V2版本(加载配置文件)
在写之前,先梳理一下思路及应该有哪些对象:
(1)应该有个spring的配置文件,配置文件中有bean标签,标签内有id、name、class、initMethod等属性,同时可能存在属性对应的property标签(有可能多个),那么这里就使用一个BeanDefinition对象来封装每一个Bean的配置信息
(2)property标签中存在name、ref、value属性,其中value属性表示的是简单类型,ref表示的是引用类型,这里我们创建一个PropertyValue对象,来封装property标签,同时使用TypeStringValue来封装简单类型,使用RuntimeBeanRefeerance来封装引用类型
(3)对于加载流程,就是加载spring配置文件进行解析,将所有的配置信息放入一个map集合中
(4)对于执行流程,我们使用一个单例map存储单例的bean,保证单例bean只有一个,因此使用bean时,从单例map中获取,如果获取不到,就创建bean,如果创建成功,且是单例bean,则将bean放入单例map中
(5)最重要的就是创建bean的步骤了,首先根据beanName从BeanDefinition中获取配置信息,然后使用反射获取类对象,然后循环property集合,对集合中的每一个属性进行赋值操作。
总体流程如上所示,那么接下来就一步步实现:
(1)创建配置文件及BeanDefinition对象
<beans> <bean id="userService" class="com.lcl.galaxy.spring.frame.service.UserServiceImpl"> <property name="userDao" ref="userDao"/> </bean> <bean id="userDao" class="com.lcl.galaxy.spring.frame.dao.UserDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://******:3306/ins_buying_0"/> <property name="username" value="*******"/> <property name="password" value="******"/> </bean> </beans>
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; import java.util.ArrayList; import java.util.List; @Data public class MyBeanDefinition { private String clazzName; private String scope; private String beanName; private String initMethod; private List<MyPropertyValue> propertyValueList = new ArrayList<>(); private final String SCOPE_SINGLETON = "singleton"; private final String SCOPE_PROTOTYPE = "prototype"; public MyBeanDefinition(String clazzName, String beanName) { this.clazzName = clazzName; this.beanName = beanName; } public boolean isSingleton(){ if(SCOPE_SINGLETON.equals(scope)){ return true; } return false; } public boolean isPrototype(){ if(SCOPE_PROTOTYPE.equals(scope)){ return true; } return false; } public void addPropertyValue(MyPropertyValue pv) { propertyValueList.add(pv); } }
(2)property标签中存在name、ref、value属性,其中value属性表示的是简单类型,ref表示的是引用类型,这里我们创建一个PropertyValue对象,来封装property标签,同时使用TypeStringValue来封装简单类型,使用RuntimeBeanRefeerance来封装引用类型
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; @Data public class MyPropertyValue { private String name; private Object typedStringValue; public MyPropertyValue(String name, Object typedStringValue) { this.name = name; this.typedStringValue = typedStringValue; } }
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; @Data public class MyTypedStringValue { private String value; private Class<?> targetType; public MyTypedStringValue(String value) { this.value = value; } }
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; @Data public class MyRuntimeBeanReference { private String ref; public MyRuntimeBeanReference(String ref) { this.ref = ref; } }
(3)对于加载流程,就是加载spring配置文件进行解析,将所有的配置信息放入一个map集合中
/** * 加载配置文件 */ public void init() { String location = "write-frame-beans.xml"; InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location); Document document = createDocument(inputStream); registerBeanDefinition(document.getRootElement()); } /** * 输入流传唤为Document * @param inputStream * @return */ private Document createDocument(InputStream inputStream) { Document document = null; SAXReader saxReader = new SAXReader(); try { document = saxReader.read(inputStream); } catch (Exception e) { e.printStackTrace(); } return document; } /** * 记载配置文件 * @param rootElement */ private void registerBeanDefinition(Element rootElement) { List<Element> elements = rootElement.elements(); for (Element element : elements) { String name = element.getName(); if ("bean".equals(name)) { parseDefaultElement(element); } else { parseCustomElement(element); } } } /** * 解析自定义标签 * @param element */ private void parseCustomElement(Element element) { } /** * 解析bean标签,封装为Bedefinition,并将BeanDefinition放入map中 * @param beanElement */ private void parseDefaultElement(Element beanElement) { if (beanElement == null) { return; } String id = beanElement.attributeValue("id"); String name = beanElement.attributeValue("name"); String clazzName = beanElement.attributeValue("class"); if (clazzName == null || "".equals(clazzName)) { return; } String initMethod = beanElement.attributeValue("initMethod"); String scope = beanElement.attributeValue("scope"); scope = scope != null ? scope : "singleton"; String beanName = id == null ? name : id; Class<?> clazzType = null; try { clazzType = Class.forName(clazzName); beanName = beanName == null ? clazzType.getName() : beanName; MyBeanDefinition beanDefinition = new MyBeanDefinition(clazzName, beanName); beanDefinition.setInitMethod(initMethod); beanDefinition.setScope(scope); List<Element> propertyElements = beanElement.elements(); for (Element propertyElement : propertyElements) { parsePropertyElement(beanDefinition, propertyElement); } beanDefinitions.put(beanName, beanDefinition); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
对property集合赋值
/** * 为BeanDefinition的property集合赋值 * @param beanDefinition * @param propertyElement */ private void parsePropertyElement(MyBeanDefinition beanDefinition, Element propertyElement) { String name = propertyElement.attributeValue("name"); String value = propertyElement.attributeValue("value"); String ref = propertyElement.attributeValue("ref"); if(value != null && !"".equals(value) && ref != null && !"".equals(ref)){ return; } MyPropertyValue pv = null; if(value != null && !"".equals(value)){ MyTypedStringValue typedStringValue = new MyTypedStringValue(value); Class<?> targetType = getTypeFieldName(beanDefinition.getClazzName(), name); typedStringValue.setTargetType(targetType); pv = new MyPropertyValue(name, typedStringValue); beanDefinition.addPropertyValue(pv); }else{ MyRuntimeBeanReference runtimeBeanReference = new MyRuntimeBeanReference(ref); pv = new MyPropertyValue(name, runtimeBeanReference); beanDefinition.addPropertyValue(pv); } } /** * 获取简单类型 * @param clazzName * @param name * @return */ private Class<?> getTypeFieldName(String clazzName, String name) { try { Class<?> clazz = Class.forName(clazzName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { log.info("================== clazzName,name ====================,{}==========,{}",clazzName, name); e.printStackTrace(); } return null; }
(4)对于执行流程,我们使用一个单例map存储单例的bean,保证单例bean只有一个,因此使用bean时,从单例map中获取,如果获取不到,就创建bean,如果创建成功,且是单例bean,则将bean放入单例map中
public void findUserById(String id) throws Exception { UserService userService = (UserService) getBean("userService"); UserDo userDo = userService.findUserById(id); log.info("=================={}=================", userDo); } /** * 根据bean名称获取bean对象 * @param beanName * @return */ private Object getBean(String beanName) { Object object = singletonObjects.get(beanName); if (object != null) { return object; } MyBeanDefinition beanDefinition = beanDefinitions.get(beanName); if (beanDefinition == null || beanDefinition.getClazzName() == null) { return null; } if (beanDefinition.isSingleton()) { object = createBean(beanDefinition); this.singletonObjects.put(beanName, object); } else if (beanDefinition.isPrototype()) { object = createBean(beanDefinition); } return object; }
(5)最重要的就是创建bean的步骤了,首先根据beanName从BeanDefinition中获取配置信息,然后使用反射获取类对象,然后循环property集合,对集合中的每一个属性进行赋值操作。
/** * 创建bean * @param beanDefinition * @return */ private Object createBean(MyBeanDefinition beanDefinition) { String clazzName = beanDefinition.getClazzName(); try { Class<?> clazz = Class.forName(clazzName); if(clazz == null){ return null; } Constructor<?> constructor = clazz.getConstructor(); Object object = constructor.newInstance(); populateBean(object, beanDefinition); initMethod(); return object; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 处理初始化方法 */ private void initMethod() { } /** * 为对象赋值 * @param object * @param beanDefinition */ private void populateBean(Object object, MyBeanDefinition beanDefinition) { List<MyPropertyValue> propertyValueList = beanDefinition.getPropertyValueList(); for (MyPropertyValue propertyValue: propertyValueList) { String name = propertyValue.getName(); Object value = propertyValue.getTypedStringValue(); Object valueToUse = null; if(value instanceof MyTypedStringValue){ MyTypedStringValue myTypedStringValue = (MyTypedStringValue) value; String stringValue = myTypedStringValue.getValue(); Class<?> targetType = myTypedStringValue.getTargetType(); if(targetType == Integer.class){ valueToUse = Integer.parseInt(stringValue); }else if(targetType == String.class){ valueToUse = String.valueOf(stringValue); }else{ } }else if(value instanceof MyRuntimeBeanReference){ MyRuntimeBeanReference myRuntimeBeanReference = (MyRuntimeBeanReference) value; valueToUse = getBean(myRuntimeBeanReference.getRef()); } Class<?> clazz = object.getClass(); try { Field declaredField = clazz.getDeclaredField(name); declaredField.setAccessible(true); declaredField.set(object, valueToUse); } catch (Exception e) { e.printStackTrace(); } } }
可以看到,对于简单类型的属性,直接进行了赋值操作,对于引用类型,则使用了递归调用创建Bean对象,然后进行赋值操作
3、V3版本(仿照Spring源码类体系结构)
由于V3版本是借鉴SpringIOC源码编写,那先来看一下Spring IOC中的一些核心概念及类的继承体系。
基础概念及对比
Spring容器:Spring容器就是IOC容器,底层是一个BeanFactory(使用简单工厂实现),spring容器分为基础容器(BeanFactory)和高级容器(ApplicationContext)
BeanFactory和ApplicationContext的区别:(1)ApplicationContext是继承自BeanFactory接口;(2)BeanFactory创建Bean实例是在第一次获取该Bean的实例时创建;(3)ApplicationContext创建Bean实例是在初始化的时候,一次性创建所有的单例bean实例。
BeanPostProcessor和BeanFactoryPostProcessor的区别:(1)BeanFactoryPostProcessor是在创建Bean对象前,对Bean的封装信息Beandefinition对象做处理;例如context:property标签,可以加载指定路径的properties文件,同时将文件中的key-value放入容器;也可将BeanDefinition中的${}占位符进行替换(2)BeanPostProcessor是在对象实例化后,对Bean对象做后置处理。例如:AOP
BeanFactory和FactoryBean的区别:(1)BeanFactory是Spring的基础容器,管理着spring中需要管理的所有Bean。(2)FactoryBean只是spring容器管理的bean的一员,只不过该bean对象比较特殊,可以产生一些指定类型的bean。
继承体系
如上图所示,是BeanFactory的类体系结构(这里没有包含AppllicationContext体系)可以将其描述为四级接口继承体系。
针对以上接口的大致内容描述如下:
接口级别 | 描述 | BeanFactory接口 | 作用 |
一级接口 | Beanfactory作为主接口,不继承任何接口,可以称为一级接口 | BeanFactory | 是Spring Bean的根接口,提供获取bean、是否包含bean、是否单例、是否原型、获取bean类型获取bean等功能接口 |
二级接口 | AutowireCapableBeanFactory、HierarchicalBeanFactory、ListableBeanFactory三个子接口继承了BeanFactory,可以称为二级接口 | AutowireCapableBeanFactory | 提供工厂的装配功能 |
ListableBeanFactory | 提供容器内bean实例的枚举功能,这边不会考虑父容器内的实例 | ||
HierarchicalBeanFactory | 提供父容器的访问功能 | ||
三级接口 | ConfigurableBeanFactory继承了二级接口HierarchicalBeanFactory,同时和继承了SingletonBeanRegistry接口 | ConfigurableBeanFactory | 提供factory的配置功能 |
四级接口 | ConfigurableListableBeanFactory是一个更强大的接口,继承了以上所有的接口。 | ConfigurableListAbleBeanFactory | 集大成者,提供解析bean、修改bean定义、初始化单例等功能 |
根据上述内容,可以暂定一下手写Ioc框架的类划分
1、加载主入口&调用主入口
2、BeanFactory体系:用于加载Bendefinition信息和创建Bean对象
3、reader:用于读取xml文件或Document文件
4、registory:用于注册BeanDefinition信息和单例Bean信息
5、utils:用于处理Dodument对象或通过反射操作一些内容
6、resource:加载配置文件
7、aware:实现了aware接口的类,就可以获取BeanFactory对象
其中utils和resource属于工具类范畴,在最后会附上代码。
其实逻辑代码跟V2是一样的,主要是对接口、类、方法进行了封装,最主要的就是BeanFactory体系,仿照Spring源码中的BeanFactory体系,我也创建了一些体系,在说BeanFactory之前,先说一下registory的接口和类:
registory
我在代码中提供了两个registory接口MyBeanDefinitonRegistory和MySingletonRegistory,分别用来注册BeanDefinition和单例Bean对象
其中MyBeanDefinitionRegistory接口提供了BeanDefinition注册、根据beanName获取BeanDefinition对象两个方法
public interface MyBeanDefinitionRegisty { void registry(String beanName, MyBeanDefinition beanDefinition); MyBeanDefinition getBeanDefinition(String beanName); }
MySingletonRegistory提供了根据beanName获取Bean实例对象和添加Bean实例对象两个方法
package com.lcl.galaxy.spring.frame.V3.register; public interface MySingletonRegistry { Object getSingletonBean(String beanName); void addSingleton(String beanName, Object object); }
为MySingletonRegistor提供了一个默认实现类MyDefaultSingletonBeanFactory,用来向单例Bean的map集合中添加单例Bean或获取单例Bean
public class MyDefaultSingletonBeanRegistory implements MySingletonRegistry { private Map<String, Object> singletonObjects = new HashMap<>(); @Override public Object getSingletonBean(String beanName) { return singletonObjects.get(beanName); } @Override public void addSingleton(String beanName, Object object) { singletonObjects.put(beanName, object); } }
BeanFactory
说完registry,然后重点说一下BeanFactory类,仿照Spring源码中的BeanFactory体系创建的BeanFactory体系如下:
首先,创建了一个根接口MyBeanFactory,该接口只提供根据BeanName获取Bean对象
public interface MyBeanFactory { Object getBean(String beanName); }
创建了两个二级接口MyAutowireCapableBeanFactory和MyListableBeanFactory,其中MyAutowireCapableBeanFactory提供了创建bean对象的方法,而MyListableBeanFactory则提供了根据指定类型获取所有bean名称集合方法和根据类型所有Bean对象集合方法
public interface MyAutowireCapableBeanFactory extends MyBeanFactory { Object createBean(String beanName, MyBeanDefinition myBeanDefinition); }
public interface MyListableBeanFactory extends MyBeanFactory { List<String> getBeanNamesByType(Class<?> type); <T> List<T> getBeansByType(Class<?> type); }
然后创建接口的实现类,首先创建一个顶层的抽象类MyAbstructBeanFactory,其实现MyListableBeanFactory接口,同时集成上述的MyDefualtSingletonRegistory类,在该类中去实现getBean方法。
在该类的getBean方法中,主要做了四步:单例bean是否已存在、获取bean对应的BeanDefinition对象、根据BeanDefinition创建Bean实例、将Bean实例放入单例Bean的map中
由于该类继承了MyDefualtSingletonRegistory,因此也拥有了单例Bean的注册和获取方法,因此对于上面说的四步中,第一步和第四步直接可以调用父类的方法完成操作。
那么在该方法中,还有第二步获取Bendefiniton对象和创建Bean实例两个步骤,具体如何创建,交由子类处理,此处只使用抽象的方法做了封装。(从这里可以看到,具体每个类做哪些事情,还是划分的比较清楚的)
public abstract class MyAbstractBeanFactory extends MyDefaultSingletonBeanRegistory implements MyListableBeanFactory { @Override public Object getBean(String beanName) { Object bean = getSingletonBean(beanName); if(bean != null){ return bean; } MyBeanDefinition beanDefinition = getBeanDefinition(beanName); if(beanDefinition == null){ return null; } if(beanDefinition.isSingleton()){ bean = createBean(beanName, beanDefinition); addSingleton(beanName, beanDefinition); }else if(beanDefinition.isPrototype()){ bean = createBean(beanName, beanDefinition); } return bean; } public abstract Object createBean(String beanName, MyBeanDefinition beanDefinition); public abstract MyBeanDefinition getBeanDefinition(String beanName); }
然后创建负责创建对象的BeanFactory:MyAbstructAutowireCapableBeanFactory,该类实现MyAutowireCapableBeanFactory接口,同时集成上一步的MyAbstructBeanFactory类,由于MyAbstructBeanFactory中有抽象的创建bean实例的方法,而MyAutowireCapableBeanFactory接口中也存在该方法,因此在MyAbstructAutowireCapableBeanFactory中实现该方法,具体实现内容其实和V2版本一致,具体就不再描述了,直接上代码:
public abstract class MyAbstructAutowireCapableBeanFactory extends MyAbstractBeanFactory implements MyAutowireCapableBeanFactory { @Override public Object createBean(String beanName, MyBeanDefinition beanDefinition) { Class<?> clazz = getResolvedClass(beanDefinition); Object object = createInstance(clazz); populateBean(object, beanDefinition); initalBean(object, beanDefinition); return object; } /** * bean的初始化 * @param object * @param beanDefinition */ private void initalBean(Object object, MyBeanDefinition beanDefinition) { //aware接口处理 if(object instanceof MyAware){ if(object instanceof MyBeanFactoryAware){ ((MyBeanFactoryAware)object).setBeanFactory(this); } } //对实现了IniallizingBean接口的类,调用他的afterProperties方法 //如果Bean中init-method属性有值,则调用指定的方法 initMethod(object, beanDefinition); } private void initMethod(Object object, MyBeanDefinition beanDefinition) { String method = beanDefinition.getInitMethod(); if(method != null && !"".equals(method)){ ReflectUtils.invokeMethod(object, method); } } private Object createInstance(Class<?> clazz){ //如果有实例工厂,则通过实例工厂创建Bean实例 //如果有工厂方法,则通过工厂方法创建Bean实例 //如果都没有,则使用构造函数创建Bean实例 return ReflectUtils.createObject(clazz); } private Class<?> getResolvedClass(MyBeanDefinition beanDefinition){ String clazzName = beanDefinition.getClazzName(); try { Class<?> clazz = Class.forName(clazzName); return clazz; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 为对象赋值 * @param object * @param beanDefinition */ private void populateBean(Object object, MyBeanDefinition beanDefinition) { List<MyPropertyValue> propertyValueList = beanDefinition.getPropertyValueList(); for (MyPropertyValue propertyValue: propertyValueList) { String name = propertyValue.getName(); Object value = propertyValue.getTypedStringValue(); Object valueToUse = null; if(value instanceof MyTypedStringValue){ MyTypedStringValue myTypedStringValue = (MyTypedStringValue) value; String stringValue = myTypedStringValue.getValue(); Class<?> targetType = myTypedStringValue.getTargetType(); if(targetType == Integer.class){ valueToUse = Integer.parseInt(stringValue); }else if(targetType == String.class){ valueToUse = String.valueOf(stringValue); }else{ } }else if(value instanceof MyRuntimeBeanReference){ MyRuntimeBeanReference myRuntimeBeanReference = (MyRuntimeBeanReference) value; valueToUse = getBean(myRuntimeBeanReference.getRef()); } ReflectUtils.setProperty(object, name, valueToUse); } } }
最后一个创建的就是功能的集大成者:MyDefaultListableBeanFactory,其实现MyBeanDefinitionRegistory接口,同时集成上一个类MyAbstructAutowireCapableBeanFactory,由于继承了MyBeanDefinitionRegistory接口,因此就需要实现对应的Beandefinition的注册和获取方法。
public class MyDefaultListableBeanFactory extends MyAbstructAutowireCapableBeanFactory implements MyBeanDefinitionRegisty { private Map<String, MyBeanDefinition> beanDefinitions = new HashedMap(); @Override public void registry(String beanName, MyBeanDefinition beanDefinition) { beanDefinitions.put(beanName, beanDefinition); } @Override public MyBeanDefinition getBeanDefinition(String beanName) { return beanDefinitions.get(beanName); } @Override public List<String> getBeanNamesByType(Class<?> type) { return null; } @Override public <T> List<T> getBeansByType(Class<?> clazz) { List<T> list = new ArrayList<>(beanDefinitions.size()); for(MyBeanDefinition beanDefinition : beanDefinitions.values()){ String clazzName = beanDefinition.getClazzName(); Class<?> type = resolveClassName(clazzName); if(clazz.isAssignableFrom(type)){ Object bean = getBean(beanDefinition.getBeanName()); list.add((T) bean); } } return list; } private Class<?> resolveClassName(String clazzName) { try { return Class.forName(clazzName); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
整体码完。。。。。。。
附录、工具类代码附录
接下来,附上对应的工具类内容
1、主入口:
@Slf4j @Data public class SpringIocFrame { public MyDefaultListableBeanFactory init(){ String location = "write-frame-beans.xml"; //加载配置文件 MyResources resources = new MyClassPathResource(location); //创建BeanFactory MyDefaultListableBeanFactory beanFactory = new MyDefaultListableBeanFactory(); // MyXmlBeanDefinitionReader reader = new MyXmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions(resources); return beanFactory; } public void findUserById(String id, MyDefaultListableBeanFactory beanFactory) throws Exception { UserService userService = (UserService) beanFactory.getBean("userService"); UserDo userDo = userService.findUserById(id); log.info("=================={}=================", userDo); } }
2、reader:
package com.lcl.galaxy.spring.frame.V3.reader; import com.lcl.galaxy.spring.frame.V2.domain.MyBeanDefinition; import com.lcl.galaxy.spring.frame.V2.domain.MyPropertyValue; import com.lcl.galaxy.spring.frame.V2.domain.MyRuntimeBeanReference; import com.lcl.galaxy.spring.frame.V2.domain.MyTypedStringValue; import com.lcl.galaxy.spring.frame.V3.register.MyBeanDefinitionRegisty; import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; import java.lang.reflect.Field; import java.util.List; @Slf4j public class MyXmlBeanDefinitionDocumentReader { private MyBeanDefinitionRegisty registy; public MyXmlBeanDefinitionDocumentReader(MyBeanDefinitionRegisty registy) { super(); this.registy = registy; } /** * 按照spring标签语义进行解析 * @param rootElement */ public void loadBeanDefinitions(Element rootElement) { List<Element> elements = rootElement.elements(); for (Element element : elements) { String name = element.getName(); if ("bean".equals(name)) { parseDefaultElement(element); } else { parseCustomElement(element); } } } /** * 解析自定义标签 * @param element */ private void parseCustomElement(Element element) { } /** * 解析bean标签,封装为Bedefinition,并将BeanDefinition放入map中 * @param beanElement */ private void parseDefaultElement(Element beanElement) { if (beanElement == null) { return; } String id = beanElement.attributeValue("id"); String name = beanElement.attributeValue("name"); String clazzName = beanElement.attributeValue("class"); if (clazzName == null || "".equals(clazzName)) { return; } String initMethod = beanElement.attributeValue("initMethod"); String scope = beanElement.attributeValue("scope"); scope = scope != null ? scope : "singleton"; String beanName = id == null ? name : id; Class<?> clazzType = null; try { clazzType = Class.forName(clazzName); beanName = beanName == null ? clazzType.getName() : beanName; MyBeanDefinition beanDefinition = new MyBeanDefinition(clazzName, beanName); beanDefinition.setInitMethod(initMethod); beanDefinition.setScope(scope); List<Element> propertyElements = beanElement.elements(); for (Element propertyElement : propertyElements) { parsePropertyElement(beanDefinition, propertyElement); } this.registy.registry(beanName, beanDefinition); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 为BeanDefinition的property集合赋值 * @param beanDefinition * @param propertyElement */ private void parsePropertyElement(MyBeanDefinition beanDefinition, Element propertyElement) { String name = propertyElement.attributeValue("name"); String value = propertyElement.attributeValue("value"); String ref = propertyElement.attributeValue("ref"); if(value != null && !"".equals(value) && ref != null && !"".equals(ref)){ return; } MyPropertyValue pv = null; if(value != null && !"".equals(value)){ MyTypedStringValue typedStringValue = new MyTypedStringValue(value); Class<?> targetType = getTypeFieldName(beanDefinition.getClazzName(), name); typedStringValue.setTargetType(targetType); pv = new MyPropertyValue(name, typedStringValue); beanDefinition.addPropertyValue(pv); }else{ MyRuntimeBeanReference runtimeBeanReference = new MyRuntimeBeanReference(ref); pv = new MyPropertyValue(name, runtimeBeanReference); beanDefinition.addPropertyValue(pv); } } /** * 获取简单类型 * @param clazzName * @param name * @return */ private Class<?> getTypeFieldName(String clazzName, String name) { try { Class<?> clazz = Class.forName(clazzName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { log.info("================== clazzName,name ====================,{}==========,{}",clazzName, name); e.printStackTrace(); } return null; } }
package com.lcl.galaxy.spring.frame.V3.reader; import com.lcl.galaxy.spring.frame.V3.register.MyBeanDefinitionRegisty; import com.lcl.galaxy.spring.frame.V3.resource.MyResources; import com.lcl.galaxy.spring.frame.V3.utils.DocumentUtils; import org.dom4j.Document; import java.io.InputStream; public class MyXmlBeanDefinitionReader { private MyBeanDefinitionRegisty registy; public MyXmlBeanDefinitionReader(MyBeanDefinitionRegisty registy) { this.registy = registy; } public void loadBeanDefinitions(MyResources resources) { InputStream inputStream = resources.getResourceAsStream(); Document document = DocumentUtils.readInputStream(inputStream); MyXmlBeanDefinitionDocumentReader xmlBeanDefinitionDocumentReader = new MyXmlBeanDefinitionDocumentReader(registy); xmlBeanDefinitionDocumentReader.loadBeanDefinitions(document.getRootElement()); } }
3、resource
public class MyClassPathResource implements MyResources { private String location; public MyClassPathResource(String location){ this.location = location; } @Override public InputStream getResourceAsStream() { return MyResources.class.getClassLoader().getResourceAsStream(location); } }
public interface MyResources { InputStream getResourceAsStream(); }
4、utils
public class DocumentUtils { public static Document readInputStream(InputStream inputStream) { SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(inputStream); return document; } catch (DocumentException e) { e.printStackTrace(); } return null; } }
public class ReflectUtils { /** * 通过反射获取对象 * @param args * @return */ public static Object createObject(Class<?> clazz, Object... args){ try { Constructor<?> constructor = clazz.getConstructor(); return constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 通过反射设置属性值 * @param object * @param name * @param valueToUse */ public static void setProperty(Object object, String name, Object valueToUse){ Class<?> clazz = object.getClass(); try { Field field = clazz.getDeclaredField(name); field.setAccessible(true); field.set(object, valueToUse); } catch (Exception e) { e.printStackTrace(); } } /** * 通过反射获取字段的类型 * @param beanClassName * @param name * @return */ public static Class<?> getTypeByFieldName(String beanClassName, String name){ try { Class<?> clazz = Class.forName(beanClassName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 通过反射执行init方法 * @param object * @param initMethod */ public static void invokeMethod(Object object, String initMethod){ Class<?> clazz = object.getClass(); try { Method declaredMethod = clazz.getDeclaredMethod(initMethod); declaredMethod.setAccessible(true); declaredMethod.invoke(object); } catch (Exception e) { e.printStackTrace(); } } }
三、总结
对于IoC框架的手写,其实V2版本已经实现了功能,V3版本只是借鉴Spring源码对V2版本进行了梳理,代码逻辑没有变化,只是对类的集成体系、设计模式等做了相应的调整,其实手写完V3版本,就已经大致了解Spring源码中相关类的继承体系,对于啃Spring Ioc的源码有很大的帮助,
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~