博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从山寨Spring中学习Spring IOC原理-自动装配注解
阅读量:4109 次
发布时间:2019-05-25

本文共 3479 字,大约阅读时间需要 11 分钟。

前言

上一篇博客完成了在xml中的自动注解的过程,本篇将会挑战一个更加有挑战性的功能,也就是Spring中自动注解功能。更多Spring内容进入。

思路

不知道大家有没有注意过,在使用Spring的时候,Spring是如何进行初始化的,换句话说Spring是怎么把交给它管理的类进行 实例化的。这里说下,大致可以分为两种情况:

  1. 基于XML的:这里注册一个<bean>,然后使用ClassPathXmlApplicationContext把整个xml加载进来,根据配置的<bean>完成扫描。
  2. 基于JavaConfig:构建一个配置类,在上面加上扫描范围@ComponentScan,然后使用AnnotationConfigApplicationContext把这个类加载进来,根据包内的类上的注解完成初始化。

但是这里还是有细微的区别的,比如单个类注册在基于Xml的配置中只要<bean>配置了,就一定会被注册和初始化,也就是说Xml中只要声明就一定会被解析和实例化。而在JavaConfig中,不仅要加了注解,还必须被扫描到才可以被注册和初始化,如果没有扫描到,Spring是不认这个注解的。当然也可以基于注解,但是注解的又必须依赖Xml或者JavaConfig去完成类的扫描才行,所以就不讨论这个了。

为什么要引入这个小知识呢?因为既然是基于注解的,那就必须要引入扫描。也就是说我们要手动完成扫描,然后针对扫描出来的包进行实例化。这个小点就给我们提供了一个山寨的思路,第一步就是想办法去扫描到包,并且拿到里面的类名。但是在编译的过程中我们已经无法找到源码的路径,所以我们此时拿到的包都会是classes包下的,因此我们此时能拿到的都是*.class文件,基于此我们把这些文件名找到对应的类(用Calss.forName方法拿出来)并构造成对象,然后再使用。

构建

既然要山寨自动装配注解,那就山寨的完全一些,首先创造一个注解出来,加上运行时有效。更多自定义注解的内容请参考。

@Retention(RetentionPolicy.RUNTIME)public @interface MyAnno {
public String value();}

经过上面的分析,我们就完成了这样的一个类MyAnnoConfigContext,里面有一个scan方法并且在这个里面对发现的类进行实例化,和一个getBean方法用来模拟Spring拿到对应对象的方法。

public class MyAnnoConfigContext {
//用来存储创建的实例 Map
newInstanceMap = new HashMap<>(); public void scan(String packagePath) {
//拿到编译后的跟路径 String rootPath = this.getClass().getResource("/").getPath(); //转化包名为路径 String basePackagePath = packagePath.replaceAll("\\.", "\\\\"); //获取包下所有文件名 File file = new File(rootPath + "//" + basePackagePath); //拿出所有*.class文件的名字 String[] fileNames = file.list(); for (String fileName : fileNames) {
//替换文件名后缀,拿出类名 fileName = fileName.replaceAll(".class", ""); try {
//获取对应名字的类对象,注意这个packagePath和basePackagePath是不一样的 Class clazz = Class.forName(packagePath + "." + fileName); //判断这个类是不是bean类,也就是是不是加了自定义注解 if (clazz.isAnnotationPresent(MyAnno.class)) {
MyAnno anno = (MyAnno) clazz.getAnnotation(MyAnno.class); //把注解的名字和实例对应,存到map里 newInstanceMap.put(anno.value(), clazz.newInstance()); } } catch (Exception e) {
e.printStackTrace(); } } } public Object getBean(String beanName) {
//从map里拿出对应的对象。 for (String key : newInstanceMap.keySet()) {
if (beanName.equals(key)) {
return newInstanceMap.get(key); } } return null; }}

声明一个Service类作为测试,因为我们要传入的是com.demo.service,务必要把Service类建在这个包下,并且加上我们的自定义注解。

@MyAnno("MyAnnoService")public class UserServiceImpl{
}

创建Test类测试,运行看看我们自己的注解有没有被初始化。

public class Test {
public static void main(String[] args) {
//山寨AnnotationConfigApplicationContext类功能 MyAnnoConfigContext myanno=new MyAnnoConfigContext(); myanno.scan("com.demo.service"); System.out.println(myanno.getBean("MyAnnoService")); }}运行结果,正确打印了UserServiceImpl实例对象com.demo.service.UserServiceImpl@4b1210ee

那么也就完成了对Spring这套代码的模拟。

AnnotationConfigApplicationContext annotation=new AnnotationConfigApplicationContext();annotation.scan("xx.xx.xx");annotation.getBean("xxx");

在上面的例子中,如果注销掉@MyAnno("MyAnnoService")则什么都不会取到,说明我们山寨的这一个注解扫描类,确实简单的完成了一个自动注解扫描并注册实例化的过程。

扩展

其实到这里再进一步就是模仿@Autowired,其实原理都是一样的,区别就在于:我们在拿到类名使用clazz.newInstance()构建实例的时候,需要使用Field数组把类中所有的属性都拿出来,然后再次遍历所有的字段找到被@Autowired注解的那些属性,然后找到对应的类型,getType出来,最后再次clazz.newInstance()出来完成实例化。这里已经在有类似的代码,就不占篇幅了,大家可以自己研究下。

转载地址:http://stmsi.baihongyu.com/

你可能感兴趣的文章
线索二叉树
查看>>
外部排序 归并排序
查看>>
POJ 3255 Roadblocks 最短路Dijkstra+堆优化
查看>>
poj 3723 最大生成树
查看>>
poj 2139 Six Degrees of Cowvin Bacon 最短路bellman 多源最短路径 (一次AC)
查看>>
Codeforces Round #329 (Div. 2) A. 2Char 字符串+暴力
查看>>
poj 3259 Wormholes 最短路bellman 题意转化很重要
查看>>
poj 2456 Aggressive cows 整数二分写法 模板题
查看>>
poj 3104 Drying 二分搜索--查找最小yes值
查看>>
poj 3111 K Best 二分搜索 最大化平均值
查看>>
POj 3258 River Hopscotch 二分搜索 最大化最小值
查看>>
poj 2674 Linear world 弹性碰撞 升级的蚂蚁
查看>>
poj 2785 4 Values whose Sum is 0
查看>>
Codeforces Round #324 (Div. 2) A. Olesya and Rodion 构造数字 思维题
查看>>
Codeforces Round #324 (Div. 2) B. Kolya and Tanya 思维题 数论
查看>>
Poj 3977 Subset 折半枚举 超大背包
查看>>
poj 2549 Sumsets 折半枚举
查看>>
poj 3276 Face The Right Way 挑战150 反转
查看>>
poj 3279 Fliptile 反转
查看>>
poj 3185 The Water Bowls 反转(开关)
查看>>