发表于: 2020-06-27 23:30:38
1 1580
今天完成的事情:
1. 学习 AOP 与自定义注解
2. 使用自定义注解与面向切面编程的方法重写缓存
收获:
1. 关于 AOP
- 概念
面向切面编程是重用通用功能的一种方式,常见的还有继承与委托
面向切面编程提供了取带继承与委托的另一种方案。我们任然可以在一个地方定义通用功能,然后通过声明的方式来决定在何处调用,而无需修改受影响的类。
横切关注点可以被模块化为特殊的类,这些类被称为切面。
好处:
- 关注点集中在一个地方,不会分散到其他代码中
- 更加简介
- AOP 的一些术语
通知(Advice):
通知定义了切面是什么以及何时去使用。描述了切面要完成的工作,解决了何时执行这个工作的问题。具体来说应该在某个方法之前还是之后调用,亦或只在抛出异常的时候调用。
spring 切面可以应用的 5 种类型的通知:
- 前置通知(Before):在目标方法被调用之前去调用通知功能
- 后置通知(After):在目标方法完成后调用通知,不会关心方法输出的结果
- 返回通知(After-returning):在目标方法成功执行之后调用通知
- 异常通知(After-Throwing):在目标方法抛出异常之后调用通知
- 环绕通知(Around):通知包裹了被通知的方法,在被通知方法调用之前与之后指定自定义行为。
连接点(Join point):
连接点是应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些切入点插入到正常流程之中,添加新的行为。
切点(Pointcut):
切点的定义会匹配通知所有要织入的连接点。可以使用明确的类与方法的名称,或者利用正则表达式来定义所匹配的类和方法的名称来指定切点。
切面(Aspect):
切面时通知与切点的结合,通知与切点共同定义了切面的全部内容。
引入(Introduction):
引入允许我们向现有的类添加新的方法或者属性。
织入(Weaving):
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标的声明周期内有多个点可以进行织入:
- 编译期:切面在目标编译的时候被织入。这种方式需要特殊的编译器支持,AspectJ 的织入就是以这种方式织入切面的。
- 类加载期:切面在目标类加载到 JVM 时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。
- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态的创建一个代理对象。Spring AOP 就是以这种方式织入切面的。
2. 重写缓存
其实之前就一直没写完缓存,只写了一个 service 验证了一下缓存没有问题,一个个 service 去加缓存逻辑实在是不够干净,不能忍。
a,编写自定义注解指定需要缓存的方法
Cache.java
/**
* @Description: 缓存注解
* @Param: key 缓存key,expiration 有效时间,默认一分钟
* @return:
* @Author: owlwinter
* @Date: 2020/6/27
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
String key();
Class clazz();
long expiration() default 60;
}
b,配置 AspectJ
引入依赖:
<!--aop 依赖-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
spring 配置文件中开启 aop:
<aop:aspectj-autoproxy/>
定义切面:
MemcachedAOP.class
/**
* @ClassName MemcachedAOP
* @Description 根据指定的注释信息对数据用 memcached 做缓存
* @Author owlwinter
* @Date 2020/6/27 21:07
* @Version 1.0
**/
@Aspect
public class MemcachedAOP {
private static Logger logger = LogManager.getLogger(MemcachedAOP.class);
@Pointcut("execution(* cn.mogeek.jsptiles.service.impl.*Impl.*(..)) && @annotation(cn.mogeek.cache.Cache)")
public void cachePoint(){}
@Around(value = "cachePoint() && @annotation(cache)")
public Object cachedData(ProceedingJoinPoint joinPoint, Cache cache) throws Throwable {
Object proceed;
Object value = MemcachedUtil.get(cache.key());
if (cache.clazz().isInstance(value)){
proceed = value;
logger.info("Memcached@Get cache key:{}\nvalue:{}", cache.key(), proceed);
}else {
proceed = joinPoint.proceed(joinPoint.getArgs());
MemcachedUtil.set(cache.key(), proceed, new Date(cache.expiration() * 60 * 1000));
logger.info("Memcached@Set cache key:{}\nvalue:{}\nexpiration:{}",
cache.key(), proceed, cache.expiration());
}
return proceed;
}
}
c,给需要缓存的方法加上 @Cache 注解
大功告成。
可以看到缓存生效了:
遇到的问题:
1. 在 aop 中获取注解值
@Pointcut("execution(* cn.mogeek.jsptiles.service.impl.*Impl.*(..)) && @annotation(cn.mogeek.cache.Cache)")
public void cachePoint(){}
@Around(value = "cachePoint() && @annotation(cache)")
public Object cachedData(ProceedingJoinPoint joinPoint, Cache cache) throws Throwable{}
重点在于需要获取注解值得方法注解里面必须带上注解(这句话我知道有点绕口)
可以看到我在定义切点的时候已经筛选了一次必须要带上指定的注解了,但是这还不够,我们可以看到在 @Around 注释的方法中我们需要使用注解里面的值,那么我们必须在这个 @Around 注解中带上一个 && @annotation(cache)
方法中只需要如下所示,就可以获取到值了。
cache.key()
明天的计划:
1. 继续搞压测···
评论