本教程先通过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;
@Controller
public class BookController {
}
创建文件:src/main/java/com/tonyliu/service/BookService.java
,代码如下
package com.tonyliu.service;
import org.springframework.stereotype.Service;
@Service
public class BookService {
}
创建文件:src/main/java/com/tonyliu/dao/BookDao.java
,代码如下
package com.tonyliu.dao;
import org.springframework.stereotype.Repository;
@Repository
public 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容器中注册组件的名字,代码如下
@Test
public 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;
@Configuration
public class ScanConfig {
}
测试代码
@Test
public 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;
@FunctionalInterface
public 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;
}
}
测试代码运行结果