AOP——深度解析二:动态代理,以及常见隐患注意

SpringAOP采用java语言作为AOL,SpringAOP属于第二代AOP,采用动态代理以及字节码生成技术实现。动态代理与字节码生成技术,在运行时为目标对象生成一个代理对象,并把横切逻辑织入到这个代理对象中。使用时使用了这个织入横切逻辑的代理对象,而非目标对象本身。

代理模式

静态代理

代理处于 访问者 与 被访问者 之间,可以隔离这两者之间 的直接交互,访问者与代理交互,就好像与被代理对象交互一样。代理通常拥有被代理者的全部职能。这样做的好处:一个是减轻了被访者的负担;另外,在请求转发给真正的对象(target Object)之前或者之后,可以加入比如安全限制、监控等等逻辑。

可参考:

http://blog.csdn.net/lemon89/article/details/44116119

这里写图片描述

/**
 * 
 * @author zhangsh
 *
 */
public interface ISubject {

    void request();
}
/**
 * 
 * @author zhangsh
 *
 */
public class SubjectImpl implements ISubject {

    @Override
    public void request() {
        System.out.println("request");

    }

}
/**
 * 
 * @author zhangsh
 *
 */
public class SubjectProxy implements ISubject {

    private ISubject subjectImpl;

    public SubjectProxy(ISubject subjectImpl) {
        this.subjectImpl = subjectImpl;
    }

    @Override
    public void request() {
        System.out.println("some before advice");
        subjectImpl.request();
        System.out.println("some after advice");

    }

}
/**
 * 
 * @author zhangsh
 *
 */
public class Client {

    public static void main(String[] args) {

        ISubject subjcet = new SubjectProxy(new SubjectImpl());

        subjcet.request();

    }

}

如上,代理实例。

动态代理——JDK动态代理

但是我们通常会有n个对象需要加入同样的advice(比如,监控逻辑,安全校验逻辑),如果我们使用这种方式,必须要为n个对象创建n个代理类。这样太麻烦了,大量重复工作。

使用动态代理,无需写代理类,运行时自动生成某个接口的代理。并在同一个的InvocationHandler中加入定制的Advice。

/**
 * 
 * @author zhangsh
 *
 */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    private Object targetObject;

    public MyInvocationHandler(Object targetObject) {
        this.targetObject = targetObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MyAdvice.beforeAdvice();

        Object result = method.invoke(targetObject, args);

        MyAdvice.afterAdvice();

        return result;

    }

}
/**
 * 
 * @author zhangsh
 *
 */
public class Client {

    public static void main(String[] args) {

        // ISubject subjcet = new SubjectProxy(new SubjectImpl());
        //
        // subjcet.request();

        ISubjectA subjectA = (ISubjectA) Proxy.newProxyInstance(ISubjectA.class.getClassLoader(),
                new Class[] { ISubjectA.class }, new MyInvocationHandler(new SubjectAImpl()));

        ISubjectB subject = (ISubjectB) Proxy.newProxyInstance(ISubjectB.class.getClassLoader(),
                new Class[] { ISubjectB.class }, new MyInvocationHandler(new SubjectBImpl()));
        // .........
        subjectA.request();
        subjectB.request();
    }

}

动态代理针对接口做代理,如果某个对象并没有实现任何接口。则无法使用JDK动态代理。

动态代理——CGLIB (code generation library)

为了解决上述问题,我们可以使用cglib,cglib可以代理非接口实现的目标对象,也可以像JDK动态代理那样为接口实现类做动态代理。

SpringAOP在发现targetObject 没有实现任何接口时,自动使用CGLIB技术,动态字节码生成,完成代理。

cglib通过动态生成字节码:继承目标对象,通过override目标对象原有方法,实现advice的织入。
这里写图片描述

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
 * 
 * @author zhangsh
 *
 */
public class MyMethodInterceptor implements MethodInterceptor {
    /**
     * All generated proxied methods call this method instead of the original
     * method. The original method may either be invoked by normal reflection
     * using the Method object, or by using the MethodProxy (faster).
     * 
     * @param obj
     *            "this", the enhanced object
     * @param method
     *            intercepted Method
     * @param args
     *            argument array; primitive types are wrapped
     * @param proxy
     *            used to invoke super (non-intercepted method); may be called
     *            as many times as needed
     * @throws Throwable
     *             any exception may be thrown; if so, super method will not be
     *             invoked
     * @return any value compatible with the signature of the proxied method.
     *         Method returning void will ignore this value.
     * @see MethodProxy
     */
    @Override
    public Object intercept(Object enhancedOobject, Method method, Object[] args, MethodProxy methodProxy)
            throws Throwable {
        System.out.println("Before invoke " + method);
        Object result = methodProxy.invokeSuper(enhancedOobject, args);
        System.out.println("After invoke" + method);
        return result;

    }

}
/**
 * 
 * @author zhangsh
 *
 */
public class NonInterfaceObjectA {

    public void request() {
        System.out.println("request NonInterfaceObjectA");
    }

}
/**
 * 
 * @author zhangsh
 *
 */
public class CglibClient {

    public static void main(String[] args) {
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
        Enhancer enhancerA = new Enhancer();
        enhancerA.setSuperclass(NonInterfaceObjectA.class);
        enhancerA.setCallback(myMethodInterceptor);
        NonInterfaceObjectA nonInterfaceObjectA = (NonInterfaceObjectA) enhancerA.create();

        Enhancer enhancerB = new Enhancer();
        enhancerB.setSuperclass(NonInterfaceObjectB.class);
        enhancerB.setCallback(myMethodInterceptor);
        NonInterfaceObjectB nonInterfaceObjectB = (NonInterfaceObjectB) enhancerB.create();
        nonInterfaceObjectA.request();

        System.out.println("----------------------------------");
        nonInterfaceObjectB.request();

    }

}

CGLIB不足是无法对final方法进行override织入切面逻辑。

隐患注意

  • 获取Target,常见proceedingJoinPoint.getTarget
    在一个joinpoint做了多次advisor,那么target的获取会出问题,比如的方法既要使用SpringCache注解、hystrix注解….等超过一个的注解:
    @SuppressWarnings("unchecked")
    public  <T> T getUltimateTargetObject(Object candidate) {
        Assert.notNull(candidate, "Candidate must not be null");
        try {
            if (AopUtils.isAopProxy(candidate) && candidate instanceof Advised) {
                return (T) getUltimateTargetObject(((Advised) candidate).getTargetSource().getTarget());
            }
        }
        catch (Throwable ex) {
            throw new IllegalStateException("Failed to unwrap proxied object", ex);
        }
        return (T) candidate;
    }
  • 获取方法参数,常见proceedingJoinPoint.getArgs(),需要仔细考虑是否需要defensive Copy Of Args,以防止引用的参数对象(引用类型)变化
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值