Spring注解驱动开发(二)之@ComponentScans、@ComponentScan和@Filter注解

除了可以通过@Bean注解的方式为IoC容器注册组件,也可以通过包扫描(@ComponentScan注解)的方式为IoC容器注册组件


项目目录结构

在这里插入图片描述


项目环境搭建

  • 创建Maven子工程demo-two,并在子工程的pom.xml文件中导入spring-context等依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-annotation</artifactId>
        <groupId>com.codermyu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>demo-two</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
    </dependencies>


</project>
  • 在controller包下创建UserController类
package com.codermyu.controller;

import org.springframework.stereotype.Controller;

/** @author Mingyu */
@Controller
public class UserController {}
  • 在service包下创建UserService类
package com.codermyu.service;

import org.springframework.stereotype.Service;

/** @author Mingyu */
@Service
public class UserService {}
  • 在dao包下创建UserDao类
package com.codermyu.dao;

import org.springframework.stereotype.Repository;

/** @author Mingyu */
@Repository
public class UserDao {}
  • 在config包下创建配置类MySpringConfig
package com.codermyu.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/** @author Mingyu */
@ComponentScan(basePackages = {"com.codermyu"})
@Configuration
public class MySpringConfig {}
  • 创建启动类App
package com.codermyu;

import com.codermyu.config.MySpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/** @author Mingyu */
public class App {
  public static void main(String[] args) {
    ApplicationContext applicationContext =
        new AnnotationConfigApplicationContext(MySpringConfig.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
      System.out.println(beanDefinitionName);
    }
  }
}

细节

  • @ComponentScan注解
    • @ComponentScan注解需要标注在配置类上

    • 为@ComponentScan注解添加basePackages属性,可以指定包扫描时的根路径,IoC容器在启动时会扫描该路径下所有标注了@Configuration、@Component、@Controller、@Service、@Repository注解的类并创建组件,然后将创建的组件注册到IoC容器中

    • 创建的组件的id为类名的首字母小写(默认),为@Configuration、@Component、@Controller、@Service、@Repository注解添加value属性,可以重新指定组件的id,而不使用默认的首字母小写作为组件的id

      • @Controller(value = "userControllerSpecified")
    • 为@ComponentScan注解添加excludeFilters属性,可以指定包扫描时的排除规则(不扫描哪些组件)

      package com.codermyu.config;
      
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.FilterType;
      import org.springframework.stereotype.Controller;
      import org.springframework.stereotype.Service;
      
      /** @author Mingyu */
      @ComponentScan(
          basePackages = {"com.codermyu"},
          excludeFilters = {
            @ComponentScan.Filter(
                /** 包扫描时不扫描标注了@Controller注解和@Service注解的类 */
                type = FilterType.ANNOTATION,
                classes = {Controller.class, Service.class})
          })
      @Configuration
      public class MySpringConfig {}
      
    • 为@ComponentScan注解添加includeFilters属性,可以指定包扫描时的包含规则(额外扫描哪些组件)

      • 包扫描时如果希望只扫描某些特定组件,需要禁用默认的过滤器
      package com.codermyu.config;
      
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.FilterType;
      import org.springframework.stereotype.Controller;
      import org.springframework.stereotype.Repository;
      
      /** @author Mingyu */
      @ComponentScan(
          basePackages = {"com.codermyu"},
          /** 禁用默认的过滤器 */
          useDefaultFilters = false,
          includeFilters = {
            @ComponentScan.Filter(
                /** 包扫描时只扫描标注了@Controller注解和@Repository注解的类 */
                type = FilterType.ANNOTATION,
                classes = {Controller.class, Repository.class})
          })
      @Configuration
      public class MySpringConfig {}
      
  • @ComponentScans注解
    • Java8以及Java8以后的Java版本支持@Repeatable注解,@ComponentScan注解可以在配置类上标注多次,用于指定不同的包扫描策略
    • Java8以前的Java版本,可以采用@ComponentScans注解包含多个@ComponentScan注解的方式,指定不同的包扫描策略
  • @Filter注解
    • @Filter注解的type属性可以指定5种类型

      • FilterType.ANNOTATION:按照指定的注解规则过滤组件
      • FilterType.ASSIGNABLE_TYPE:按照指定的类型规则过滤组件
      • FilterType.ASPECTJ:按照指定的AspectJ表达式规则过滤组件
      • FilterType.REGEX:按照指定的正则表达式规则过滤组件
      • FilterType.CUSTOM:按照自定义规则过滤组件
      package com.codermyu.config;
      
      import com.codermyu.service.UserService;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.FilterType;
      import org.springframework.stereotype.Controller;
      
      /** @author Mingyu */
      @ComponentScan(
          basePackages = {"com.codermyu"},
          useDefaultFilters = false,
          includeFilters = {
            @ComponentScan.Filter(
                type = FilterType.ANNOTATION,
                classes = {Controller.class}),
            @ComponentScan.Filter(
                type = FilterType.ASSIGNABLE_TYPE,
                classes = {UserService.class})
          })
      @Configuration
      public class MySpringConfig {}
      
    • 自定义Filter

      • 在filter包下创建MyFilter类,该类需要实现TypeFilter接口,并重写match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)方法
      • metadataReader
        • 用来获取当前正在扫描的类的信息
        • metadataReader.getResource():获取类的资源信息
        • metadataReader.getClassMetadata():获取类的类信息
        • metadataReader.getAnnotationMetadata():获取类的注解信息
      • metadataReaderFactory
        • 用来获取其他类的metadataReader,然后通过metadataReader获取类的信息
        • metadataReaderFactory.getMetadataReader(String className):通过类名获取metadataReader
      package com.codermyu.filter;
      
      import org.springframework.core.type.AnnotationMetadata;
      import org.springframework.core.type.classreading.MetadataReader;
      import org.springframework.core.type.classreading.MetadataReaderFactory;
      import org.springframework.core.type.filter.TypeFilter;
      import org.springframework.stereotype.Controller;
      
      import java.io.IOException;
      
      /** @author Mingyu */
      public class MyFilter implements TypeFilter {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
          /** 通过metadataReader获取当前正在扫描的类的注解信息 */
          AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
          /** 如果当前正在扫描的类标注了@Controller注解,match方法就返回true,否则返回false */
          return annotationMetadata.hasAnnotation(Controller.class.getName());
        }
      }
      
      • 将自定义Filter添加到Filter组中
        • basePackages指定的路径下的每个类都会通过MyFilter类中的match()方法进行过滤,如果匹配成功,就将当前正在扫描的类注册到IoC容器中
      package com.codermyu.config;
      
      import com.codermyu.filter.MyFilter;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.FilterType;
      
      /** @author Mingyu */
      @ComponentScan(
          basePackages = {"com.codermyu"},
          useDefaultFilters = false,
          includeFilters = {
            @ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {MyFilter.class})
          })
      @Configuration
      public class MySpringConfig {}
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值