1. 程式人生 > >Springboot原始碼分析之TypeFilter魔力

Springboot原始碼分析之TypeFilter魔力

摘要:

在平常的開發中,不知道大家有沒有想過這樣一個問題,為什麼我們自定義註解的時候要使用spring的原生註解(這裡指的是類似@Component@Service........),要麼就是 隨便弄個註解,搭配自己的切面程式設計來實現某些業務邏輯。這篇文章主要給大家分享一下,如何脫離Spring原生註解自定義註解注入IOC

SpringBootApplication註解分析


從原始碼很容易看出來,它的作用就是自動裝配和掃描我們的包,並將符合的類進行註冊到容器。自動裝配非常簡單,這裡不做過多分析,接下來分析一下什麼叫做符合規則的類。在@ComponentScan註解上面的過濾器型別的定義

public enum FilterType {
    ANNOTATION, //註解型別
    ASSIGNABLE_TYPE, //指定的型別
    ASPECTJ, //按照Aspectj的表示式,基本上不會用到
    REGEX, //按照正則表示式
    CUSTOM; //自定義

    private FilterType() {
    }
}

excludeFilters排除過濾器

這個是給我們排除符合的類,不讓他註冊到IOC的時候使用的, Springboot預設使用兩個排除過濾器,很簡單的,網上隨便搜都可以找到相關說明,在這兒我舉個特舒列子就行了.

package com.github.dqqzj.springboot.filter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author qinzhongjian
 * @date created in 2019-07-30 19:14
 * @description: TODO
 * @since JDK 1.8.0_212-b10
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dqqzj {
    String value();
}
package com.github.dqqzj.springboot.filter;

import org.springframework.stereotype.Component;

/**
 * @author qinzhongjian
 * @date created in 2019-07-29 22:30
 * @description: TODO
 * @since JDK 1.8.0_212-b10
 */
@Dqqzj(value = "dqqzj")
@Component
public class Tt {
}
package com.github.dqqzj.springboot.filter;

import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

/**
 * @author qinzhongjian
 * @date created in 2019-07-30 19:13
 * @description: TODO
 * @since JDK 1.8.0_212-b10
 */
public class MyTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        if (metadataReader.getAnnotationMetadata().isAnnotated(Dqqzj.class.getName())) {
            return true;
        }
        return false;
    }
}


以上程式碼是正常邏輯,反過來這樣想,如果將Tt類的@Component註解去掉是不是也行的,所以這種排除註解一般都用在正常可以注入到容器的時候進行新增的,那麼我們上面說過,脫離Spring也可以注入到容器,該怎麼實現呢?

includeFilters包含過濾器

脫離Spring原生註解,將將Tt類的@Component註解去掉

package com.github.dqqzj.springboot.filter;

import org.springframework.stereotype.Component;

/**
 * @author qinzhongjian
 * @date created in 2019-07-29 22:30
 * @description: TODO
 * @since JDK 1.8.0_212-b10
 */
@Dqqzj(value = "dqqzj")
//@Component
public class Tt {
}

透過現象看本質

流程進行梳理一下,註解驅動在注入容器的關鍵掃描類(注意這裡是指的掃描,而不是什麼@Bean,@Import等其餘註解都是建立在這個基礎之上的)

  • ComponentScanAnnotationParser
  • ClassPathBeanDefinitionScanner
  • ClassPathScanningCandidateComponentProvider
ClassPathScanningCandidateComponentProvider#registerDefaultFilters
protected void registerDefaultFilters() {
        this.includeFilters.add(new AnnotationTypeFilter(Component.class));
        ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();

        try {
            this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false));
            this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
        } catch (ClassNotFoundException var4) {
        }

        try {
            this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false));
            this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
        } catch (ClassNotFoundException var3) {
        }

    }

此處會將@Component,JSR-250 'javax.annotation.ManagedBean',JSR-330 'javax.inject.Named'的註解進行註冊,所以難怪我們的自定義註解必須要有這些派生註解,換一個角度來思考,它們這個地方進行類AnnotationTypeFilter的新增,我們也可以自定義AnnotationTypeFilter來將自己的定義規則的註解進行注入容器