【JAVA基礎(chǔ)】自定義注解

注解是一種能被添加到j(luò)ava源代碼中的元數(shù)據(jù)堵第,方法、類隧出、參數(shù)和包都可以用注解來修飾踏志。注解可以看作是一種特殊的標(biāo)記,可以用在方法胀瞪、類针余、參數(shù)和包上,程序在編譯或者運(yùn)行時可以檢測到這些標(biāo)記而進(jìn)行一些特殊的處理凄诞。

1.基本使用

java.lang.annotation中提供了元注解圆雁,可以使用這些注解來定義自己的注解。

1.1 聲明定義一個注解

(1)五要素:
修飾符:訪問修飾符必須為public,不寫默認(rèn)為pubic帆谍;
關(guān)鍵字:關(guān)鍵字為@interface
注解名:注解名稱為自定義注解的名稱
注解類型元素:注解類型元素是注解中內(nèi)容伪朽,可以理解成自定義接口的實(shí)現(xiàn)部分;
元注解:使用元注解

@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文件中,運(yùn)行時忽略
RUNTIME:在運(yùn)行時有效
只有定義為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.應(yīng)用場景

2.1 自定義注解+攔截器 實(shí)現(xiàn)登錄校驗(yàn)

使用spring攔截器實(shí)現(xiàn)這樣一個功能,如果方法上加了@OAuth绘雁,則表示用戶該接口需要登錄才能訪問(沒有登錄直接跳轉(zhuǎn)到登錄頁面)橡疼,否則不需要登錄。
(1)先定義一個OAuth注解庐舟,默認(rèn)值是登錄頁面地址

@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)實(shí)現(xiàn)登錄攔截邏輯
如果當(dāng)前用戶沒有登錄,直接跳轉(zhuǎn)到登錄頁面

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) {
        // 不處于登錄狀態(tài)
        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 自定義注解+攔截器 實(shí)現(xiàn)限制訪問

(1)先定義一個Follow注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Follow
{
    // 觸屏版(域名配置)是否允許訪問
    boolean isWapAccess() default false;
    // APP是否允許訪問
    boolean isAppAccess() default false;
    // 只需要openId, 并不強(qiáng)制用戶關(guān)注
    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)實(shí)現(xiàn)登錄攔截邏輯

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標(biāo)簽+非微信訪問+允許wap訪問
        }
    }
}

2.3 自定義注解+AOP 實(shí)現(xiàn)日志保存

此例子使用Spring boot 實(shí)現(xiàn)
(1)導(dǎo)入切面需要的依賴包

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

(2)定義一個注解@SysLog

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

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

     // 獲取費(fèi)用信息挪略,匯總后的
    @SysLog("查詢費(fèi)用信息历帚,主頁面")
    @PostMapping("getExpense")
    public ResultDto getExpense(@RequestBody Map<String, Object> map)
    {
        return sExpenseService.getExpense(map);
    }

(4)定義一個切面類,實(shí)現(xiàn)攔截邏輯

/**
 * 系統(tǒng)日志杠娱,切面處理類
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect {
    private final SysLogService sysLogService;
    @Autowired
    public SysLogAspect(SysLogService sysLogService) {
        this.sysLogService = sysLogService;
    }
    //PointCut表示這是一個切點(diǎn)挽牢,@annotation表示這個切點(diǎn)切到一個注解上,后面帶該注解的全類名,切面最主要的就是切點(diǎn)摊求,所有的故事都圍繞切點(diǎn)發(fā)生禽拔。
    //logPointCut是一個切點(diǎn)名字,@Around注解使用室叉,表示圍繞該切點(diǎn)發(fā)生
    @Pointcut("@annotation(com.hao24.common.annotation.SysLog)")
    public void logPointCut() { 
    }
    // logPointCut1是另外一個切點(diǎn)名字
    @Pointcut("execution(* com.hao24.modules..*.*Controller.*(..))")
    public void logPointCut1()
    {
    }
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //執(zhí)行方法
        Object result = point.proceed();
        //執(zhí)行時長(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        saveSysLog(point, time);
        return result;
    }
    //保存到數(shù)據(jù)庫中
    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 + "()");
        //請求的參數(shù)
        Object[] args = joinPoint.getArgs();
        try{
            String params = new Gson().toJson(args);
            sysLog.setParams(params);
        }catch (Exception ignored){
        }
        //獲取request
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        //設(shè)置IP地址
        sysLog.setIp(IpUtils.getIpAddr(request));
        //用戶名
        String username = ((TblSysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
        sysLog.setUsername(username);
        sysLog.setTime(time);
        sysLog.setCreateDate(new Date());
        //保存系統(tǒng)日志
        sysLogService.save(sysLog);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睹栖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茧痕,更是在濱河造成了極大的恐慌野来,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踪旷,死亡現(xiàn)場離奇詭異曼氛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)令野,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門舀患,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人气破,你說我怎么就攤上這事构舟。” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵狗超,是天一觀的道長。 經(jīng)常有香客問我朴下,道長努咐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任殴胧,我火速辦了婚禮渗稍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘团滥。我一直安慰自己竿屹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布灸姊。 她就那樣靜靜地躺著拱燃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪力惯。 梳的紋絲不亂的頭發(fā)上碗誉,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音父晶,去河邊找鬼哮缺。 笑死,一個胖子當(dāng)著我的面吹牛甲喝,可吹牛的內(nèi)容都是我干的磷醋。 我是一名探鬼主播狰闪,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了器联?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤甸饱,失蹤者是張志新(化名)和其女友劉穎耕皮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谊惭,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汽馋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了圈盔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豹芯。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驱敲,靈堂內(nèi)的尸體忽然破棺而出铁蹈,到底是詐尸還是另有隱情,我是刑警寧澤众眨,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布握牧,位于F島的核電站容诬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沿腰。R本人自食惡果不足惜览徒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颂龙。 院中可真熱鬧习蓬,春花似錦、人聲如沸措嵌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽企巢。三九已至枫慷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間包斑,已是汗流浹背流礁。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罗丰,地道東北人神帅。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像萌抵,于是被迫代替她去往敵國和親找御。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容