SpringAOP之使用切入点创建通知
之前已经说过了SpringAOP中的几种通知类型以及如何创建简单的通知见地址
一、什么是切入点
通过之前的例子中,我们可以创建ProxyFactory的方式来创建通知,然后获取目标类中的方法。通过不同类型的通知,能对这些方法做不同的事。但是,这种方式会对整个类中的所有方法都有作用,但是很多时间我们只想对这个类中的部分方法进行通知处理,那就要使用切入点来精确地控制到特定的方法
- 也就是说,我们的切入点就是用来确定一个类中的方法(精确到方法),类似于定义一些规则一样,来找到和这个规则相匹配的类,知道这一点,往下看就容易多了。
二、切入点的分类
在Spring中的要创建切入点时,就要实现Pointcut类。
package org.springframework.aop;
public interface Pointcut{
ClassFilter getClassFilter();
MethodMatcher getMethodMacher();
}
以上两个方法返回的类的源码如下:
- ClassFilter
package org.springframework.aop;
/**
* 这是一个函数式接口,就是传入一个类,
* 如果这个类满足我们的要求,就返回true
* 也就是说这个切入点适用于这个类(也就是这个类不匹配我们的规则)
*/
@FunctionalInterface
public interface ClassFilter {
boolean matches(Class<?> var1);
}
- MethodMatcher
package org.springframework.aop;
import java.lang.reflect.Method;
/**
* 这个当然就是用来匹配方法了,
* 有两种类型,动态和静态,这个由isRuntime()的返回值来决定,true就是动态,false就是静态。这个类型就是决定了这个切入点是动态的还是静态的
*
*/
public interface MethodMatcher {
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
//用于静态匹配,就是和方法的参数无关
boolean matches(Method var1, Class<?> var2);
boolean isRuntime();
//用于动态匹配,也就是和方法的参数有关,因为参数是会变的
boolean matches(Method var1, Class<?> var2, Object... var3);
}
综上,切入点分为两种,动态切入点和静态切入点,动态切入点要每次检查方法的实参是不是满足要求,这会产生额外的开支。如果可以,如果可以,尽可能使用静态切入点。
三、切入点的八个实现类
实现类 | 描述 |
---|---|
org.springframework.aop.support.annotation.AnnotationMatchingPointcut | 在类或方法上找特定的注解,需要JDK5以上版本 |
org.springframework.aop.aspectj.AspectJExpressionPointcut | 使用AspectJ织入器以AspectJ语法评估切入点表态式 |
org.springframework.aop.support.ComposablePointcut | 使用诸如union()和intersection()等操作组合两个或多个切入点 |
org.springframework.aop.support.ControlFlowPointcut | 是一种特殊的切入点,它们匹配另一个方法的控制流中的所有方法,即任何作为另一个方法的结果而直接或间接调用的方法 |
org.springframework.aop.support.JdkRegexpMethodPointcut | 对方法名使用正则表达式定义切入点,要JDK4以上 |
org.springframework.aop.support.NameMatchMethodPointcut | 顾名思义,这是对方法名称列表进行简单的匹配 |
org.springframework.aop.support.DynamicMethodMatcherPointcut | 这个类作为创建动态切入点的基类 |
org.springframework.aop.support.StaticMethodMatcherPointcut | 作为创建表态切入点的基类 |
四、使用StaticMethodMatcherPointcut来创建静态切入点
- 创建一个类,两个方法。我们的目的就是只在walk()方法中创建环绕通知,打印一句,"I am a cute cat."
public class Cat {
public void sleep(){
System.out.println("sleep....");
}
public void walk(){
System.out.println("walking....");
}
}
- 创建切入点
public class MethodPointcutDemo extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, Class<?> aClass) {
return method.getName().equals("walk");
}
@Override
public ClassFilter getClassFilter() {
return clz -> clz == Cat.class;
// 上边的lambda表达式等于下边的这个
// return new ClassFilter() {
// @Override
// public boolean matches(Class<?> clz) {
// return clz == Cat.class;
// }
// };
}
}
上边的lambda表达式可查看https://www.cnblogs.com/Lyn4ever/p/11967959.html,后边出现时只写lambda表达式,且不再说明
在上边这个方法中,当然,我们可以不用实现getClassFilter()方法,因为这个方法已经被上级实现过了,我们就可以在matches方法中直接去判断这个类是不是Cat.class
- 通知类(这个和上一次是一样的,创建一个环绕通知)
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class CatAdvisor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//最不靠谱的方法,我们可以在这里判断这个method的是不是walk,从而要不要进行通知
System.out.println("I am a cute Cat.");
Object proceed = invocation.proceed();
return proceed;
}
}
当然,我们在这里也是可以判断这个方法名和类名的,为什么还要用切入点呢。可是这并不靠谱,我们中需要在这里实现我们的逻辑代码,而通过切入点来控制哪个类,哪个方法要被通知,这样更灵活。
- 测试方法
public static void main(String[] args) {
Cat cat = new Cat();
Pointcut pointcut = new MethodPointcutDemo();//切入点实例
Advice advice = new CatAdvisor();//通知类实例(就是我们的通知代码存放的类的对象)
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);//切面类,就是切入点和通知类的集合
ProxyFactory proxyFactory = new ProxyFactory();
//和之前代码的区别就是这一句,这里我们使用的是切入点控制,如果把这句注释了,就要用设置通知类
proxyFactory.addAdvisor(advisor);//设置切面类,包含切入点(控制通知点)和通知类(逻辑代码)
//如果注释了上面一句,用这一句的话,就会对这个类中的所有方法都通知
// proxyFactory.addAdvice(advice);//设置通知类
proxyFactory.setTarget(cat);
Cat proxy = (Cat) proxyFactory.getProxy();
proxy.sleep();
proxy.walk();
}
运行结果肯定就是我们想的那样
sleep....
I am a cute Cat.
walking....
五、使用DyanmicMatcherPointcut创建动态切入点
这个和上边的静态切入点是一样的,只不过是让传入方法的参数满足一定要求时,才会执行通知。由于篇幅原因,就不写了,在本文最后下载代码就能看懂。
六、其他类型的PointCut的实现类
1.简单的名称匹配 ( NameMatchMethodPointcut )
目标类和通知类还是之前的Cat类,之前的切入点的实现类不用写,因为这个类已经做了默认实现,感兴趣的可以看下它的源码,很简单的,就是匹配类名,和我们刚才创建的静态切入点差不多。
public class NameMatchPointcutDemo {
public static void main(String[] args) {
Cat cat = new Cat();
//这个类已经是个实现类,我们就不需要再去写实现类了
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("walk");
Advisor advisor = new DefaultPointcutAdvisor(pointcut,new CatAdvisor());
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(advisor);
proxyFactory.setTarget(cat);
Cat proxy = (Cat) proxyFactory.getProxy();
proxy.sleep();
proxy.walk();
}
}
2.使用正则表达式创建切入点 ( JdkRegexpMethodPointcut )
只写测试类,其他的都和上边一样
public class JdkRegexpPointcutDemo {
public static void main(String[] args) {
Cat cat = new Cat();
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*ee.*");//匹配中间有ee字母的,sleep()
Advisor advisor = new DefaultPointcutAdvisor(pointcut,new CatAdvisor());
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(advisor);
proxyFactory.setTarget(cat);
Cat proxy = (Cat) proxyFactory.getProxy();
proxy.sleep();
proxy.walk();
}
}
3.使用AspectJ切入点表达式创建切入点 ( AspectJExpressionPointcut )
使用AspectJ时要加入相关依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
public class AspectJExpressionPointcutDemo {
public static void main(String[] args) {
Cat cat = new Cat();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* walk*(..))");
Advisor advisor = new DefaultPointcutAdvisor(pointcut, new CatAdvisor());
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(advisor);
proxyFactory.setTarget(cat);
Cat proxy = (Cat) proxyFactory.getProxy();
proxy.sleep();
proxy.walk();
}
}
这个execution表达式的意思是:任何以walk开头的,具有任何参数和任何返回值的方法
4.创建注解匹配的切入点 ( AnnotationMatchingPointcut )
首先自定义一个注解,如果不是很懂,参考Java中自定义注解类,并加以运用
/**
* 这个注解是用于运行时期、可用于类、方法上
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAdvice {
}
然后在目标方法上添加这个注解(要被通知的类)
/**
* 为了不与之前的冲突,就新写了一个方法
*/
@MyAdvice
public void eat(){
System.out.println("eating....");
}
然后在main方法中指定这个注解名:
public class AnnotationPointcutDemo {
public static void main(String[] args) {
Cat cat = new Cat();
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut
.forMethodAnnotation(MyAdvice.class);
//这个类还有一个.forClassAnnotation()方法,就是指定类的
Advisor advisor = new DefaultPointcutAdvisor(pointcut, new CatAdvisor());
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(advisor);
proxyFactory.setTarget(cat);
Cat proxy = (Cat) proxyFactory.getProxy();
proxy.sleep();//不会被通知
proxy.walk();//不会被通知
proxy.eat();//会被通知
}
}
上一篇:SpringAOP基础
SpringAOP之使用切入点创建通知的更多相关文章
- Android 普通通知栏新方法,现在需要创建通知渠道才可以
先看看效果看看是不是你想要的 点击后 话不多所,贴代码 xml文件: <?xml version="1.0" encoding="utf-8"?>& ...
- iOS之创建通知、发送通知和移除通知的坑
1.创建通知,最好在viewDidLoad的方法中创建 - (void)viewDidLoad { [super viewDidLoad]; //创建通知 [[NSNotificationCenter ...
- 【FAQ】HMS Core推送服务与本地创建通知消息如何相互覆盖?
我们知道,单独使用HMS Core推送服务或本地创建通知消息,都可以实现通知消息的覆盖,方式分别为: 1.本地创建通知消息(简称本地通知消息) 通过notificationManager.notify ...
- 利用Spring AOP的通知类型以及创建通知
写在最前端 1.SpringAOP中共有六种通知类型,只要我们自定义一个类实现对应的接口,它们全都是org.springframework.aop包中的. 2.AOP的连接点可以是方法调用.方法调用本 ...
- SpringAOP学习之5种通知
一.Spring的AOP分为以下5种类型通知 ①前置通知(Before):在连接点执行前执行该通知 ②正常返回通知(AfterReturning):在连接点正常执行完后执行该通知,若目标方法执行异常则 ...
- SpringAOP中切入点的高级使用
上一篇 SpringAOP之使用切入点创建通知 SpringAOP中切点的高级使用 一.使用控制流切入点(ControlFlowPointcut) 什么是控制流切入点呢?看下面的代码(为了方便,就写进 ...
- spring-AOP之通知和顾问
通知和顾问都是切面的实现形式,其中通知可以完成对目标对象方法简单的织入功能. 而顾问包装了通知,可以让我们对通知实现更加精细化的管理,让我们可以指定具体的切入点. 通知分为前置通知,环绕通知及后置通知 ...
- Spring通知类型及使用ProxyFactoryBean创建AOP代理
Spring 通知类型 通过前面的学习可以知道,通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Ad ...
- 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring通知类型及使用ProxyFactoryBean创建AOP代理
通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口. Spring 通知按照在目标类 ...
随机推荐
- .net layui 批量导出
.net开发,前台使用layui框架,后台使用WCF 废话不多,直接上代码 1>文件引用: admin.css layui.css layui.js jquery.min.js layerToo ...
- git 的基本使用命令
1,git 的作用:git是目前世界上最先进的分布式版本控制系统(没有之一) 用在版本控制 和 代码整合 2,git 配置: 1,git init 初始化文件,会在自己的文件夹下创建一个.git ...
- (JavaScript) 字符串转16进制
function strToBase64() { var str = "https://www.baidu.com/"; var val = ""; for ( ...
- Java 并发编程(四):如何保证对象的线程安全性
01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合 ...
- VMware问题--无法获得 VMCI 驱动程序的版本: 句柄无效
关于VMware问题:无法获得 VMCI 驱动程序的版本: 句柄无效.驱动程序“vmci.sys”的版本不正确 问题截图: 解决 1.根据配置文件路径找到对应的.vmx文件: 2.用编辑器打开,找到v ...
- 「2019.8.9 考试」神仙的dp总让人无所适从
T1是个容斥,我掐手指一算他为了卡容斥的正确性,绝不会把n和m出的很相近($O(n^2)$算法在nm相等的时候达到最高时间复杂度),不然就太好做了,于是开了特判+各种卡常和滚动数组优化,卡到了70分, ...
- python学习之【第十六篇】:Python中的常用模块之OS模块、sys模块、random模块
1. OS模块 OS模块是与操作系统交互的一个接口.内部提供了以下方法: os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname& ...
- C++中对C的扩展学习新增内容———面向对象(继承)函数扩展性及虚函数机制
1.c语言中的多态,动态绑定和静态绑定 void do_speak(void(*speak)()) { speak(); } void pig_speak() { cout << &quo ...
- [LC] 112题 路径总和(在二叉树里判断是否有哪条路径之和等于某个值)
①题目 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum ...
- Spring boot 加入shiro支持
在项目添加依赖 <!-- shiro spring. --> <dependency> <groupId>org.apache.shiro</groupId& ...