美文网首页
【JAVA基础】自定义注解

【JAVA基础】自定义注解

作者: 嘻洋洋 | 来源:发表于2021-12-16 17:09 被阅读0次

    注解是一种能被添加到java源代码中的元数据,方法、类、参数和包都可以用注解来修饰。注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。

    1.基本使用

    java.lang.annotation中提供了元注解,可以使用这些注解来定义自己的注解。

    1.1 声明定义一个注解

    (1)五要素:
    修饰符:访问修饰符必须为public,不写默认为pubic;
    关键字:关键字为@interface
    注解名:注解名称为自定义注解的名称
    注解类型元素:注解类型元素是注解中内容,可以理解成自定义接口的实现部分;
    元注解:使用元注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OAuth
    {
        String loginUrl() default WapUrl.LOGINPAGE;
    }
    

    1.2 常用元注解

    (1)Target
    描述了注解修饰的对象范围,取值在java.lang.annotation.ElementType定义,常用的包括:
    METHOD:用于描述方法
    PACKAGE:用于描述包
    PARAMETER:用于描述方法变量
    TYPE:用于描述类、接口或enum类型
    (2)Retention
    表示注解保留时间长短。取值在java.lang.annotation.RetentionPolicy中,取值为:
    SOURCE:在源文件中有效,编译过程中会被忽略
    CLASS:随源文件一起编译在class文件中,运行时忽略
    RUNTIME:在运行时有效
    只有定义为RetentionPolicy.RUNTIME时,我们才能通过注解反射获取到注解

    1.3 获取注解

    可以通过反射获取注解,比如获取@ResponseBody注解

        // 判断是否是AJAX请求,AJAX请求一般使用@ResponseBody
        private boolean isJsonRequest(HandlerMethod handler)
        {
            ResponseBody responseBody = handler.getMethodAnnotation(ResponseBody.class);
            boolean isJsonRequest = false;
            if (responseBody != null)
            {
                isJsonRequest = true;
            }
            return isJsonRequest;
        }
    

    2.应用场景

    2.1 自定义注解+拦截器 实现登录校验

    使用spring拦截器实现这样一个功能,如果方法上加了@OAuth,则表示用户该接口需要登录才能访问(没有登录直接跳转到登录页面),否则不需要登录。
    (1)先定义一个OAuth注解,默认值是登录页面地址

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface OAuth
    {
        String loginUrl() default WapUrl.LOGINPAGE;
    }
    

    (2)添加登录注解

    接口方法添加我们的登录注解@OAuth,要求登录才能访问

        // 添加购物车商品
        @OAuth
        @ResponseBody
        @RequestMapping(value = WebURL.CART_ADD, method = RequestMethod.POST)
        public ResponseJSON actionCartAdd(@RequestBody @Valid CartAddModRequest cartAddModRequest, BindingResult bindingResult)
        {
        }
    

    (3)实现登录拦截逻辑
    如果当前用户没有登录,直接跳转到登录页面

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 反射获取方法上的OAuth注解
        OAuth oAuth = handlerMethod.getMethodAnnotation(OAuth.class);
        // 登录用户信息
        OAuthUser oAuthUser = authService.getOAuth(sid);    
        if (oAuth != null) {
            // 不处于登录状态
            if (oAuthUser == null || oAuthUser.getCustId() == null) {
              // 判断是否是AJAX请求
              boolean isJsonRequest = isJsonRequest((HandlerMethod) handler);   
              // redirect到登录界面
              String baseUrl = Constants.BASE_SERVER;
              String returnUrl = baseUrl + request.getRequestURI() + (request.getQueryString() != null ?
                  "?" + request.getQueryString() :
                  "");
              response.setStatus(403);
              if (Strings.isNullOrEmpty(oAuth.loginUrl())) {
                response.sendRedirect(
                    baseUrl + WapUrl.LOGINPAGE + "?returnUrl=" + URLEncoder.encode(returnUrl, "utf-8"));
              } else {
                response.sendRedirect(
                    baseUrl + oAuth.loginUrl() + "?returnUrl=" + URLEncoder.encode(returnUrl, "utf-8"));
              }
              return false;
            } 
        }
        return true;
    }
    

    2.2 自定义注解+拦截器 实现限制访问

    (1)先定义一个Follow注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Follow
    {
        // 触屏版(域名配置)是否允许访问
        boolean isWapAccess() default false;
        // APP是否允许访问
        boolean isAppAccess() default false;
        // 只需要openId, 并不强制用户关注
        boolean onlyOpenId() default false;
    }
    

    (2)添加触屏版是否允许访问注解

    @Follow(isWapAccess = false)
    @RequestMapping(value = WapUrl.TODAY_SHOW_LIST_FOLLOW, method = RequestMethod.GET)
    public String todayWxList(@ModelAttribute("model") ModelMap model)
    {
        return todayList(model);
    }
    

    (3)实现登录拦截逻辑

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 反射获取方法上的Follow注解
        Follow follow = handlerMethod.getMethodAnnotation(Follow.class);
        if (follow != null) {
            if (follow.isAppAccess() && WebCookiesUtil.getIsAppLogin()) {
              return true;
            }
            if (!follow.isWapAccess()) {
              redirectWechatPage(request, response);
              return false;
            } else {
              // 有follow标签+非微信访问+允许wap访问
            }
        }
    }
    

    2.3 自定义注解+AOP 实现日志保存

    此例子使用Spring boot 实现
    (1)导入切面需要的依赖包

    <dependency>
          <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    

    (2)定义一个注解@SysLog

     // 系统日志注解
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface SysLog {
        String value() default "";
    }
    

    (3)方法上添加日志注解

         // 获取费用信息,汇总后的
        @SysLog("查询费用信息,主页面")
        @PostMapping("getExpense")
        public ResultDto getExpense(@RequestBody Map<String, Object> map)
        {
            return sExpenseService.getExpense(map);
        }
    

    (4)定义一个切面类,实现拦截逻辑

    /**
     * 系统日志,切面处理类
     */
    @Aspect
    @Component
    @Slf4j
    public class SysLogAspect {
        private final SysLogService sysLogService;
        @Autowired
        public SysLogAspect(SysLogService sysLogService) {
            this.sysLogService = sysLogService;
        }
        //PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名,切面最主要的就是切点,所有的故事都围绕切点发生。
        //logPointCut是一个切点名字,@Around注解使用,表示围绕该切点发生
        @Pointcut("@annotation(com.hao24.common.annotation.SysLog)")
        public void logPointCut() { 
        }
        // logPointCut1是另外一个切点名字
        @Pointcut("execution(* com.hao24.modules..*.*Controller.*(..))")
        public void logPointCut1()
        {
        }
        @Around("logPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            long beginTime = System.currentTimeMillis();
            //执行方法
            Object result = point.proceed();
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveSysLog(point, time);
            return result;
        }
        //保存到数据库中
        private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            SysLogEntity sysLog = new SysLogEntity();
            SysLog syslog = method.getAnnotation(SysLog.class);
            if(syslog != null){
                //注解上的描述
                sysLog.setOperation(syslog.value());
            }
            //请求的方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = signature.getName();
            sysLog.setMethod(className + "." + methodName + "()");
            //请求的参数
            Object[] args = joinPoint.getArgs();
            try{
                String params = new Gson().toJson(args);
                sysLog.setParams(params);
            }catch (Exception ignored){
            }
            //获取request
            HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
            //设置IP地址
            sysLog.setIp(IpUtils.getIpAddr(request));
            //用户名
            String username = ((TblSysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
            sysLog.setUsername(username);
            sysLog.setTime(time);
            sysLog.setCreateDate(new Date());
            //保存系统日志
            sysLogService.save(sysLog);
        }
    }
    

    相关文章

      网友评论

          本文标题:【JAVA基础】自定义注解

          本文链接:https://www.haomeiwen.com/subject/wxsvfrtx.html