Spring自动注入的简单实现
作者:网络转载 发布时间:[ 2017/3/9 10:35:01 ] 推荐标签:Java
本文不是讲解Spring如何使用注解,本文只是通过一个简单的实现,来理解Spring是如何注入一个对象的。
用过Spring的同学都知道,Spring利用注解来实现依赖注入,使得各个类之间的耦合性极大的降低了。但是仅仅是使用,并不能理解到Spring内部是怎么实现的。笔者没有看过Spring的源码。只能从自己的角度来谈谈Spring是怎么实现的。感兴趣的同学可以在看过本文之后,深入的了解Spring.
很多时候,我们都有这样的应用场景。比如DAO层,你会先申明一个接口,比如IUserDao,表示用来处理User的一个接口,然后再写一个实现类UserDaoImpl实现了IUserDao中的方法,然后在上层service层中注入。启动之后Spring将自己扫描自动为我们注入实例化的对象,使得我们不用在意各个对象的生命周期。接下来来聊聊具体是怎么注入的。
假设现在已经有以下的类:
<code>public interface IUserDao {
public void setData(String data);
public String getData();
}
public class UserDaoImpl implements IUserDao{
@Override
public void setData(String data) {
System.out.println("data is : " + data);
}
@Override
public String getData() {
return "just test";
}
}</code>
其中FieldInject是笔者模仿写的一个注解,具体定义如下
<code>@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldInject {
//假设有一些变量用于控制策略
}
</code>
具体关于注解上面的元注解的含义,可以看另外一篇博客。这里不展开说明了。
以上是准备工作了,接下来是讲解真正的初始化方法了。
假设我们现在有一个类的Class对象,那么我们可以根据这个Class对象找到哪些成员变量是加了指定的注解的。代码如下
//下面开始注入
for(int i=0; i<field.length; i++){="" fieldinject="" annotation="field[i].getAnnotation(FieldInject.class);" if(annotation="" !="null){//说明这个成员变量有注解" 获取到成员变量的全限定名="" string="" fullclassname="field[i].getType().getName();" class="" sub="findSubClass(fullClassName);" if(sub="=" null)="" continue;="" 找不到,略过="" lowcase="set" +field[i].getname();="" 采用setter方法幅值,与下面的代码二选一即可="" injectmethod(method,obj,="" sub,="" lowcase.tolowercase());="" 以下是直接幅值="" injectfield(field[i],obj,sub);="" }="" }<="" code=""></field.length;></code>
在这段代码中,笔者查询的注解是自己实现的一个FieldInject注解,注解本身并不影响代码的执行。通过判断是否为空可以得出某个成员变量是否加了指定的注解。如果发现成员变量加了注解,可以为该成员变量注入实例化的对象了。
问题1:怎么知道注入哪个对象?
问题2:怎么注入?
问题2很好解决,如果原来的类中带有setter方法,那么可以使用method.invoke()方法来调用并注入。或者通过field直接注入都可以。那么主要是问题1,怎么找到合适的注入对象。
Spring有多种注入的策略,比如按照装配名称,或者是默认实现了接口或者抽象类的子类实例对象来注入。总之,不同的策略只是选择的不同,我们可以假定使用找到的第一个合适子类的实例对象来注入。
<code> //找到某个类的子类【涉及到Spring的选择策略】
private Class findSubClass(String fullClassName){
try {
Class target = Class.forName(fullClassName);
//不是抽象类,不是接口,那自身好了。
if(!target.isInterface()){
boolean isAbs = Modifier.isAbstract(target.getModifiers());
if(!isAbs) return target;
}
int size = clazzList.size();
for(int i=0; i<size; i++){="" class="" p="clazzList.get(i);" if(p.getsuperclass()="" !="null" &&="" p.getsuperclass().getname().equals(fullclassname))="" return="" p;="" class[]="" inter="p.getInterfaces();" for(class="" c="" :="" inter){="" if(c.getname().equals(fullclassname))="" }="" catch="" (classnotfoundexception="" e)="" {="" todo="" auto-generated="" block="" e.printstacktrace();="" null;="" }<="" code=""></size;></code>
findSubClass是用来找到某个类的合适子类,类似于Spring中根据某种策略来查找,这里使用了比较简单的方法。找到第一个合适的子类即可。这个方法中,做了一些简单的判断,如果这个类本身不是一个抽象类或者不是一个接口,那么这个类是第一个合适的类。如果这个类是一个接口或者一个抽象类,那么在全局扫描的classList中找到合适的类。找到合适的类之后,下一步是一个注入了,笔者采用的是给setter方法注入,如果想直接给成员变量赋值也是非常简单的。只要替换掉方法injectMethod,换成下面两句代码即可。
<code>field[i].setAccessible(true);
field[i].set(target, obj);</code>
injectMethod实现也是比较简单,通过比对Method中的方法,找到合适的setter方法(这里是通过field的名称来判断的),并将实例对象赋值进去即可。以上是一个简单的注入过程的实现。笔者写的比较匆忙,可能有些细节上经不起推敲。但是如果能为迷惑的初学者提供一个思路也是不错的,这份代码我都上传到github上了,如果想下载进行运行的可以移步我的github。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11