本教程先通过xml配置方式,引渡到使用注解的方式,重点讲注解的使用
通过xml配置文件配置包扫描的方式
创建xml配置文件
创建文件:src/main/resources/scan.xml
代码如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.tonyliu"/></beans>
节点<context:component-scan>指定包扫描,base-package指定要扫描的包范围
在base-package指定的包内的类,只要但不限于使用以下下注解,都会被扫描到并注册到IOC容器中
@Controller、@Component、@Service、@Repository
创建需要扫描的类文件
将使用@Controller、@Service、@Repository这三个注解,创建3个类,用于测试扫描包的配置
创建文件:src/main/java/com/tonyliu/controller/BookController.java,代码如下
package com.tonyliu.controller;import org.springframework.stereotype.Controller;@Controllerpublic class BookController {}
创建文件:src/main/java/com/tonyliu/service/BookService.java,代码如下
package com.tonyliu.service;import org.springframework.stereotype.Service;@Servicepublic class BookService {}
创建文件:src/main/java/com/tonyliu/dao/BookDao.java,代码如下
package com.tonyliu.dao;import org.springframework.stereotype.Repository;@Repositorypublic class BookDao {}
引入单元测试依赖
接下来我们来做个测试,将使用单元测试的方式运行程序进行测试
引入单元测试依赖
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.6.2</version><scope>test</scope></dependency>
书写单元测试方法
书写单元测试方法,打印IOC容器中注册组件的名字,代码如下
@Testpublic void test01() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scan.xml");String[] names = applicationContext.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}}
结果如下,会打印出扫描到的组件名字,忽略其他打印的内容
变更测试,将xml配置文件中的base-package的值变更为base-package=”com.tonyliu.controller”之后,在运行一下测试程序,结果如下
通过注解配置包扫描方式
涉及注解 @Configuration @ComponentScan @ComponentScans
主要讲解@ComponentScan注解
@ComponentScan注解说明
- 将添加特定注解的类注册到IOC容器中
@ComponentScan注解和@Configuration注解一同使用- 和xml配置文件中的
<context:component-scan>元素功能相同 - 该注解可以设置需要扫描的类、包等和需要排除扫描的类、包等
@ComponentScan注解的定义
package org.springframework.context.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Repeatable;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.beans.factory.support.BeanNameGenerator;import org.springframework.core.annotation.AliasFor;import org.springframework.core.type.filter.TypeFilter;@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 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;boolean useDefaultFilters() default true;Filter[] includeFilters() default {};Filter[] excludeFilters() default {};boolean lazyInit() default false;@Retention(RetentionPolicy.RUNTIME)@Target({})@interface Filter {FilterType type() default FilterType.ANNOTATION;@AliasFor("classes")Class<?>[] value() default {};@AliasFor("value")Class<?>[] classes() default {};String[] pattern() default {};}}
@ComponentScan注解的使用
创建一个配置类文件
创建文件:src\main\java\com\tonyliu\config\ScanConfig.java
package com.tonyliu.config;import org.springframework.context.annotation.Configuration;@Configurationpublic class ScanConfig {}
测试代码
@Testpublic void test02(){ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);String[] names = applicationContext.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}}
扫描指定包下可以注册的组件
package com.tonyliu.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan(value = "com.tonyliu")public class ScanConfig {}
value:指定要扫描的包范围com.tonyliu
排除需要扫描注册的组件
package com.tonyliu.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;@Configuration@ComponentScan(value = "com.tonyliu",excludeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,value = {Controller.class, Repository.class})})public class ScanConfig {}
使用属性excludeFilters排除需要扫描注册的类型:Filter[] excludeFilters() default {};
该属性是一个Filter类型的数组形式,Filter类型在@ComponentScan注解中被定义Filter中的属性type指定需要按照哪种规则进行筛选
上面是我们示例要排除添加了@Controller、@Repository这两个注解的类
tip:直接中的数组类型使用{}包裹,中间用逗号分隔
只包含需要注册的组件
package com.tonyliu.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;@Configuration@ComponentScan(value = "com.tonyliu", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Repository.class})}, useDefaultFilters = false)public class ScanConfig {}
includeFilters:只包含需要注册的组件规则
注:使用该规则需要将useDefaultFilters设置为false,筛选规则时才是只包含的关系,对应xml配置文件的use-default-filters熟悉,示例
<context:component-scan base-package="com.tonyliu.controller" use-default-filters="false"/>
组合注解的使用
可以设置@ComponentScan的组合模式,详见代码
package com.tonyliu.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.ComponentScans;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Controller;import org.springframework.stereotype.Repository;@Configuration@ComponentScans(value = {@ComponentScan(value = "com.tonyliu", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})}, useDefaultFilters = false),@ComponentScan(value = "com.tonyliu", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Repository.class})}, useDefaultFilters = false)})public class ScanConfig {}
FilterType讲解
public enum FilterType {/*** 按照注解规则筛选*/ANNOTATION,/*** 按照类型筛选*/ASSIGNABLE_TYPE,/*** 按照ASPECTJ表达式筛选*/ASPECTJ,/*** 按照正则表达式筛选*/REGEX,/** Filter candidates using a given custom* {@link org.springframework.core.type.filter.TypeFilter} implementation.* 自定义筛选规则*/CUSTOM}
CUSTOM自定义筛选规则的使用
根据文档可知,使用该方式需要实现org.springframework.core.type.filter.TypeFilter这个接口org.springframework.core.type.filter.TypeFilter接口的定义如下
package org.springframework.core.type.filter;import java.io.IOException;import org.springframework.core.type.classreading.MetadataReader;import org.springframework.core.type.classreading.MetadataReaderFactory;@FunctionalInterfacepublic interface TypeFilter {/*** Determine whether this filter matches for the class described by* the given metadata.* @param metadataReader 读取到的当前的正在扫描的类的信息* @param metadataReaderFactory 可以获取到其他任何类的信息* @return whether this filter matches* @throws IOException in case of I/O failure when reading metadata*/boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException;}
创建文件实现该接口:
package com.tonyliu.config;import org.springframework.core.io.Resource;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.ClassMetadata;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;public class MyTypeFilter implements TypeFilter {public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {// 获取当前类注解的信息AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();// 获取当前正在扫描类的类的信息ClassMetadata classMetadata = metadataReader.getClassMetadata();// 获取当前类的资源信息(类的路径等)Resource resource = metadataReader.getResource();String className = classMetadata.getClassName();if (className.equals("com.tonyliu.controller.BookController")) {return true;}return false;}}
测试代码运行结果
