Tiny-Spring——AOP系列

详情请见 https://github.com/StrongChenYu/Tiny-Spring

1. JDK代理和Cglib代理两种方式的区别

JDK动态代理使用的是运行期间使用反射
CGlib使用的是动态生成class文件字节码,为原先的类生成子类class文件,运行的时候使用相应的子类。

传统的代理模式缺陷和动态代理的优势

代理模式是两个类同时实现一个接口,一个类持有另一个类的实例,然后自定义逻辑以增强。
为什么在AOP中不使用传统的代理模式,是因为传统的代理模式,针对同样一个接口中同样的一个方法,需要写不同的类来进行代理,会增加代码的复杂度。(针对与同一个JoinPoint,要写不同的逻辑来实现)(《Spring揭秘》p138-p139)

JDK代理标准实现

实现InvocationHandler接口,Proxy.newInstance生成

public class RequestCtrlInvocationHandler implements InvocationHandler {

    private Object target;

    public RequestCtrlInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("request")) {
            System.out.println("invoke method");
            return method.invoke(target, args);
        }
        return null;
    }
}
RequestAble requestAble = (RequestAble) Proxy.newProxyInstance(
    JDKProxy.class.getClassLoader(),
    new Class[]{RequestAble.class},
    new RequestCtrlInvocationHandler(new RequestImpl())
);

上述的这种方式又被成为Around Advice,可以在方法调用之前和调用之后分别使用插入不同的逻辑。

CgLib代理标准实现

实现MethodInterceptor接口,Enhancer实现

public class RequestCtrlCallback implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println(method);
        return proxy.invokeSuper(obj, args);
    }
}
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RequestAble.class);
        enhancer.setCallback(new RequestCtrlCallback());

        RequestAble proxy = (RequestAble) enhancer.create();
        proxy.request();
    }
}

生成的方式不同,Cglib可以对任意类进行增强和代理,但是JDK动态代理的Proxy.newInstance方法的存在只能对实现了某一接口的类实现动态代理。

两种实现方式中,拦截方法的第一个参数的含义
public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

官方文档的解释是
proxy the proxy instance that the method was invoked on
我的理解是生成的那个代理对象。具体的用途是链式调用。
https://stackoverflow.com/questions/22930195/understanding-proxy-arguments-of-the-invoke-method-of-java-lang-reflect-invoca
proxy对象是com.sun.proxy.$Proxy0
链式调用的例子:
需要被代理的对象:

public class RequestImpl implements RequestAble {
    @Override
    public RequestAble request() {
        System.out.println("invoke request");
        return this;
    }
}

链式调用方法:

public static void main(String[] args) {
    RequestAble requestAble = (RequestAble) Proxy.newProxyInstance(
        JDKProxy.class.getClassLoader(),
        new Class[]{RequestAble.class},
        new RequestCtrlInvocationHandler(new RequestImpl())
    );
    requestAble.request().request();
}

如果要对requestAble.request()第一次调用拦截和第二次调用拦截,就需要返回这个Proxy对象。
如果使用这种方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getName().equals("request")) {
        System.out.println("invoke method");
        return method.invoke(target, args);
    }
    return null;
}

那么requestAble.request()返回值是requestImpl中的this,即requestImpl对象本身,这个对象是原始对象,不包括任何横切逻辑。即不会被代理。即跟以下方式一样。

public class RequestCtrlInvocationHandler implements InvocationHandler {    private Object target;    public RequestCtrlInvocationHandler(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if (method.getName().equals("request")) {            System.out.println("invoke method");            method.invoke(target, args);            return target;        }        return null;    }}

如果return proxy的话,第二次调用的时候使用的就是被代理的对象,就会执行代理逻辑。

2. AOP基础概念

AOP全名为Aspect Oriented Programming,如果说传统的开发方式是由上到下的,由抽象到具体的化,AOP则是在代码中插入横切逻辑,即对代码进行水平扩展。即诞生了几个基本概念。

JoinPoint

JoinPoint描述的是在何处插入横切逻辑,比如以下情况:

  • 构造方法之前
  • 某set,get方法之前或者之后
  • 某方法执行之前

即插入横切逻辑的位置

PointCut

PointCut则是JoinPoint的表述方式。Spring中表示的接口如下:

public interface Pointcut {    ClassFilter getClassFilter();    MethodMatcher getMethodMatcher();}

大概含义就是满足了某Class要求,并且满足方法限定的要求的JoinPoint

Advice

有了上面的描述之后,Advice就是在这些个JoinPoint处需要做的事情,分为以下几种:

  1. before advice:在JoinPoint位置之前执行的类型
  2. after advice:在JointPoint位置之后执行的advice类型
  3. around advice:能环绕执行。上述代理方式中的JDK动态代理和CGlib动态代理,都可以看作是around advice
Aspect

PointCutAdvice这两个独立的概念封装在一起,Aspect可以直接表述在某一个JoinPoint执行的逻辑。

3. 两个MethodInterceptor

org.aopalliance.intercept.MethodInterceptor
public interface MethodInterceptor extends Interceptor {	    /**     * Implement this method to perform extra treatments before and     * after the invocation. Polite implementations would certainly     * like to invoke {@link Joinpoint#proceed()}.     *     * @param invocation the method invocation joinpoint     * @return the result of the call to {@link     * Joinpoint#proceed()}, might be intercepted by the     * interceptor.     *     * @throws Throwable if the interceptors or the     * target-object throws an exception.  */    Object invoke(MethodInvocation invocation) throws Throwable;}

这个MethodInterceptror是来自AOP联盟,是Java自带的一个接口。
MethodInvocation的含义是可以表示方法的调用逻辑,可以获取到类,主要目的是使用MethodInvocation.proceed()方法,让被拦截的方法继续执行。
在这里插入图片描述

MethodInvocation的一个实例

public class ReflectiveMethodInvocation implements MethodInvocation {    protected final Object target;    protected final Method method;    protected final Object[] arguments;    public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) {        this.target = target;        this.method = method;        this.arguments = arguments;    }    @Override    public Method getMethod() {        return this.method;    }    @Override    public Object[] getArguments() {        return this.arguments;    }    @Override    public Object proceed() throws Throwable {        return this.method.invoke(target, arguments);    }    @Override    public Object getThis() {        return target;    }    @Override    public AccessibleObject getStaticPart() {        return method;    }}
net.sf.cglib.proxy.MethodInterceptor
public interface MethodInterceptorextends Callback{    /**     * 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     */        public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,                               MethodProxy proxy) throws Throwable;}

这个接口是为了CGlib代理。和上面的不一样,注意区分。

4. AOP实现

第一步实现
public void testDynamicProxy() {    IUserService userService = new UserService();    TargetSource source = new TargetSource(userService);    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* com.csu.springframework.test.aop.IUserService.queryUserInfo(..))");    AdvisedSupport advisedSupport = new AdvisedSupport();    advisedSupport.setTargetSource(source);    advisedSupport.setMethodMatcher(pointcut);    advisedSupport.setMethodInterceptor(new UserServiceInterceptor());    IUserService proxyJDK = (IUserService) new JDKDynamicAopProxy(advisedSupport).getProxy();    proxyJDK.queryUserInfo();    proxyJDK.register("chenyu");    IUserService proxyCGLIB= (IUserService) new Cglib2AopProxy(advisedSupport).getProxy();    proxyCGLIB.queryUserInfo();    proxyCGLIB.register("chenyu");}
第二步实现

将上面的步骤整合到Spring周期中并且支持beforeAdvice
AOP的实现本质上是在初始化bean之前,判断该类是否为需要代理的类。(PointCut过滤)
其实也就是那么将这么几步融合在一起:

  1. 创建PointCut: 控制切面,在哪里插入切面
  2. 创建AdvisorSupportadvisor中主要管在切面出执行什么逻辑
  3. 然后根据AdvisorSupport:创建代理对象
  4. 将上面的三部分集成到一个PostProcessor中,只不过这个PostProcessor需要重新创建一个,要在对象初始化之前执行。

最后的实现效果就是,只需要继承beforeAdvice,就可以实现相应对象的动态代理。

5. 总结

设计模式

这部分的设计模式主要涉及到的是代理模式,但是由于传统的代理模式的扩展性问题的存在,使用了JDKCGlib动态代理。

其他

剩下的部分就是根据AOP的基础概念,将整个AOP的概念扩展到Spring框架中并与Spring集成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值