- 注解+攔截器 實作登錄校驗
專案中在進入方法之前判斷用戶是否登錄、登錄了則繼續執行方法,未登錄則回傳例外資訊,
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 = "https://www.cnblogs.com/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 = "https://www.cnblogs.com/ieas/p/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) {
//業務邏輯
}
//****
本文來自博客園,作者:iyandongsheng,轉載請注明原文鏈接:https://www.cnblogs.com/ieas/p/15926128.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/431406.html
標籤:Java
