Spring系列25:Spring AOP 切点详解
本文内容
- Spring 10种切点表达式详解
- 切点的组合使用
- 公共切点的定义
声明切点@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
简单分析下:
- C1中重写了m1方法,上面有没有 @Ann4,所有方法没有被拦截
- 其它的m2在父类上有注解@Ann4,m3在子类上也有注解@Ann4,所以拦截了。
- 最后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,同时介绍切点如何组合使用和如何抽取公共的切点。
知识分享,转载请注明出处。学无先后,达者为先!
Spring系列25:Spring AOP 切点详解的更多相关文章
- spring框架 AOP核心详解
		AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ... 
- Spring Boot的每个模块包详解
		Spring Boot的每个模块包详解,具体如下: 1.spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. 2.spring-boot-s ... 
- 2017.2.13 开涛shiro教程-第十二章-与Spring集成(一)配置文件详解
		原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第十二章-与Spring集成(一)配置文件详解 1.pom.xml ... 
- 【Spring】——声明式事务配置详解
		项目中用到了spring的事务: @Transactional(rollbackFor = Exception.class, transactionManager = "zebraTrans ... 
- Spring Boot源码中模块详解
		Spring Boot源码中模块详解 一.源码 spring boot2.1版本源码地址:https://github.com/spring-projects/spring-boot/tree/2.1 ... 
- 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. ... 
- spring boot 配置文件properties和YAML详解
		spring boot 配置文件properties和YAML详解 properties中配置信息并获取值. 1:在application.properties配置文件中添加: 根据提示创建直接创建. ... 
- Spring源码之九finishRefresh详解
		Spring源码之九finishRefresh详解 公众号搜索[程序员田同学],专职程序员兼业余写手,生活不止于写代码 Spring IoC 的核心内容要收尾了,本文将对最后一个方法 finishRe ... 
- Spring系列之Spring常用注解总结 转载
		Spring系列之Spring常用注解总结 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点:1.如果所有的内容都配置在.xml文件中,那么.x ... 
随机推荐
- js window.event
			转载请注明来源:https://www.cnblogs.com/hookjc/ 描述event代表事件的状态,例如触发event对象的元素.鼠标的位置及状态.按下的键等等.event对象只在事件发生的 ... 
- MySQL高质量博文链接集合
			1. 『浅入浅出』MySQL 和 InnoDB https://draveness.me/mysql-innodb.html 
- Spark RDD学习
			RDD(弹性分布式数据集)是Spark的核心抽象.它是一组元素,在集群的节点之间进行分区,以便我们可以对其执行各种并行操作. 创建RDD的两种方式: 并行化驱动程序中的现有数据: 引用外部存储系统中的 ... 
- 500行代码了解Mecached缓存客户端驱动原理
			原创不易,求分享.求一键三连 缓存一般是用来加速数据访问的效率,在获取数据耗时高的场景下使用缓存可以有效的提高数据获取的效率. 比如,先从memcached中获取数据,如果没有则查询mysql中的数据 ... 
- 搭建 NFS 服务 & 实时同步
			今日内容 NFS简介 实现 NFS 文件同步功能 NFS 配置详解 统一用户 搭建 web 服务 NFS 实现文件共享 内容详细 1.NFS 简介 1.1 介绍 实现多台 web 服务器可以共享数据资 ... 
- Solution -「ZJOI 2020」「洛谷 P6631」序列
			\(\mathcal{Description}\) Link. 给定一个长为 \(n\) 的非负整数序列 \(\lang a_n\rang\),你可以进行如下操作: 取 \([l,r]\),将 ... 
- 黑客高端de浏览器使用秘籍
			搜索引擎已经成为上网必不可少的工具之一,聪明的黑客们发现,搜索引擎也能成为发动网络攻击的工具. Google Hacking,原指利用Google搜索引擎搜索信息来进行入侵的技术和行为,如今已不再局限 ... 
- WebKit Inside: DOM树的构建
			当客户端App主进程创建WKWebView对象时,会创建另外两个子进程:渲染进程与网络进程.主进程WKWebView发起请求时,先将请求转发给渲染进程,渲染进程再转发给网络进程,网络进程请求服务器.如 ... 
- Spring Security即将弃用WebSecurityConfigurerAdapter配置类
			用过WebSecurityConfigurerAdapter的都知道对Spring Security十分重要,总管Spring Security的配置体系.但是马上这个类要废了,你没有看错,这个类将在 ... 
- ZCC2410同步升压变换芯片
			ZCC2410???? 22V/25A同步升压变换器 ZCC2410是一种高效率.高功率密度.宽输入范围.电流模式升压变换器.该转换器集成了一个10mΩ.24V电源开关和一个同步门高转换器效率的驱动 ... 
