1.项目结构
2.引入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies>
3.yml配置
server:port: 8080
spring:redis:database: 0host: x.x.x.xport: 6379password: 123456jedis:pool:max-active: 10max-wait: -1msmax-idle: 5min-idle: 1
4.自定义注解
package com.example.springbootaop.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*** 通用开关注解* @author shixc* 2023/10/17*/
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时起作用
public @interface ServiceSwitch {/*** 业务开关的key(不同key代表不同功效的开关)* {@link Constant.ConfigCode}*/String switchKey();// 开关,0:关(拒绝服务并给出提示),1:开(放行)String switchVal() default "0";// 提示信息,默认值可在使用注解时自行定义。String message() default "当前请求人数过多,请稍后重试。";
}
5.定义常量
package com.example.springbootaop.constant;
/*** 常量* @author shixc* 2023/10/17*/
public class Constant {// .... 其他业务相关的常量 ....// 配置相关的常量public static class ConfigCode {// 挂号支付开关(0:关,1:开)public static final String REG_PAY_SWITCH = "reg_pay_switch";// 其他业务相关的配置常量// ....}
}
6.AOP核心实现
package com.example.springbootaop.aop;
import com.example.springbootaop.annotation.ServiceSwitch;
import com.example.springbootaop.constant.Constant;
import com.example.springbootaop.util.Result;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/*** AOP核心实现* @author shixc* 2023/10/17*/
@Aspect
@Component
public class ServiceSwitchAOP {private final StringRedisTemplate redisTemplate;public ServiceSwitchAOP(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 定义切点,使用了@ServiceSwitch注解的类或方法都拦截*/@Pointcut("@annotation(com.example.springbootaop.annotation.ServiceSwitch)")public void pointcut() {}@Around("pointcut()")public Object around(ProceedingJoinPoint point) {// 获取被代理的方法的参数Object[] args = point.getArgs();// 获取被代理的对象Object target = point.getTarget();// 获取通知签名MethodSignature signature = (MethodSignature) point.getSignature();try {// 获取被代理的方法Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());// 获取方法上的注解ServiceSwitch annotation = method.getAnnotation(ServiceSwitch.class);// 核心业务逻辑if (annotation != null) {String switchKey = annotation.switchKey();String switchVal = annotation.switchVal();String message = annotation.message();/*获取配置项说明这里有两种方式:1、配置加在Redis,查询时从Redis获取;2、配置加在数据库,查询时从表获取。(MySQL单表查询其实很快,配置表其实也没多少数据)我在工作中的做法:直接放到数据库,但是获取配置项的方法用SpringCache缓存,然后在后台管理中操作配置项,变更时清理缓存即可。我这么做就是结合了上面两种各自的优点,因为项目中配置一般都是用后台管理来操作的,查表当然更舒适,同时加上缓存提高查询性能。*/// 下面这块查询配置项,大家可以自行接入并修改。// 数据库这么查询:String configVal = systemConfigService.getConfigByKey(switchKey);// 这里我直接从redis中取,使用中大家可以按照意愿自行修改。String configVal = redisTemplate.opsForValue().get(Constant.ConfigCode.REG_PAY_SWITCH);if (switchVal.equals(configVal)) {// 开关打开,则返回提示。return Result.fail(HttpStatus.FORBIDDEN.value()+"", message);}}// 放行return point.proceed(args);} catch (Throwable e) {throw new RuntimeException(e.getMessage(), e);}}
}
7.使用注解
package com.example.springbootaop.service;
import com.example.springbootaop.annotation.ServiceSwitch;
import com.example.springbootaop.constant.Constant;
import com.example.springbootaop.util.Result;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
/*** @author shixc* 2023/10/17*/
@Service
public class RegService {/*** 挂号下单*/@ServiceSwitch(switchKey = Constant.ConfigCode.REG_PAY_SWITCH)public Result createOrder() {// 具体下单业务逻辑省略....return Result.succeed("挂号下单成功");}
}
8.工具类
import java.io.Serializable;
/*** @author shixc* 2023/10/17*/
public final class Result<T> implements Serializable {private static final long serialVersionUID = -8780880627029425781L;private String code;private long timestamp = System.currentTimeMillis();private String message;private T data;public Result() {}private Result(String code, String message, T data) {this.code = code;this.message = message;this.data = data;}public String getCode() {return this.code;}public long getTimestamp() {return this.timestamp;}public String getMessage() {return this.message;}public T getData() {return this.data;}public static <T> Result<T> succeed() {return new Result("0", "", (Object) null);}public static <T> Result<T> succeed(T data) {return new Result("0", "", data);}public static <T> Result<T> panic() {return new Result("000000", "系统运行发生异常,请联系IT处理", (Object) null);}public static <T> Result<T> illegalArgument(String message) {return new Result("000001", message, (Object) null);}public static <T> Result<T> fail(String code, String message) {return new Result(code, message, (Object) null);}public static <T> Result<T> fail(String code, String message, T data) {return new Result(code, message, data);}public boolean success() {return this.code.equals("0");}public boolean failure() {return !this.success();}
}
9.测试接口
package com.example.springbootaop.controller;
import com.example.springbootaop.service.RegService;
import com.example.springbootaop.util.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*** 挂号Controller* @author shixc* 2023/10/17*/
@RestController
@RequestMapping("reg")
public class RegController {private final RegService regService;public RegController(RegService regService) {this.regService = regService;}@GetMapping("/create")public Result createOrder() {return regService.createOrder();}
}
10.Redis中把开关加上
11.启动服务
将redis中开关置为1
欢迎大家积极留言交流学习心得,点赞的人最美丽!