目录
十一、手写Spring框架
第一步:创建模块myspring
第二步:准备好要管理的Bean
第三步:准备myspring.xml配置文件
第四步:核心接口实现
第五步:实例化Bean
第六步:给Bean属性赋值
第七步:测试
第八步:打包发布
第十一步:使用myspring框架
十一、手写Spring框架
-
Spring IoC容器的实现原理:工厂模式+解析XML+反射机制。
第一步:创建模块myspring
配置pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion>
<groupId>org.myspringframework</groupId><artifactId>myspring</artifactId><version>1.0.0</version><packaging>jar</packaging>
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><!--使用dom4j解析XML配置文件--><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.2.0</version></dependency></dependencies>
<properties><maven.compiler.source>20</maven.compiler.source><maven.compiler.target>20</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties>
</project>
第二步:准备好要管理的Bean
-
这些Bean在将来开发完框架之后是要删除的。
User
package com.hhb.myspring.bean;
public class User {private String name;private int age;
public void setName(String name) {this.name = name;}
public void setAge(int age) {this.age = age;}
@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}
UserDao
package com.hhb.myspring.bean;
public class UserDao {public void insert() {System.out.println("保存用户信息");}
}
UserService
package com.hhb.myspring.bean;
public class UserService {private UserDao userDao;
public void setUserDao(UserDao userDao) {this.userDao = userDao;}
public void save() {userDao.insert();}
}
第三步:准备myspring.xml配置文件
-
将来在框架开发完毕之后,这个文件也是要删除的。
-
文件放在类的根路径下。
myspring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="user" class="com.hhb.myspring.bean.User"><property name="name" value="张三"/><property name="age" value="23"/></bean>
<bean id="userDaoBean" class="com.hhb.myspring.bean.UserDao"/>
<bean id="userService" class="com.hhb.myspring.bean.UserService"><property name="userDao" ref="userDaoBean"/></bean>
</beans>
-
使用value给简单属性赋值,使用ref给非简单属性赋值。
第四步:核心接口实现
ApplicationContext
package org.myspringframework.core;
/*** MySpring框架应用上下文接口。*/
public interface ApplicationContext {/*** 根据bean的名称获取对象的bean对象** @param beanName myspring配置文件中bean标签的id* @return 对应的单例bean对象*/Object getBean(String beanName);
}
ClassPathXmlApplicationContext
package org.myspringframework.core;
import java.util.HashMap;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext {
private Map<String, Object> singletonObjects = new HashMap<>();
/*** 解析myspring的配置文件,然后初始化所有的Bean对象** @param configLocation spring配置文件的路径*/public ClassPathXmlApplicationContext(String configLocation) {//解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
}
@Overridepublic Object getBean(String beanName) {return singletonObjects.get(beanName);}
}
第五步:实例化Bean
ClassPathXmlApplicationContext
package org.myspringframework.core;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext {
private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);
private Map<String, Object> singletonObjects = new HashMap<>();
/*** 解析myspring的配置文件,然后初始化所有的Bean对象** @param configLocation spring配置文件的路径*/public ClassPathXmlApplicationContext(String configLocation) {try {// 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中。// 这是dom4j解析XML文件的核心对象。SAXReader reader = new SAXReader();// 获取一个输入流,指向配置文件InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);// 读文件Document document = reader.read(in);// 获取所有的bean标签List<Node> nodes = document.selectNodes("//bean");// 遍历bean标签nodes.forEach(node -> {try {// 向下转型的目的是为了使用Element接口里更加丰富的方法。Element beanElt = (Element) node;// 获取id属性String id = beanElt.attributeValue("id");// 获取class属性String className = beanElt.attributeValue("class");logger.info("beanName=" + id);logger.info("beanClassName=" + className);//通过反射机制创建对象,将其放到Map集合中,提前曝光//获取ClassClass<?> aClass = Class.forName(className);//获取无参数构造方法Constructor<?> defaultCon = aClass.getDeclaredConstructor();//调用无参数构造方法实例化BeanObject bean = defaultCon.newInstance();//将Bean曝光,加入到Map集合singletonObjects.put(id, bean);//记录日志logger.info(singletonObjects.toString());} catch (Exception e) {e.printStackTrace();}});} catch (DocumentException e) {e.printStackTrace();}}
@Overridepublic Object getBean(String beanName) {return singletonObjects.get(beanName);}
}
第六步:给Bean属性赋值
package org.myspringframework.core;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*** @author 动力节点* @version 1.0* @className ClassPathXmlApplicationContext* @since 1.0**/
public class ClassPathXmlApplicationContext implements ApplicationContext{
private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);
private Map<String, Object> singletonObjects = new HashMap<>();
/*** 解析myspring的配置文件,然后初始化所有的Bean对象。* @param configLocation spring配置文件的路径。注意:使用ClassPathXmlApplicationContext,配置文件应当放到类路径下。*/public ClassPathXmlApplicationContext(String configLocation) {try {// 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中。// 这是dom4j解析XML文件的核心对象。SAXReader reader = new SAXReader();// 获取一个输入流,指向配置文件InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);// 读文件Document document = reader.read(in);// 获取所有的bean标签List<Node> nodes = document.selectNodes("//bean");// 遍历bean标签nodes.forEach(node -> {try {// 向下转型的目的是为了使用Element接口里更加丰富的方法。Element beanElt = (Element) node;// 获取id属性String id = beanElt.attributeValue("id");// 获取class属性String className = beanElt.attributeValue("class");logger.info("beanName=" + id);logger.info("beanClassName="+className);// 通过反射机制创建对象,将其放到Map集合中,提前曝光。// 获取ClassClass<?> aClass = Class.forName(className);// 获取无参数构造方法Constructor<?> defaultCon = aClass.getDeclaredConstructor();// 调用无参数构造方法实例化BeanObject bean = defaultCon.newInstance();// 将Bean曝光,加入Map集合singletonObjects.put(id, bean);// 记录日志logger.info(singletonObjects.toString());} catch (Exception e) {e.printStackTrace();}});
// 再次重新把所有的bean标签遍历一次,这一次主要是给对象的属性赋值。nodes.forEach(node -> {try {Element beanElt = (Element) node;// 获取idString id = beanElt.attributeValue("id");// 获取classNameString className = beanElt.attributeValue("class");// 获取ClassClass<?> aClass = Class.forName(className);// 获取该bean标签下所有的属性property标签List<Element> propertys = beanElt.elements("property");// 遍历所有的属性标签propertys.forEach(property -> {try {// 获取属性名String propertyName = property.attributeValue("name");// 获取属性类型Field field = aClass.getDeclaredField(propertyName);logger.info("属性名:" + propertyName);// 获取set方法名String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);// 获取set方法Method setMethod = aClass.getDeclaredMethod(setMethodName, field.getType());// 获取具体的值String value = property.attributeValue("value"); // "30"Object actualValue = null; // 真值String ref = property.attributeValue("ref");if (value != null) {// 说明这个值是简单类型// 调用set方法(set方法没有返回值)// 我们myspring框架声明一下:我们只支持这些类型为简单类型// byte short int long float double boolean char// Byte Short Integer Long Float Double Boolean Character// String// 获取属性类型名String propertyTypeSimpleName = field.getType().getSimpleName();switch (propertyTypeSimpleName) {case "byte":actualValue = Byte.parseByte(value);break;case "short":actualValue = Short.parseShort(value);break;case "int":actualValue = Integer.parseInt(value);break;case "long":actualValue = Long.parseLong(value);break;case "float":actualValue = Float.parseFloat(value);break;case "double":actualValue = Double.parseDouble(value);break;case "boolean":actualValue = Boolean.parseBoolean(value);break;case "char":actualValue = value.charAt(0);break;case "Byte":actualValue = Byte.valueOf(value);break;case "Short":actualValue = Short.valueOf(value);break;case "Integer":actualValue = Integer.valueOf(value);break;case "Long":actualValue = Long.valueOf(value);break;case "Float":actualValue = Float.valueOf(value);break;case "Double":actualValue = Double.valueOf(value);break;case "Boolean":actualValue = Boolean.valueOf(value);break;case "Character":actualValue = Character.valueOf(value.charAt(0));break;case "String":actualValue = value;}
setMethod.invoke(singletonObjects.get(id), actualValue);}if (ref != null) {// 说明这个值是非简单类型// 调用set方法(set方法没有返回值)setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));}} catch (Exception e) {e.printStackTrace();}});} catch (Exception e) {e.printStackTrace();}});} catch (Exception e) {e.printStackTrace();}}
@Overridepublic Object getBean(String beanName) {return singletonObjects.get(beanName);}
}
第七步:测试
@Test
public void test1(){ApplicationContext applicationContext=new ClassPathXmlApplicationContext("myspring.xml");Object user = applicationContext.getBean("user");System.out.println(user);
UserService userService = (UserService) applicationContext.getBean("userService");userService.save();
}
第八步:打包发布
第十一步:使用myspring框架
配置pom.xml
<dependencies><!--用myspring框架,需要引入依赖--><dependency><groupId>org.myspringframework</groupId><artifactId>myspring</artifactId><version>1.0.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency>
</dependencies>