AOP全称Aspect Oriented Programming意为面向切面编程,也叫做面向方法编程,是通过预编译方式和运行期动态代理的方式实现不修改源代码的情况下给程序动态统一添加功能的技术。
首先导入AOP依赖
<!--AOP联盟-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--Spring Aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
创建自定义注解
package tech.niua.admin.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解, 用于帖子浏览量计数
*/
//注解用在方法上
@Target({ElementType.PARAMETER, ElementType.METHOD})
//@Retention的作用是定义被他所注解的注解保留多久,RUNTIME运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface PostViewCount {
String description() default "";
}
切面类:
package tech.niua.admin.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import tech.niua.admin.util.RedisUtil;
/**
* 帖子浏览量计数的切面
*/
//说明当前对象是一个切面
@Aspect
@Configuration
@Slf4j
public class PostViewAspect {
@Autowired
private RedisUtil redisUtil;
/**
* 切入点
*/
@Pointcut("@annotation(tech.niua.admin.annotation.PostViewCount)")
public void PostViewAspect() {
}
/**
* 切入处理
* @param joinPoint
* @return
*/
//环绕通知,最强大的通知类型,可以控制方法入参、执行、返回结果登方面细节
@Around("PostViewAspect()")
public Object around(ProceedingJoinPoint joinPoint) {
Object[] object = joinPoint.getArgs();
Object postId = object[0];
log.info("postId:{}", postId);
Object obj = null;
try {
String key = "postId_" + postId;
// 浏览量存入redis中
redisUtil.add(key);
obj = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}
创建Redis工具类,用来暂存浏览量
package tech.niua.admin.util;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* Redis 工具类, 用于暂存浏览量
*/
//允许在Spring IOC对当前对象实例化b并管理
@Component
public class RedisUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public void del(String... key) {
redisTemplate.delete(key[0]);
}
/**
* 计数
*
* @param key
*/
public Long add(String key) {
return redisTemplate.opsForValue().increment(key, 1);
}
/**
* 获取浏览量
*
* @param key
*/
public Integer size(String key) {
return (Integer) redisTemplate.opsForValue().get(key);
}
}
获取浏览量并存到数据库
package tech.niua.admin.schedule;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import tech.niua.admin.post.domain.Post;
import tech.niua.admin.post.service.IPostService;
import tech.niua.admin.util.RedisUtil;
import javax.annotation.Resource;
import java.util.List;
@Component
@Slf4j
@EnableScheduling
public class Schedule {
@Resource
private RedisUtil redisUtil;
@Resource
IPostService postService;
/**
* 定时保存帖子浏览量
*/
//Scheduled定时器 每30秒触发一次
@Scheduled(cron = "30 * * * * ? ")
@Transactional(rollbackFor=Exception.class)
public void savePostViewCount() {
log.info("浏览量入库开始");
QueryWrapper<Post> qw = new QueryWrapper<>();
qw.select("id").eq("delete_flag", 0).eq("audit_status", 1);
List<Object> list = postService.listObjs(qw);
list.forEach(id -> {
// 获取每一篇帖子在redis中的浏览量,存入到数据库中
String key = "postId_" + id;
Integer readingVolume = redisUtil.size(key);
if (readingVolume != null && readingVolume > 0) {
Boolean updated = postService.setPostViewCount((Long) id, readingVolume);
if (updated) {
log.info("{}:浏览量更新成功", id);
redisUtil.del(key);
}
}
});
log.info("浏览量入库结束");
}
}