最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring @Component自定義注解實現(xiàn)詳解

 更新時間:2022年09月08日 16:17:14   作者:夜貓nightcat  
@Component是一個元注解,意思是可以注解其他類注解,如@Controller @Service @Repository @Aspect。官方的原話是:帶此注解的類看為組件,當(dāng)使用基于注解的配置和類路徑掃描的時候,這些類就會被實例化

引子

Java里的注解

注解是什么

注解是Java SE5中引入的總要特性,注解的出現(xiàn)使得我們能夠以將有編譯器來測試和驗證的格式,存儲有關(guān)程序的額外信息.

元注解

Java 中的元注解主要有以下四個

  • @Target 用來定義你的注解將應(yīng)用于什么地方,例如類,方法,字段
  • @Retention用來定義該注解在哪個級別可用 Source,Class Runtime.
  • @Document將此注解包含在Javadoc中
  • @Inherited 允許子類繼承父類中的注解

注解的保存策略

上面提到@Retention注解的取值有Source,Class,Runtime這三個,這三個值的意思如下

  • Source注解只保留在源碼中,被編譯器丟棄,實際開發(fā)中幾乎沒有用到
  • Class 注解保留在Class文件中,但不在運行時由虛擬機保留
  • Runtime注解保留在Class文件中,并且在運行時由虛擬機保留,我們知道反射是運行時動態(tài)修改類信息的技術(shù),如果我們自定義的注解保存策略聲明為Runtime,那么我們就能通過反射在運行時動態(tài)獲取注解,然后通過注解處理器來實現(xiàn)對應(yīng)的邏輯。

自定義注解

有了Java中提供的元注解,我們就可以依賴元注解來編寫自定義注解了,我們只需要指定兩個關(guān)鍵信息:注解在哪個級別可用,以及注解作用的位置就可以了,即給我們的注解打上@Target和@Retention注解。我們來看一個例子,下面的代碼聲明了一個定義注解MyAnnotion

package com.nightcat.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

可以看到@Target制定了我們自定義的注解可以使用在類上,@Rentention中的保存策略指定了我們的注解將在運行期保留(這很重要)。

注解元素

首先我們?yōu)樽远x注解添加一些屬性:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String title() default "";
    int type() default 0;
}

我們向自定義注解中添加了一個String類型的title屬性和一個int類型的type屬性。

那么自定義注解中可以使用的元素類型有哪些呢?

  • 所有基本類型
  • String
  • Class
  • enum
  • Annotation
  • 以上所有類型的數(shù)組

注解處理器

我們定義好了注解,它能做些什么?這就必須提到注解處理器,因為如果沒有用來處理注解的工具,那注解也不會有什么用處,那么什么是注解處理器呢?其實就是由我們自己編寫的處理自定義注解的程序。下面我們就來編寫一個注解處理器,用來處理我們的自定義注解。

由于我們的注解保存策略指定為Runtime,那么我們就能通過反射技術(shù)來獲取注解。我們先來看一段代碼

package com.nightcat.bean;
import com.nightcat.annotaion.MyAnnotation;
@MyAnnotation(title = "《好好學(xué)習(xí)》", type = 1)
public class Article {
}

我們聲明了一個Article類,并且給它打上了我們自定義的注解,下面我們通過自定義的注解處理器來獲取注解中屬性的值。

package com.nightcat.test;
import com.nightcat.annotaion.MyAnnotation;
import com.nightcat.bean.Article;
public class AnnotationTest {
    public static void main(String[] args) {
        Class<Article> articleClass = Article.class;
        MyAnnotation annotation = articleClass.getAnnotation(MyAnnotation.class);
        System.out.println("annotation.title() = " + annotation.title()); //《好好學(xué)習(xí)》
        System.out.println("annotation.type() = " + annotation.type());// 1
    }
}

你會發(fā)現(xiàn),我們用了反射技術(shù)獲取到了定義注解的屬性值,這就是自定義注解的使用方法:

  • 將保存策略聲明為Runtime.
  • 通過反射獲取注解信息,執(zhí)行對應(yīng)的業(yè)務(wù)邏輯.

注解是什么

學(xué)習(xí)了上面的知識,我們對注解有了一個基本認(rèn)識,那么注解究竟是什么呢?

其實注解就是繼承了Annotion接口的一個接口而已,拿我們自定義的MyAnnotation舉例子:

我們先來看一下MyAnnotation.class文件

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.nightcat.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String title() default "";
    int type() default 0;
}

這是IDEA替我們處理好的,接著我們用javap -c命令反編譯一下看看

通過反編譯我們發(fā)現(xiàn)注解其實就是一個接口。

自己寫一個@Component注解

準(zhǔn)備工作

有了上面的基礎(chǔ)我們終于要開始寫自己的@Component注解了。我們知道SpringFramework中常用的注解有 @Component @Controller @Service @Bean @Repository 等等,這些注解有一個基礎(chǔ)的功能,將注解標(biāo)記的類加到Spring容器中,交給Spring管理。那么這就涉及到兩個問題:

  • 掃描指定路徑下被這些注解標(biāo)記的類
  • 把這些類加入到IOC容器中

好了,接下來我們就開始寫代碼了

代碼實現(xiàn)

準(zhǔn)備@MyComponent注解

package com.nightcat.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponent {
}

你會發(fā)現(xiàn)我們的這個注解的保存策略是Runtime,原因不再贅述。

準(zhǔn)備一個自定義類

package com.nightcat.bean;
import com.nightcat.annotaion.MyComponent;
@MyComponent
public class Student {
    private Integer age;
    private String stuName;
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", stuName='" + stuName + '\'' +
                '}';
    }
}

注意這個類上打上了我們自定義的@MyComponent注解,方便檢測哪些類需要加入到ioc容器,哪些不需要。

準(zhǔn)備一個簡易的ApplicationContext

我們在使用ioc容器時,主要使用的是ClassPathXmlApplicationContext或者AnnotationConfigApplicationContext,通過這兩個類的構(gòu)造得到ioc容器,然后通過getBean方法獲取指定的bean,這個過程涉及到BeanFactory,需要判斷以下內(nèi)容

  • Bean的scope是單例的還是多例的
  • 是不是IOC容器中已經(jīng)有這個Bean了

等等。

我們這里不這么麻煩,直接通過一個線程安全的HashMap來模擬oc容器

主要實現(xiàn)思路如下

  • 用ConcurrentHashMap來模擬容器
  • 掃描傳入的包路徑及其子路徑
  • 如果該路徑下存在我們自定義注解@MyComponent標(biāo)記的類,就獲取該類的全類名,然后通過反射創(chuàng)建對象,并將其放入我們自定義的ioc容器中
  • 測試我們自定義的注解是否起作用

實現(xiàn)代碼

package com.nightcat;
import com.nightcat.annotaion.MyComponent;
import java.io.File;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
public class ApplicationContext {
    private Logger logger = Logger.getLogger(ApplicationContext.class.toString());
    private Map<String, Object> ioc = new ConcurrentHashMap();
    public Object getBean(String beanName) {
        return ioc.get(beanName);
    }
    public ApplicationContext(String packagePath) {
        scanPackagePath(packagePath);
    }
    private void scanPackagePath(String packagePath) {
        File file = getFile(packagePath);
        File[] allFile = getAllFile(packagePath, file);
        handleClassFile(packagePath, allFile);
    }
    private void handleClassFile(String packagePath, File[] allClassFile) {
        for (File file : allClassFile) {
            String classFileName = file.getName();
            String fileName = classFileName.substring(0, classFileName.lastIndexOf("."));
            String beanName = String.valueOf(fileName.charAt(0)).toLowerCase() + fileName.substring(1);
            String fullClassName = packagePath + "." + fileName;
            try {
                Class<?> aClass = Class.forName(fullClassName);
                if (aClass.isAnnotationPresent(MyComponent.class)) {
                    Object o = aClass.newInstance();
                    ioc.put(beanName, o);
                }
            } catch (Exception e) {
                logger.warning("實例化異常");
            }
        }
    }
    private File[] getAllFile(String packagePath, File file) {
        File[] files = file.listFiles(file1 -> {
            if (file1.isDirectory()) {
                scanPackagePath(packagePath + "." + file1.getName());
            } else {
                String name = file1.getName();
                if (name.endsWith(".class")) return true;
                else return false;
            }
            return false;
        });
        return files;
    }
    private File getFile(String packagePath) {
        String dirPath = packagePath.replaceAll("\\.", "/");
        URL url = this.getClass().getClassLoader().getResource(dirPath);
        String fileName = url.getFile();
        return new File(fileName);
    }
}

通過上面的代碼,我們會發(fā)現(xiàn),我們傳入的basePackage也就是包名,是以點號分割的,而我們磁盤上的文件是以斜線分割的,所以我們要將點號替換成斜線

再接著我們獲取packagePath對應(yīng)的url資源,通過它獲取文件的全路徑名,有些小伙伴可能會說,哪里用這么麻煩,我直接用packagePath new File(String path)的形式創(chuàng)建文件不行么,干嘛用url啊,這是因為你傳入的類似com.nightcat這種包名,你去直接newFile ,是不是發(fā)現(xiàn)少了一些東西?沒錯,就是你的磁盤路徑,發(fā)現(xiàn)了么,而Java提供的Url類可以解決這個問題。

剩下的工作就簡單了,我們?nèi)呙鑲魅氲陌窂胶妥月窂剑⑶覚z測類上是否有我們自定義的注解,如果有,我們就用創(chuàng)建對象,同時將類名首字母小寫,作為ConcurrentHashMap的key,創(chuàng)建的對象作為Value就可以了。

有些小伙伴可能會問,你這個Bean無法保證單例呀,也很簡單,你寫代碼的時候判斷一下容器里有沒有,有了就不添加了。至于其他的知識,不在這篇文章的范疇,可以自行研究。

測試自定義注解和容器

package com.nightcat.test;
import com.nightcat.ApplicationContext;
import com.nightcat.bean.Student;
public class CodeTest {
    public static void main(String[] args) {
        ApplicationContext context = new ApplicationContext("com.nightcat");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
    }
}

通過測試,發(fā)現(xiàn)我們自定義的@MyComponet注解生效了。

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

相關(guān)文章

  • Java實現(xiàn)FTP上傳到服務(wù)器

    Java實現(xiàn)FTP上傳到服務(wù)器

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)FTP上傳到服務(wù)器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Java異步調(diào)用轉(zhuǎn)同步的方法

    Java異步調(diào)用轉(zhuǎn)同步的方法

    這篇文章主要介紹了Java異步調(diào)用轉(zhuǎn)同步的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • MyBatis-Plus 通用IService使用詳解

    MyBatis-Plus 通用IService使用詳解

    這篇文章主要介紹了MyBatis-Plus 通用IService使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java開發(fā)ShardingSphere的路由引擎類型示例詳解

    java開發(fā)ShardingSphere的路由引擎類型示例詳解

    這篇文章主要為大家介紹了java開發(fā)ShardingSphere的路由引擎類型示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Java實現(xiàn)將Markdown轉(zhuǎn)換為純文本

    Java實現(xiàn)將Markdown轉(zhuǎn)換為純文本

    這篇文章主要為大家詳細(xì)介紹了兩種在 Java 中實現(xiàn) Markdown 轉(zhuǎn)純文本的主流方法,文中的示例代碼講解詳細(xì),大家可以根據(jù)需求選擇適合的方案
    2025-03-03
  • 深入剖析ArrayList的remove方法

    深入剖析ArrayList的remove方法

    這篇文章主要介紹了ArrayList的remove方法使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教。</p>
    2021-09-09
  • Kotlin this詳解及實例

    Kotlin this詳解及實例

    這篇文章主要介紹了Kotlin this詳解及實例的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Java接口返回省市區(qū)樹形結(jié)構(gòu)的實現(xiàn)

    Java接口返回省市區(qū)樹形結(jié)構(gòu)的實現(xiàn)

    本文主要介紹了Java接口返回省市區(qū)樹形結(jié)構(gòu)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • spring boot+spring cache實現(xiàn)兩級緩存(redis+caffeine)

    spring boot+spring cache實現(xiàn)兩級緩存(redis+caffeine)

    這篇文章主要介紹了spring boot+spring cache實現(xiàn)兩級緩存(redis+caffeine),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • SpringMVC中使用@PathVariable綁定路由中的數(shù)組的方法

    SpringMVC中使用@PathVariable綁定路由中的數(shù)組的方法

    這篇文章主要介紹了SpringMVC中使用@PathVariable綁定路由中的數(shù)組的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07

最新評論

青河县| 凤城市| 元氏县| 迭部县| 三江| 霍林郭勒市| 雷波县| 化德县| 革吉县| 田东县| 稻城县| 眉山市| 邳州市| 库车县| 苍梧县| 绵竹市| 泰宁县| 子洲县| 大丰市| 东山县| 吉木乃县| 镇原县| 寻乌县| 贺州市| 泸定县| 二手房| 镇安县| 铁岭县| 金溪县| 澳门| 凌海市| 陇西县| 凌源市| 同心县| 江永县| 吴江市| 前郭尔| 胶州市| 酒泉市| 郓城县| 平罗县|