申明:本文不是讲解Spring如何使用注解,本文只是经过一个简单的实现,来理解Spring是如何注入一个对象的。html
用过Spring的同窗都知道,Spring利用注解来实现依赖注入,使得各个类之间的耦合性极大的下降了。可是仅仅是使用,并不能理解到Spring内部是怎么实现的。笔者没有看过Spring的源码。只能从本身的角度来谈谈Spring是怎么实现的。感兴趣的同窗能够在看过本文以后,深刻的了解Spring.
不少时候,咱们都有这样的应用场景。好比DAO层,你会先申明一个接口,好比IUserDao,表示用来处理User的一个接口,而后再写一个实现类UserDaoImpl实现了IUserDao中的方法,而后在上层service层中注入。启动以后Spring将本身扫描自动为咱们注入实例化的对象,使得咱们不用在乎各个对象的生命周期。接下来就来聊聊具体是怎么注入的。
假设如今已经有如下的类:git
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"; } }
其中FieldInject是笔者模仿写的一个注解,具体定义以下github
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FieldInject { //假设有一些变量用于控制策略 }
具体关于注解上面的元注解的含义,能够看另一篇博客。这里就不展开说明了。
以上就是准备工做了,接下来就是讲解真正的初始化方法了。
假设咱们如今有一个类的Class对象,那么咱们能够根据这个Class对象找到哪些成员变量是加了指定的注解的。代码以下ide
//下面开始注入 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; //找不到,略过 String lowCase = "set"+field[i].getName(); //采用setter方法幅值,与下面的代码二选一便可 // injectMethod(method,obj, sub, lowCase.toLowerCase()); //如下是直接幅值 injectField(field[i],obj,sub); } }
在这段代码中,笔者查询的注解是本身实现的一个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)) return p; } } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
findSubClass是用来找到某个类的合适子类,相似于Spring中根据某种策略来查找,这里使用了比较简单的方法。找到第一个合适的子类便可。这个方法中,作了一些简单的判断,若是这个类自己就不是一个抽象类或者不是一个接口,那么这个类就是第一个合适的类。若是这个类是一个接口或者一个抽象类,那么就在全局扫描的classList中找到合适的类。找到合适的类以后,下一步就是一个注入了,笔者采用的是给setter方法注入,若是想直接给成员变量赋值也是很是简单的。只要替换掉方法injectMethod,换成下面两句代码便可。htm
field[i].setAccessible(true); field[i].set(target, obj);
injectMethod实现也是比较简单,经过比对Method中的方法,找到合适的setter方法(这里是经过field的名称来判断的),并将实例对象赋值进去便可。以上就是一个简单的注入过程的实现。笔者写的比较匆忙,可能有些细节上经不起推敲。可是若是能为迷惑的初学者提供一个思路也是不错的,这份代码我都上传到github上了,若是想下载进行运行的能够移步个人github。对象