AOP实现原理——动态代理

      前几天阿里面试问AOP是怎么实现的,感觉自己当时答的不好,于是回来重新研究了一下,找了下资料,现在来做个分享.
      Spring两大核心IOC与AOP.IOC负责将对象动态的注入到容器,让容器来管理bean,AOP就是可以让容器中的对象都享有容器中的公共服务(如日志记录等等).那么AOP是怎么实现的,下面讲一下我的理解——动态代理。
      动态代理就是利用反射和动态编译将代理模式变成动态的.原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁。下面看代码:

假设我们要对下面这个用户管理类进行代理:

package com.kevindai.AOP;

public interface UserService {
    /** * * @Title: addUser * @Description: 增加user * @param * @author kevindai * @return void 返回类型 * @throws */
    public void addUser();
    /** * * @Title: delUser * @Description: 删除user * @param * @author kevindai * @return void 返回类型 * @throws */
    public void delUser();
    /** * * @Title: updateUser * @Description: 修改user * @param * @author kevindai * @return void 返回类型 * @throws */
    public void updateUser();
}
package com.kevindai.AOP;

public class UserServiceImpl implements UserService {
    /** * * @Title: addUser * @Description: 增加User * @param * @author kevindai * @return * @throws */
    public void addUser() {
        System.out.println("addUser....");
    }
    /** * * @Title: delUser * @Description: 删除User * @param * @author kevindai * @return * @throws */
    public void delUser() {
        System.out.println("delUser....");

    }
    /** * * @Title: updateUser * @Description: 删除User * @param * @author kevindai * @return * @throws */
    public void updateUser() {
        System.out.println("updateUser....");

    }

}

按照代理模式的实现方式,一般是用一个代理类,让它也实现UserService接口,然后在其内部声明一个UserServiceImpl,然后分别调用addUser和delUser等方法,并在调用前后加上我们需要的其他操作。但是这样都是写死的,不能做到动态呢。要实现代理,那么代理类跟被代理类都要实现同一接口,但是动态代理的话根本不知道我们将要代理谁,也就不知道要实现哪个接口。这时候就应该动态生成代理类!
来来一个方法来接收被代理类,这样我们就可以通过反射知道它的信息。

package com.kevindai.AOP;

import java.lang.reflect.Method;

public interface InvocationService {
    public void invoke(Object o, Method m);
}

实现动态代理的关键部分,通过Proxy动态生成我们具体的代理类:

package com.kevindai.AOP;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/** * * @ClassName: Proxy * @Description: 通过动态代理实现AOP * @author kevindai * @date 2016-3-31 下午3:56:58 * */
public class Proxy {
    /** * * @Title: proxyInterface * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param 传入的接口 * @param @param 代理类 * @param @return * @author kevindai * @return Object 返回类型 * @throws */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Object proxyInterface(Class clazz,InvocationService invo) throws Exception{
        StringBuffer method = new StringBuffer();
        String br = "\r\n";

        Method[] methods = clazz.getMethods();
        for(Method m : methods){
            method
            //.append(" @Override").append(br)
            .append(" public ").append(m.getReturnType()).append(" ").append(m.getName()).append("(){").append(br)
            .append(" try{").append(br)
            .append(" Method md = ").append(clazz.getName()).append(".class.getMethod(\"").append(m.getName()).append("\");").append(br)
            .append(" invo.invoke(this,md);").append(br)
            .append(" }catch(Exception e){e.printStackTrace();}").append(br)
            .append(" }").append(br).append(br);
        }
        //根据要代理的方法信息来生成java源文件
        StringBuffer codeSrc = new StringBuffer();
        codeSrc.append("package com.kevindai.AOP;").append(br)
                .append("import java.lang.reflect.Method;").append(br)
                .append("public class $Proxy1 implements ").append(clazz.getName()).append("{").append(br)
                .append(" public $Proxy1(InvocationService invo){").append(br)
                .append(" this.invo = invo;").append(br)
                .append(" }").append(br)
                .append(" com.kevindai.AOP.InvocationService invo;").append(br)
                .append(method).append(br)
                .append("}");
        String fileName = "C:/myeclipse/starsinoWs/Test/src/com/kevindai/AOP/$Proxy1.java";
        File f = new File(fileName); 
        FileWriter fw = new FileWriter(f); 
        fw.write(codeSrc.toString()); 
        fw.flush(); 
        fw.close();

        //将Java文件编译成class文件  
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); 
        Iterable units = fileMgr.getJavaFileObjects(fileName); 
        CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); 
        t.call(); 
        fileMgr.close(); 

        //加载到内存,并实例化  
        URL[] urls = new URL[] {new URL("file:/" + "C:/myeclipse/starsinoWs/Test/src/")}; 
        URLClassLoader ul = new URLClassLoader(urls); 
        Class c = ul.loadClass("com.kevindai.AOP.$Proxy1"); 

        Constructor ctr = c.getConstructor(InvocationService.class); 
        Object m = ctr.newInstance(invo); 

        return m; 
    }
}

这个类的主要功能就是,根据被代理对象的信息,动态组装一个代理类,生成 Proxy1.java Proxy1.class。这样就可以在运行的时候,根据具体的被代理对象生成需要的代理类了。这样一来,就算不知道需要代理谁,也能生成相应的代理类。

然后写一些代理信息,我这里写的是事务和时间:

package com.kevindai.AOP;

import java.lang.reflect.Method;

public class TransactionService implements InvocationService {  

    private Object target;  

    public TransactionService(Object target) {  
        super();  
        this.target = target;  
    }  

    public void invoke(Object o, Method m) {  
        System.out.println("开启事务.....");  
        try {  
            m.invoke(target);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        System.out.println("提交事务.....");  
    }  

} 
package com.kevindai.AOP;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TimeService implements InvocationService {

    private Object target;  

    public TimeService(Object target) {  
        super();  
        this.target = target;  
    }


    public void invoke(Object o, Method m) {  
        System.out.println("开始时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E").format(new Date()));  
        try {  
            m.invoke(target);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        System.out.println("开始时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E").format(new Date()));  
    }  
}

现在开始测试一下:

package com.kevindai.AOP;


public class Test {
    public static void main(String[] args) throws Exception {
        UserService userService = new UserServiceImpl();  

        //为用户管理添加事务处理 
        InvocationService h = new TransactionService(userService);  
        UserService u = (UserService)Proxy.proxyInterface(UserService.class,h);  

        //为用户管理添加显示方法执行时间的功能 
        TimeService h2 = new TimeService(u);  
        u = (UserService)Proxy.proxyInterface(UserService.class,h2);  

        u.addUser();  
        System.out.println("\r\n==================================\r\n");  
        u.delUser();  
    }
}

看看输出:

开始时间:2016-03-31 17:48:23 星期四
开启事务.....
addUser....
提交事务.....
开始时间:2016-03-31 17:48:23 星期四

==================================

开始时间:2016-03-31 17:48:23 星期四
开启事务.....
delUser....
提交事务.....
开始时间:2016-03-31 17:48:23 星期四

OK,动态代理完成,这也就是AOP的实现方式。

    原文作者:AOP
    原文地址: https://blog.csdn.net/kevindai007/article/details/51028854
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞