目录
3 日志打印+优美的打印格式 (找问题时一幕了然。尤其使用elk)
实战注意事项
1 环绕通知:环绕通知以及任何通知,不要将异常抓住,否则全局异常处理不到了。
2 注意抛异常时为了搭配全局异常处理器完成的。
准备
<!-- aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
注解和包扫描 一起组合等
1 aop实现限流
场景: 报表,图片 的下载接口 。
方式二: 网关层,读取配置中心,接口url,动态的做成根据url限流。
自定义注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRateLimiter {
/**
* 默认每秒两次限流
* @return
*/
int permitsPerSecond() default 2;
}
@RequestMapping("/test1")
@MyRateLimiter(permitsPerSecond=3)
public String test1() throws Exception {
System.out.println("我是目标方法");
// throw new Exception("我就是要抛异常");
return "成功";
}
import cn.hutool.core.util.ObjectUtil;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
/**
* @创建人 赵伟
* @创建时间 2022/12/12
* @描述 限流aop
*/
@Aspect
@Component
@Order(value = 9)
@Slf4j
public class RateLimiterAspectaa {
/**
* 限流map
*/
private ConcurrentHashMap<String,RateLimiter> limiterMap =new ConcurrentHashMap();
/**
* 环绕通知
*
* @param joinPoint
* @throws Exception
*/
@Around(value = "@annotation(com.example.comm.ratelimiter.aop.MyRateLimiter)") public Object Around1(
ProceedingJoinPoint joinPoint) throws Throwable {
//获取当前执行类
String typeName = joinPoint.getSignature().getDeclaringTypeName();
//获取当前执行类中的执行方法
String name = joinPoint.getSignature().getName();
//获取当前执行方法中的执行参数
Object[] object = joinPoint.getArgs();
//前置通知
Signature sig = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)sig;
//获取注解
MyRateLimiter declaredAnnotation = methodSignature.getMethod().getDeclaredAnnotation(MyRateLimiter.class);
//获取限流次数
int i = declaredAnnotation.permitsPerSecond();
String key = typeName + "." + name;
RateLimiter rateLimiter = limiterMap.get(key);
if (ObjectUtil.isNull(rateLimiter)) {
rateLimiter = RateLimiter.create(i);
limiterMap.put(key, rateLimiter);
}
boolean b = rateLimiter.tryAcquire();
if (!b) {
throw new Exception("访问人数过多");
}
log.info("Around1 方法执行前");
// 执行目标方法
Object result = joinPoint.proceed();
log.info("Around1 方法执行后");
//后置通知
return result;
}
}
2 aop实现 防止 SQL 注入
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @创建人 赵伟
* @创建时间 2022/12/12
* @描述 限流aop
*/
@Aspect
@Component
@Slf4j
@Order(value = 9)
public class RateLimiterAspectaa {
/**
* 环绕通知
* @param joinPoint
* @throws Exception
*/
@Around(value="execution(* com.example.comm..*.controller..*.*(..)) && execution(public * *(..))")
public Object Around1(ProceedingJoinPoint joinPoint) throws Throwable {
this.checkRequestParam(joinPoint);
log.info("Around1 方法执行前");
// 执行目标方法
Object result = joinPoint.proceed();
log.info("Around1 方法执行后");
return result;
}
/**
* 参数校验,防止 SQL 注入
*
* @param joinPoint
*/
private void checkRequestParam(ProceedingJoinPoint joinPoint) throws Exception {
Object[] args = joinPoint.getArgs();
if (args == null || args.length <= 0) {
return;
}
String params = Arrays.toString(joinPoint.getArgs()).toUpperCase();
String[] keywords = {"DELETE ", "UPDATE ", "SELECT ", "INSERT ", "SET ", "SUBSTR(", "COUNT(", "DROP ",
"TRUNCATE ", "INTO ", "DECLARE ", "EXEC ", "EXECUTE ", " AND ", " OR ", "--"};
for (String keyword : keywords) {
if (params.contains(keyword)) {
log.warn("参数存在SQL注入风险,其中包含非法字符 {}.", keyword);
throw new Exception("参数存在SQL注入风险:params=" + params);
}
}
}
}
3 日志打印+优美的打印格式 (找问题时一幕了然。尤其使用elk)
4 自定义注解 整合 redis
已经有现成的了 ,就懒得写了。
5 自定义注解整合 权限 (模仿shiro)
单体架构还行,微服务了都,基本都在网关层做权限校验了。
6 动态的日志记录功能
记录个人改了哪些数据,(原始数据,修改后数据)
7 异步线程注解形式
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAsync {
}
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @创建人 赵伟
* @创建时间 2022/12/14
* @描述
*/
@Aspect
@Component
@Slf4j
@Order(value = 15)
public class AsyncAspect {
@Resource
private ThreadPoolTaskExecutor myExecutor;
/**
* 环绕通知
* @param joinPoint
* @throws Exception
*/
@Around(value = "@annotation(com.example.demo.annotation.MyAsync)")
public Object Around1(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Around1 方法执行前");
Object result = myExecutor.submit(() -> {
try {
System.out.println(Thread.currentThread().getName());
return joinPoint.proceed();
} catch (Throwable throwable) {
return throwable;
}
}).get();
if(result instanceof Throwable){
//一定用error打印
log.error(((Throwable)result).getMessage(),((Throwable)result));
throw (Throwable)result;
}
return result;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @创建人 赵伟
* @创建时间 2022/12/14
* @描述
*/
@Configuration
public class ThreadPoolConfig {
@Bean("myExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数信息
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setQueueCapacity(2);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("eventHandle--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
// 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
taskExecutor.setAwaitTerminationSeconds(60);
//修改拒绝策略为使用当前线程执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化线程池
taskExecutor.initialize();
return taskExecutor;
}
}