<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          怎么在Java中自定義注解?

          共 5108字,需瀏覽 11分鐘

           ·

          2021-03-09 19:59

          文章已收錄Github精選,歡迎Star:https://github.com/yehongzhi/learningSummary

          什么是注解

          注解是JDK1.5引入的新特性,主要用于簡化代碼,提高編程的效率。其實在日常開發(fā)中,注解并不少見,比如Java內(nèi)置的@Override、@SuppressWarnings,或者Spring提供的@Service、@Controller等等,隨著這些注解使用的頻率越來越高,作為開發(fā)人員當(dāng)真有必要深入學(xué)習(xí)一番。

          Java內(nèi)置的注解

          先說說Java內(nèi)置的三個注解,分別是:

          @Override:檢查當(dāng)前的方法定義是否覆蓋父類中的方法,如果沒有覆蓋,編譯器就會報錯。

          @SuppressWarnings:忽略編譯器的警告信息。

          8cf0e31c8513a8bdeceaad3276d49849.webpa69250c5dc60b4d3ce4b0da20ca2399f.webp

          @Deprecated:用于標(biāo)識該類或方法已過時,建議開發(fā)人員不要使用該類或方法。

          bbaea367fbd5b815927908eb728d1dfc.webp6b75e2bc914b269fac2339a978c05065.webp元注解

          元注解其實就是描述注解的注解。主要有四個元注解,分別是:

          @Target

          用于描述注解的使用范圍,也就是注解可以用在什么地方,取值有:

          CONSTRUCTOR:用于描述構(gòu)造器。

          FIELD:用于描述字段。

          LOCAL_VARIABLE:用于描述局部變量。

          METHOD:用于描述方法。

          PACKAGE:用于描述包。

          PARAMETER:用于描述參數(shù)。

          TYPE:用于描述類,包括class,interface,enum。

          @Retention

          表示需要在什么級別保存該注釋信息,用于描述注解的生命周期,取值由枚舉RetentionPoicy定義。

          6b0c1f5cac01f8b8371c4d30082fba3d.webp

          SOURCE:在源文件中有效(即源文件保留),僅出現(xiàn)在源代碼中,而被編譯器丟棄。

          CLASS:在class文件中有效(即class保留),但會被JVM丟棄。

          RUNTIME:JVM將在運行期也保留注釋,因此可以通過反射機制讀取注解的信息。

          如果只是做一些檢查性操作,使用SOURCE,比如@Override,@SuppressWarnings。

          如果要在編譯時進(jìn)行一些預(yù)處理操作,就用 CLASS。

          如果需要獲取注解的屬性值,去做一些運行時的邏輯,可以使用RUNTIME。

          @Documented

          將此注解包含在 javadoc 中 ,它代表著此注解會被javadoc工具提取成文檔。它是一個標(biāo)記注解,沒有成員。

          d8a9ff6805a57f42839df0a345567ecb.webp

          @Inherited

          是一個標(biāo)記注解,用來指定該注解可以被繼承。使用 @Inherited 注解的 Class 類,表示這個注解可以被用于該 Class 類的子類。

          自定義注解

          下面實戰(zhàn)一下,自定義一個注解@LogApi,用于方法上,當(dāng)被調(diào)用時即打印日志,在控制臺顯示調(diào)用方傳入的參數(shù)和調(diào)用返回的結(jié)果。

          定義注解

          首先定義注解@LogApi,在方法上使用,為了能在反射中讀取注解信息,當(dāng)然是設(shè)置為RUNTIME。

          @Target(value?=?ElementType.METHOD)
          @Documented
          @Retention(value?=?RetentionPolicy.RUNTIME)
          public?@interface?LogApi?{
          }

          這種沒有屬性的注解,屬于標(biāo)記注解。

          多說幾句,如果需要傳遞屬性值,也可以設(shè)置屬性值value,比如@RequestMapping注解。

          @Target({ElementType.METHOD,?ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Mapping
          public?@interface?RequestMapping?{
          ????@AliasFor("path")
          ?String[]?value()?default?{};
          }

          如果在使用時。只設(shè)置value值,可以忽略value,比如這樣:

          //完整是@RequestMapping(value?=?{"/list"})
          //忽略value不寫
          @RequestMapping("/list")
          public?Map<String,?Object>?list()?throws?Exception?{
          ????Map<String,?Object>?userMap?=?new?HashMap<>();
          ????userMap.put("1號佳麗",?"李嘉欣");
          ????userMap.put("2號佳麗",?"袁詠儀");
          ????userMap.put("3號佳麗",?"張敏");
          ????userMap.put("4號佳麗",?"張曼玉");
          ????return?userMap;
          }

          標(biāo)記注解

          剛剛定義完注解之后,就可以在需要的地方標(biāo)記注解,很簡單。

          @LogApi
          @RequestMapping("/list")
          public?Map<String,?Object>?list()?throws?Exception?{
          ?//業(yè)務(wù)代碼...
          }

          解析注解

          最關(guān)鍵的一步來了,解析注解,一般在項目中會使用Spring的AOP技術(shù)解析注解,當(dāng)然如果只需要解析一次的話,也可以使用Spring容器的生命周期函數(shù)。

          這里的場景是打印每次方法被調(diào)用的日志,所以使用AOP比較合適。

          創(chuàng)建一個切面類LogApiAspect進(jìn)行解析。

          @Aspect
          @Component
          public?class?LogApiAspect?{
          ?//切面點為標(biāo)記了@LogApi注解的方法
          ????@Pointcut("@annotation(io.github.yehongzhi.user.annotation.LogApi)")
          ????public?void?logApi()?{
          ????}
          ????
          ?//環(huán)繞通知
          ????@Around("logApi()")
          ????@SuppressWarnings("unchecked")
          ????public?Object?around(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
          ????????long?starTime?=?System.currentTimeMillis();
          ????????//通過反射獲取被調(diào)用方法的Class
          ????????Class?type?=?joinPoint.getSignature().getDeclaringType();
          ????????//獲取類名
          ????????String?typeName?=?type.getSimpleName();
          ????????//獲取日志記錄對象Logger
          ????????Logger?logger?=?LoggerFactory.getLogger(type);
          ????????//方法名
          ????????String?methodName?=?joinPoint.getSignature().getName();
          ????????//獲取參數(shù)列表
          ????????Object[]?args?=?joinPoint.getArgs();
          ????????//參數(shù)Class的數(shù)組
          ????????Class[]?clazz?=?new?Class[args.length];
          ????????for?(int?i?=?0;?i?<?args.length;?i++)?{
          ????????????clazz[i]?=?args[i].getClass();
          ????????}
          ????????//通過反射獲取調(diào)用的方法method
          ????????Method?method?=?type.getMethod(methodName,?clazz);
          ????????//獲取方法的參數(shù)
          ????????Parameter[]?parameters?=?method.getParameters();
          ????????//拼接字符串,格式為{參數(shù)1:值1,參數(shù)2::值2}
          ????????StringBuilder?sb?=?new?StringBuilder();
          ????????for?(int?i?=?0;?i?<?parameters.length;?i++)?{
          ????????????Parameter?parameter?=?parameters[i];
          ????????????String?name?=?parameter.getName();
          ????????????sb.append(name).append(":").append(args[i]).append(",");
          ????????}
          ????????if?(sb.length()?>?0)?{
          ????????????sb.deleteCharAt(sb.lastIndexOf(","));
          ????????}
          ????????//執(zhí)行結(jié)果
          ????????Object?res;
          ????????try?{
          ????????????//執(zhí)行目標(biāo)方法,獲取執(zhí)行結(jié)果
          ????????????res?=?joinPoint.proceed();
          ????????????logger.info("調(diào)用{}.{}方法成功,參數(shù)為[{}],返回結(jié)果[{}]",?typeName,?methodName,?sb.toString(),?JSONObject.toJSONString(res));
          ????????}?catch?(Exception?e)?{
          ????????????logger.error("調(diào)用{}.{}方法發(fā)生異常",?typeName,?methodName);
          ????????????//如果發(fā)生異常,則拋出異常
          ????????????throw?e;
          ????????}?finally?{
          ????????????logger.info("調(diào)用{}.{}方法,耗時{}ms",?typeName,?methodName,?(System.currentTimeMillis()?-?starTime));
          ????????}
          ????????//返回執(zhí)行結(jié)果
          ????????return?res;
          ????}
          }

          定義完切面類后,需要在啟動類添加啟動AOP的注解。

          @SpringBootApplication
          //添加此注解,開啟AOP
          @EnableAspectJAutoProxy
          public?class?UserApplication?{
          ????public?static?void?main(String[]?args)?{
          ????????SpringApplication.run(UserApplication.class,?args);
          ????}

          }

          測試

          我們再在Controller控制層增加一個有參數(shù)的接口。

          @LogApi
          @RequestMapping("/get/{id}")
          public?String?get(@PathVariable(name?=?"id")?String?id)?throws?Exception?{
          ????HashMap<String,?Object>?user?=?new?HashMap<>();
          ????user.put("id",?id);
          ????user.put("name",?"關(guān)之琳");
          ????user.put("經(jīng)典角色",?"十三姨");
          ????return?JSONObject.toJSONString(user);
          }

          啟動項目,然后請求接口list(),我們可以看到控制臺出現(xiàn)被調(diào)用方法的日志信息。

          bb4a1503ab0c8edef2abbc4b57be6e6c.webp

          請求有參數(shù)的接口get(),可以看到參數(shù)名稱和參數(shù)值都被打印在控制臺。

          a15dcfef66346c375caec336b3f74d73.webp

          這種記錄接口請求參數(shù)和返回值的功能,在實際項目中基本上都會使用,因為這能利于系統(tǒng)的排錯和性能調(diào)優(yōu)等等。

          我們也可以在這個例子中,學(xué)會使用注解和切面編程,可謂是一舉兩得!

          總結(jié)

          注解的使用能大大地減少開發(fā)的代碼量,所以在實際項目的開發(fā)中會使用到非常多的注解。特別是做一些公共基礎(chǔ)的功能,比如日志記錄,事務(wù)管理,權(quán)限控制這些功能,使用注解就非常高效且優(yōu)雅。

          對于自定義注解,主要有三個步驟,定義注解,標(biāo)記注解,解析注解,并不是很難。

          這篇文章講到這里了,感謝大家的閱讀,希望看完這篇文章能有所收獲!

          覺得有用就點個贊吧,你的點贊是我創(chuàng)作的最大動力~

          我是一個努力讓大家記住的程序員。我們下期再見?。?!

          能力有限,如果有什么錯誤或者不當(dāng)之處,請大家批評指正,一起學(xué)習(xí)交流!

          瀏覽 71
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码操逼片 | 日韩在线观看网址 | 亚洲精品宾馆在线 | 亚洲AV无码久久久久久 | 久国产视频。 |