欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring自定義注解及使用方法詳細例子

 更新時間:2024年01月15日 11:36:00   作者:清云青云  
這篇文章主要給大家介紹了關(guān)于spring自定義注解及使用方法的相關(guān)資料,Spring 是一個非常強大的框架,可以使用自定義注解來完成許多任務(wù),文中通過代碼介紹的非常詳細,需要的朋友可以參考下

簡介

在spring項目中使用注解,簡化了代碼量,減輕對業(yè)務(wù)代碼的侵入性;對框架統(tǒng)一處理鑒權(quán)、日志等起到極大的作用,可以結(jié)合著攔截器、aop在請求調(diào)用前后添加額外處理。spring有內(nèi)置的@Controller、@Service等注解,出于業(yè)務(wù)考慮,我們可以自定義想要的注解。

一、定義注解

自定義注解類似于定義接口,但是需要指明注解的作用范圍、生命周期等屬性。

1.注解示例

下面是一個簡單的自定義注解示例,使用@interface修飾,定義了三個屬性值,使用注解的時候可以給這些屬性賦值。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LogAnnotation {

    String moduleName() default "";
    String operaName() default "";
    String operaType() default "";

}

2.元注解含義

從jdk1.5開始,在包java.lang.annotation下提供了四種元注解:@Target、@Retention、@Documented、@Inherited,java1.8后,annotation包下新提供了兩種元注解:@Native、@Repeatable。自定義注解的時候需要使用元注解修飾,來看下各個元注解的使用說明。

(1)@Target

標識注解可以使用的范圍,例如使用在方法、字段、構(gòu)造方法上??聪略创a:

//Target源碼
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

//Target可配置的類型
public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

從源碼中可以看出@Target只有一個屬性value,屬性類型為ElementType類型的數(shù)組,ElementType各個枚舉值的作用范圍如下:

①ElementType.TYPE:允許被修飾的注解作用在:類、接口、枚舉上;

②ElementType.FIELD:允許被修飾的注解作用在:屬性字段上;

③ElementType.METHOD:允許被修飾的注解作用在:方法上;

④ElementType.PARAMETER:允許被修飾的注解作用在:方法參數(shù)上;

⑤ElementType.CONSTRUCTOR:允許被修飾的注解作用在:構(gòu)造器上;

⑥ElementType.LOCAL_VARIABLE:允許被修飾的注解作用在:本地局部變量上;

⑦ElementType.ANNOTATION_TYPE:允許被修飾的注解作用在:注解上;

⑧ElementType.PACKAGE:允許被修飾的注解作用在:包名上;

⑨ElementType.TYPE_PARAMETER:允許被修飾的注解作用在:類型參數(shù)上,jdk1.8提供;

//ElementType.TYPE_PARAMETER示例
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
}

//泛型聲明
public class TypeParameterClass<@TypeParameterAnnotation T> {
    public <@TypeParameterAnnotation P> T too(T t){
        return t;
    }
}

⑩ElementType.TYPE_USE:允許被修飾的注解作用在:任何語句中(聲明語句、泛型、強制轉(zhuǎn)化),jdk1.8提供。

(2)@Retention

標識注解的生命周期,來看下源碼:

//Retention源碼
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {

    RetentionPolicy value();
}

//RetentionPolicy源碼
public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

從源碼可以看出@Retention只有一個屬性value,屬性類型為RetentionPolicy,看下RetentionPolicy枚舉值的生命周期:

①RetentionPolicy.SOURCE:編譯階段丟棄,編譯之后注解沒有任何作用,不會寫入字節(jié)碼文件中。例如@Override、@SuppressWarnings、@Deprecated都屬于這類注解;

②RetentionPolicy.CLASS:類加載階段丟棄,類加載進jvm后沒有任何作用,在字節(jié)碼文件處理中有用。注解默認使用這種方式;

③RetentionPolicy.RUNTIME:始終不會丟棄,程序運行期也保留此注解,自定義注解通常使用這種方式,因此可以通過反射獲取到注解配置的屬性值。

(3)@Documented

標識注解是否在javadoc文檔中顯示,看下源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

當定義的注解中加入了@Documented元注解,則生成的javadoc文檔中包含注解,來看一個例子:

@Documented
public @interface DocumentAnnotation {
    String name() default "張三";
    int age() default 18;
}

public class DocumentTest {

    @DocumentAnnotation(name="lisi",age = 30)
    public void test(){

    }
}

此時生成javadoc文件,生成的方式為:

? 文檔中包含注解信息:

自定義注解DocumentAnnotation去掉@Documented,javadoc文檔中不包含注解:

(4)@Inherited

標識注解是否能繼承到子類,看下源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

使用@Inherited修飾的注解,在class使用它時,class的子類能夠繼承此注解,類似于InheritableThreadLocal,父子類能夠共享資源。

(5)@Native

標識字段是否可以被本地代碼引用,看下源碼:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}

此注解作用在字段上,生命周期為編譯階段丟棄。

(6)@Repeatable

標識可以重復使用注解,看下源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}

作用在注解上,只有一個屬性value,屬性的類型繼承了Annotation,之所以繼承Annotation是因為Annotation是所有注解的父接口,看下關(guān)系圖:

來看一個demo:

//定義注解
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}

//定義注解,Repeatable聲明RepeatableAnnotations
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    String name();
    int age();
}

//測試類
public class RepeatableDemo {
    @RepeatableAnnotation(name="張三",age=18)
    @RepeatableAnnotation(name="李四",age=30)
    private String userMessage;

    public static void main(String[] args) throws NoSuchFieldException {
        Field declaredField = RepeatableDemo.class.getDeclaredField("userMessage");
        Annotation[] annotations = declaredField.getDeclaredAnnotations();
        System.out.println("注解的數(shù)量:"+annotations.length);
        System.out.println("注解內(nèi)容:"+Arrays.toString(annotations));
    }
}

測試類輸出結(jié)果:

注解的數(shù)量:1
注解內(nèi)容:[@com.RepeatableAnnotations(value=[@com.RepeatableAnnotation(name=張三, age=18), @com.RepeatableAnnotation(name=李四, age=30)])]

定義一個可重復的注解,需要使用@Repeatable來聲明,@Repeatable的值為此原注解數(shù)組形式的新注解。從測試類可以看出最終注解的數(shù)量還是1個,是使用@Repeatable值的數(shù)組形式接收,每個值為原注解類型。

在spring中ComponentScan定義bean的掃描范圍,就是這樣使用的,看下它的源碼:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
    ComponentScan[] value();
}

//使用
@ComponentScan(basePackages = {"com.xxx1","com.xxx2"})

使用@Repeatable注意事項:

①原注解的@Target作用范圍要比@Repeatable值的范圍大或者相同,否則編譯錯誤,例如:

//比RepeatableAnnotation多了ElementType.METHOD
@Target(value={ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}

@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    String name();
    int age();
}

②原注解的@Retention生命周期要比@Repeatable值的小或者相同,否則編譯錯誤,生命周期大?。篠OURCE <
CLASS < RUNTIME。例如:

//定義的CLASS比RUNTIME要小
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}

@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    String name();
    int age();
}

二、使用注解

定義注解就是為了方便系統(tǒng)開發(fā),現(xiàn)在來看一些使用場景。

1.aop切點使用注解

自定義注解結(jié)合著aop來使用的場景很多,例如日志的收集就可以使用。

①定義注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    //模塊名稱(枚舉類)
    ModuleNameEnum moduleName() default ModuleNameEnum.UNKNOWN;

    //操作對象
    String operaName() default "";

    //操作類型(枚舉類)
    OperaTypeEnum operaType() default OperaTypeEnum.UNKNOWN;
}

②定義aop切面類:

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    XxxLogService xxxLogService;

    //切點:使用LogAnnotation注解標識的方法都進行切入,也可以使用通配符配置具體要切入的方法名
    @Pointcut("@annotation(com.xxx.aop.LogAnnotation)")
    public void pointCut(){

    }

    //環(huán)繞通知
    @Around("pointCut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object jsonResult = joinPoint.proceed(); //執(zhí)行方法
        try {
            //獲取請求簽名
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            //獲取切入點所在的方法
            Method method = signature.getMethod();
            //獲取注解值
            LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
            //獲取屬性
            String moduleName = annotation.moduleName().getValue();
            String operaName = annotation.operaName();
            String operaType = annotation.operaType().getValue();

            XxxLog xxxLog = new XxxLog();
            xxxLog.setModuleName(moduleName);
            xxxLog.setOperaName(operaName);
            xxxLog.setOperaType(operaType);
            //添加日志
            xxxLogService.insertOne(xxxLog);
        } catch (Exception e){
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return jsonResult;
    }
}

③方法中添加注解

       當注解屬性名為value時,賦值的時候可以省略屬性名,其他名稱的屬性名需要使用xx=yy的方式指定。

    @LogAnnotation(moduleName= ModuleNameEnum.FeedBack,operaName="添加消息",operaType=OperaTypeEnum.Insert)
    public void insertOne(Integer id) {
        
    }

過程為:定義注解,定義屬性值;創(chuàng)建切面類,使用@annotation來指定切點為自定義注解,環(huán)繞方法獲取注解及屬性值,把屬性值保存到業(yè)務(wù)數(shù)據(jù)庫中;業(yè)務(wù)代碼中需要保存日志的方法加上注解,并設(shè)置屬性值。

2.攔截器獲取注解

可以在攔截器中獲取注解,在controller層響應(yīng)前后做一些額外的處理或判斷,例如判斷權(quán)限、判斷是否需要分頁等。來看一個分頁的demo:

①定義注解

@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface EnablePaging {
    int value() default 50;
}

②定義攔截器

public class PagingInterceptor implements HandlerInterceptor {
    //controller響應(yīng)之前執(zhí)行
    @Override
    public boolean preHandle(@NotNull HttpServletRequest request,
                             @NotNull HttpServletResponse response,
                             @NotNull Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //獲取方法中的注解
        EnablePaging enablePaging = handlerMethod.getMethodAnnotation(EnablePaging.class);
        //不包含注解,直接通過
        if (enablePaging == null) {
            return true;
        }
        //包含注解,則獲取注解中的值,值保存到TreadLocal線程變量中(此處使用RequestContextHolder.currentRequestAttributes().setAttribute保存),在執(zhí)行sql查詢時取出使用
        PagingContextData data = PagingContextData.getInstance(RequestAttributes.SCOPE_REQUEST, true);
        data.setEnabled(true);
        //把注解中配置的值設(shè)置進去
        if (enablePaging.value() > 0) {
            data.setDefaultPageSize(enablePaging.value());
        }
        return true;
    }
}

③注冊攔截器

@Configuration
public class PagingHttpConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PagingInterceptor()).order(Ordered.HIGHEST_PRECEDENCE);
    }
}

④方法中添加注解

    @PostMapping("/datasource/xxxPage")
    @EnablePaging(20)
    public Object xxxPage(@RequestBody String json) {
        return xxxService.xxxPage(json);
    }

過程為:定義注解,定義屬性值;創(chuàng)建攔截器,在攔截器的方法中獲取注解及屬性值,把屬性值保存到線程變量ThreadLocal中;把攔截器注冊到InterceptorRegistry中;業(yè)務(wù)代碼中需要分頁的接口方法加上注解,并設(shè)置屬性值。

3.class獲取注解

通過class可以獲取到注解,提供了從method方法、field字段等獲取注解。獲取class的方式有:

①對象.getClass()方法

Student stu = new Student();
Class clazz = stu.getClass();

②對象.class

Class clazz = Student.class;

③Class.forName(“xxx”),例如數(shù)據(jù)庫驅(qū)動的獲取

Class clazz = Class.forName("com.xxx.Student")

從method中獲取注解示例:

//獲取所有方法
Method[] methods = SampleClass.class.getMethods();
for(int i = 0;i < methods.length;i++) {
    //獲取方法中的注解
    CustomAnnotaion annotation = methods[i].getAnnotation(CustomAnnotaion.class);
    if(null != annotation) {
        //輸出屬性值
        System.out.println(annotation.name());
    }
}

//獲取指定方法
Method oneMethod = SampleClass.class.getDeclaredMethod("getSampleField");
//獲取方法中的注解值
CustomAnnotaion annotation = oneMethod.getAnnotation(CustomAnnotaion.class);
System.out.println("annotation="+annotation.name());

從字段中獲取注解示例:

//獲取指定字段
Field declaredField = RepeatableDemo.class.getDeclaredField("userMessage");
//獲取字段中的注解
Annotation[] annotations = declaredField.getDeclaredAnnotations();

4.spring容器獲取注解

在bean對象中加入注解,當spring容器加載完bean之后,可以從bean中獲取到哪些方法加了指定的注解,從而拿到方法,對這些方法進行特殊處理。在xxl-job開源項目中就有使用,看下使用方式:

private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
        if (applicationContext == null) {
            return;
        }
        // init job handler from method
        //從程序上下文中獲取到所有的bean名稱集合
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        //遍歷bean集合
        for (String beanDefinitionName : beanDefinitionNames) {
            //根據(jù)bean名稱從程序上下文獲取到此bean對象
            Object bean = applicationContext.getBean(beanDefinitionName);

            Map<Method, XxlJob> annotatedMethods = null; 
            try {
                //對Bean對象進行方法過濾,查詢到方法被XxlJob注解修飾,是則放到annotatedMethods集合中
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        new MethodIntrospector.MetadataLookup<XxlJob>() {
                            @Override
                            public XxlJob inspect(Method method) {
                                //判斷方法被XxlJob注解修飾才返回
                                return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
                            }
                        });
            } catch (Throwable ex) {
                logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
            }
            //當前遍歷的bean沒有被XxlJob注解修飾,則跳過處理
            if (annotatedMethods==null || annotatedMethods.isEmpty()) {
                continue;
            }

            //循環(huán)處理當前Bean下被XxlJob修飾的方法
            for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
                //執(zhí)行的方法
                Method executeMethod = methodXxlJobEntry.getKey();
                //XxlJob注解類
                XxlJob xxlJob = methodXxlJobEntry.getValue();
                //注冊此任務(wù)處理器
                registJobHandler(xxlJob, bean, executeMethod);
            }
        }
    }

從spring上下文applicationContext中獲取到所有的bean名稱集合,遍歷bean名稱集合,根據(jù)bean名稱從程序上下文獲取到此bean對象,對Bean對象進行方法過濾,查詢到被XxlJob注解修飾的方法,放到map集合中,循環(huán)處理map中的記錄,key為Method方法,value為XxlJob注解,這也是使用注解的場景。

總結(jié)

到此這篇關(guān)于spring自定義注解及使用方法的文章就介紹到這了,更多相關(guān)spring自定義注解使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java替換(新增)JSON串里面的某個節(jié)點操作

    Java替換(新增)JSON串里面的某個節(jié)點操作

    這篇文章主要介紹了Java替換(新增)JSON串里面的某個節(jié)點操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • java如何判斷Date是上午還是下午

    java如何判斷Date是上午還是下午

    文章介紹了三種在Java中判斷Date對象是上午還是下午的方法:使用Calendar類、使用Java 8的java.time包以及使用SimpleDateFormat進行格式化輸出,每種方法都有代碼示例和解釋,幫助開發(fā)者根據(jù)具體需求選擇合適的方法
    2025-03-03
  • Java導出網(wǎng)頁表格Excel過程詳解

    Java導出網(wǎng)頁表格Excel過程詳解

    這篇文章主要介紹了Java導出網(wǎng)頁表格Excel過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • Springboot筆記之熱部署及不生效的解決方案

    Springboot筆記之熱部署及不生效的解決方案

    這篇文章主要介紹了Springboot筆記之熱部署及不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • java使用ArrayList遍歷及效率比較實例分析

    java使用ArrayList遍歷及效率比較實例分析

    這篇文章主要介紹了java使用ArrayList遍歷及效率比較,實例分析了ArrayList遍歷的方法與使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • Java中實現(xiàn)高清圖片壓縮的兩種方案(最新推薦)

    Java中實現(xiàn)高清圖片壓縮的兩種方案(最新推薦)

    文章首先介紹了Java中進行高清圖片壓縮的基本方法,包括使用Java標準庫ImageIO和第三方庫ApacheCommonsCompress,通過示例代碼展示了如何調(diào)整圖像質(zhì)量和使用第三方工具來壓縮圖片文件,感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • SpringBoot整合Mybatis Plus多數(shù)據(jù)源的實現(xiàn)示例

    SpringBoot整合Mybatis Plus多數(shù)據(jù)源的實現(xiàn)示例

    本文主要介紹了SpringBoot整合Mybatis Plus多數(shù)據(jù)源的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文(必看)

    IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文(必看)

    這篇文章主要介紹了IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文了,本文通過截圖的形式給大家展示,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • SpringBoot個性化啟動Banner設(shè)置方法解析

    SpringBoot個性化啟動Banner設(shè)置方法解析

    這篇文章主要介紹了SpringBoot個性化啟動Banner設(shè)置方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • Spring在多線程下@Resource注入為null的問題

    Spring在多線程下@Resource注入為null的問題

    這篇文章主要介紹了Spring在多線程下@Resource注入為null的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評論