AOP
AOP是一种设计思想,是软件设计领域中的的面向切面编程,它是对面向对象编程的一种补充和完善,可以在不修改源代码的情况下给程序动态统一添加额外功能的技术
项目中的非核心业务一般是通过特定方式切入到系统中,一般这种方式就是要借助AOP实现.
实现AOP的两种方式
(1) 如果目标对象实现了接口,底层可以采用JDK动态代理来为目标创建代理对象,目标类和代理类会实现共同接口(组合方式)
(2) 如果目标对象没有实现接口name底层可以采用CGLIB代理机制为目标对象创建代理对象默认创建的代理类汇集成目标对象(继承方式)
由于现在Springboot中默认使用的是CGLIB代理加入要使用 JDK代理的话需要在配置文件applicatiion.properties中配置spring.aop.proxy-target-class=false
aop:
proxy-target-class: true
AOP知识点
切面(aspect)横切面对象,一般为一个具体类对象,需要@Aspect声明,横切面对象中一般添加了新增的功能
AOP相关术语
Aspect切面: 横切面对象一般为一个具体类对象,使用@Aspect声明
Advice: 通知: 在切面的某个特定连接点上执行的扩展功能(见通知类型)
joinpoint: 连接点:
pointcut: 对多个连接点的定义,理解为多个连接点的集合
通知类型
通知(Advice)在某个特定的连接点上执行的动作,一般指被拦截到的方法
通知类型:在spring AOP编程的过程中基于Aspect框架标准,spring定义了五种业务的通知类型
-
@Before
前置通知 在连接点前面执行,前置通知不会影响连接点的执行,除非抛出异常 -
@AfterReturning
正常返回通知 在连接点正常执行完成后执行 假如有After先执行After -
@AfterThrowwing
异常返回通知 在连接点抛出异常后执行 假如有After先执行After -
@After
返回通知 在连接点执行完成后执行,不管是正常执行还是抛出异常后都会执行返回通知中的内容 -
@Around(优先级最高)
环绕通知 环绕通知围绕在连接点前后,可以在方法调用前后自定义一些操作,环绕通知需要负责决定是继续处理目标方法还是中断执行 -
@Pointcut
此注解用于定义切入点,具体方式可以基于特定表达式进行实现 -
@Pointcut(“切入点表达式(切入点)”)
切入点表达式
Spring中通过切入点表达式来定义具体切入点,AOP切入点表达式
bean表达式(常用)
bean表达式一般应用于类级别,实现粗粒度的切入点定义
bean:用于匹配指定的bean对象的所有方法
bean表达式一般适用于类级别,适合一个粗粒度的切入点定义
bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
bean(" * ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法
within表达式
within表达式应用于类级别,实现粗粒度的切入点表达式定义
within:用于匹配指定包下的所有类中的所有方法
within适用于类级别实现粗粒度的切入点表达式定义
案例:
within(“aop.service.UserServiceImpl”)指定当前包中这个类内部的所有方法。
within(“aop.service.") 指定当前目录下的所有类的所有方法。
within("aop.service…”) 指定当前目录以及子目录中类的所有方法
execution表达式
**execution(返回值类型 包名.注解名)
execution表达式应用于方法级别,实现细粒度的切入点表达式定义
execution:按制定语法规则匹配到具体方法
execution(void aop.service.UserServiceImpl.addUser())匹配addUser方法。
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为String的addUser方法。
execution( * aop.service…*. * (…)) 万能配置。
annotion表达式
@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义
@annotation:用于匹配指定注解修饰的方法
@annotation(RequiredCache)自定义注解,匹配有此注解描述的方法
自定义注解RequiredCache
自定义注解,默认所有注解都是一个Annotation类型的对象
- @Target : 告诉jdk次注解可以描述的对象(METHOD)
- @Retention ; 告诉jdk注解何时有效
所有注解都是一种元数据(Meta Data)一种描述数据的数据()
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredCache {}
简单Cache的实现
新建简单Cache对象
产品级Cache要考虑
- 1)存储结构,存储内容(存储对象字节还是存储对象引用)
- 2)缓存淘汰策略(缓存满的时是否要淘汰数据)
- 3)GC策略(JVM内存不足时,是否允许清除Cache中数据)
- 4)任务调度策略(是否需要每隔一段时间刷新一下缓存)
- 5)日志记录方式(记录命中次数)
- 6)线程并发安全策略
- @Component 标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器
@Component
public class SimpleCache {
//封装Map对象
private Map<Object,Object> cache=new ConcurrentHashMap<>();
//添加构造方法调用此方法并传入查询到的数据,将此数据存入Cache
public boolean putObject(Object key,Object value) {
cache.put(key, value);
return true;
}
//传入key取出Cache中的值
public Object getObject(Object key) {
return cache.get(key);
}
//清空Cache
public void clearObject() {
cache.clear();
}
自定义清除缓存注解ClearCache
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClearCache {}
ASpect切面类型
使用@Aspect修饰的类为切面类型和
@Aspect
@Component
public class SysCacheAspect {}
@Autowired依赖注入(Cache对象)
@Autowired
private SimpleCache simpleCache;
@Pointcut切入点
@annnotation切入点表达式
com…RequiredCache 自定义注解 带有此注解的方法为切入点
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")
public void doCachePointCut() {}
清除缓存切面ClearCache
@Pointcut("@annotation(com.cy.pj.common.annotation.ClearCache)")
public void doClearCachePointCut() {}
定义@AfterReturning通知,原方法执行完以后执行,如果有After则先执行AfterReturning
- 此处切入点要定义在更新 新增 删除 方法上,因为缓存内数据需要更新
@AfterReturning("doClearCachePointCut()")
public void doAfterReturning() {//目标方法正常结束以后执行
simpleCache.clearObject();
}
数据缓存切面方法
- @Around 环绕通知
ProceedingJoinPoint 获取当前切入点的方法 参数 - 1.通过Cache对象获取缓存里的值,并且判断,如果缓存的值!=null的话直接返回缓存里的值
- 2.通过ProceedingJoinPoint 对象获取切入点方法的返回值或者执行方法,参数是从数据库查询出的数据,
- 3.将数据库查询出的数据存入缓存中(Cache)key=value
- 4.返回数据
@Around("doCachePointCut()")
public Object around(ProceedingJoinPoint jp)throws Throwable{
Object obj=simpleCache.getObject("deptCache");//这个key的名字先自己写个固定值
if(obj!=null)return obj;
Object result=jp.proceed();//最终要执行目标方法
simpleCache.putObject("deptCache", result);
return result;
}