IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【手写 Spring 框架 AOP 篇】一、基于 JDK、CGlib 实现 AOP 切面 -> 正文阅读

[Java知识库]【手写 Spring 框架 AOP 篇】一、基于 JDK、CGlib 实现 AOP 切面

这个系列是基于小傅哥的「手撸 Spring」学习的一个记录

目标

AOP 意为面向切面编程,通过预编译的方式和运行期间动态代理实现程序功能的统一维护。这句话可能不太好理解,可以先看下图:
在这里插入图片描述
从图中可以看出, AOP 的作用就是在对某些具有共同特点的方法进行拦截,并执行方法拦截器中的功能,完成对原有方法的功能的扩展。
这一次首先是要实现一个简单的切面功能。

设计

首先从代理一个方法,来观察代理方法的过程。

public void test_proxy_method() {
        Object targetObj = new UserService();
   
        IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(),
                new InvocationHandler() {
                    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        long start = System.currentTimeMillis();
                        Object value= method.invoke(targetObj, args);
                        long end = System.currentTimeMillis();
                        System.out.println("程序执行时间=" + (end-start) + "ms");
                        return value;
                    }
                });
        proxy.queryUserInfo();
    }

在这个代码中,我们对需要 targetObj 接口的实现类进行了代理。在 AOP 的实现过程中,需要对这个过程进行进一步的抽象。

  1. 抽象出解析表达式的类:表达式中规定了给那些类的那些方法进行增强
  2. 抽象出代理类生成:可能有不同的代理生成策略,或者是使用 JDK 方式,或者是使用 Cglib 方式
  3. 抽象出对代理方法的自定义拦截方法:不同的拦截对象可能需要增强的功能不同,有些是统计代码运行时间,有的只是简单增加日志,所以不能把功能都一股脑卸载具体的 invoke 逻辑中。并且这部分是要提供给用户的。

接下来,就可以根据这些点来看一个简易的 AOP 是怎么完成的了。

实现

在这里插入图片描述
这个是完成简易 AOP 功能的类图,看起来很复杂,但是仔细解析一下,就发现其实并不是很难。

1. 表达式校验

接口定义

  1. Pointcut 接口:定义了获取 ClassFilter、MethodMatcher 的两个方法
  2. ClassFilter 接口:定义 matches(Class clazz) 方法,匹配类
  3. MethodMatcher 接口:定义 matches(Method method, Class clazz) 方法,匹配方法
public interface PointCut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}
public interface ClassFilter {
    boolean matches(Class<?> clazz);
}
public interface MethodMatcher {
    boolean matches(Method method, Class<?> targetClass);
}

AspectJExpressionPointcut 实现类

AspectJExpressionPointcut 这个类继承了上述三个接口,完成了对具体表达式的解析和匹配规则,这里的匹配规则直接调用了 aspectjweaver 的实现。

public class AspectJExpressionPointcut implements PointCut, ClassFilter, MethodMatcher {

    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();

    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
    }

    private final PointcutExpression pointcutExpression;

    public AspectJExpressionPointcut(String expression) {
        PointcutParser pointCutParse = PointcutParser.
                getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
        pointcutExpression = pointCutParse.parsePointcutExpression(expression);
    }

    @Override
    public boolean matches(Class<?> clazz) {
        return pointcutExpression.couldMatchJoinPointsInType(clazz);
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
    }

    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }
}

在这一步,其实已经完成了对方法的拦截,可以测试一下。

public void test_aop() throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* cn.wufu.springframework.bean.UserService.*(..))");
        Class<UserService> clazz =  UserService.class;
        Method method = clazz.getDeclaredMethod("queryUserInfo");

        System.out.println(pointcut.matches(clazz));
        System.out.println(pointcut.matches(method, clazz));
    }

2. 生成代理对象

AdvisedSupport 类

这个类主要是持有了被代理对象、方法拦截器、方法匹配器,这是完成代理的重要组成部分,这里将其包装到一个类中,方便后续使用。

自定义的拦截器就是注入到这个类中。

public class AdvisedSupport {
    /**
     * 要代理的对象
     */
    private TargetSource targetSource;
    /**
     * 拦截器,对代理对象的功能进行增强
     */
    private MethodInterceptor methodInterceptor;
    /**
     * 方法匹配,判断方法是不是需要被
     */
    private MethodMatcher methodMatcher;

    public MethodInterceptor getMethodInterceptor() {
        return methodInterceptor;
    }
	// 省略 get/set 方法
}

/**
 * 目标对象,提供 Object 入参属性以及获取目标类 TargetClass 信息
 * @author wufu
 */
public class TargetSource {

    private final Object target;

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

    public Class<?>[] getTargetClass() {
        return this.target.getClass().getInterfaces();
    }

    public Object getTarget() {
        return target;
    }
}

代理实现

这里实现两种方式一种是 JDK 方式,一种是 Cglib 方式,所以首先定义一个接口 AopProxy。

public interface AopProxy {
    /**
     * 获取代理类,具体实现代理的方式有两种:JDK 方式、Cglib 方式
     * @return
     */
    Object getProxy();
}
// JDK 实现方式
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private final AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                advised.getTargetSource().getTargetClass(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
            MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args));
        }
        return method.invoke(advised.getTargetSource().getTarget(), args);
    }
}
// cglib 实现方式
public class CglibAopProxy implements AopProxy{

    private final AdvisedSupport advised;

    public CglibAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
        enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {
        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
            if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
                return advised.getMethodInterceptor().invoke(methodInvocation);
            }
            return methodInvocation.proceed();
        }

    }

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
            super(target, method, arguments);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            return this.methodProxy.invoke(this.target, this.arguments);
        }
    }
}

这两种方式只需要看他共性的部分:一个是 JDK 方式中的 invoke 方法,一个是 cglib 中的 DynamicAdvisedInterceptor 内部类的 intercept 方法,这两个方法中,如果匹配成功调用 methodInterceptor.invoke() 逻辑。

3. 测试

以下是准备的 IUserservice 接口、UserService 类以及自定义的拦截器 UserServiceInterceptor 。

public interface IUserService {

    String queryUserInfo();

    String register(String userName);
}
public class UserService implements IUserService{

    public String queryUserInfo() {
        return "wufu";
    }

    public String register(String userName) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "注册用户:" + userName + " success!";
    }
}
public class UserServiceInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            System.out.println("监控 - Begin By AOP");
            System.out.println("方法名称:" + invocation.getMethod());
            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
            System.out.println("监控 - End\r\n");
        }
    }

}
 @Test
    public void test_dynamic() {
        IUserService userService = new UserService();

        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTargetSource(new TargetSource(userService));
        advisedSupport.setMethodInterceptor(new UserServiceInterceptor());
        advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.wufu.springframework.bean.IUserService.*(..))"));

        IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        System.out.println("测试结果=" + proxy_jdk.queryUserInfo());

        IUserService proxy_cglib = (IUserService) new CglibAopProxy((advisedSupport)).getProxy();
        System.out.println("测试结果=" + proxy_cglib.register("wufu 02"));
    }

在这里插入图片描述

总结

这个部分设计比代码更重要一些,主要关注一下 AOP 的代码结构。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 07:54:27  更:2022-05-08 07:56:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:28:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码