• 二、Spring中的@ComponentScan自动扫描组件


    在以往采用xml配置的方式中,我们通常需要配置<context:component-scan>标签

    比如这样:

    <!-- 包扫描、只要标注了@Controller、@Service、@Repository,@Component -->
    <context:component-scan base-package="com.atguigu"></context:component-scan>
    

    那在javaConfig的配置方式中,对应于@ComponentScan注解

    我们现在就建一个例子 ,来具体演示一下。

    我们web工程中,新建一个BookController类,具体如下:

    package com.atguigu.controller;
    // 省略了包的导入
    @Controller
    public class BookController {
    	@Autowired
    	private BookService bookService;
    }
    

    BookService

    package com.atguigu.service;
    @Service
    public class BookService {
    	@Autowired
    	private BookDao bookDao;
    	public void print(){
    		System.out.println(bookDao);
    	}
    	@Override
    	public String toString() {
    		return "BookService [bookDao=" + bookDao + "]";
    	}
    
    }
    

    最后是BookDao

    package com.atguigu.dao;
    //名字默认是类名首字母小写
    @Repository
    public class BookDao {
    	private String lable = "1";
    	public String getLable() {
    		return lable;
    	}
    	public void setLable(String lable) {
    		this.lable = lable;
    	}
    	@Override
    	public String toString() {
    		return "BookDao [lable=" + lable + "]";
    	}
    }
    

    以上三个类没什么需要说的,无须关注类的具体内容,只须关注类上的注解,和类所处的包即可。

    与上节同样的,我们采用Javaconfig类的方式,还是需要一个配置类。

    所以我们新建:MainConfig类,作为我们的配置类

    package com.atguigu.config;
    //配置类==配置文件
    @Configuration  //告诉Spring这是一个配置类
    @ComponentScan(value="com.atguigu") // 注意这一行。
    public class MainConfig {
    	//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    	@Bean(name = "person")
    	public Person person01(){
    		return new Person("lisi", 20);
    	}
    }
    

    在配置类上加了@ComponentScan注解,这里value="com.atguigu"写成basePackages="com.atguigu"都是指明我们需要扫描的包

    我们写个测试方法:

    public class IOCTest {
    
    	@SuppressWarnings("resource")
    	@Test
    	public void test01(){
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    		String[] definitionNames = applicationContext.getBeanDefinitionNames();
    		for (String name : definitionNames) {
    			System.out.println(name);
    		}
    	}
    }
    

    加载配置类,并获取到容器中所有的Bean的名字,然后遍历进行打印

    我们看下打印结果

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory // 这几个都是spring容器本身的
    mainConfig
    bookController
    bookDao
    bookService
    person
    

    我们可以看到将com.atguigu下所有的bean都已经扫描了进来,这里说一下MainConfig这个配置类,也在扫描的包下。它的类上有个注解@Configuration,表示配置类也是一个bean。还有个person是我们在@Bean注解定义的Bean.


    接下来我们要对@ComponentScan注解进行详细的讲解。

    1.我们有这样一样一个需求:不扫描@Controller注解。

    该怎么写呢

    @ComponentScan(basePackages="com.atguigu",excludeFilters= {@Filter(type=FilterType.ANNOTATION,classes={Controller.class})})
    

    这就表示排除掉了@Controller注解,

    看看打印结果:

    // 可以看到确实没有bookController类了,它被@Controller注解所修饰,所以被排除在了扫描之外,自然也不没有纳入容器之中
    mainConfig
    bookDao
    bookService
    person
    
    2.只扫描@Repository标注的注解

    该如何写呢??

    @ComponentScan(basePackages="com.atguigu",includeFilters= {@Filter(type=FilterType.ANNOTATION,
    classes={Repository.class})},useDefaultFilters=false)
    

    测试方法不变,打印结果:

    mainConfig
    bookDao
    person
    

    可以看到 容器中只有bookDao这一个bean了。

    注意:useDefaultFilters=false是禁用掉默认的扫描规则,默认当然是扫描包下的@Controller、@Service、@Repository,@Component这四大金刚咯(其实还包括@Configuration注解),所以禁用掉,就不再扫描了,那么只扫描我们定义的。这点与上面的排除规则不同,注意理解,毕竟排除是从所有扫描中再排除。

    让我们再深入一点,嘿嘿

    我们来看看 @ComponentScan注解里面能写哪些东西

    常用的几个注解我们已经讲解过了,

    我们主要来看下excludeFilters和includeFilters的写法

    在@ComponentScan中,我们拿到这个类的源码看看呗。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
        
    	boolean useDefaultFilters() default true;
        
    	Filter[] includeFilters() default {};
        
        
    	Filter[] excludeFilters() default {};
        
        
    	@Retention(RetentionPolicy.RUNTIME)
    	@Target({})
    	@interface Filter {
    		FilterType type() default FilterType.ANNOTATION; // 过滤的类型,默认是通过注解的类型,
            
    		@AliasFor("classes")
    		Class<?>[] value() default {}; 
            
    		@AliasFor("value")
    		Class<?>[] classes() default {};
            
    		String[] pattern() default {};
    
    	}
    
    }
    
    

    我们发现它们是Filter数组类型,从我们刚刚的写法中也能略窥一二。这个Filter也是一个注解,刚好定义在内部,

    也就是说

    excludeFilters= {@Filter(type=FilterType.ANNOTATION,classes={Controller.class})}

    的意思是说排除掉Controller类型的注解。

    那除了通过注解的类型进行排除,还有其他的方式么?

    那我们就要去上面这个FilterType.ANNOTATION中的FilterType中一探究竟了。

    发现它有五个类型,那我们再举个通过ASSIGNABLE_TYPE的例子

    比如这样@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}

    这就是说通过指定类型,排除掉BookService.class类型,即不扫描,这其实包括了它的子类,父类等。

    其他还有一种CUSTOM

    也就是自定义过滤规则。不再讲解。


    再回来,我们看看@ComponentScan的源码,发现它被@Repeatable(ComponentScans.class)注解所修饰,这个注解代表中,我们可以在类上重复加这个注解,定义多个不同的扫描策略。


    该睡觉了,又是一个深夜了,晚安!

    你所看得到的天才不过是在你看不到的时候还在努力罢了!
  • 相关阅读:
    SP3267 DQUERY
    P3808 【模板】AC自动机(简单版)
    What Are You Talking About HDU
    【博客同步】【微信首发】GC-server的安装与使用
    博客园界面背景及一些特效设置
    C# 常用特性(Attribute)
    CRC常用参数模型及C#代码实现
    简单实用算法——字节位序反转
    C#串口开发之SerialPort类封装
    C#泛型的类型参数约束
  • 原文地址:https://www.cnblogs.com/heliusKing/p/11349299.html
Copyright © 2020-2023  润新知