本文内容

  1. Spring 10种切点表达式详解
  2. 切点的组合使用
  3. 公共切点的定义

声明切点@Poincut

@Poincut 的使用格式如下:

@Poincut("PCD") // 切点表达式 表示对哪些方法进行增强
public void pc(){} // 切点签名,返回值必须为void

10种切点表达式

AspectJ的切点指示符AspectJ pointcut designators (PCD) ,也就是俗称的切点表达式,Spring中支持10种,如下表:

表达式类型 作用 匹配规则
execution 用于匹配方法执行的连接点
within 用于匹配指定类型内的方法执行 within(x)匹配规则target.getClass().equals(x)
this 用于匹配当前AOP代理对象类型的执行方法,包含引入的接口类型匹配 this(x)匹配规则:x.getClass.isAssingableFrom(proxy.getClass)
target 用于匹配当前目标对象类型的执行方法,不包括引入接口的类型匹配 target(x)匹配规则:x.getClass().isAssignableFrom(target.getClass());
args 用于匹配当前执行的方法传入的参数为指定类型的执行方法 传入的目标位置参数.getClass().equals(@args(对应的参数位置的注解类型))!= null
@target 用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解 target.class.getAnnotation(指定的注解类型) != null
@args 用于匹配当前执行的方法传入的参数持有指定注解的执行 传入的目标位置参数.getClass().getAnnotation(@args(对应的参数位置的注解类型))!= null
@within 用于匹配所有持有指定注解类型内的方法 被调用的目标方法Method对象.getDeclaringClass().getAnnotation(within中指定的注解类型) != null
@annotation 用于匹配当前执行方法持有指定注解的方法 target.getClass().getMethod("目标方法名").getDeclaredAnnotation(@annotation(目标注解))!=null
bean Spring AOP扩展的,AspectJ没有对应的指示符,用于匹配特定名称的Bean对象的执行方法 ApplicationContext.getBean("bean表达式中指定的bean名称") != null

简单介绍下AspectJ中常用的3个通配符:

  • *:匹配任何数量字符
  • ..:匹配任何数量字符的重复,如任何数量子包,任何数量方法参数
  • +:匹配指定类型及其子类型,仅作为后缀防过载类型模式后面。

execution

用于匹配方法执行,最常用。

格式说明
   execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
  • 其中带 ?号的 modifiers-pattern?declaring-type-pattern?throws-pattern?是可选项
  • ret-type-pattern,name-pattern, parameters-pattern是必选项
  • modifier-pattern? 修饰符匹配,如public 表示匹配公有方法,*表示任意修饰符
  • ret-type-pattern 返回值匹配,* 表示任何返回值,全路径的类名等
  • declaring-type-pattern? 类路径匹配
  • name-pattern 方法名匹配,* 代表所有,xx*代表以xx开头的所有方法
  • (param-pattern) 参数匹配,指定方法参数(声明的类型),(..)代表所有参数,(*,String)代表第一个参数为任何值,第二个为String类型,(..,String)代表最后一个参数是String类型
  • throws-pattern? 异常类型匹配
举例说明
public class PointcutExecution {

    // com.crab.spring.aop.demo02包下任何类的任意方法
@Pointcut("execution(* com.crab.spring.aop.demo02.*.*(..))")
public void m1(){} // com.crab.spring.aop.demo02包及其子包下任何类的任意方法
@Pointcut("execution(* com.crab.spring.aop.demo02..*.*(..))")
public void m2(){} // com.crab.spring.aop包及其子包下IService接口的任意无参方法
@Pointcut("execution(* com.crab.spring.aop..IService.*(..))")
public void m3(){} // com.crab.spring.aop包及其子包下IService接口及其子类型的任意无参方法
@Pointcut("execution(* com.crab.spring.aop..IService+.*(..))")
public void m4(){} // com.crab.spring.aop.demo02.UserService类中有且只有一个String参数的方法
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(String))")
public void m5(){} // com.crab.spring.aop.demo02.UserService类中参数个数为2且最后一个参数类型是String的方法
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(*,String))")
public void m6(){} // com.crab.spring.aop.demo02.UserService类中最后一个参数类型是String的方法
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..,String))")
public void m7(){}
}

within

格式说明

within(类型表达式):目标对象target的类型是否和within中指定的类型匹配

匹配规则: target.getClass().equals(within表达式中指定的类型)

举例说明
public class PointcutWithin {
// 匹配 com.crab.spring.aop.demo02包及其子包下任何类的任何方法
@Pointcut("within(com.crab.spring.aop.demo02..*)")
public void m() {
} // 匹配m.crab.spring.aop.demo02包及其子包下IService类型及其子类型的任何方法
@Pointcut("within(com.crab.spring.aop.demo02..IService+)")
public void m2() {
} // 匹配com.crab.spring.aop.demo02.UserService类中所有方法,不含其子类
@Pointcut("within(com.crab.spring.aop.demo02.UserService)")
public void m3() {
}
}

this

格式说明

this(类型全限定名):通过aop创建的代理对象的类型是否和this中指定的类型匹配;this中使用的表达式必须是类型全限定名,不支持通配符。

this(x)的匹配规则是:x.getClass.isAssingableFrom(proxy.getClass)
举例说明
package com.crab.spring.aop.demo02.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils; /**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class PointcutThis {
interface I1{
void m();
}
static class C1 implements I1{ @Override
public void m() {
System.out.println("C1 m()");
}
}
// 匹配 I1类型或是其子类
@Pointcut("this(com.crab.spring.aop.demo02.aspectj.PointcutThis.I1)")
public void pc(){} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
// proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutThis.class);
// 获取代理
I1 proxy = proxyFactory.getProxy();
// 调用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判断代理对象是否是C1类型的
System.out.println(C1.class.isAssignableFrom(proxy.getClass()));
} }

来观察下输出

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutThis$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true

使用JDK动态代理生成的代理对象,其类型是I1类型。

思考下:将切点表达式改成下面的输出结果是?

// 匹配 C1类型或是其子类

@Pointcut("this(com.crab.spring.aop.demo02.aspectj.PointcutThis.C1)")

public void pc(){}

target

格式说明

target(类型全限定名):判断目标对象的类型是否和指定的类型匹配;表达式必须是类型全限定名,不支持通配符。

target(x)匹配规则:x.getClass().isAssignableFrom(target.getClass());
举例说明
@Aspect
public class PointcutTarget {
interface I1{
void m();
}
static class C1 implements I1{ @Override
public void m() {
System.out.println("C1 m()");
}
} // 匹配目标类型必须是
@Pointcut("target(com.crab.spring.aop.demo02.aspectj.PointcutTarget.C1)")
public void pc(){} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutTarget.class);
// 获取代理
I1 proxy = proxyFactory.getProxy();
// 调用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判断代理对象是否是C1类型的
System.out.println(C1.class.isAssignableFrom(target.getClass()));
} }

输出结果

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutTarget$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true

args

格式说明

args(参数类型列表)匹配当前执行的方法传入的参数是否为args中指定的类型;参数类型列表中的参数必须是类型全限定名,不支持通配符args属于动态切入点,也就是执行方法的时候进行判断的,开销非常大,非特殊情况最好不要使用。

args(String) //    方法个数为1,类型是String
args(*,String) // 方法参数个数2,第2个是String类型
args(..,String) // 方法个数不限制,最后一个必须是String
举例说明
package com.crab.spring.aop.demo02.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils; /**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class PointcutArgs {
interface I1{
void m(Object name);
}
static class C1 implements I1{ @Override
public void m(Object name) {
String type = name.getClass().getName();
System.out.println("C1 m() 参数类型 " + type);
}
} // 匹配方法参数个数1且类型是必须是String
@Pointcut("args(String)")
public void pc(){} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutArgs.class);
// 获取代理
I1 proxy = proxyFactory.getProxy();
// 调用方法
proxy.m("xxxx");
proxy.m(100L);
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判断代理对象是否是C1类型的
System.out.println(C1.class.isAssignableFrom(target.getClass()));
} }

观察下输出

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutArgs$C1.m(Object))
C1 m() 参数类型 java.lang.String
C1 m() 参数类型 java.lang.Long
JDK代理? false
CGLIB代理? true
true

参数类型传递是String时候增强了,而Long的时候没有执行增强方法。

@within

格式说明

@within(注解类型):匹配指定的注解内定义的方法。

匹配规则: 被调用的目标方法Method对象.getDeclaringClass().getAnnotation(within中指定的注解类型) != null
举例说明
package com.crab.spring.aop.demo02.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class PointcutAnnWithin {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnn {
} interface I1 {
void m();
} @MyAnn
static class C1 implements I1 {
@Override
public void m() {
System.out.println("C1 m()");
}
} // 匹配目标类型必须上必须有注解MyAnn
@Pointcut("@within(com.crab.spring.aop.demo02.aspectj.PointcutAnnWithin.MyAnn)")
public void pc() {
} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnWithin.class);
// 获取代理
I1 proxy = proxyFactory.getProxy();
// 调用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
//判断代理对象是否是C1类型的
System.out.println(C1.class.isAssignableFrom(target.getClass()));
} }

输出

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnWithin$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true

思考下父类上有注解,子类继承父类的方法,同时考虑下注解@Inherited是否在切点注解的场景?

@target

格式说明

@target(注解类型):判断目标对象target类型上是否有指定的注解;@target中注解类型也必须是全限定类型名。

匹配规则: target.class.getAnnotation(指定的注解类型) != null

注意,如果目标注解是标注在父类上的,那么定义目标注解时候应使用@Inherited标注,使子类能继承父类的注解。

举例说明
package com.crab.spring.aop.demo02.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils; import java.lang.annotation.*; /**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class PointcutAnnTarget {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited // 子类能继承父类的注解
@interface MyAnn2 {
} @MyAnn2 // 注解在父类上
static class P1 {
void m(){}
} static class C1 extends P1 {
@Override
public void m() {
System.out.println("C1 m()");
}
} // 匹配目标类型必须上必须有注解MyAnn
@Pointcut("@target(com.crab.spring.aop.demo02.aspectj.PointcutAnnTarget.MyAnn2)")
public void pc() {
} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnTarget.class);
// 获取代理
C1 proxy = proxyFactory.getProxy();
// 调用方法
proxy.m();
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
// 目标类上是否有切点注解
System.out.println(target.getClass().getAnnotation(MyAnn2.class)!= null);
} }

输出结果

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnTarget$C1.m())
C1 m()
JDK代理? false
CGLIB代理? true
true

从结果最后一行看,目标对象继承了父类的注解,符合@target的切点规则。

@args

格式说明

@args(注解类型):方法参数所属的类上有指定的注解;注意不是参数上有指定的注解,而是参数类型的类上有指定的注解。和args类似,不过针对的是参数类型上的注解。

匹配规则: 传入的目标位置参数.getClass().getAnnotation(@args(对应的参数位置的注解类型))!= null
举例说明
package com.crab.spring.aop.demo02.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils; import java.lang.annotation.*; /**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class PointcutAnnArgs {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited // 子类能继承父类的注解
@interface MyAnn3 {
} @MyAnn3
static class MyParameter{ } static class C1 {
public void m(MyParameter myParameter) {
System.out.println(myParameter.getClass().getAnnotation(MyAnn3.class));
System.out.println("C1 m()");
}
} // 匹配方法上最后的一个参数类型上有注解MyAnn3
@Pointcut("@args(..,com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs.MyAnn3)")
public void pc() {
} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnArgs.class);
// 获取代理
C1 proxy = proxyFactory.getProxy();
// 调用方法
MyParameter myParameter = new MyParameter();
proxy.m(myParameter);
System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
// 目标类上是否有切点注解
System.out.println(myParameter.getClass().getAnnotation(MyAnn3.class)!= null);
} }

观察结果

before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs$C1.m(MyParameter))
@com.crab.spring.aop.demo02.aspectj.PointcutAnnArgs$MyAnn3()
C1 m()
JDK代理? false
CGLIB代理? true
true

第二行中目标方法上输出了参数的注解。

最后一行判断参数类型上确实有注解。

@annotation

格式说明

@annotation(注解类型):匹配被调用的目标对象的方法上有指定的注解

匹配规则:target.getClass().getMethod("目标方法名").getDeclaredAnnotation(@annotation(目标注解))!=null

这个在针对特定注解的方法日志拦截场景下应用比较多。

举例说明
package com.crab.spring.aop.demo02.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils; import java.lang.annotation.*; /**
* @author zfd
* @version v1.0
* @date 2022/2/6 21:41
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class PointcutAnnotation {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnn4 {
} /**
* 父类 方法上都有@MyAnn4
*/
static class P1{
@MyAnn4
public void m1(){
System.out.println("P1 m()");
}
@MyAnn4
public void m2(){
System.out.println("P1 m2()");
}
} /**
* 子类
* 注意重新重写了父类的m1方法但是没有声明注解@Ann4
* 新增了m3方法带注解@Ann4
*/
static class C1 extends P1 {
@Override
public void m1() {
System.out.println("C1 m1()");
} @MyAnn4
public void m3() {
System.out.println("C1 m3()");
}
} // 匹配调用的方法上必须有注解
@Pointcut("@annotation(com.crab.spring.aop.demo02.aspectj.PointcutAnnotation.MyAnn4)")
public void pc() {
} @Before("pc()")
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint);
} public static void main(String[] args) throws NoSuchMethodException {
C1 target = new C1();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
// 获取C1上所有接口 spring工具类提供的方法
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
// 设置代理接口
proxyFactory.setInterfaces(allInterfaces);
// 添加切面
proxyFactory.addAspect(PointcutAnnotation.class);
// 获取代理
C1 proxy = proxyFactory.getProxy();
// 调用方法
proxy.m1();
proxy.m2();
proxy.m3(); System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy)); // 目标对象的目标方法上是否直接声明了注解MyAnn4
System.out.println(target.getClass().getMethod("m1").getDeclaredAnnotation(MyAnn4.class)!=null);
System.out.println(target.getClass().getMethod("m2").getDeclaredAnnotation(MyAnn4.class)!=null);
System.out.println(target.getClass().getMethod("m3").getDeclaredAnnotation(MyAnn4.class)!=null);
} }

观察下结果

C1 m1()
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnotation$P1.m2())
P1 m2()
before: execution(void com.crab.spring.aop.demo02.aspectj.PointcutAnnotation$C1.m3())
C1 m3()
JDK代理? false
CGLIB代理? true
false
true
true

简单分析下:

  1. C1中重写了m1方法,上面有没有 @Ann4,所有方法没有被拦截
  2. 其它的m2在父类上有注解@Ann4,m3在子类上也有注解@Ann4,所以拦截了。
  3. 最后3行输出了目标对象的3个方法上是否有注解的情况。

bean

格式说明

bean(bean名称):这个用在spring环境中,匹配容器中指定名称的bean。

匹配格式:ApplicationContext.getBean("bean表达式中指定的bean名称") != null
举例说明

定义一个bean

package com.crab.spring.aop.demo02.aspectj;

/**
* @author zfd
* @version v1.0
* @date 2022/2/6 23:30
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
public class MyBean {
private String beanName; public MyBean(String beanName) {
this.beanName = beanName;
} public void m() {
System.out.println("我是" + this.beanName);
}
}

切面中的切点和通知定义

@Aspect
public class PointcutBean {
// 容器中bean名称是"myBean1"的方法进行拦截
@Pointcut("bean(myBean1)")
public void pc() {
} @Before("pc()")
public void m(JoinPoint joinPoint) {
System.out.println("start " + joinPoint);
}
}

组合使用

@Aspect
@Configuration
@EnableAspectJAutoProxy // 自动生成代理对象
public class PointcutBeanConfig { // 注入 myBean1
@Bean("myBean1")
public MyBean myBean1() {
return new MyBean("myBean1");
} // myBean2
@Bean("myBean2")
public MyBean myBean2() {
return new MyBean("myBean2");
} // 注入切面
@Bean("pointcutBean")
public PointcutBean pointcutBean() {
return new PointcutBean();
} public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(PointcutBeanConfig.class);
MyBean myBean1 = context.getBean("myBean1", MyBean.class);
myBean1.m();
MyBean myBean2 = context.getBean("myBean2", MyBean.class);
myBean2.m();
} }

观察下结果

start execution(void com.crab.spring.aop.demo02.aspectj.MyBean.m())
我是myBean1
我是myBean2

myBean1的方法被拦截了。

上面介绍了Spring中10中切点表达式,下面介绍下切点的组合使用和公共切点的抽取。

切点的组合

切点与切点直接支持逻辑逻辑组合操作: &&||、 !。使用较小的命名组件构建更复杂的切入点表达式是最佳实践。

同一个类内切点组合
public class CombiningPointcut {

    /**
* 匹配 com.crab.spring.aop.demo02包及子包下任何类的public方法
*/
@Pointcut("execution(public * com.crab.spring.aop.demo02..*.*(..))")
public void publicMethodPc() {
} /**
* com.crab.spring.aop.demo02.UserService类的所有方法
*/
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..))")
public void serviceMethodPc(){} /**
* 组合的切点
*/
@Pointcut("publicMethodPc() && serviceMethodPc()")
public void combiningPc(){ }
/**
* 组合的切点2
*/
@Pointcut("publicMethodPc() || !serviceMethodPc()")
public void combiningPc2(){ } }
不同类之间切点组合

切点方法的可见性会影响组合但是不影响切点的匹配。

public class CombiningPointcut2 {

    /**
* com.crab.spring.aop.demo02.UserService类的所有方法
*/
@Pointcut("execution(* com.crab.spring.aop.demo02.UserService.*(..))")
public void serviceMethodPc2(){} /**
* 组合的切点,跨类组合
*/
@Pointcut("com.crab.spring.aop.demo02.aspectj.reuse.CombiningPointcut.publicMethodPc() && serviceMethodPc2()")
public void combiningPc(){ }
/**
* 组合的切点,跨类组合,由于serviceMethodPc是private, 此处无法组合
*/
@Pointcut("com.crab.spring.aop.demo02.aspectj.reuse.CombiningPointcut.serviceMethodPc() && serviceMethodPc2()")
public void combiningPc2(){ }
}

切点的公用

在使用企业应用程序时,开发人员通常希望从多个方面引用应用程序的模块和特定的操作集。建议为此目的定义一个捕获公共切入点表达式的 CommonPointcuts 方面。直接看案例。

不同层的公共切点

/**
* 公用的切点
* @author zfd
* @version v1.0
* @date 2022/2/7 8:53
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
public class CommonPointcuts { /**
* web层的通用切点
*/
@Pointcut("within(com.xyz.myapp.web..*)")
public void inWebLayer() {} @Pointcut("within(com.xyz.myapp.service..*)")
public void inServiceLayer() {} @Pointcut("within(com.xyz.myapp.dao..*)")
public void inDataAccessLayer() {} @Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
public void businessService() {} @Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
public void dataAccessOperation() {}
}

程序中可以直接引用这些公共的切点

/**
* 使用公共的切点
* @author zfd
* @version v1.0
* @date 2022/2/7 8:56
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
@Aspect
public class UseCommonPointcuts { /**
* 直接使用公共切点
*/
@Before("com.crab.spring.aop.demo02.aspectj.reuse.CommonPointcuts.inWebLayer()")
public void before(JoinPoint joinPoint){
System.out.println("before:" + joinPoint);
}
}

总结

本文介绍Spring中10种切点表达式,最常用的是execution,同时介绍切点如何组合使用和如何抽取公共的切点。

本节案例源码地址: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-aop/src/main/java/com/crab/spring/aop/demo02/aspectj

知识分享,转载请注明出处。学无先后,达者为先!

Spring系列25:Spring AOP 切点详解的更多相关文章

  1. spring框架 AOP核心详解

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...

  2. Spring Boot的每个模块包详解

    Spring Boot的每个模块包详解,具体如下: 1.spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. 2.spring-boot-s ...

  3. 2017.2.13 开涛shiro教程-第十二章-与Spring集成(一)配置文件详解

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第十二章-与Spring集成(一)配置文件详解 1.pom.xml ...

  4. 【Spring】——声明式事务配置详解

    项目中用到了spring的事务: @Transactional(rollbackFor = Exception.class, transactionManager = "zebraTrans ...

  5. Spring Boot源码中模块详解

    Spring Boot源码中模块详解 一.源码 spring boot2.1版本源码地址:https://github.com/spring-projects/spring-boot/tree/2.1 ...

  6. idea spring+springmvc+mybatis环境配置整合详解

    idea spring+springmvc+mybatis环境配置整合详解 1.配置整合前所需准备的环境: 1.1:jdk1.8 1.2:idea2017.1.5 1.3:Maven 3.5.2 2. ...

  7. spring boot 配置文件properties和YAML详解

    spring boot 配置文件properties和YAML详解 properties中配置信息并获取值. 1:在application.properties配置文件中添加: 根据提示创建直接创建. ...

  8. Spring源码之九finishRefresh详解

    Spring源码之九finishRefresh详解 公众号搜索[程序员田同学],专职程序员兼业余写手,生活不止于写代码 Spring IoC 的核心内容要收尾了,本文将对最后一个方法 finishRe ...

  9. Spring系列之Spring常用注解总结 转载

    Spring系列之Spring常用注解总结   传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点:1.如果所有的内容都配置在.xml文件中,那么.x ...

随机推荐

  1. Java线程--CountDownLatch使用

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11867656.html Java线程--CountDownLatch使用, 代码里头有详细注释 ...

  2. 泛型、Junit

    一. 泛型 1.泛型的好处 **1)编译时,检查添加元素的类型,提高了安全2)减少了类西较换的次数,提高效率 不使用泛型Dog -> Object -> Dog //放入到ArrayLis ...

  3. Lesson12——NumPy 字符串函数之 Part1:字符串操作函数

    NumPy 教程目录 1 NumPy 字符串函数 以下函数用于对 dtype 为 numpy.string_ 或 numpy.unicode_ 的数组执行向量化字符串操作. 它们基于 Python 内 ...

  4. CentOS7搭建ntp时钟服务器

    文章目录 服务器配置 远程客户端配置 服务器配置 # 关闭防火墙,selinux=disabled 1.# 服务器部署 [root@localhost ~]# yum -y install ntp n ...

  5. python 2048游戏控制器

    2048游戏控制器 1 evaluate 要用程序来处理就得对现实的问题进行量化,用数字来表示.在2048游戏中,我们的输入是一个棋局,让我们输出一个移动方向,这样我们需要对棋局进行量化,即我们要评估 ...

  6. php base64格式的图片字符串和图片文件相互转换的代码

    在移动端上传图片的时候通常会将图片转换成base64格式的字符串提交,所以此时需要使用服务器端的程序进行转换成二进制的数据.如下PHP代码实现了图片文件和base64格式的图片字符串相互转换的方法,同 ...

  7. mysql视图,索引

    一.视图 View 视图是一个虚拟表,是sql语句的查询结果,其内容由查询定义.同真实的表一样,视图包含一系列带有名称的列和行数据,在使用视图时动态生成.视图的数据变化会影响到基表,基表的数据变化也会 ...

  8. 实践GoF的23种设计模式:SOLID原则(上)

    摘要:本文以我们日常开发中经常碰到的一些技术/问题/场景作为切入点,示范如何运用设计模式来完成相关的实现. 本文分享自华为云社区<实践GoF的23种设计模式:SOLID原则(上)>,作者: ...

  9. 2022李宏毅作业hw1—新冠阳性人员数量预测。

    ​ 事前  : kaggle地址:ML2021Spring-hw1 | Kaggle 我的git地址: https://github.com/xiaolilaoli/lihongyi2022homew ...

  10. RFC3918聚合组播吞吐量测试——网络测试仪实操

    一.简介 1.RFC3918简介 历史 · 在1999年3月成为正式标准 功能 · 评测网络互连设备或网络系统的性能 · 网络设备: 交换机,路由器- 内容 · 定义了一整套测试方法,为不同厂家的设备 ...