AOP实现机制

1.什么是AOP

Aspect Oriented Programming:面向切面编程,和OOP面向对象编程相辅相成,也是对OOP很好的补充。

怎么理解 "面向切面" 呢,我把他看做对系统的一次“重构”

在OOP的世界里,存在很多重复且与核心业务逻辑无关的代码,比如性能监控、日志记录、事务提交与回滚

虽然可以通过良好的设计将这些重复的代码抽象出来,但是调用这些公共方法的【客户端】仍然无法解脱出来的

AOP就是专门用来解决这些问题的,他把与核心业务无关的代码、以及客户端的调用都释放出来

使得原本交织在一起看起来像一张网一样的系统结构,变成像积木一样可任意选用、装配

因此我把AOP当做对系统的重构来了解,他完成了传统OOP很难甚至无法完成的任务


2. AOP 机制

分为静态AOP和动态AOP两种机制

静态AOP:在Java代码编译成字节码的过程中,修改字节码。优点性能高,缺点是灵活性不够。

动态AOP:动态修改字节码,比如 JDK动态代理、cglib类增强、自定义类加载器、AspectJ和Javassist动态修改字节码等方式都可以实现


3.AOP 家族成员

JoinPoint:连接点,比如 类初始化前、初始化后、方法调用前、调用后,也就是说连接点是客观存在的!

PointCut:切点,一种规则或者表达式,能匹配一组连接点。比如以 update 为前缀的方法,就能匹配到多个连接点

Advice:增强,比如以 update 开头的方法结束之后,统一提交或者回滚事务。

Target:目标对象,比如 UserServiceImpl 业务类的实例,也就是原生对象。

Proxy:代理对象,目标对象的代理。客户端要使用的其实是这个代理对象。

Aspect:切面,由PointCut + Advice 组成,也可以看作是 JoinPoint(点)---->PointCut(线)----->Aspect(面) 的一个过程

Weaving:织入,这是一个过程:把切面逻辑织入到切点匹配的连接点上,实际上就构造了最终的代理对象。


4.AOP 实现机制

4.1 JDK动态代理

package com.yli.aop;

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

/**
 * JDK 简单动态代理要求必须定义接口<br>
 * 借助InvocationHandler和Proxy就可以生成代理类<br>
 * InvocationHandler就是AOP概念中的切面 Advice <br>
 * 在这个类的invoke方法内部可以代理原生类方法的一切行为<br>
 * 原生类的所有方法就相当于JoinPoint,这一组JoinPoint构成了一个PoinCut
 * 
 * @author yli
 */
public class JdkDynamicTest {

    public static void main(String[] args) {
        // 原生对象
        IBusiness bi = new BusinessImpl();

        // 代理对象
        IBusiness proxyBI = UnitJdkDynamicUtil.getProxy(bi);
        proxyBI.printHello("Tom");
    }
}


/**
 * 定义一个简单的工具类,提供一个泛型方法,返回任意类型的代理对象
 */
class UnitJdkDynamicUtil {
    public static <T> T getProxy(T t) {
        // 1.生成InvocationHandler
        // InvocationHandler handler = new UnitJdkDynamicHandler(initObj);
        InvocationHandler handler = new UnitJdkDynamicHandler(t);
        // 2.获取类加载器
        ClassLoader loader = t.getClass().getClassLoader();
        // 3.获取接口(JDK 的动态代理要求必须定义接口...)
        Class<?>[] interfaces = t.getClass().getInterfaces();
        // 对原生对象包装,生成代理对象
        return (T) Proxy.newProxyInstance(loader, interfaces, handler);
    }
}

/**
 * 定义InvocationHandler:目标对象依赖他生成代理对象
 */
class UnitJdkDynamicHandler implements InvocationHandler {

    private Object obj;

    public UnitJdkDynamicHandler(Object target) {
        obj = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("*** 方法开始执行前 ****");
        System.out.println("**** 方法入参 ****");
        if (null != args) {
            for (Object obj : args) {
                System.out.println(String.valueOf(obj));
            }
        }
        System.out.println("\n**** 准备执行方法 ****\n");

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

        System.out.println("\n**** 方法执行完,返回结果 ****");
        System.out.println(String.valueOf(result));

        return result;
    }

}

/**
 * 定义业务接口
 */
interface IBusiness {
    void printHello(String name);
}

/**
 * 业务实现类
 */
class BusinessImpl implements IBusiness {

    @Override
    public void printHello(String name) {
        System.out.println("hello, " + name);
    }
}



class MyClassLoaderT extends ClassLoader {

    byte[] classBytes;

    public MyClassLoaderT(byte[] classBytes) {
        this.classBytes = classBytes;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> cl = defineClass(name, classBytes, 0, classBytes.length);
        if (cl == null) {
            throw new ClassNotFoundException();
        }
        return cl;
    }
}

JDK 动态代理的核心实现过程,通过查看JDK原代码可以总结为以下几句

//得到对象字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);

//InvocationHandler对应的class对象
Class[] constructorParams = { InvocationHandler.class };

//得到代理对象的Class对象
Class proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

//通过反射机制构造代理对象的实例
Constructor cons = cl.getConstructor(constructorParams);
Object proxy = (Object) cons.newInstance(new Object[] { handler });


4.2 cglib 类增强

package com.yli.aop;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * cglib:为原生类生成子类,子类即代理对象
 * 
 * @author yli
 */
public class CglibDynamicTest {

    public static void main(String[] args) {

        // 创建一个织入器
        Enhancer enhancer = new Enhancer();
        // 设置父类 (不要求依赖于接口)
        enhancer.setSuperclass(BusinessImpl.class);
        // 设置需要织入的逻辑
        enhancer.setCallback(new LogIntercept());
        // 使用织入器创建子类
        IBusiness newBusiness = (IBusiness) enhancer.create();
        newBusiness.printHello("Tom");
    }

    static class LogIntercept implements MethodInterceptor {

        @Override
        public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {

            System.out.println("--->方法执行前");

            // 执行原有逻辑,注意这里是invokeSuper
            Object rev = proxy.invokeSuper(target, args);

            System.out.println("--->方法执行后");
            return rev;
        }
    }
}

4.3 Javassist 动态修改类的字节码

注意依赖于 javassist 这个jar包,可以网上去下载

package com.yli.aop;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

/**
 * Javassist插件可以直接修改类的字节码
 */
public class SelfClassLoaderDynamicTest {

    public static void main(String[] args) throws Exception {
        // 用于取得字节码类,必须在当前的classpath中,使用全称
        CtClass ctClass = ClassPool.getDefault().get("com.yli.aop.A");
        // 需要修改的方法名称
        String mname = "print1";
        CtMethod mold = ctClass.getDeclaredMethod(mname);
        // 修改原有的方法名称
        String nname = mname + "$impl";
        mold.setName(nname);
        // 创建新的方法,复制原来的方法
        CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
        // 主要的注入代码
        StringBuffer body = new StringBuffer();
        body.append("{\nlong start = System.currentTimeMillis();\n");
        body.append("System.out.println(\"before\");\n");
        // 调用原有代码,类似于method();($$)表示所有的参数
        body.append(nname + "($$);\n");
        body.append("System.out.println(\"Call to method " + mname
                + " took \" +\n (System.currentTimeMillis()-start) + " + "\" ms.\");\n");

        body.append("}");
        // 替换新方法
        mnew.setBody(body.toString());
        // 增加新方法
        ctClass.addMethod(mnew);
        // 类已经更改,注意不能使用A a = new A();
        // 因为在同一个classloader中,不允许装载同一个类两次
        A a = (A) ctClass.toClass().newInstance();
        a.print();
    }

}

/**
 * 原生类:负责核心业务逻辑
 *
 * @author yli
 */
class A {

    public void print() {
        print1();
        System.out.println("aaaaaaaaa");

    }

    private void print1() {
        System.out.println("a1111111111");
    }

}

5. AOP 框架

主流的AOP框架有

AspectJ:这是语言级别的AOP框架实现,可以再编译阶段即完成切面织入

Spring AOP:基于JDK动态代理和cglib类增强来实现,他不是为了提供一个完整的AOP框架,而是为了很好的融合Spring IOC,也支持将AspectJ整合进来使用。

Jboss AOP:Jboss维护的一个开源AOP框架,在JBoss应用服务器上有很好的实现和融合



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值