美文网首页
AOP原理详解

AOP原理详解

作者: 你值得拥有更好的12138 | 来源:发表于2018-05-05 16:41 被阅读0次

1. 原理


AOP切面编程分为静态编织和动态代理两种模式。AOP其实就是设计模式中代 理模式,代理类全权代理被代理类执行方法。调用方法的对象实际拿到的不是原始的类,而是被增强后的类。

    静态编织:
            在原始类进行编译的时候,进行编译生成新的类。Aspectj为代表。
    动态代理:
            在代码运行中进行动态的生成代理类。比如:java动态代理,CGLIB技术。

2. 分析

  • 静态编织
    编织后的代码通过字节码可以看到类似的代码:
   public void ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983(AroundClosure ajc$aroundClosure) {
        System.out.println("开始事务 ...");
        ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983proceed(ajc$aroundClosure);
        System.out.println("事务结束 ...");
    }
  • 动态代理
    java动态代理:主要通过实现InvocationHandler和使用Proxy来生成新的代理类。
    
package jdkproxy;

/**
 * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口
 * 
 * @author typ
 * 
 */
public interface Service {
    /**
     * add方法
     */
    public void add();

    /**
     * update方法
     */
    public void update();
}

package jdkproxy;

/**
 * 被代理类,即目标类target
 * 
 * @author typ
 * 
 */
public class AService implements Service {
    /*
     * (non-Javadoc)
     * 
     * @see jdkproxy.Service#add()
     */
    public void add() {
        System.out.println("AService add>>>>>>>>>>>>>>>>>>");
    }

    /*
     * (non-Javadoc)
     * 
     * @see jdkproxy.Service#update()
     */
    public void update() {
        System.out.println("AService update>>>>>>>>>>>>>>>");
    }
}

package jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author typ
 *
 */
public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    MyInvocationHandler() {
        super();
    }

    MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // 程序执行前加入逻辑,MethodBeforeAdviceInterceptor
        System.out.println("before-----------------------------");
        // 程序执行
        Object result = method.invoke(target, args);
        // 程序执行后加入逻辑,MethodAfterAdviceInterceptor
        System.out.println("after------------------------------");
        return result;
    }

}

public static void main(String[] args) {
        Service aService = new AService();
        MyInvocationHandler handler = new MyInvocationHandler(aService);
        // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
        Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
                .getClass().getClassLoader(), aService.getClass()
                .getInterfaces(), handler);
        // 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口
        aServiceProxy.add();
        System.out.println();
        aServiceProxy.update();
        // 以下是对B的代理
        // Service bService = new BService();
        // MyInvocationHandler handler = new MyInvocationHandler(bService);
        // Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
        // .getClass().getClassLoader(), bService.getClass()
        // .getInterfaces(), handler);
        // bServiceProxy.add();
        // System.out.println();
        // bServiceProxy.update();
    }
  • 代码详解:

其实InvactionHandler的实现类就类似于Spring中的切面类。Proxy中代码就是Spring内部去做的事。这种java动态代理实际必须要按照设计模式中的UML图中的架构,需要被代理类实现一个接口。然后思考一个问题,Proxy到底做了什么?


其实他做了以下几件事:

  • 创建一个新的类代码
  • 实现原始类的接口和方法
  • 使用反射获得方法,回调InvocationHandler的实现类的方法来增强方法
  • 将这个类加载入内存

类似于下面的代码

package com.tgb.proxy;  

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.StandardJavaFileManager;  
import javax.tools.ToolProvider;  
import javax.tools.JavaCompiler.CompilationTask;  

public class Proxy {  
    /** 
     *  
     * @param infce 被代理类的接口 
     * @param h 代理类 
     * @return 
     * @throws Exception 
     */  
    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {   
        String methodStr = "";  
        String rt = "\r\n";  

        //利用反射得到infce的所有方法,并重新组装  
        Method[] methods = infce.getMethods();    
        for(Method m : methods) {  
            methodStr += "    @Override" + rt +   
                         "    public  "+m.getReturnType()+" " + m.getName() + "() {" + rt +  
                         "        try {" + rt +  
                         "        Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +  
                         "        h.invoke(this, md);" + rt +  
                         "        }catch(Exception e) {e.printStackTrace();}" + rt +                          
                         "    }" + rt ;  
        }  

        //生成Java源文件  
        String srcCode =   
            "package com.tgb.proxy;" +  rt +  
            "import java.lang.reflect.Method;" + rt +  
            "public class $Proxy1 implements " + infce.getName() + "{" + rt +  
            "    public $Proxy1(InvocationHandler h) {" + rt +  
            "        this.h = h;" + rt +  
            "    }" + rt +            
            "    com.tgb.proxy.InvocationHandler h;" + rt +                           
            methodStr + rt +  
            "}";  
        String fileName =   
            "d:/src/com/tgb/proxy/$Proxy1.java";  
        File f = new File(fileName);  
        FileWriter fw = new FileWriter(f);  
        fw.write(srcCode);  
        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:/" + "d:/src/")};  
        URLClassLoader ul = new URLClassLoader(urls);  
        Class c = ul.loadClass("com.tgb.proxy.$Proxy1");  

        Constructor ctr = c.getConstructor(InvocationHandler.class);  
        Object m = ctr.newInstance(h);  

        return m;  
    }  

}  

CGLIB代理:
它是基于继承来实现代理,java的动态代理是基于反射的。主要是使用MethodInterceptor 和工厂类Enhancer来实现,其实和java的代理差不多,限制少一点。

package cglibproxy;

/**
 * 被代理类,即目标对象target
 * 
 * @author typ
 * 
 */
public class Base {
    /**
     * 一个模拟的add方法
     */
    public void add() {
        System.out.println("add ------------");
    }
}

package cglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 此为代理类,用于在pointcut处添加advise
 * 
 * @author typ
 * 
 */
public class CglibProxy implements MethodInterceptor {

    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
        System.out.println("before-------------");
        // 执行目标类add方法
        proxy.invokeSuper(object, args);
        // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
        System.out.println("after--------------");
        return null;
    }

}

package cglibproxy;

import net.sf.cglib.proxy.Enhancer;

/**
 * 工厂类,生成增强过的目标类(已加入切入逻辑)
 * 
 * @author typ
 * 
 */
public class Factory {
    /**
     * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
     * 
     * @param proxy
     * @return
     */
    public static Base getInstance(CglibProxy proxy) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Base.class);
        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
        enhancer.setCallback(proxy);
        // 此刻,base不是单纯的目标类,而是增强过的目标类
        Base base = (Base) enhancer.create();
        return base;
    }
}

package cglibproxy;

/**
 * @author typ
 *
 */
public class Test {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        // base为生成的增强过的目标类
        Base base = Factory.getInstance(proxy);
        base.add();
    }
}

其实代码结构上和java动态代理一样的。
查看源码看不懂,也没搞明白它是怎么实现继承的。
以后再说吧。

引用以下文章:

Spring AOP的实现原理
Spring 容器AOP的实现原理——动态代理
Spring AOP 实现原理与 CGLIB 应用
AOP的底层实现-CGLIB动态代理和JDK动态代理

相关文章

  • AOP原理详解

    1. 原理 AOP切面编程分为静态编织和动态代理两种模式。AOP其实就是设计模式中代 理模式,代理类全权代...

  • [java][SpringAOP]

    为什么会有AOP 实现AOP原理 AOP通知类型 AOP在Spring中配置

  • Spring Boot AOP 拦截Get请求

    前置内容:请查看参考文章关于Spring AOP 的讲解 细说Spring——AOP详解(AOP概览) 补充说明S...

  • Spring AOP详解及简单实现

    Spring AOP详解及简单实现# AOP 说明:本文转载至 http://www.cnblogs.com/xr...

  • 03 AOP学习之五种通知

    Spring AOP五种通知详解 spring aop通知(advice)分成五类: 前置通知Before adv...

  • Spring(一)

    First And MOST Important Spring AOP的实现原理和场景? 什么是AOP?AOP--...

  • spring AOP

    1,原理:Spring AOP底层原理采用运行时生成动态代理的方式来增强目标对象。AOP 代理其实是由 AOP 框...

  • Android aop工作原理

    Android aop工作原理

  • Spring Aop标签解析原理详解

    对于Spring Aop的实现,是非常复杂的,其实现过程主要包含xml标签的解析,切面表达式的解析,判断bean是...

  • SpringBoot开发随记--AOP的使用

    SpringBoot开发随记--AOP的使用 Aop原理 1、什么是Aop2、Aop常用术语3、AOP表达式4、A...

网友评论

      本文标题:AOP原理详解

      本文链接:https://www.haomeiwen.com/subject/itferftx.html