什么是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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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的基本语法格式为:
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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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:xuanyouwu@163.com
**/
@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,环绕通知
本演示的例子代码点击