这个系列是基于小傅哥的「手撸 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() {@Overridepublic 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);}@Overridepublic boolean matches(Class<?> clazz) {return pointcutExpression.couldMatchJoinPointsInType(clazz);}@Overridepublic boolean matches(Method method, Class<?> targetClass) {return pointcutExpression.matchesMethodExecution(method).alwaysMatches();}@Overridepublic ClassFilter getClassFilter() {return this;}@Overridepublic 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;}@Overridepublic Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),advised.getTargetSource().getTargetClass(), this);}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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 {@Overridepublic 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");}}}
 @Testpublic 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 的代码结构。

【手写 Spring 框架 AOP 篇】一、基于 JDK、CGlib 实现 AOP 切面相关推荐

  1. 手把手教你手写Spring框架

    手写spring准备工作: 新建一个maven工程: 架构 新建类: package com.spring;public class keweiqinApplicationContext {priva ...

  2. 30个类仿真手写spring框架V2.0版本

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  3. 05. 手写Spring核心框架

    目录 05 手写Spring核心框架 Pt1 手写IoC/DI Pt1.1 流程设计 Pt1.2 基础配置 application.properties pom.xml web.xml Pt1.3 注 ...

  4. 从头开始实现一个小型spring框架——手写Spring之集成Tomcat服务器

    手写Spring之集成Tomcat与Servlet 写在前面 一.Web服务模型及servlet 1.1 Web服务器 1.2 请求流程 二.实现 三.小结 写在前面 最近学习了一下spring的相关 ...

  5. 手写篇:如何手写RPC框架?

    手写篇:如何手写RPC框架? 首先我们讲下什么是RPC? RPC(Remote Procedure Call)远程过程调用协议,他是一种通过网络从远程计算机程序请求服务.简单的来说,就是通过网络进行远 ...

  6. 十年架构师:我是这样手写Spring的,用300行代码体现优雅之道

    起源 Spring作为一个开源框架,于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作<Expert One-On-One J2EE Development ...

  7. Marco's Java【Dubbo 之手写Dubbo框架实现远程调用】

    前言 关于Dubbo入门的网上教程也特别多,因此我没有专门出关于Dubbo的系列博文(主要呢- 也是在忙些工作上的事儿),用Dubbo特别简单,但是想要把Dubbo学好,学精还得花费不少时间的,特别是 ...

  8. 手写 Spring 事务、IOC、DI 和 MVC

    Spring AOP 原理 什么是 AOP? AOP 即面向切面编程,利用 AOP 可以对业务进行解耦,提高重用性,提高开发效率 应用场景:日志记录,性能统计,安全控制,事务处理,异常处理 AOP 底 ...

  9. JAVA项目代码手写吗_一个老程序员是如何手写Spring MVC的

    见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多 ...

最新文章

  1. Web Api单元测试写法
  2. 曝光 Facebook 内部高效工作 PPT 指南
  3. 一篇不错的讲DHCP的文章
  4. CodeForces - 197A Plate Game(博弈+思维)
  5. 如何在php中写内容,请问如何在内容模板中写标题和内容呢
  6. 前端学习(100):float注意点整理1
  7. get_magic_quotes_gpc()函数用法介绍
  8. Swift学习笔记八
  9. 机器学习-笔试题总结1
  10. 软件测试--用例编写
  11. android打飞机游戏素材,微信打飞机游戏素材
  12. redis cluster(5)- redis集群原理
  13. 小迪外卖小程序源码+后台_外卖cps 赚钱小程序源码
  14. 远程桌面连接如何重启计算机,如何使用“远程桌面连接”连接到另一台计算机...
  15. matlab elseif语句用法,Matlab if…elseif…elseif…else…end语句
  16. java全角数字_Java全角、半角字符的关系以及转换
  17. dat文件导入cad画图步骤_图说CAD|多文件、多布局图纸批量打印设置的8个关键步骤...
  18. 奥维地图显示所有标注
  19. 深入理解 TORCH.NN
  20. voip网络抓包录音

热门文章

  1. RAC(ReactiveCocoa)之 RAC宏定义
  2. 中国农历的Java实现
  3. 字体“XX”不支持样式“Regular”。
  4. 浅析加密算法三【Playfair密码】
  5. Kaiser窗口FIR滤波器设计估算参数
  6. 使用Node.js,AWS Lambda和MongoDB Atlas进行无服务器开发
  7. SAP中导出期末或指定日期库存
  8. Python课程第一天_上午_课程笔记(硬件以及进制)
  9. 基于遗传算法的LQR主动悬架控制
  10. spark小案例---根据IP计算归属地