1.手写spring框架
1.1准备工作
package com.powernode.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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.powernode.myspring.bean;
public class UserDao{
public void insert(){
System.out.println("mysql数据库正在保存信息");
}
}
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save(){
userDao.insert();
}
}
1.2核心接口实现
不知道怎么写就看看之前写的项目模块。
package org.myspringframework.core;
//MySpring框架应用上下文接口
public interface ApplicationContext {
/*
* 根据Bean的名称获取对应的bean对象
* @param BeanName myspring配置文件中bean标签 的id
* @return 对应的单例Bean对象
* */
Object getBean(String BeanName);
}
ApplicationContext里面有个getBean,它还能new ClassPathXmlApplicationContext
package org.myspringframework.core;
import java.util.HashMap;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext{
private Map singltonObjects = new HashMap<>();//把bean对象放到这
// 你想,反射机制springDI核心实现时,不是讲了三个缓存吗
// 构造方法来配置spring文件,一执行,解析spring文件,实例化所有bean对象
/*
* 解析myspring的配置文件,再初始化所有Bean对象
* @param configLocation spring配置文件的路径。注意:使用ClassPathXmlApplicationContext,配置文件应放到类路径下
* */
public ClassPathXmlApplicationContext(String configLocation) {
// 解析myspring的配置文件,再初始化所有Bean对象,将Bean存放到singltonObjects集合当中
}
@Override
public Object getBean(String BeanName) {
return singltonObjects.get(BeanName);
}
}
1.3实例化Bean
再往下就得实现ClassPathXmlApplicationContext的构造方法的核心程序了。
对上面代码的类ClassPathXmlApplicationContext的构造方法进行补充
private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);
public ClassPathXmlApplicationContext(String configLocation) {
try {
// 解析myspring的配置文件,再初始化所有Bean对象,将Bean存放到singltonObjects集合当中
// SAXReader是dom4j解析xml文件的核心对象。
SAXReader reader = new SAXReader();
// 获取一个输入流,指向配置文件 类加载器,get系统类加载器,再输入文件config,将其资源变为流
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
// 读文件
Document document = reader.read(in);
// 获取所有的bean标签
List nodes = document.selectNodes("//bean");
// 遍历bean标签
nodes.forEach(node -> {
try {
// System.out.println(node);
// 向下转型的目的是为了使用Element接口更加丰富的方法---
// --为啥说向下。 Element extends Branch;Branch extends Node,类图中,Element在下,Node在上,在下的有更多方法
Element beanEle = (Element) node;
// 获取id属性(beanName)
String id = beanEle.attributeValue("id");
// 获取class属性
String className = beanEle.attributeValue("class");
// logger.info("beanName="+id);
// logger.info("ClassName="+className);
// 通过反射机制创建对象,将其放到Map集合中,提前曝光。
// 获取class
Class> aClass = Class.forName(className);
// 获取无参数构造方法
Constructor> defaultCon = aClass.getDeclaredConstructor();
// 调用无参数构造方法实例化Bean
Object bean = defaultCon.newInstance();
// 将Bean曝光,加入Map集合
singltonObjects.put(id,bean);
// 记录日志
logger.info(singltonObjects.toString());
}catch (Exception e){
e.printStackTrace();
}
});
}catch (Exception e){
e.printStackTrace();
}
}
1.4获取所有set方法
再次把所有bean标签遍历一次,这一次主要是给对象的属性赋值
nodes.forEach(node -> {
try {
Element beanEle = (Element) node;
// 获取id
String id = beanEle.attributeValue("id");
// 获取className
String className = beanEle.attributeValue("class");
// 获取类class
Class> aClass = Class.forName(className);
// 获取该bean标签下 所有的属性property标签
List propertys = beanEle.elements("property");
// 遍历所有的属性标签
propertys.forEach(property -> {
try {
// 获取属性名
String propertyName = property.attributeValue("name");
logger.info("属性名"+propertyName);
// 获取属性类型
Field field = aClass.getDeclaredField(propertyName);
// 获取set方法名
String setMethodName = "set" + propertyName.toUpperCase().charAt(0)+propertyName.substring(1);
// 获取set方法
Method setMethod = aClass.getDeclaredMethod(setMethodName, field.getType());
调用set方法(set方法没有返回值) setMethod.invoke(singltonObjects.get(id),传入什么具体的值?);
1.5给非简单类型属性赋值
非简单的不就是ref=”bean对象id“ , 既然它是beanId,那就是拿个bean对象作属性,我们前面不是把所有实例化bean对象都放到Map中了吗,那我拿到ref不就拿到BeanId,就用id从map中取出不就好了。
获取属性对应的具体的值
String value = property.attributeValue("value");
//既然我不知道它属性对应的值是value还是ref,我就都要
String ref = property.attributeValue("ref");
if(value!=null){
// 说明这个值是简单类型
// 调用set方法(set方法没有返回值)
setMethod.invoke(singltonObjects.get(id),传入什么类型的具体值);
}
if (ref!=null){
// 说明这个值是非简单类型
// 调用set方法(set方法没有返回值)
setMethod.invoke(singltonObjects.get(id),singltonObjects.get(ref));
能进哪个if,就说明它是value 还是 ref
1.6给简单类型属性赋值
简单类型有很多种,不想ref,直接拿个bean对象作为属性。
我们得知道它得简单类型 是 什么类型,,才好传具体值
switch-case太惨了,这么多种。。。