欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Springboot项目实现将类从@ComponentScan中排除

程序员文章站 2022-07-02 23:12:58
目录将类从@componentscan中排除问题描述方案一方案二方案三方案四@componentscan 详解将类从@componentscan中排除问题描述最近在学习springcloud的ribb...

将类从@componentscan中排除

问题描述

最近在学习springcloud的ribbon,在使用

@ribbonclient(name = "springcloud-p-dept", 
   configuration = ribbonconfig.class)

为服务指定负载均衡策略的时候,根据ribbon官方文档介绍,自定义的ribbon配置类不允许被springboot的**@componentscan**注解扫描到,所以需要将自定义的配置类ribbonconfig从在springboot自动注入的范围内排除

方案一

我们都知道,springboot的**@springbootapplication**会自动扫描本类所在包下的所有类和子类,所以只需要将ribbonconfig定义在springboot启动类所在包外面即可

方案二

通过在启动类中添加

@componentscan(excludefilters = @componentscan.filter(
  type = filtertype.assignable_type, 
  classes = ribbonconfig.class))

通过filtertype.assignable_type来指定要排除的类

如果需要排除的类太多了这个就很麻烦

方案三

通过自定义注解实现

@componentscan(excludefilters = @componentscan.filter(
  type = filtertype.annotation, 
  classes = scanignore.class))

与方案二不同的是,这里用的是filtertype.annotation

方案四

通过实现typefilter类来自定义过滤器

@componentscan(excludefilters = { 
 @filter(
  type = filtertype.custom, 
  classes = typeexcludefilter.class),
 @filter(
  type = filtertype.custom, 
  classes = autoconfigurationexcludefilter.class) })

此处给出的就是**@springbootapplication中的实现方式,通过filtertype.custom**来根据自动一过滤器来排除bean

最后贴出枚举类filtertype:

/*
 * copyright 2002-2013 the original author or authors.
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *      https://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.springframework.context.annotation;
/**
 * enumeration of the type filters that may be used in conjunction with
 * {@link componentscan @componentscan}.
 *
 * @author mark fisher
 * @author juergen hoeller
 * @author chris beams
 * @since 2.5
 * @see componentscan
 * @see componentscan#includefilters()
 * @see componentscan#excludefilters()
 * @see org.springframework.core.type.filter.typefilter
 */
public enum filtertype {
 /**
  * filter candidates marked with a given annotation.
  * @see org.springframework.core.type.filter.annotationtypefilter
  */
 annotation,
 /**
  * filter candidates assignable to a given type.
  * @see org.springframework.core.type.filter.assignabletypefilter
  */
 assignable_type,
 /**
  * filter candidates matching a given aspectj type pattern expression.
  * @see org.springframework.core.type.filter.aspectjtypefilter
  */
 aspectj,
 /**
  * filter candidates matching a given regex pattern.
  * @see org.springframework.core.type.filter.regexpatterntypefilter
  */
 regex,
 /** filter candidates using a given custom
  * {@link org.springframework.core.type.filter.typefilter} implementation.
  */
 custom
}

@componentscan 详解

@componentscan 的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中,注解定义如下。

 
@retention(retentionpolicy.runtime)
@target({elementtype.type})
@documented
@repeatable(componentscans.class)
public @interface componentscan {
    @aliasfor("basepackages")
    string[] value() default {};
    @aliasfor("value")
    string[] basepackages() default {};
    class<?>[] basepackageclasses() default {};
    class<? extends beannamegenerator> namegenerator() default beannamegenerator.class;
    class<? extends scopemetadataresolver> scoperesolver() default annotationscopemetadataresolver.class;
    scopedproxymode scopedproxy() default scopedproxymode.default;
    string resourcepattern() default "**/*.class";
    boolean usedefaultfilters() default true;
    componentscan.filter[] includefilters() default {};
    componentscan.filter[] excludefilters() default {};
    boolean lazyinit() default false;
    @retention(retentionpolicy.runtime)
    @target({})
    public @interface filter {
        filtertype type() default filtertype.annotation;
        @aliasfor("classes")
        class<?>[] value() default {};
        @aliasfor("value")
        class<?>[] classes() default {};
        string[] pattern() default {};
    }
}
  • basepackagesvalue: 用于指定包的路径,进行扫描
  • basepackageclasses: 用于指定某个类的包的路径进行扫描
  • namegenerator: bean的名称的生成器
  • usedefaultfilters: 是否开启对@component,@repository,@service,@controller的类进行检测
  • includefilters: 包含的过滤条件

filtertype.annotation:按照注解过滤

filtertype.assignable_type:按照给定的类型

filtertype.aspectj:使用aspectj表达式

filtertype.regex:正则

filtertype.custom:自定义规则

  • excludefilters: 排除的过滤条件,用法和includefilters一样

我的工程结构如下,测试对controller和service的扫描,其中hellocontroller没有加@controller等任何注解,就是一个普通类。

Springboot项目实现将类从@ComponentScan中排除

修改配置类如下:应用默认的过滤器,扫描service包:

@configuration
@componentscan(value = "com.xhx.spring.service",
        usedefaultfilters = true
)
public class myconfig {
}

系统注入了两个service进去

Springboot项目实现将类从@ComponentScan中排除

改成如下所示:hellocontroller所在的包的类也被扫描了进去

@configuration
@componentscan(value = "com.xhx.spring.service",
        usedefaultfilters = true,
        basepackageclasses = hellocontroller.class
)
public class myconfig {
}

系统中会注入下面就给类

Springboot项目实现将类从@ComponentScan中排除

把默认的过滤器关掉,扫描带controller注解的。

@configuration
@componentscan(value = "com.xhx.spring",
        usedefaultfilters = false,
        includefilters = {
            @componentscan.filter(type = filtertype.annotation,classes = {controller.class})
        }
)
public class myconfig {
}

Springboot项目实现将类从@ComponentScan中排除

按照类的类型扫描,虽然hellocontroller没有加注解,但是被注入到了spring容器中

@configuration
@componentscan(value = "com.xhx.spring",
        usedefaultfilters = false,
        includefilters = {
            @componentscan.filter(type = filtertype.assignable_type,classes = {hellocontroller.class})
        }
)
public class myconfig {
}

Springboot项目实现将类从@ComponentScan中排除

自定义扫描过滤器

package com.xhx.spring.componentscan.config; 
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;
 
/**
 * xuhaixing
 * 2018/9/18 23:07
 **/
public class mytypefilter implements typefilter {
    @override
    public boolean match(metadatareader metadatareader, metadatareaderfactory metadatareaderfactory) throws ioexception {
        string classname = metadatareader.getclassmetadata().getclassname();
        if(classname.contains("controller")){
            return true;
        }
        return false;
    }
}

修改配置类

@configuration
@componentscan(value = "com.xhx.spring",
        usedefaultfilters = false,
        includefilters = {
            @componentscan.filter(type = filtertype.custom,classes = {mytypefilter.class})
        }
)
public class myconfig {
}

输出结果:

Springboot项目实现将类从@ComponentScan中排除

输出spring容器中的bean的测试类:只过滤输出了名字中含有hello的类。

package com.xhx.spring.componentscan; 
import com.xhx.spring.componentscan.config.myconfig;
import org.junit.test;
import org.junit.runner.runwith;
import org.springframework.boot.test.context.springboottest;
import org.springframework.context.annotation.annotationconfigapplicationcontext;
import org.springframework.test.context.junit4.springrunner; 
import java.util.arrays;
import java.util.list;
import java.util.stream.collectors;
 
@runwith(springrunner.class)
@springboottest
public class componentscanapplicationtests { 
    @test
    public void testloads() {
        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(myconfig.class);
        list<string> hello = arrays.stream(context.getbeandefinitionnames()).collect(collectors.tolist());
        hello.stream().filter(name->name.contains("hello")).peek(system.out::println).count();
    } 
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。