【JAVA基础】自定义注解

2021-12-16  本文已影响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);
    }
}
上一篇下一篇

猜你喜欢

热点阅读