java自定义注解annotation记录操作日志
說到注解我們平常用的可以說非常多啦,說幾個常用的的注解 @RestController @Service @Autowired
這些都是我們平常使用spring框架最常見的注解了,我們只知道它們非常好用,使用@RestController 就能構建一個restful的控制器,@Service 這個是我們常用的mvc架構中的業務層使用的注解,將類交給spring容器管理,我們要用的話直接使用@Autowired就能將類自動注入。我們都知道用了這些注解非常的方便,今天我們自己也來寫一個自己的注解。
需求
一個項目,有些方法是需要被保護起來的,有寫方法是開放出來不需要保護的,比如登錄 注冊等 都不需要保護,解決方案有很多,今天我們就使用springboot的aop 和自定義注解來解決這個需求。
在創建自己的注解之前,了解一些注解的知識。
1.首先java jdk給我們提供了meta-annotation用于自定義注解的時候使用,這四個注解為:@Target,@Retention,@Documented 和@Inherited。
- 第一個注解@Target
@Target:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方),其源碼如下:
我們可以看到它只有一個屬性值,是個枚舉類型的數組,我們進該枚舉類型看看
public enum ElementType {TYPE,//用于描述類、接口(包括注解類型) 或enum聲明FIELD, //用于描述成員變量;METHOD,//用于描述方法PARAMETER,//用于描述參數CONSTRUCTOR,//用于描述構造器LOCAL_VARIABLE,//用于描述局部變量;ANNOTATION_TYPE,//注解類型聲明 該注解可用于描述注解PACKAGE,//用于描述包TYPE_PARAMETER,//這個是jdk1.8后加入的類型 表示這個 Annotation 可以用在 Type 的聲明式前TYPE_USE//表示這個 Annotation 可以用在所有使用 Type 的地方(如:泛型,類型轉換等) }- 第二個注解@Retention
@Retention:指定被描述的注解在什么范圍內有效。源碼如下:
可以看到,該注解也只有一個枚舉的屬性,我們進該枚舉類型看看
public enum RetentionPolicy {SOURCE,//表示描述程序編譯時CLASS,//在class文件中有效(即class保留RUNTIME//在運行時有效(即運行時保留) }- 第三個注解:@Documented
@Documented 是一個標記注解,木有成員,用于描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }- 第四個注解 @Inherited
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention {RetentionPolicy value(); }下面我們開始編寫我們自己的注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface IgnoreToken { } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Log {String name() default ""; }編寫aop攔截所有的controller請求
@Component @Aspect @Order(1) public class TokenAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate HttpServletResponse response;@Autowiredprivate LoginController loginController;@Pointcut("within(com.niehziliang.annotation.demo.controller..*)")public void checkToken () {}@Before("checkToken()")public void checkToken (JoinPoint joinPoint) throws IOException {MethodSignature signature = (MethodSignature)joinPoint.getSignature();//獲取當前訪問的類方法Method targetMethod = signature.getMethod();//判斷是否是注解修飾的類,如果是則不需要校驗tokenif(!targetMethod.isAnnotationPresent(IgnoreToken.class)){String token = request.getParameter("token");if (null == token || "".equals(token)) {response.setCharacterEncoding("utf-8");response.setContentType("application/json; charset=utf-8");PrintWriter out = response.getWriter();out.print("token不能為空");out.flush();out.close();} else {if (!loginController.chkToken(token)) {response.setCharacterEncoding("utf-8");response.setContentType("application/json; charset=utf-8");PrintWriter out = response.getWriter();out.print("token不合法");out.flush();out.close();}}}}}aop日志攔截
@Component @Aspect @Order(2) public class LogAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate LoginController loginController;@Pointcut("@annotation(com.niehziliang.annotation.demo.annos.Log)")public void saveLog() {}@Around("saveLog()")public Object saveLog(ProceedingJoinPoint point) throws Throwable {long start = System.currentTimeMillis();MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Log logAnnotation = method.getAnnotation(Log.class);String name = null;if (logAnnotation != null) {// 注解上的描述name = logAnnotation.name();}// 請求的方法名String className = point.getTarget().getClass().getName();String methodName = signature.getName();// 請求的方法參數值Object[] args = point.getArgs();LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();String[] paramNames = u.getParameterNames(method);String params = "";if (args != null && paramNames != null) {for (int i = 0; i < args.length; i++) {params += " " + paramNames[i] + ": " + args[i];}}String ip = IpUtils.getIpAddress(request);long time = System.currentTimeMillis() - start;StringBuffer log = new StringBuffer();log.append("注解上的name:").append(name).append("=======").append("請求的方法:").append(className).append(".").append(methodName).append("=====").append("請求參數:").append(params).append("=======").append("請求的ip:").append(ip).append("耗時:").append(time).append("ms");System.out.println(log.toString());loginController.saveLog(log.toString());return point.proceed();} }編寫controller代碼, 登錄方法加了我自己定義的注解@IgnoreToken aop在攔截的時候判斷有我們自己定義注解就不會去校驗token啦,獲取密碼的方法則會進行token的校驗
@RestController public class LoginController {public static Map<String,String> map = new HashMap<>();public static List<String> logList = new ArrayList<>();public static Set<String> tokenSet = new HashSet<>();@RequestMapping(value = "login")@IgnoreToken@Logpublic String login(String userName,String password) {map.put(userName,password);//保存tokentokenSet.add(userName+password);//返回tokenreturn userName+password;}@RequestMapping(value = "query")@Log(name = "獲取密碼")public String getPassword(String userName) {//獲取用戶密碼return map.get(userName);}@RequestMapping(value = "logs")@Log(name = "獲取日志信息")public String getLogMap() {return JSON.toJSONString(logList);}}下面我在瀏覽器輸入請求登錄地址進行登錄
// 登錄獲取token http://127.0.0.1:8080/login?userName=admin&password=adminadmin不帶token訪問受保護的方法
// 獲取用戶密碼 http://127.0.0.1:8080/query?userName=admin帶正確的token訪問受保護的犯法
// 獲取用戶密碼 http://127.0.0.1:8080/query?userName=admin&token=adminadminadmin 12獲取用戶訪問日志信息
總結
以上是生活随笔為你收集整理的java自定义注解annotation记录操作日志的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux内核杂记(6)-进程调度(1)
- 下一篇: linux系统编程之使用C++(1)-打