模拟实现spring框架的IOC跟AOP功能

来源:互联网 发布:奇商网络 编辑:程序博客网 时间:2024/05/17 09:39

公司老系统没有实现spring框架的功能,只是自己写了个beanfactory里面把需要用的类全部new好,感觉很繁琐,正好自己想要研究下JDK的反射包,以及想研究下Spring,但是想想如果是我自己开发spring会如何,所以按照自己就想写个IOC跟AOP的功能出来。

首先看下我们项目的整体结构:,主要分为三块:AnnotateLeader类为总类接口,实现类为AnnotateResolve,这个类就是解析所有注解的,AnnotateHandle为处理所有对应注解的桥接接口,实现类为HandleImpl,它只负责生成对应注解处理类的实例,然后调用相同的handle方法,AnnotateRealizition类为所有注解实现类的接口,所有注解实现类实现它,并实现里面的realize方法,pojo包里面,就是对应的注解已经它的实现方法。除此之外还有个ScanImpl为扫描包的实现类,ProxyInterface为AOP提供的接口。


我们先看下AnnotateResolve的主要方法;

@Overridepublic  void getAllAnnotate(Class<?> cla) {initClass(cla);Field[] fields = cla.getDeclaredFields();for(Field f:fields){Annotation[] annotations = f.getAnnotations();ResolveAnnotate(annotations,f,cla);}Method[] methods=cla.getDeclaredMethods();for(Method m:methods){Annotation[] methodAnnotation =m.getAnnotations();if(methodAnnotation.length!=0){ResolveAnnotate(methodAnnotation,null,cla);}}
这个方法为使用ScanImpl扫描test包下所有类得到所有类的Class文件之后循环传入的,进入之后首先initClass();看下实现代码
public void initClass(Class<?> cla){Annotation[] annotation = cla.getAnnotations();try {if(annotation.length!=0){String[] split = annotation[0].toString().split("\\.");if(("init()").equals(split[split.length-1])){//String[] split = cla.getName().split(".");//System.out.println(cla.getName());beanList.put(cla.getName(), cla.newInstance());}}} catch (Exception e){// TODO Auto-generated catch blocke.printStackTrace();} }
先获取所有类上的注解,然后注解的toString方法打印的为自定义注解的全路径名,自定义注解这么不在讲述,我们看下测试类上注解格式:@init为我们自定义的注解,上面使用string方法来得到拆分全路径得到名字,如果有这个名字,则直接讲该类的实例放入到一个beanList中,在需要的时候可以直接取,这样就首先完成了初始化类的功能,但是类中的参数对象,如何注入,我们继续看。


在上图的getAllAnnotate方法中ResolveAnnotate方法就是实现所有注解的核心方法,

public void ResolveAnnotate(Annotation[] annotations,Field f,Class<?> cla){try {for(Annotation an:annotations){handle.Handle(ResolveClass( an.annotationType()),f,cla,an);}} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
public Class<? extends AnnotateRealizition> ResolveClass(Class<? extends Annotation>  an) throws ClassNotFoundException{String str=an.toString().split(" ")[1]+"Realiztion";return (Class<? extends AnnotateRealizition>) Class.forName(str);}

在获取所有注解后,注解的toString方法得到的是它的类型+空格加全路径名字,就比如interface com.ioc.demo.test这样。所以分割字符串来得到它的全路径注解名在加上Realiztion得到它的处理类名,通过刚才看整体项目架构可以看到,所有注解的处理类名,都是注解名+Realiztion、这样得到它的全路径的处理类名然后得到它的Class对象,强转成AnnotateRealizition接口类,因为所有实现类都是实现AnnotateRealizition这个接口,你传的是哪个注解对应的实现类,就会生成那对应的实现类的实例,这里就是使用设计模式中的桥接模式,handleImpl中的方法
@Overridepublic void Handle(Class<? extends AnnotateRealizition> anno,Field f,Class<?> cla,Annotation an) {try{AnnotateRealizition newInstance = anno.newInstance();newInstance.Realize(f,cla,an);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}
就是直接拿到对应的实现类直接调用实现类的Realize方法。不同的注解对应各种的处理方法。


我实现IOC使用的注解名为insert注解,直接看insert注解的实现方法

public class insertRealiztion implements AnnotateRealizition{private AnnotateResolve resolve;@Overridepublic void Realize(Field f,Class<?> cla,Annotation an) {try {f.setAccessible(true);Object object =  resolve.getBeanList().get(cla.getName());f.set(object,f.getType().newInstance());//System.out.println(object.getList());} catch (Exception e){// TODO Auto-generated catch blocke.printStackTrace();}}}

传入对应的属性的Field对象,然后设置权限,从beanList里面取出使用INIT注解的对象实例,然后给这个对象的参数赋值,赋上对应的类型的实例,这样就完成了IOC功能,不过这里可以看出来,我目前写的IOC必须这个类上也使用我的init注解,不然是无效的。


下面就到了AOP部分了。其余的都不累述,主要就是提供一个注解可以将使用注解的类变成代理类并且可以实现在该之前实现自己想要的功能,spring主要是提供事务功能,我这里因为目前是模拟写,就主要使用注解调用另外一个类的方法。如果需要使用事务稍加改造也是可以完成。

自定义的注解名为proxy,并且该注解拥有属性value,里面值为需要代理到这个方法上的类的方法的全路径名以及类的名字,就是说,我现在调用这个test方法,那么另外会调用com.ioc.demo.test包下的demo类的demo方法。

看下demo方法

@initpublic class demo {private String name="12";public String getName() {return name;}public void setName(String name) {this.name = name;}public void demo(){System.out.println("执行代理");}}
demo类就是给测试使用.

我们来看下注解实现类的具体代码:

private AnnotateResolve resolve;@Overridepublic void Realize(Field f, Class<?> cla,Annotation an) {try {proxy p=(proxy) an;String method=p.value();String[] str=method.split("\\*");String className=str[0];String methodName=str[1];final Class classObject=Class.forName(className);final Method method2 = classObject.getMethod(methodName, null);final Object object=cla.newInstance();Object newProxyInstance = (Object) Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {method2.invoke(classObject.newInstance(),null );Object invoke = method.invoke(object, args);return invoke;}});resolve.getProxyList().put(cla.getName(),newProxyInstance);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}
首先获取的注解肯定是proxy,然后获取注解里面的值,通过分割字符串的方法获取类名以及方法名,获取对应的Class对象,这样就可以直接调用该类的这个方法。

其次给使用了注解的类,生成代理类,并且把代理类放入一个代理集合proxyList中,在你需要使用代理类时直接从该集合中获取即可。

PS:需要实现代理功能的类必须实现一个接口。我声明了一个接口,里面的方法就是需要代理的方法,让需要被代理的类实现它

public interface ProxyInterface {public void test();}
这个就是我声明的代理接口。
public class Test implements ProxyInterface{
需要被代理的类需要实现它,并实现test方法,就可以完成AOP的功能,

最后看下实际效果。

public static void main(String[] args) throws Exception {AnnotateResolve le=new AnnotateResolve();//le.getAllAnnotate(Test.class);le.startScan(le);ProxyInterface object =(ProxyInterface) le.getProxyList().get("com.ioc.demo.test.Test");object.test();Test test=(Test) le.getBeanList().get("com.ioc.demo.test.Test");System.out.println(test.demo.getName());}

首先扫面所有包,从代理集合中获取代理对象,调用代理方法,从类集合中获取实例,获取test类里面的demo类查看是否有插入生成实例。


运行结果:

执行代理测试方法12

可以看到,调用test方法,demo方法也被调用了,并且可以获取test实例,里面的参数已经被初始化了,这样就模拟spring完成IOC以及AOP功能。


PS:我还没有仔细研读过spring的源码只是有大概它的思路,所以可能方法实现比较繁琐,不足之处希望见谅,也希望可以给初学者带来一点帮助


1 0
原创粉丝点击