Spring AOP 的使用
admin
2024-03-01 21:23:14
0

声明切点@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
beanSpring 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;@Aspect
public class PointcutThis {interface I1{void m();}static class C1 implements I1{@Overridepublic 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{@Overridepublic 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;@Aspect
public class PointcutArgs {interface I1{void m(Object name);}static class C1 implements I1{@Overridepublic 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;@Aspect
public class PointcutAnnWithin {@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@interface MyAnn {}interface I1 {void m();}@MyAnnstatic class C1 implements I1 {@Overridepublic 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;@Aspect
public class PointcutAnnTarget {@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Inherited // 子类能继承父类的注解@interface MyAnn2 {}@MyAnn2 // 注解在父类上static class P1 {void m(){}}static class C1 extends P1 {@Overridepublic 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;@Aspect
public class PointcutAnnArgs {@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Inherited // 子类能继承父类的注解@interface MyAnn3 {}@MyAnn3static 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;@Aspect
public class PointcutAnnotation {@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@interface MyAnn4 {}/*** 父类 方法上都有@MyAnn4*/static class P1{@MyAnn4public void m1(){System.out.println("P1 m()");}@MyAnn4public void m2(){System.out.println("P1 m2()");}}/*** 子类* 注意重新重写了父类的 m1 方法但是没有声明注解@Ann4* 新增了 m3 方法带注解@Ann4*/static class C1 extends P1 {@Overridepublic void m1() {System.out.println("C1 m1()");}@MyAnn4public 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));// 目标对象的目标方法上是否直接声明了注解 MyAnn4System.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

简单分析下:

  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;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 方面。直接看案例。

不同层的公共切点

/*** 公用的切点*/
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() {}
}

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


@Aspect
public class UseCommonPointcuts {/*** 直接使用公共切点*/@Before("com.crab.spring.aop.demo02.aspectj.reuse.CommonPointcuts.inWebLayer()")public void before(JoinPoint joinPoint){System.out.println("before:" + joinPoint);}
}

带参数的切点

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AspectConfig.class)
public class AspectDemo {@Autowiredprivate List performList;@Testpublic void testPerform(){for (Perform perform : performList) {perform.doPerform("dc1");}}
}@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AspectConfig {@Beanpublic Audience audience() {return new Audience();}
}@Aspect
public class Audience {/*** 声明一个切点*/@Pointcut("execution(* io2.dc.Perform.doPerform(..)) && bean(poet)")public void perform(){};/*** 声明一个可以接收参数的切点* @param name*/@Pointcut("execution(* io2.dc.Perform.doPerform(String)) && args(name)")public void performWithArg(String name){};@Before("perform()")public void drinkWater() {System.out.println("喝口水");}@Before("performWithArg(name)")public void printName(String name) {System.out.println((name + "开始表演了"));}
}public interface Perform{int doPerform(String name);
}@Component
public class Poet implements Perform {@Overridepublic int doPerform(String name) {System.out.println(("朗诵" + name));return 0;}
}@Component
public class Singer implements Perform {@Overridepublic int doPerform(String name) {System.out.println(("歌唱家" + name));return 0;}
}

参考资料

  • https://www.cnblogs.com/kongbubihai/p/16017046.html
  • 《Spring 实战第4版》

相关内容

热门资讯

河南现制茶饮品牌加快布局上合国... 新华社郑州7月26日电(记者牛少杰 徐正源)7月23日至27日,上海合作组织媒体智库峰会在河南省郑州...
原创 湿... 那天在厨房收拾,看着一袋子蔫头耷脑的生菜,心里直犯愁。前几天超市打折囤多了,放冰箱没两天就软趴趴的,...
原创 3... 救命!这3道菜简直是米饭界的"黑社会",每次上桌都引发全家抢饭大战!作为一个常年和体重秤对抗的吃货,...
东阳美食盛宴,午餐味蕾的狂欢之... 东阳美食盛宴呈现了一场味蕾之旅的盛宴,午餐时分,各色美食琳琅满目,香气扑鼻,让人垂涎欲滴,从传统的东...
原创 烘... 清晨六点的厨房,烤箱的暖光透过玻璃门晕染开,像一块刚出炉的戚风蛋糕的金黄边缘。搅拌碗里渐渐蓬发的蛋白...
原创 清... 晨光透过纱帘洒在木质餐桌上,玻璃杯里的温水正冒着丝丝热气。你从橱柜深处取出那罐被遗忘许久的蜂蜜,琥珀...
坐火车去参观免费开放的博物馆,... 近日,河南洛阳汉魏洛阳故城遗址博物馆正式开馆免费面向公众开放引来众多文博爱好者打卡正逢暑期不少家长也...
原创 雅... 雅鲁藏布江的江水滚滚奔腾,永不停歇,它们用无尽的力量和生命,诠释着大自然的伟大与磅礴。由于雅鲁藏布江...
避暑旅行迎高峰:热门城市景区游... “谁能告诉我现在哪个城市最凉快,我要去避暑。” 2025年暑运周期接近半程,避暑旅行迎来高峰。 美团...
在内蒙古的五天时光里怎么玩?来... 内蒙古是种瘾,去过一次就戒不掉。它的美,不是刻意的雕琢,而是自然的馈赠 —— 草原的绿能治愈焦虑,沙...
北京旅游跟团5天4晚多少钱,北... 北京,这座融合了古老与现代、传统与创新的城市,一直是我心中的向往之地。为了能让这次旅行更加顺利和充实...
原创 清... 清晨的阳光透过窗帘缝隙洒进厨房,玻璃杯上凝结的水珠缓缓滑落。冰箱门打开的瞬间,冷气裹挟着奶香扑面而来...
见识了预制米饭里的黑科技,打工... “大米,水。” 我再三确认了配料表,“常温保存9个月?” 等等,这合理么? 面对手里写着“鲜米饭”...
天热这么吃鸭,清爽解腻,真有一... 大部分人喜欢吃鸭子,鸭肉性寒,清热养胃,很适合燥热的吃货们,尤其是火气旺,容易上火的。 于是乎就有了...
原创 厨... 清晨的阳光透过纱帘洒进厨房,铸铁锅在灶台上微微冒着热气。记得小时候,每当外婆开始准备早餐,整个屋子都...
原创 一... 晨光中的米香 清晨的阳光透过厨房的纱帘,在灶台上投下斑驳的光影。电饭煲的蒸汽孔正缓缓吐出白色的雾气...
文化中国行|徐州淮海战役纪念馆... 现代快报讯(记者 张晓培 文/摄)盛夏的徐州,烈日炙烤着大地,热浪在空气中不断翻涌,可游客们的热情却...
【温馨提示】房山区启动防汛三级... 2025年7月27日11时房山区已启动防汛三级应急响应,为保障市民游客安全,目前,房山区周口店遗址、...
一家人去北京5日攻略费用,北京... 北京,这座古老与现代交织的城市,以其丰富的历史文化遗产和现代化的都市风貌吸引着无数游客的目光。从宏伟...