首页 > 代码库 > IOC和AOP的简单实现

IOC和AOP的简单实现

一直使用spring,说起来就是IOC和AOP,看过不少原理的书,但是spring的代码太多,梳理起来很困难,于是想自己实现一下,昨天下午写出代码来,分享一下。

目标:

1、使用annotation编程进行分层,有service层和dao层(mapper层)。

2、设置容器,将所有的实例注入到容器里,类似spring的applicationContext。

3、dao层使用接口,没有实现类,类似于ibitas的使用方式。

4、读取本地文件内容。

 

根据目标大概思考并实践了以下几点:

1、动态代理选用cglib实现,首先这是spring的实现方式,其次亲测使用jdk的反射包无法实现对接口的动态代理,无法满足上述目标3。

2、容器使用单例模式创建,保证所有的bean只有一个。

 

代码如下:

首先是自定义的Annotation,为了和spring的加以区别,我都加上了Fx前缀。

第一个是FxMapper,类似spring的@Repository,也就是Dao层应该加的Annotation

package com.smikevon.proxy.annotations;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by fengxiao on 15-1-27. */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FxMapper {    String value();}

第二个是FxService对应于spring的service层,类似spring的@Service。

package com.smikevon.proxy.annotations;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by fengxiao on 15-1-27. */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FxService {    String value();}

第三个FxResource对应spring里的@Resource,也就是类内饮用的注释

package com.smikevon.proxy.annotations;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by fengxiao on 15-1-27. */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface FxResource {    String value();}

 

Annotation类全部贴完,下面贴出各层的一个示例类。

按上面顺序,第一个mapper类

package com.smikevon.proxy.dynamic;
import com.smikevon.proxy.annotations.FxMapper;/** * Created by fengxiao on 15-1-27. */@FxMapper("fileMapper")public interface FileMapper { public String read(String fileDir); public void append(String fileDir , String message);}

第二个service类

package com.smikevon.proxy.dynamic;import com.smikevon.proxy.annotations.FxResource;import com.smikevon.proxy.annotations.FxService;/** * Created by fengxiao on 15-1-27. */@FxService("fileService")public class FileService {    String file = "rFile.txt";        @FxResource(value="fileMapper")    public FileMapper fileMapper;    public void read(){        System.out.println("hello world");        String txt = fileMapper.read(file);        System.out.println("message:"+txt);    }}

 

示例类已经贴完,下面贴出容器类。容器类通过使用一个hashmap来实现容器,这里没有考虑线程安全的问题,只是为了示例。初始化使用静态内部类来实现单例模式。

package com.smikevon.proxy.dynamic;import com.smikevon.proxy.Processors.FileMapperProcessor;import com.smikevon.proxy.Processors.FileServiceProcessor;import com.smikevon.proxy.annotations.FxMapper;import com.smikevon.proxy.annotations.FxService;import java.io.File;import java.util.HashMap;import java.util.Iterator;import java.util.Map;/** * Created by fengxiao on 15-1-27. */public class FxApplicationContext {        private static final String JAVA_SOURCE_DIR = "src/main/java";    private static final String JAVA_PACKAGE_DIR = "com/smikevon/proxy/dynamic";    private static final String JAVA_PACKAGE_PATH = "com.smikevon.proxy.dynamic";        private Map<String,Object> context = new HashMap<String,Object>();        private FxApplicationContext(){}    /**     * 容器初始化     */    public FxApplicationContext init(){        try {            File file1 = new File(JAVA_SOURCE_DIR+File.separator+JAVA_PACKAGE_DIR);            File[] files = file1.listFiles();            for(File file : files){                if(file.getName().endsWith("java")){                    String name = file.getName().substring(0,file.getName().indexOf("."));                    Class<?> clazz = Class.forName(JAVA_PACKAGE_PATH+"."+name);                    if(clazz.getAnnotation(FxMapper.class)!=null){                        String key = clazz.getAnnotation(FxMapper.class).value();                        //实例化mapper的处理类                        FileMapperProcessor processor = new FileMapperProcessor();                        if(context.get(key) == null){                            context.put(key,processor.bind(clazz));                        }                    }                    if(clazz.getAnnotation(FxService.class)!=null){                        String key = clazz.getAnnotation(FxService.class).value();                        //实例化service的处理类                        FileServiceProcessor processor = new FileServiceProcessor();                        if(context.get(key) == null){                            context.put(key,processor.bind(clazz));                        }                    }                }            }        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return this;    }    public Object getBean(String beanId){        Iterator<Map.Entry<String, Object>> iterator = context.entrySet().iterator();        while(iterator.hasNext()){            Map.Entry<String, Object> entry = iterator.next();            String key = entry.getKey();            if(key.equals(beanId)){                return entry.getValue();            }        }        return null;    }    /**     * 静态内部类方式实现单例     */    private static class FxApplicationContextHolder{        public static FxApplicationContext instance = new FxApplicationContext();    }        public static FxApplicationContext getInstance(){        return FxApplicationContextHolder.instance;    }    }

 

动态代理引用了cglib包,在项目里要加上如下引用:

<dependency>    <groupId>cglib</groupId>    <artifactId>cglib</artifactId>    <version>2.2.2</version>
</dependency>

代理类如下:

先是对有@FxMapper标记的类的动态代理方法

package com.smikevon.proxy.Processors;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.lang.reflect.Method;/** * Created by fengxiao on 15-1-27. */public class FileMapperProcessor implements MethodInterceptor{    public Object bind(Class clazz) {        Enhancer enhancer = new Enhancer();        enhancer.setCallback(this);        enhancer.setSuperclass(clazz);        Object obj = enhancer.create();        return obj;    }    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        StringBuilder sb = new StringBuilder();        if(method.getName().toLowerCase().startsWith("read")){            if (objects.length > 0) {                String fileDir = (String) objects[0];                File file = new File(fileDir);                BufferedReader br = new BufferedReader(new FileReader(file));                                String line = null;                while((line = br.readLine())!=null){                    sb.append(line);                }            }        }        return sb.toString();    }}

其次是对有@FxService标记的类的动态代理方法

package com.smikevon.proxy.Processors;import com.smikevon.proxy.annotations.FxResource;import com.smikevon.proxy.dynamic.FxApplicationContext;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Field;import java.lang.reflect.Method;/** * Created by fengxiao on 15-1-27. */public class FileServiceProcessor implements MethodInterceptor{    private Object target;        public Object bind(Class clazz) {        try {            target = clazz.newInstance();                        Enhancer enhancer = new Enhancer();            enhancer.setSuperclass(clazz);            enhancer.setCallback(this);            Object obj = enhancer.create();                //将mapper的实例bean赋值给对应的引用            Field[] fields = clazz.getDeclaredFields();            for(Field field : fields){                if (field.getAnnotation(FxResource.class)!=null){                    String value = field.getAnnotation(FxResource.class).value();                    Object object = FxApplicationContext.getInstance().getBean(value);                    //看到没下面这行代码和注释掉的代码效果是一样的,反射的强大之处                    field.set(target,object);                    //((FileService)target).fileMapper = (FileMapper)object;                }            }            return obj;        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }        return null;    }        @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        method.invoke(target,objects);        return null;    }}

其实,@FxService和@FxMapper可以合并成一个Annotation,对应的动态代理类也可以合并为一个(类似Spring的@Component ),但是那样的话,每个类的处理逻辑就会比较复杂,不易理解。

下面是测试方法类:

package com.smikevon.proxy.dynamic;import java.lang.reflect.InvocationTargetException;/** * Created by fengxiao on 15-1-27. */public class FileDemo {        public static void main(String[] args) throws InvocationTargetException {        FxApplicationContext fxApplicationContext = FxApplicationContext.getInstance().init();        FileService fileService = (FileService)fxApplicationContext.getBean("fileService");        fileService.read();    }    }

我的文件内容(文件名fFile.txt,位置就在项目根目录):

hello , my name is Mr Read!I love reading books!

输出结果:

hello worldmessage:hello , my name is Mr Read!I love reading books!

 

看下有没有很熟悉,和spring使用bean的方式是不是很一致? 原理原来没有什么神秘的,就是这么简单。

 

这里那里用到了IOC?

容器方式初始化bean,将bean的生命周期交付给容器(hashmap),而不是调用者,示例里容器的生命周期比较简单,即调用init()方法后开始,程序执行完成结束。

 

这里那里用到了AOP?

通过动态代理读取接口参数,获取相应文件内容,这就是aop。

 

IOC和AOP的简单实现