AOP动态代理--基本原理

来源:互联网 发布:像素设计软件 编辑:程序博客网 时间:2024/06/03 19:52

 AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。

今天来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。

        AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
        那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。
        AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。
还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释
package com.service;/** * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口  * @author xx * 2017年3月24日 下午4:17:53 */public interface StudentInfoService {void findInfo(String studentName);}
2、实现类,我们真正的业务代码,需要做切面

package com.service.impl;import com.service.StudentInfoService;public class StudentInfoServiceImpl implements StudentInfoService{@Overridepublic void findInfo(String studentName) {//System.out.println("#####你目前输入的名字是:"+studentName);System.out.println("#####业务代码xxx1");System.out.println("#####业务代码xxx2");}}
3,实现动态动态代理类
  3.1先写一个工厂类AOPFactory,连接需要代理的接口(可以不写)
package com.handler;public class AOPFactory {public static <T> Object getAOPProxyedObject(Class<T> cls){Object proxy = null;MyHandler handler = new MyHandler();try {Object obj = cls.newInstance();if (obj != null) {proxy = handler.bind(obj);} else {System.out.println("Can't get the proxyobj");}} catch (Exception e) {e.printStackTrace();}return proxy;}}

 代理类MyHandler
package com.handler;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import org.apache.log4j.Logger;public class MyHandler implements InvocationHandler {private Object proxyObj;private static Logger log = Logger.getLogger(MyHandler.class);public Object bind(Object obj) {this.proxyObj = obj;return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy,Method method,Object[] args) throws Throwable{Object result=null;try{//请在这里插入代码,在方法前调用log.debug("调用log日志方法 before"+method.getName());result=method.invoke(proxyObj,args); //原方法//请在这里插入代码,方法后调用log.debug("调用log日志方法 after"+method.getName());}catch(Exception e){e.printStackTrace();}return result;}}
4.运行结果
03/24/2017,16:35:26:com.handler.MyHandler[main]-DEBUG:调用log日志方法 beforefindInfo#####你目前输入的名字是:阿飞#####业务代码xxx1#####业务代码xxx203/24/2017,16:35:26:com.handler.MyHandler[main]-DEBUG:调用log日志方法 afterfindInfo

5.分析一下。这个是最基本的JDK动态代理,通过java反射原理,将需要代理的method前后绑定了逻辑,不影响其方法内的运行!
缺陷是,该方法必须是通过实现接口的。
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this)

二、cglib动态代理实现AOP拦截

还是拿刚才的代理类做示范。
一,cglib代理处理类(实现了MethodInterceptor类,用将目标类传进Enhancer调用intercept方法,intercept和jdk invoke类似

package com.handler.AOPInstrumenter;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;import org.apache.log4j.Logger;public class AOPInstrumenter implements MethodInterceptor {private Logger log = Logger.getLogger(AOPInstrumenter.class);private Enhancer enhancer = new Enhancer();public Object getInstrumentedClass(Class<?> clz) {enhancer.setSuperclass(clz);//this便是传进来的clz,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法  enhancer.setCallback(this);return enhancer.create();}public Object intercept(Object o, Method method, Object[] args,MethodProxy proxy) throws Throwable {// 请在这里插入代码,在方法前调用log.debug("调用log日志方法 before" + method.getName());Object result = proxy.invokeSuper(o, args);// 请在这里插入代码,方法后调用log.debug("调用log日志方法 after" + method.getName());return result;}}
二 测试代码
AOPInstrumenter instrumenter=new AOPInstrumenter();StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl.class);studentInfo.findInfo("阿飞");
三 ,结果
03/24/2017,17:03:11:com.handler.AOPInstrumenter.AOPInstrumenter[main]-DEBUG:调用log日志方法 beforefindInfo#####你目前输入的名字是:阿飞#####业务代码xxx1#####业务代码xxx203/24/2017,17:03:11:com.handler.AOPInstrumenter.AOPInstrumenter[main]-DEBUG:调用log日志方法 afterfindInfo

完成,基本的原来就是这些,spring 源码使用它的AOP当然远远比这个复杂,后续再使用spring aop分析



0 0