快來(lái)自定義一個(gè)屬于你自己的java注解吧
“大家好,我是公眾號(hào):【java小杰要加油】,最近在項(xiàng)目中,發(fā)現(xiàn)了很多地方都用到了自定義注解, 根據(jù)自定義的注解,再去做一些個(gè)性化的操作,非常方便,今天來(lái)分享給大家
話不多說,直接開車
注解大致介紹
首先,讓我們來(lái)聲明一個(gè)注解
// 注解可以作用在哪里
@Target({ElementType.TYPE})
// 該注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
// 指示默認(rèn)情況下,帶有類型的注釋將由javadoc *和類似工具來(lái)記錄
@Documented
// 可以繼承父類注解
@Inherited
// bean
@Component
public @interface DIYClassAnnotation {
DIYEnum diyEnum();
// 年齡默認(rèn)24 歲
int age() default 24;
}
可以注意到,我們聲明的這個(gè)注解,他自己又帶著很多元注解,我們依此來(lái)解釋下,對(duì)應(yīng)可取的值也如下
@Target : 指此注解可以標(biāo)注在哪些地方,是字段?還是類?還是方法? TYPE :類,接口(包括注釋類型)或枚舉聲明 FIELD:字段聲明(包括枚舉常量) METHOD:方法聲明 PARAMETER:形式參數(shù)聲明 CONSTRUCTOR:構(gòu)造函數(shù)聲明 LOCAL_VARIABLE:局部變量聲明 ANNOTATION_TYPE:注釋類型聲明 PACKAGE:包聲明 TYPE_PARAMETER:類型參數(shù)聲明 TYPE_USE:使用類型 @Retention :指該注解的生命周期,存活在哪個(gè)階段 SOURCE:批注將被編譯器丟棄 CLASS:注釋將由編譯器記錄在類文件中,但不必在運(yùn)行時(shí)由VM保留。這是默認(rèn)的行為。 RUNTIME:注釋將由編譯器記錄在類文件中,并在運(yùn)行時(shí)由VM保存*,因此可以通過反射方式讀取它們 @Documented :指是默認(rèn)情況下,帶有類型的注釋將由javadoc *和類似工具來(lái)記錄 @Inherited :可以繼承父類注解 里面的值只能用基本類型boolean、int、double、float、long、byte、short、char和String、Enum、Class以及一些其他注解
“我們一般寫注解的時(shí)候。就是用圖中上面那幾個(gè)加粗顏色的屬性和值
實(shí)戰(zhàn)演練
“
其實(shí)使用這個(gè)自定義注解,千言萬(wàn)語(yǔ)就一句話
先聲明一個(gè)自定義的注解 通過反射等方式取出這個(gè)注解,再根據(jù)這個(gè)注解中自己設(shè)定的值去做一些定制化的操作
本文將演示三種類型的自定義注解怎么用,平常開發(fā)也就這三種了(我接觸的)
一、自定義類注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface DIYClassAnnotation {
// 自定義枚舉類型
DIYEnum diyEnum();
// 年齡默認(rèn)24 歲
int age() default 24;
}
看一下這個(gè)枚舉類型
public enum DIYEnum {
xiaoJie("小杰","打代碼"),
TEACHER("老師","教書"),
CHEF("廚師","做飯");
private String name;
private String worker;
DIYEnum(String name,String worker) {
this.name = name;
this.worker = worker;
}
public String getWorker() {
return worker;
}
public void setWorker(String worker) {
this.worker = worker;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
二、自定義字段注解
// 注解到什么地方 屬性 上
@Target({ElementType.FIELD})
// 該注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
// 指示默認(rèn)情況下,帶有類型的注釋將由javadoc *和類似工具來(lái)記錄
@Documented
// 可以繼承父類注解
@Inherited
// bean
@Component
public @interface DIYFieldAnnotation {
// 性別
String sex();
}
三、自定義方法注解
// 注解到什么地方 方法 上
@Target({ElementType.METHOD})
// 該注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
// 指示默認(rèn)情況下,帶有類型的注釋將由javadoc *和類似工具來(lái)記錄
@Documented
// 可以繼承父類注解
@Inherited
// bean
@Component
public @interface DIYMethodAnnotation {
// 是否校驗(yàn)
int verification();
// 接口名稱
String interfaceName();
}
其實(shí)我們注意到,感覺大家都大差不差啊,是的沒錯(cuò),也就是target作用域不一樣罷了
我們自定義注解定義完了,下面要開始真正使用啦
定義個(gè)抽象父類Person
// 抽象父類
public abstract class Person {
public abstract void hobby();
}
定義學(xué)生Student子類
@DIYClassAnnotation(diyEnum = DIYEnum.xiaoJie,age=23 )
public class Student extends Person {
@DIYFieldAnnotation(sex = "男")
private String sex;
@Override
public void hobby() {
System.out.println(DIYEnum.xiaoJie.getWorker());
}
}
定義老師Teacher子類
@DIYClassAnnotation(diyEnum = DIYEnum.TEACHER,age=46 )
public class Teacher extends Person {
@DIYFieldAnnotation(sex = "女")
private String sex;
@Override
public void hobby() {
System.out.println(DIYEnum.TEACHER.getWorker());
}
}
定義廚師Chef子類
@DIYClassAnnotation(diyEnum = DIYEnum.CHEF,age=50 )
public class Chef extends Person {
@DIYFieldAnnotation(sex = "男")
private String sex;
@Override
public void hobby() {
System.out.println(DIYEnum.CHEF.getWorker());
}
}
再來(lái)一個(gè)注解工具類
public class DIYAnnotationUtils {
public static Person getPerson(Person ...persons){
for (Person person:persons) {
// 判斷這個(gè)類是否有這個(gè)注解
if (person.getClass().isAnnotationPresent(DIYClassAnnotation.class)){
// 得到這個(gè)自定義的注解
DIYClassAnnotation workerAnnotation = person.getClass().getAnnotation(DIYClassAnnotation.class);
// 判斷這個(gè)自定義注解注解的值是否是我們想要的
if (DIYEnum.xiaoJie.getName().equals(workerAnnotation.diyEnum().getName())){
// 反射得到這個(gè)對(duì)象的屬性
Field[] fields = person.getClass().getDeclaredFields();
for (Field field:fields) {
// 如果這個(gè)字段有這個(gè)注解
if (field.isAnnotationPresent(DIYFieldAnnotation.class)){
// 打印出這個(gè)屬性上有這個(gè)注解的值
DIYFieldAnnotation annotation = field.getAnnotation(DIYFieldAnnotation.class);
System.out.println(annotation.sex());
}
}
return person;
}
}
}
return null;
}
}
最主要的就是這個(gè)工具類(用到反射),其中根據(jù)傳進(jìn)來(lái)的對(duì)象判斷符合不符合我們的要求 (注解時(shí)的名字是不是小杰),如果符合的話,把注解在屬性上的注解拿出來(lái)
我們通過測(cè)視類來(lái)調(diào)用一下
public class Test {
public static void main(String[] args) {
Student student =new Student();
Chef chef = new Chef() ;
Teacher teacher = new Teacher();
Person person = DIYAnnotationUtils.getPerson(student, chef, teacher);
if (person != null){
person.hobby();
}
}
}
輸出結(jié)果是
男
打代碼
下面我們來(lái)演示下怎么在方法上使用這注解,最常見的組合就是自定義注解+AOP
下面來(lái)看下controller
@RestController
public class Controller {
// 此方法需要校驗(yàn)
@DIYMethodAnnotation(verification = 1,interfaceName = "學(xué)生愛好接口")
@RequestMapping("/verification")
public String verificationMethod(String id){
new Student().hobby();
return "校驗(yàn)";
}
// 此方法不需要校驗(yàn)
@DIYMethodAnnotation(verification = 0,interfaceName = "老師愛好接口")
@RequestMapping("/noVerification")
public String noVerificationMethod(String id){
new Teacher().hobby();
return "不校驗(yàn)";
}
// 此方法沒有注解
@RequestMapping("/noAnnotation")
public String noAnnotationMethod(String id){
new Chef().hobby();
return "無(wú)注解";
}
}
再看下切面類 本文注重講解注解,這個(gè)切面類還有很多完善的地方不過不在本文范圍內(nèi)
@Component
@Aspect
public class LogAspect {
// 注解的位置
@Pointcut("@annotation(com.example.demo.annotation.DIYMethodAnnotation)")
public void diyPointCut(){};
@Around("diyPointCut()")
public Object diyAround(ProceedingJoinPoint joinPoint){
//獲得被增強(qiáng)的方法相關(guān)信息
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 獲得這個(gè)方法
Method method = signature.getMethod();
// 獲得這個(gè)方法上面的注解
DIYMethodAnnotation diyMethodAnnotation = method.getAnnotation(DIYMethodAnnotation.class);
// 根據(jù)注解自定義的一些屬性去做自定義的操作
if (diyMethodAnnotation.verification() == 1){
System.out.println("當(dāng)前校驗(yàn)的是:"+diyMethodAnnotation.interfaceName());
System.out.println("方法名稱是:"+method.getName());
System.out.println("傳遞參數(shù)是:"+JSON.toJSONString(joinPoint.getArgs()));
}
System.out.println("aop 攔截器里 verification:"+diyMethodAnnotation.verification() );
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
我們將項(xiàng)目跑起來(lái),分別訪問兩個(gè)打了注解的接口
需要校驗(yàn)的接口
http://localhost:8081/verification?id=1
當(dāng)前校驗(yàn)的是:學(xué)生愛好接口
方法名稱是:verificationMethod
傳遞參數(shù)是:["1"]
aop 攔截器里 verification:1
打代碼
不需要校驗(yàn)的接口
http://localhost:8081/noVerification?id=1
aop 攔截器里 verification:0
做飯
沒有注解的接口
http://localhost:8081/noAnnotation?id=1
做飯
由輸出結(jié)果可以得出一個(gè)結(jié)論,
沒有注解的接口,走不到AOP,因?yàn)槲覀傾OP配置的是只有注解的接口才進(jìn)行AOP校驗(yàn), 如果接口上有注解的話,又有兩種情況(這是我們自己設(shè)置的) 輸出 ”當(dāng)前校驗(yàn)的是,方法名稱是,傳遞參數(shù)是 verification 0 的時(shí)候 這個(gè)注解也不進(jìn)行特別的操作 verification 1 的時(shí)候 這個(gè)注解進(jìn)行特別的操作
“綜上所述,我們?cè)谌粘i_發(fā)中,如果對(duì)某個(gè)類/字段/方法有什么特殊的要求的話,可以使用自定義注解,再通過反射獲取到此注解,再根據(jù)這個(gè)注解中自定義的值在進(jìn)行我們自定義的操作
好文推薦
多圖慎入,從四層模型上解析網(wǎng)絡(luò)是怎么連接的 想和你聊聊操作系統(tǒng)的內(nèi)存管理 mysql中的各種鎖把我搞糊涂啦 五千來(lái)字小作文,是的,我們是有個(gè)HTTP。 京東面試官問我:“聊聊MySql事務(wù),MVCC?”
最后
再貼一張最近火爆全網(wǎng)同時(shí)也對(duì)我觸動(dòng)很大的話

自助者,天助之
“我是【java小杰要加油】,歡迎大家關(guān)注,有什么想說的想看的評(píng)論區(qū)里留言呀,我們下期見。
