aop,学JAVA的大多知道,面向切面编程,意思就是,我们去坐动车,本来是一路畅通的,可是从火车站口到坐上动车的这段路上,经常要各种检查。这样的检查,就有点类似拦截器的味道。这种生活实例就是面向切面编程。它的好处很多,无论是从维护代码的阅读性方面,还是今后维护是否要动源码方面,以及对系统性能,事物,安全性等有着非常重要的作用。
闲话不说,贴上代码- public class LogInvoHandler implements InvocationHandler {
- private Object target; // 代理目标
- private Object proxy; // 代理对象
-
- /*
- * 将真实对象与抽象对象以KEY-VALUE的形式存放到Map中。往后可以一一对应。
- */
- private static Map<Class<?>,LogInvoHandler> invoHandlers = new HashMap<Class<?>,LogInvoHandler>();
-
- private static Logger logger;
-
- private LogInvoHandler() {
- }
-
- /**
- * @see 获得代理类实例
- * @param <T> 需要代理的类
- * @param clazz 需要代理的类字节码
- * @return a proxy instance with the specified invocation handler of a proxy class
- * that is defined by the specified class loader and that implements the specified interfaces
- * @author Genekhun(林煜坤)
- */
- public static<T> T getProxyInstance(Class<T> clazz){
-
- logger = Logger.getLogger(clazz);//日志记录对象
-
- LogInvoHandler invoHandler = invoHandlers.get(clazz);
- if(invoHandler == null){
- invoHandler = new LogInvoHandler();
-
- try {
-
- T tar = clazz.newInstance();
- invoHandler.setTarget(tar);
- invoHandler.setProxy(Proxy.newProxyInstance(
- tar.getClass().getClassLoader(),
- tar.getClass().getInterfaces(),
- invoHandler));
-
- } catch (InstantiationException e) {
- logger.error("日志记录代理发生异常");
- throw new CreateLogProxyException("日志记录代理发生异常",e);
- } catch (IllegalAccessException e) {
- logger.error("日志记录代理发生异常");
- throw new CreateLogProxyException("日志记录代理发生异常",e);
- }
-
- invoHandlers.put(clazz, invoHandler);
- }
- return (T)invoHandler.getProxy();
- }
-
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Object result = method.invoke(target, args);
-
- //打印日志
- logger.info("___invoke method:"+method.getName()+
- "; agrs: "+ (args == null? "null":Arrays.asList(args).toString()+
- "; return: " + result));
-
- return result;
- }
复制代码 这里我们定义了一个代理对象和代理目标。
动态代理的原理,有点像AJAX的原理,AJAX是创建一个XHP代理,让代理去获取需要的数据然后返回。
现实生活中也有很多这样的事例,比如某天你的邮件到了,可是你在开会,没法自己去拿,一般人都会叫自己的朋友去帮忙带领。这样的行为就是代理。
动态代理也是这样,创建一个代理对象和目标。
这里我们还得知道Class对象, 我们知道对象的字节码里面蕴含了该对象的所有信息。所有我们要得到一个和真实目标有相同的功能,就必须要拿到它的所有信息。所以getProxyInstance(Class<T> clazz)这个方法中的参数,你们应该明白了吧!
那么怎么将真实目标的所有信息都能复制到代理身上,这里就用到了反射机制的知识。我相信如果你学完张老师的反射机制你 应该能明白的,这里我就不做详述了。
得到了代理目标的信息,接下来我们要获取代理对象的一些信息,比如去哪里获取信息,要获得什么信息等。
接下来我们分析 一下 方法- invoHandler.setProxy(Proxy.newProxyInstance(
- tar.getClass().getClassLoader(),
- tar.getClass().getInterfaces(),
- invoHandler));
复制代码 从方法名就能看上,这个操作是获得代理对象的。 那么要获得被代理对象的信息,我们肯定要拿到它的字节码,第一个参数,就是被代理对象的加载器也就是它的字节码,第二个参数是代理对象要进行的操作,人家叫你去做东西,你总得知道做什么是把。第三个参数 InvocationHandler 对象,意思呢就是调用处理器,都叫处理器了你们应该明白作用了吧。
经过上面的分析,我们是不是拿到了代理目标的所有信息以及代理对象的所有信息。那么接下来就是开始两者联系的这么一个过程。
invoke,对象上的调用函数。看过源码的同学应该会知道,类与类之间调用就是通过这个方法,那么我们重载这个方法,就是说明我们现在有了比较邪恶的意图了。是把!就是要把它拦截下来,就好比前面说的,你去车站的中间把你拦住,要做个安检。同理!我们就在这个方法体中加入我们想要的操作。
就是这么一个简单的原理。
我可能不太幽默,不知道大家满不满意对我的描述呢?欢迎交流。!!
|