您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Spring AOP 切点详解

时间:2022-03-18 10:18:32  来源:博客园  作者:Alinmei

本文内容

  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
 */
@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
 */
@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
 */
@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
 */
@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
 */
@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
 */
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
 */
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
 */
@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://www.cnblogs.com/kongbubihai/p/16017046.html



Tags:Spring AOP   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
你了解Spring AOP的这个技能点吗?有什么应用场景?
环境:Spring5.3.231. 介绍今天看Spring文档看到这么一个知识点《Control Flow Pointcuts》都不好翻译官方原文:Spring control flow pointcuts are conceptually similar to A...【详细内容】
2023-12-08  Search: Spring AOP  点击:(149)  评论:(0)  加入收藏
如何动态修改 Spring Aop 切面信息?让自动日志输出框架更好用
业务背景很久以前开源了一款 auto-log[1] 自动日志打印框架。其中对于 spring 项目,默认实现了基于 aop 切面的日志输出。但是发现一个问题,如果切面定义为全切范围过大,于是 v...【详细内容】
2023-07-22  Search: Spring AOP  点击:(65)  评论:(0)  加入收藏
在 SpringBoot 中使用 Spring AOP 实现接口鉴权
在 Spring Boot 中使用 Spring AOP 实现接口鉴权可以帮助我们对接口的调用进行权限控制。下面是一些常见的方法:1 基于注解的方法:在接口方法上添加自定义注解,通过定义切面类...【详细内容】
2023-03-30  Search: Spring AOP  点击:(269)  评论:(0)  加入收藏
什么是AOP,AOP能做什么?AOP的特点,Spring AOP的实现
一、什么是AOP?Aspect Oriented Programing 面向切面编程,相比较 oop 面向对象编程来说,Aop关注的不再是程序代码中某个类,某些方法,而aop考虑的更多的是一种面到面的切入,即层与...【详细内容】
2022-12-10  Search: Spring AOP  点击:(263)  评论:(0)  加入收藏
Spring AOP 切点详解
本文内容 Spring 10种切点表达式详解 切点的组合使用 公共切点的定义声明切点@Poincut@Poincut 的使用格式如下:@Poincut("PCD") // 切点表达式 表示对哪些方法进行增强publi...【详细内容】
2022-03-18  Search: Spring AOP  点击:(383)  评论:(0)  加入收藏
Spring AOP的8个关键术语:连接点、切点,增强目标等
概念AOP是“面向方面编程”,要解决的问题是,把重复性的横切逻辑独立出来,然后融合到业务逻辑中,达到和原来一样的业务流程;AOP的工作重心在于如何将增强应用于目标对象的连接点上...【详细内容】
2020-10-13  Search: Spring AOP  点击:(945)  评论:(0)  加入收藏
面试官问 Spring AOP 中两种代理模式的区别,我懵逼了
基本介绍代理模式是一种结构性设计模式。为对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,并允许在将请求提交给对象前后进行一些处理。被代理的对象...【详细内容】
2020-06-23  Search: Spring AOP  点击:(430)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(6)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(22)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(59)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(50)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(41)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(56)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(71)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(92)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(88)  评论:(0)  加入收藏
站内最新
站内热门
站内头条