1、Aop 面向切面编程
-通知
1】before
2】After
3】After-returning
4】After-throwing
5】Around
-切点 :定义在切面中的方法,和通知一起使用组成切面
-连接点
-切面 :一般单独作为一个类
-引入
-织入
2、代理模式:
为目标对象创造一个代理对象,以控制对目标对象的访问。
代理模式的应用:权限信息的预处理,token验证;
1】Target:目标对象
2】Proxy :代理对象
3、AOP思想的实现基于代理模式,在java中一般采用JDK 动态代理模式。(仅代理接口)所以无法代理类。
因此,Spring AOP 同时支持CGLIB、ASPECTJ、JDK动态代理,当有实现接口时候,默认使用JDK 动态代理模式,否则采用CGLIB代理。
4、spring 新增注解
注解说明
1】@Target(ElementType.METHOD) 说明了Annotation所修饰的对象范围
2】@Retention 注解用来定义其声明周期
3】@Documented 是一个标记注解
5、实现一个注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeat {
/**
*** 时长
** @return*
*/
**long longTime() default 5000L;
/**
*** 检查入参
** @return*
*/
**boolean checkBody() default false;
}
6、定义一个切面类 ,进一步实现自定义注解功能
1】 **@Pointcut("@annotation(xxxx)") //** 配置织入点*-xxx*代表自定义注解的存放位置
2】@PostConstruct spring容器启动的时候执行,加载常规数据
@Aspect @Component public class NoRepeatAspect { @Autowired StringRedisUtil stringRedisUtil;
RedisDistributedLock redisDistributedLock;
@PostConstruct
private void initRedis() {
if (redisDistributedLock == null) {
redisDistributedLock = new RedisDistributedLock(stringRedisUtil.getRedisTemplate());
}
}
@Pointcut("@annotation(com.cjkj.common.annotation.NoRepeat)")
public void enterprisePointCut() {
}
@Around("enterprisePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
// String ip = IPUtil.getIpAddr(request); String token = request.getHeader("Authorization"); LogUtil.info("token:" + token); // 获取注解 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 目标类、方法 String className = method.getDeclaringClass().getName(); String name = method.getName();
String ipKey = "";
NoRepeat noRepeat = method.getAnnotation(NoRepeat.class);
if(noRepeat.checkBody()) {
Object[] objs = joinPoint.getArgs();
String body = "";
if(objs.length>0){
body = JSONObject.toJSONString(objs[0]);
}
ipKey = String.format("%s#%s#%s#%s", token, className, name, body);
} else {
ipKey = String.format("%s#%s#%s", token, className, name);
}
int hashCode = Math.abs(ipKey.hashCode());
String key = String.format("%d",hashCode);
// String key = String.format("%s_%d",ip,hashCode); LogUtil.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
LogUtil.info("ipKey={},hashCode={}",ipKey,hashCode);
long timeout = noRepeat.longTime();
if (timeout < 0){
//过期时间5分钟
timeout = 60*5;
}
try{
if (redisDistributedLock.lock("lock:" + key,1,1L)) {
String value = stringRedisUtil.getStrValue(key);
if (!StringUtils.isBlank(value)) {
throw new BusinessException("请勿重复提交");
}
stringRedisUtil.set(key, LocalDateTime.now().toString(), timeout / 1000);
} else {
throw new BusinessException("请勿重复提交");
}
}finally {
redisDistributedLock.releaseLock("lock:" + key);
}
return joinPoint.proceed();
}
}
2、获取注解
3、获取 目标类、方法
4、定义一个ipkey
5、实例化目标对象 NoRepeat
6、创建一个随机数hashCode
7、创建key
8、获取timeout
9、分布式锁
try{
*// * @param key key*
*// * @param expire* 获取锁超时时间
*// * @param retryTimes* 重试次数
*// * @param sleepMillis* 获取锁失败的重试间隔
**if (redisDistributedLock.lock("lock:" + key,1,1L))
{ String value = stringRedisUtil.getStrValue(key); *//redis* 存在*key*则 **是重复提交的
**if (!StringUtils.*isBlank*(value)) {
throw new BusinessException("请勿重复提交");
} *//* 不存在*key* 则*key*放入*redis +timeout*
**stringRedisUtil.set(key, LocalDateTime.*now*().toString(), timeout / 1000);
} else {
throw new BusinessException("请勿重复提交");
}
}finally {
redisDistributedLock.releaseLock("lock:" + key);
}
java 锁
synchronized锁 :一次只能允许一个线程进入被锁住的代码块
1、锁的持有者是“线程”,而不是“调用”
2、释放锁
1】当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。
2】当一个线程执行的代码出现异常时,其所持有的锁会自动释放
Lock锁:支持中断、超时不获取、是非阻塞的,必须手动释放锁, 允许多个读线程同时访问共享资源
分布式锁
1、保证多个服务之间互斥的访问共享资源,如果一个服务抢占了分布式锁,其他服务没获取到锁,就不进行后续操作