上一篇:(十)Spring之回顾反射机制
下一篇:(十二)Spring IoC注解式开发
Spring IoC容器的实现原理:工厂模式 + 解析XML + 反射机制。
采用Maven方式新建模块:取名MySpring
引入dom4j和jaxen的依赖,因为要使用它解析XML文件,还有junit依赖和log4j2的依赖,方便测试。
log4j2.xml文件放到类路径下。
<!--dem4j依赖-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!--jaxen依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
准备好我们要管理的Bean,这些Bean只是为了方便测试,也就是说这些Bean在将来开发完框架之后是要删除。
创建User类:
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建UserDao类:
public class UserDao {
public void insert(){
System.out.println("正在新增用户信息。。。");
}
}
创建UserDao类:
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save(){
userDao.insert();
}
}
注意这个配置文件也是框架使用者提供,将来开发完框架之后是要删除。
使用value给简单属性赋值。使用ref给非简单属性赋值。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--这个配置文件也是框架使用者提供-->
<bean id="user" class="com.myspring.bean.User">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
<bean id="userDao" class="com.myspring.bean.UserDao">
</bean>
<bean id="userService" class="com.myspring.bean.UserService">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
使用Spring的时候我们第一个要做的是获取Spring容器对象,是一个ApplicationContext接口,所以我们也提供这样一个接口,这个接口将来是要通过getBean方法获取Bean的,所以我们提供这样一个方法。
/**
* MySpring框架应用上下文
*/
public interface MyApplicationContext {
/**
* 根据Bean的名称获取对应的Bean对象
* @param beanName myspring配置文件中bean标签的id
* @return 对应的Bean单例对象
*/
Object getBean(String beanName);
}
使用Spring的时候获取Spring容器对象是通过new ClassPathXmlApplicationContext得到的,这个类ApplicationContext接口的实现类。要加载类路径下的Spring配置文件。
所以我们创建MyClassPathXmlApplicationContext类继承MyApplicationContext接口。
public class MyClassPathXmlApplicationContext implements MyApplicationContext{
@Override
public Object getBean(String beanName) {
return null;
}
}
Spring是提供三级缓存存储不同时期的Bean,我们不用那么麻烦,采用一个Map集合存储Bean实例。Map集合的key存储bean的id,value存储Bean实例。Map
在MyClassPathXmlApplicationContext类中添加Map
并且在MyClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件。
同时实现getBean方法。
public class ClassPathXmlApplicationContext implements ApplicationContext{
/**
* 存储bean的Map集合
*/
private Map<String,Object> singletonObjects = new HashMap<>();
/**
* 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
* @param resource 配置文件路径(要求在类路径当中)
*/
public ClassPathXmlApplicationContext(String configLocation) {
}
@Override
public Object getBean(String beanName) {
return singletonObjects.get(beanName);
}
}
在MyClassPathXmlApplicationContext的构造方法中解析配置文件,获取所有bean的类名,通过反射机制调用无参数构造方法创建Bean。并且将Bean对象存放到Map集合中。
public MyClassPathXmlApplicationContext(String configLocation) {
try {
//1.使用Dom4j创建reader对象
SAXReader reader = new SAXReader();
//2.通过类加载器获取SqlMapper的配置文件输入流
InputStream myspring_stream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
//3.读XML文件,返回一个文档对象document
Document document = reader.read(myspring_stream);
//4.对标签进行处理
//(1)获取所有的bean标签
List<Node> nodes = document.selectNodes("//bean");
//(2)对bean标签进行遍历,创建对象曝光到Map集合中
nodes.forEach(node -> {
try {
//向下转型,因为Element有更多的方法 Element 继承了 Branch 又继承了 Node ,Node方法比较少。
Element beanElt = (Element) node;
//获取Bean的id
String id = beanElt.attributeValue("id");
//获取class属性值
String className = beanElt.attributeValue("class");
logger.info("beanName:" + id);//使用log4j2记录
logger.info("beanClassName:" + className);
//通过反射机制创建对象,将其放到Map集合中
Class<?> aClass = Class.forName(className);
Constructor<?> defaultCon = aClass.getDeclaredConstructor();
Object beanObj = defaultCon.newInstance();
//将bean曝光,加入Map集合
singletonObjects.put(id,beanObj);
logger.info(singletonObjects.toString());
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
通过反射机制调用set方法,给Bean的属性赋值。
继续在MyClassPathXmlApplicationContext构造方法中编写代码。
//(3)再次对bean标签进行遍历,这次主要对bean进行赋值
nodes.forEach(node -> {
try {
//向下转型,因为Element有更多的方法 Element 继承了 Branch 又继承了 Node ,Node方法比较少。
Element beanElt = (Element) node;
//获取Bean的id
String id = beanElt.attributeValue("id");
//获取class属性值
String className = beanElt.attributeValue("class");
//获取Class
Class<?> aClass = Class.forName(className);
//获取该bean标签下所有的属性property标签
List<Element> propertys = beanElt.elements("property");
//遍历property标签
propertys.forEach(property ->{
try {
//获取属性名
String propertyName = property.attributeValue("name");
logger.info(propertyName);
//获取set方法名
String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
//获取属性类型
Field field = aClass.getDeclaredField(propertyName);
//获取set方法
Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType());
//调用set方法
//这里的具体值有可能是value或者ref,需要进行判断
String value = property.attributeValue("value");
String ref = property.attributeValue("ref");
Object actualValue = null;
if (value != null) {//简单类型
//这里比较麻烦,因为获取到的value的是字符串,需要根据属性类型转换成对应的类型
//为了程序简单化,我们定义8种基本类型以及对应的包装类,加上String为简单类型
//首先获取属性类型名的简称
String propertySimpleName = field.getType().getSimpleName();
switch (propertySimpleName){
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;break;
}
setMethod.invoke(singletonObjects.get(id), actualValue);
}
if (ref != null) {//非简单类型
setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
}
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
});
@Test
public void testMySpring(){
MyApplicationContext myApplicationContext = new MyClassPathXmlApplicationContext("myspring.xml");
Object user = myApplicationContext.getBean("user");
System.out.println(user);
UserService userService = ((UserService) myApplicationContext.getBean("userService"));
userService.save();
}