- 注解+拦截器 实现登录校验
项目中在进入方法之前判断用户是否登录、登录了则继续执行方法,未登录则返回异常信息。
a.先定义一个注解@NeedLogin
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLogin {
}
b.再写个拦截器,控制具体逻辑
public class NeedLoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 反射获取方法上的NeedLogin注解
if (handler instanceof HandlerMethod){
/**
* 注意:
* 当接口顶层没有设置根路劲时,Spring boot 将接口理解为静态资源(ResourceHttpRequestHandler)
* @see com.yds.start.controller.DemoErrorController
* (HandlerMethod)handler 会报错(ClassCastException)
*
* ResourceHttpRequestHandler是用来处理静态资源的;而HandlerMethod则是springMVC中用@Controller声明的一个bean及对应的处理方法.
*
*/
HandlerMethod handlerMethod = (HandlerMethod)handler;
NeedLogin loginRequired = handlerMethod.getMethod().getAnnotation(NeedLogin.class);
if(loginRequired != null){
// 有NeedLogin注解说明需要登录,提示用户登录
response.setContentType("text/plain; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("你访问的资源需要登录");
writer.flush();
writer.close();
return false;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
c.看效果(写两个接口一个带@NeedLogin,一个不带)
@RestController
@RequestMapping("/demo")
public class DemoController {
@GetMapping("/sourcea")
public String sourceA(String str){
return "你正在访问sourceA资源";
}
@NeedLogin
@GetMapping("/sourceb")
public String sourceB(String str){
System.out.println("sourceb");
return "你正在访问sourceB资源";
}
}
http://127.0.0.1:8080/demo/sourcea
http://127.0.0.1:8080/demo/sourceb
- 注解+AOP 日志打印
项目中可以根据注解自定义打印日志
a.定义注解@MyLog
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface MyLog {
}
b.AOP 实现逻辑
@Slf4j
@Aspect
@Component
public class LogAspect {
/**
* PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
* 切面最主要的就是切点,所有的故事都围绕切点发生
* logPointCut()代表切点名称
*/
@Pointcut("@annotation(com.yds.start.common.annotation.MyLog)")
public void logPointCut() {
}
/**
* 环绕通知
* @param joinPoint
*/
@Around("logPointCut()")
public void logAround(ProceedingJoinPoint joinPoint){
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
// 获取入参
Object[] param = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
for(Object o : param){
sb.append(o + "; ");
}
//自由发挥…………
log.info("进入[{}]方法,参数为:{}" ,methodName, sb);
// 继续执行方法
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
log.info(methodName + "方法执行结束");
}
}
c.看效果(写个接口,加上@MyLog注解)
@MyLog
@GetMapping(value = "/test")
public String test(String str) {
return str;
}
访问http://127.0.0.1:8080/demo/test?str=123
观察控制台输出:
2022-02-23 10:10:04.317 INFO 95922 --- [nio-8080-exec-6] com.yds.start.common.aop.LogAspect : 进入[test]方法,参数为:123;
2022-02-23 10:10:04.318 INFO 95922 --- [nio-8080-exec-6] com.yds.start.common.aop.LogAspect : test方法执行结束
- 注解+AOP+Redis 实现锁机制
a.先定义注解@SyncLock
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SyncLock {
//键
String lockKey() default "";
//附加键
String extendKey() default "";
//过期时间
int remainSecond() default 60;
//是否例外
boolean needException() default false;
}
b.实现锁机制
@Component
@Aspect
@Slf4j
public class SyncLockAspect {
@Autowired
private RedisUtilsContent redisUtil;
@Pointcut("@annotation(com.yds.start.common.annotation.SyncLock)")
public void lockOperator() {
}
@Around(value = "lockOperator()")
public Object lockOperatorImpl(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
//获取锁Key
StringBuilder lockKey = new StringBuilder(method.getAnnotation(SyncLock.class).lockKey());
//获取附加Key
String extendKey = method.getAnnotation(SyncLock.class).extendKey();
//获取过期时间
int remainSecond = method.getAnnotation(SyncLock.class).remainSecond();
//是否例外
boolean needException = method.getAnnotation(SyncLock.class).needException();
//额外键可以是方法参数中的唯一值,例如uuid等,达到小颗粒度化
if (!StringUtils.isEmpty(extendKey)) {
//方法参数
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
if (parameterNames != null && parameterNames.length > 0) {
lockKey.append("_").append(parseSpEL(extendKey, parameterNames, joinPoint.getArgs()));
}
}
log.info("sync lock : {}", lockKey);
if (!StringUtils.isEmpty(lockKey.toString()) && !redisUtil.setNX(lockKey.toString(), "locked", remainSecond)) {
log.warn("lockKey has locked : {}", lockKey);
if (!needException) {
return null;
} else {
throw new RuntimeException("资源被锁");
}
}
Object obj;
try {
obj = joinPoint.proceed();
} catch (Throwable throwable) {
log.error("lock function exception : {}, throwable", lockKey, throwable);
throw throwable;
} finally {
redisUtil.del(lockKey.toString());
}
return obj;
}
/**
* spel转换
*/
private String parseSpEL(String lockValue, String[] parameterNames, Object[] args) {
StringBuilder extendKey = new StringBuilder();
SpelExpressionParser parser = new SpelExpressionParser();
//设置参数上下文
EvaluationContext context = new StandardEvaluationContext();
IntStream.range(0, parameterNames.length)
.forEach(i -> context.setVariable(parameterNames[i], args[i]));
Expression expr = parser.parseExpression(lockValue);
extendKey.append(expr.getValue(context));
return extendKey.toString();
}
}
c.使用
//param 中的uuid的值作为extendKey 拼接到key中,已做到细颗粒度化
@SyncLock(lockKey = "updateInfo",extendKey="#uuid")
public void updateInfo(Object param) {
//业务逻辑
}
@SyncLock
public void updateInfo2(Object param) {
//业务逻辑
}
//****
声明:本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。