zoukankan      html  css  js  c++  java
  • 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 world
    message:hello , my name is Mr Read!I love reading books!

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

    这里那里用到了IOC?

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

    这里那里用到了AOP?

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

  • 相关阅读:
    Shell IFS
    Crontab
    linux awk
    free
    条件语句练习2
    条件语句练习
    打印菜单
    条件测试语法
    read 命令
    jQuery(实例)
  • 原文地址:https://www.cnblogs.com/seanvon/p/4255505.html
Copyright © 2011-2022 走看看