AOP

    xiaoxiao2022-07-13  147

    AOP的基本概念

    Advice(通知、切面): 某个连接点所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。 @Before: 标识一个前置增强方法,相当于BeforeAdvice的功能. @After: final增强,不管是抛出异常或者正常退出都会执行. @AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行. @AfterThrowing: 异常抛出增强,相当于ThrowsAdvice. @Around: 环绕增强,相当于MethodInterceptor. JointPoint(连接点):程序运行中的某个阶段点,比如方法的调用、异常的抛出等。 Pointcut(切入点): JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切入点表达式。 Advisor(增强): 是PointCut和Advice的综合体,完整描述了一个advice将会在pointcut所定义的位置被触发。 @Aspect(切面): 通常是一个类的注解,里面可以定义切入点和通知 AOP Proxy: AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。

    示例:

    //Pointcut表示式 @Pointcut("execution(* com.savage.aop.MessageSender.*(..))") //Point签名 private void log(){}

    由下列方式来定义或者通过 &&、 ||、 !、 的方式进行组合:

    execution:用于匹配方法执行的连接点;

    within:用于匹配指定类型内的方法执行;

    this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

    target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

    args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;

    @within:用于匹配所以持有指定注解类型内的方法;

    @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;

    @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

    @annotation:用于匹配当前执行方法持有指定注解的方法;

    eg.

    任意公共方法的执行:execution(public * *(..))

    任何一个以“set”开始的方法的执行:execution(* set*(..))

    AccountService 接口的任意方法的执行:execution(* com.xyz.service.AccountService.*(..))

    定义在service包里的任意方法的执行: execution(* com.xyz.service.*.*(..))

    定义在service包和所有子包里的任意类的任意方法的执行:execution(* com.xyz.service..*.*(..))第一个表示匹配任意的方法返回值, …(两个点)表示零个或多个,第一个…表示service包及其子包,第二个表示所有类, 第三个*表示所有方法,第二个…表示方法的任意参数个数

    定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")

    pointcutexp包里的任意类: within(com.test.spring.aop.pointcutexp.*)

    pointcutexp包和所有子包里的任意类:within(com.test.spring.aop.pointcutexp..*)

    实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类:this(com.test.spring.aop.pointcutexp.Intf)

    当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型 带有@Transactional标注的所有类的任意方法:

    @within(org.springframework.transaction.annotation.Transactional) @target(org.springframework.transaction.annotation.Transactional)

    带有@Transactional标注的任意方法:@annotation(org.springframework.transaction.annotation.Transactional)

    @within和@target针对类的注解,@annotation是针对方法的注解

    参数带有@Transactional标注的方法:@args(org.springframework.transaction.annotation.Transactional) 参数为String类型(运行是决定)的方法: args(String)

    织入 @AfterReturning( pointcut="execution(* com.abc.service.*.access*(..)) && args(time, name)", returning="returnValue") public void access(Date time, Object returnValue, String name) { System.out.println("目标方法中的参数String = " + name); System.out.println("目标方法中的参数Date = " + time); System.out.println("目标方法的返回结果returnValue = " + returnValue); }

    直接使用@Order注解来修饰一个切面类:使用这个注解时可以配置一个int类型的value属性,该属性值越小,优先级越高

    同一个切面类里的两个相同类型的增强处理在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这两个增强处理,没有办法指定它们的织入顺序。即使给这两个 advice 添加了 @Order 这个注解,也不行!

    具体用法 .

    一、匹配包、类型 within

    /** * 匹配WithinService类里头的所有方法 * @Pointcut("within(cn.evchar.aop.within.WithinService)") * 匹配com.imooc包及子包下所有类的方法 * @Pointcut("within(cn.evchar.aop.within..*)") */ @Aspect @Component public class WithinAspectConfig { @Pointcut("within(cn.evchar.aop.within..*)") public void matchType(){ } @Before("matchType()") public void before(){ // 编写需要 织入 System.out.println(""); System.out.println("### before 开始织入逻辑"); } }

    二、匹配对象 bean, this, target

    /** * * //匹配AOP对象的目标对象为指定类型的方法,即LogService的aop代理对象的方法 * @Pointcut("this(cn.evchar.aop.target.Loggable)") * * //匹配实现Loggable接口的目标对象(而不是aop代理后的对象)的方法 * @Pointcut("target(cn.evchar.aop.target.Loggable)") * * //this 可以拦截 DeclareParents(Introduction) * //target 不拦截 DeclareParents(Introduction) * * //匹配所有以Service结尾的bean里头的方法 * */ @Aspect @Component public class ObjectAspectConfig { @Pointcut("bean(logService)") public void matchCondition(){} // @Pointcut("this(cn.evchar.aop.target.Loggable)") // public void matchCondition(){} // @Pointcut("target(cn.evchar.aop.target.Loggable)") // public void matchCondition(){} @Before("matchCondition()") public void before(){ System.out.println(""); System.out.println("### before "); } }

    三、匹配参数 Args

    /** * * //匹配任何以find开头而且只有一个Long参数的方法 * @Pointcut("execution(* *..find*(Long))") * //匹配任何以find开头的而且第一个参数为Long型的方法 * @Pointcut("execution(* *..find*(Long,..))") * //匹配任何只有一个Long参数的方法 * @Pointcut("within(cn.evchar.aop.service.*) && args(Long)") * //匹配第一个参数为Long型的方法 * @Pointcut("within(cn.evchar.aop.service.*) && args(Long,..)") * */ @Aspect @Component public class ArgAspectConfig { // 匹配一个参数 @Pointcut("args(Long) && within(cn.evchar.aop.service.*)") public void matchArgs(){} // 匹配,两个参数,第二个为任意参数 // @Pointcut("args(Long,..) && within(cn.evchar.aop.service.*)") // public void matchArgs(){} // 匹配,两个具体类型参数 // @Pointcut("args(Long,String) && within(cn.evchar.aop.service.*)") // public void matchArgs(){} @Before("matchArgs()") public void beforeArgs(){ System.out.println(""); System.out.println("#### before"); } }

    四、匹配注解 anno @annotation,@within,@target,@args

    /** * //匹配方法标注有AdminOnly的注解的方法 * @Pointcut("@annotation(cn.evchar.aop.anno.AdminOnly) && within(cn.evchar.aop..*)") * //匹配标注有NeedSecured的类底下的方法 //class级别 * @Pointcut("@within(cn.evchar.aop.anno.NeedSecured) && within(cn.evchar.aop..*)") * //匹配标注有NeedSecured的类及其子类的方法 //runtime级别 * 在spring context的环境下,二者没有区别 * @Pointcut("@target(cn.evchar.aop.anno.NeedSecured) && within(cn.evchar.aop..*)") * //匹配传入的参数类标注有Repository注解的方法 * @Pointcut("@args(cn.evchar.aop.anno.NeedSecured) && within(cn.evchar.aop..*)") * */ @Aspect @Component public class AnnoAspectConfig { // 方法上面注解 // @Pointcut("@annotation(cn.evchar.aop.anno.AdminOnly)") // public void matchAnno(){} // 类上面注解 // @Pointcut("@within(cn.evchar.aop.anno.NeedSecured) && within(cn.evchar.aop..*)") // public void matchAnno(){} // 类上面注解 @Pointcut("@target(cn.evchar.aop.anno.NeedSecured) && within(cn.evchar.aop..*)") public void matchAnno(){} // 参数上面注解 // @Pointcut("@args(cn.evchar.aop.anno.NeedSecured) && within(cn.evchar.aop..*)") // public void matchAnno(){} @Before("matchAnno()") public void before(){ System.out.println(""); System.out.println("### before annotation "); } }

    五、匹配方法 execution 标注 ? 部分,可以省略 ,其余必须存在。

    execution( modifier-pattern? 修饰符部分 例如 public private... ret-type-pattern 返回值部分 例如 return String; declaring-type-pattern? 描述包名 例如 cn.evchar.... name-pattern(param-pattern) 描述方法名,描述方法参数 throws-pattern? 匹配抛出的异常 ) /** * * 匹配任何公共方法 * @Pointcut("execution(public * cn.evchar.aop.service.*.*(..))") * * 匹配com.imooc包及子包下Service类中无参方法 * @Pointcut("execution(* cn.evchar.aop..*Service.*())") * * 匹配com.imooc包及子包下Service类中的任何只有一个参数的方法 * @Pointcut("execution(* cn.evchar.aop..*Service.*(*))") * * 匹配com.imooc包及子包下任何类的任何方法 * @Pointcut("execution(* cn.evchar.aop..*.*(..))") * * 匹配com.imooc包及子包下返回值为String的任何方法 * @Pointcut("execution(String cn.evchar.aop..*.*(..))") * * 匹配异常 * execution(public * cn.evchar.aop.service.*.*(..) throws java.lang.IllegalAccessException) * */ @Aspect @Component public class ExecutionAspectConfig { // 扫描包下面的类,不包括子包 // @Pointcut("execution(public * cn.evchar.aop.service.*Service.*(..))") // public void matchExecution(){} // 扫描包下面的所有类,包括子包 // @Pointcut("execution(public * cn.evchar.aop.service..*Service.*(..))") // public void matchExecution(){} // 拦截返回是String 类型 // @Pointcut("execution(public String cn.evchar.aop.service..*Service.*(..))") // public void matchExecution(){} // 拦截返回是void // @Pointcut("execution(public String cn.evchar.aop.service..*Service.*(..))") // public void matchExecution(){} // 返回无参 // @Pointcut("execution(public String cn.evchar.aop.service..*Service.*())") // public void matchExecution(){} // 返回第一个参数Long 类型的 // @Pointcut("execution(public String cn.evchar.aop.service..*Service.*(Long))") // public void matchExecution(){} @Pointcut("execution(public String cn.evchar.aop.service..*Service.*(..) " + "throws java.lang.IllegalAccessException)") public void matchExecution(){} @Before("matchExecution()") public void before(){ System.out.println(""); System.out.println("#### before"); } }
    最新回复(0)