什么是AOP
1. AOP式一种编程范式,不是编程语言,如:面向对象编程 函数式编程 事件驱动编程 面向切面编程
2.解决特定问题,不能解决所有问题
3. 是oop的补充,不是替代
面向切面编程的目的:
1. 集中处理某一关注点/横切逻辑
2.可以很方便地添加/删除关注点
3.侵入性少,增强代码可读性及可维护性
AOP的应用场景:
1.权限控制
2.缓存控制
3.事务控制
4.审计控制
5.性能监控
6.分布式追踪
7.异常处理
实例:检查权限
如果要限定一些接口 只能超级管理员可以访问,那么,一般的做法就是在每个方法的开始检查权限,这样非常不灵活,更期待一个注解来完成,所以@Aspect注解就能快速定义这种校验注解
@Aspect三部曲
1. 建立普通注解
package com.asange.aop.security; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * com.asange.aop.security * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AdminOnly { }
2. @Aspect建立关联
package com.asange.aop.security; import com.asange.aop.service.AuthService; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * com.asange.aop.security * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Aspect @Component public class SecurityAspect { @Pointcut("@annotation(AdminOnly)") public void adminOnly() { } }
@Pointcut参数 模版:@anootation(xxx) xxx就是我们声明注解的名字
3. 设置切面关注点(在方法之前@Before,在方法之后 @After...)
package com.asange.aop.security; import com.asange.aop.service.AuthService; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * com.asange.aop.security * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Aspect @Component public class SecurityAspect { @Autowired AuthService authService; @Pointcut("@annotation(AdminOnly)") public void adminOnly() { } @Before("adminOnly()") public void check() { authService.checkPermission(); } }
注意 @Before("xxx()") 是PointCut声明的方法名
在这里 我们模拟插入商品和删除商品 我们都关注这两个动作是否符合超级管理员权限
package com.asange.aop.service; import com.asange.aop.security.AdminOnly; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * com.asange.aop.service * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Service public class ProductService2 { @Autowired AuthService authService; @AdminOnly public void insert() { System.out.println("insert success"); } @AdminOnly public void delete(String id) { System.out.println("delete success id:" + id); } }
用admin管理员账号测试:
用test用户测试:
本演示的例子代码点击
spring AOP使用方式
Aop常见注解3个:
其中 @Aspect非常固定,重点介绍一下@Pointcut 切面表达式
分类 | 含义 |
designators指示器 | execution ... |
通配符 | * .. +等符号匹配 |
运算操作符 | && || !等操作符 |
通配符:
1. * 匹配任意数量的字符
2. + 匹配指定类以及其子类
3 .. 两个点;一般用于匹配任意数的子包或参数
运算操作符:
1. && 与操作符号
2. || 或操作符号
3. ! 非操作符号
designators 指示器
1. 匹配方法 execution()
2. 匹配注解 @target() @args() @within() @annotation()
3. 匹配包/类型 within()
4. 匹配对象 this() bean() target()
5. 匹配参数 args()
匹配包/类型
/** * 匹配ProductService2类里面的所有方法 */ @Pointcut("within(com.asange.aop.service.ProductService2)") public void adminService() { } /** * 匹配 com.asange.aop.service包及子包下所有类的方法 */ @Pointcut("within(com.asange.aop.service..*)") public void adminService2() { }
实例: 用within 校验ProductService所有方法检查用户权限!
package com.asange.aop.security; import com.asange.aop.service.AuthService; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * com.asange.aop.security * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Aspect @Component public class SecurityAspect { @Autowired AuthService authService; /** * 匹配ProductService类里面的所有方法 */ @Pointcut("within(com.asange.aop.service.ProductService)") public void adminService() { } @Before("adminService()") public void checkClass() { authService.checkPermission(); } }
package com.asange.aop.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * com.asange.aop.service * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Service public class ProductService { @Autowired AuthService authService; public void insert() { System.out.println("insert success"); } public void delete(String id) { System.out.println("delete success id:" + id); } }
测试:
用admin账号测试
用非admin账号测试
在这里ProductService的方法上并为添加注解
可以发现@PointCut("within("类的全路径"))可以校验这个指定的类的所有方法 检查我们指定的逻辑
匹配对象
/** * 匹配AOP对象的目标对象为指定类型的方法,即UserDao的aop代理对象的方法 */ @Pointcut("this(com.asange.aop.dao.UserDao)") public void thisDao() { } /** * 匹配IUserDao接口的目标对象(而不是aop代理后的对象)的方法,这里即UserDao */ @Pointcut("target(com.asange.aop.dao.IUserDao)") public void targetDao() { } /** * 匹配所有以Service结尾的bean里面的方法 */ @Pointcut("bean(*Service)") public void beanDemo() { }
实例:关注log打印开始
package com.asange.aop; /** * com.asange.aop.service * icourt * 2018/4/6 * author:asange * email:[email protected] **/ public interface Loggable { void d(String s); }
package com.asange.aop.service; import com.asange.aop.Loggable; import org.springframework.stereotype.Component; /** * com.asange.aop.service * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Component public class LoggerService implements Loggable { @Override public void d(String s) { System.out.println("==========>" + s); } }
package com.asange.aop.service; import com.asange.aop.Log; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * com.asange.aop.service * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Service public class ProductService3 { @Autowired AuthService authService; @Autowired LoggerService loggerService; public void insert() { loggerService.d("insert success"); } }
aspct配置:
package com.asange.aop.security; /** * com.asange.aop.security * icourt * 2018/4/6 * author:asange * email:[email protected] **/ @Aspect @Component public class SecurityAspect { @Pointcut("this(com.asange.aop.Loggable)") public void log() { } @Before("log()") public void logBefore() { Log.log("------------>logBefore"); } }
测试结果:
可以发现在注入的loggerservice 在打印插入成功的时候,先打印的logbefore
匹配参数
/** * 匹配任何以find开头并且只有一个Long参数的方法 */ @Pointcut("execution(* *..find*(Long))") public void argsDemo1() { } /** * 匹配任何只有一个Long参数的方法 */ @Pointcut("args(Long)") public void argsDemo2() { } /** * 匹配任何以find开头的并且第一个参数为Long型的方法 */ @Pointcut("execution(* *..find*(Long,..))") public void argsDemo3() { } /** * 匹配第一个参数为Long型的方法 */ @Pointcut("args(Long,..)") public void argsDemo4() { }
实例:关注某个类中带Long型的参数的方法
package com.asange.aop.match_param; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * com.asange.aop.match_param * icourt * 2018/4/7 * author:asange * email:[email protected] **/ @Aspect @Component public class MatchAspect {
@Pointcut("args(Long)&& within(com.asange.aop.match_param.MatchParamService)") public void argsMatchs() { } @Before("argsMatchs()") public void argsMatchsBind() { System.out.println("\n==========只有一个Long参数的方法执行了\n"); } @Pointcut("args(Long,..)&& within(com.asange.aop.match_param.MatchParamService)") public void argsMatchs2() { } @Before("argsMatchs()") public void argsMatchsBind2() { System.out.println("\n==========带Long参数的方法执行了\n"); } }
package com.asange.aop.match_param; import org.springframework.stereotype.Service; /** * com.asange.aop.match_param * icourt * 2018/4/7 * author:asange * email:[email protected] **/ @Service public class MatchParamService { public void sayNum(Long num) { System.out.println("\n=======sayNum:" + num + "\n"); } }
测试结果:都观察到了MatchParamService中的带Long参数的方法
注解匹配
/** * 匹配方法标注有AdminOnly的注解的方法 */ @Pointcut("@annotation(com.asange.aop.security.AdminOnly)") public void annoDemo() { } /** * 匹配标注有Beta的类底下的方法,要求的annotation的RetentionPolcy级别为Class */ @Pointcut("@within(com.google.common.annotations.beta)") public void annoWithinDemo() { } /** * 匹配标注有repository的类底下的方法,要求annotation的RetentionPolicy级别为RUNTIME */ @Pointcut("@target(org.springframework.stereotype.Repository)") public void annoTargetDemo() { } /** * 匹配传入的参数类标注有Repository注解的方法 */ @Pointcut("@args(org.springframework.stereotype.Repository)") public void annoArgsDemo() { }
execution表达式
execution(* com.xxx.service..*.*(..)
符号 | 含义 |
execution() | 表达式主体 |
第一个 “*”号 | 代表返回值的任意类型 |
com.xxx.service | aop所切的服务的包名 |
包名后面的“.." | 表示当前包及子包 |
第二个"*" | 表示类名,* 表示所有类 |
.*(..) | 表示任何方法名,括号表示参数,两个点表示任何参数类型 |
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
除了返回类型模式,方法名模式和参数模式外,其他都是可选的。
1. 通过方法签名定义切点:
execution(public * *(..))
匹配所有目标类的public方法,第一个*代表返回类型,第二个*代表方法名,而..表示任意入参的方法
package com.asange.aop.execution; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; /** * com.asange.aop.execution * icourt * 2018/4/7 * author:asange * email:[email protected] **/ @Service public class ExecutionService { public void sayHello() { System.out.println("========>hello"); } }
package com.asange.aop.execution; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * com.asange.aop.execution * icourt * 2018/4/7 * author:asange * email:[email protected] **/ @Aspect @Component public class ExecutionAspect { @Pointcut("execution(public * *(..)) && within(ExecutionService)") public void executiondemo() { } @Before("executiondemo()") public void executionBind() { System.out.println("========>execution(* *(..))"); } }
变种版本,如:execution(**(String))
package com.asange.aop.execution; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * com.asange.aop.execution * icourt * 2018/4/7 * author:asange * email:[email protected] **/ @Aspect @Component public class ExecutionAspect { @Pointcut("execution(* *(String)) && within(ExecutionService)") public void executiondemo2() { } @Before("executiondemo2()") public void executionBind2() { System.out.println("========>execution(* *(String))"); } }
package com.asange.aop.execution; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; /** * com.asange.aop.execution * icourt * 2018/4/7 * author:asange * email:[email protected] **/ @Service public class ExecutionService { public void sayHello(String str) { System.out.println("========>"+str); } }
可以看出 execution表达式组合太多, 如execution(* say(String,int)) 那么只会关注 say(String xx,int xx)的方法,
又如exection(* *To(..)) 则会关注 convertToxx(xx)等类似方法,
又如execution(* com.xx.xservice.*(..)) 匹配xservice下面的的所有方法
太多组合,参考官方文档
Advice注解
1. @Before 前置通知
2. @After(finally),后置通知,成功执行之后
3. @AfterReturning, 返回通知,成功执行之后
4. @AfterThrowing 异常通知,抛出异常之后
5. @Around,环绕通知
本演示的例子代码点击