手写模拟Spring IOC 基本原理
①先了解IOC是干嘛的
IOC定义
Spring IoC负责创建对象、管理对象(通过依赖注入(DI)、装配对象、配置对象,并且管理这些对象的整个生命周期。
简单说就是将我们的对象都交由一个容器来管理.那我们为什么要将对象交由对象来管理,我们自己管理有什么问题吗?接下来我们来看看不使用容器管理会有哪些问题
@Test
public void serviceTest() {
System.out.println("我自己创建对象");
UserService service = new UserServiceImpl();
System.out.println(service.userLogin());
}
问题: 代码耦合度高,会出现改一处,处处改的问题
思考: 能否使用工厂模式来解决这个问题?
@Test
public void simpleTest() {
System.out.println("使用了简单工厂");
UserService s = (UserService) ServiceFactory.getInstance("UserService");
System.out.println(s.userLogin());
}
总结: 使用了简单工厂之后确实为我们降低了一些耦合度,但是我们仔细看看其实还有存在问题,或者是一些比较繁琐的操作.
问题: 每次通过工厂获取对象,工厂都需要事先知道key
也就是上述代码的"UserService"
,当我们的业务足够庞大的时候,我们会发现这个东西是有可能写错的,并且IDEA并不会提示我们写错了.而且每次都要强转类型很是繁琐.当然最主要的问题是,如果我们项目已经编译打包之后我们需要去改动业务,那我们还得重新编译打包,这样很不人性化,也就是我们通常说的维护困难.
思考: 以上问题如何解决?
思路: 既然是维护困难,那相信大家在使用数据库的时候其实就已经遇到这个问题的,那就是我们的数据库连接配置,为了方便配置我们采用了配置文件.
总结: 其实Spring的容器也只是帮助我们搭建好了以上的工厂而已,那既然我们知道了原理,我们能否自己来写一个呢?
拓展
- 配置文件类型
- properties
- xml
- json
- yaml
②自己实现Spring容器管理对象
我们模仿mybatis也采用xml来试着模拟Spring的容器
Bean标签步骤解析
-
创建xml文件
-
读取xml文件
-
导入
dom4j
坐标,解析xml文件-
读取bean标签中的
id=UserService
,class=com.achao.service.impl.UserServiceImpl
-
读取到class中的值,利用反射将其实例化
-
将
id
作为key,class
作为值,添加到一个map中
-
-
通过工厂拿对象
A. 创建xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--bean的声明-->
<bean id="UserService" class="com.achao.service.impl.UserServiceImpl"/>
</beans>
B. 读取xml文件
/**
* 解析xml文件,返回一个Document
* @param configName
* @return
*/
private static Document parse(String configName) {
SAXReader reader = new SAXReader();
try {
document = reader.read(configName);
} catch (DocumentException e) {
e.printStackTrace();
}
return document;
}
C. 解析xml文件
/**
* 解析<beans></beans>标签
*/
private static void parsBeansElement() {
List<Node> nodes = document.selectNodes("//beans/bean");
// 遍历拿到所有的bean标签,将标签转换成元素,从元素中获取标签值
System.out.println("nodes集合的长度: " + nodes.size());
for (Node node : nodes) {
Element element = (Element) node;
String id = element.attributeValue("id");
String className = element.attributeValue("class");
map.put(id, getInstance(className, false));
}
}
D. 工厂帮我们拿对象
/**
* 使用泛型封装简单工厂
* @param tClass
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> tClass) {
String className = tClass.getSimpleName();
System.out.println("通过反射获取的类名: "+className);
Object o = map.get(className);
if (o == null) {
System.err.println("工厂没有注册该实现类...");
return null;
} else {
return (T) o;
}
}
Package标签步骤解析
-
读取xml文件(同上)
-
解析xml文件
- 解析,拿到包的路径(“com.achao.service”)
- 根据包路径拿到
io
流读取的文件路径 - 递归文件,如果是文件夹再次调用自己,并且包名加上自己
- 利用反射将注解的值作为key,实例作为value存入map中
-
通过工厂拿对象
A. 创建xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--bean的声明-->
<package name = "com.achao.service"/>
</beans>
B. 读取xml文件
同上
C. 解析xml文件
/**
* 解析<package></package>标签
*/
private static void parsPageElement() {
// 1.读取配置文件
List<Node> nodes = document.selectNodes("//beans/package");
for (Node node : nodes) {
Element element = (Element) node;
// 2.解析,拿到包的路径("com.achao.service")
String packageName = element.attributeValue("name");
// 3.路径转换格式("")
String path = packagePath(packageName);
System.out.println("io流要的路径: " + path);
// 4.根据第二第三步获取的路径和包名,拿到["com.achao.service.AdminService",...]
List<String> classPathFrom = getClassPathFrom(path, packageName);
for (String s : classPathFrom) {
System.out.println(s);
// 5.通过注解和反射,将其一一对应加入map中
getInstance(s, true);
}
}
}
递归拿包
/**
* 获取文件下的所有文件
*
* @param file
* @return
*/
public static void getAllFile(File file, String packageName) {
File[] listFiles = file.listFiles();
for (File listFile : listFiles) {
if (listFile.isDirectory()) {
String s = packageName +"."+ listFile.getName();
getAllFile(listFile, s);
} else {
String name = listFile.getName();
int i = name.lastIndexOf(".");
if (name.substring(i).equals(".java")) {
String rs = packageName + "." + name;
String classPath = rs.substring(0, rs.length() - 5);
classPaths.add(classPath);
}
}
}
}
D. 工厂帮我们拿对象
/**
* 使用泛型封装简单工厂
* @param tClass
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> tClass) {
String className = tClass.getSimpleName();
System.out.println("通过反射获取的类名: "+className);
Object o = map.get(className);
if (o == null) {
System.err.println("工厂没有注册该实现类...");
return null;
} else {
return (T) o;
}
}