首先idea创建项目, 使用maven创建工程,工程目录如下:
config包下面是配置类:
package config;
import importselector.CustomSelector;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("guangming")
@Import(CustomSelector.class)
public class SpringConfiguration {
}
service 和 util包下面都是用来待会测试的类
package guangming.service.impl;
import guangming.service.inter.AccountService;
public class AccountServiceImpl implements AccountService {
public void save() {
System.out.println("添加账户!");
}
}
--------------------------------------------------------------------
package guangming.util;
public class LogUtil {
public void record(){
System.out.println("这是一个日志记录!");
}
}
importselector包下面是自定义扫描类
package importselector;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* 自定义导入器
*/
public class CustomSelector implements ImportSelector {
private String expression;
private String expression2;
/**
* 默认构造函数
* 用于读取配置文件,给表达式赋值
*/
public CustomSelector(){
try {
// 1. 获取Properties 对象
Properties properties = PropertiesLoaderUtils.loadAllProperties("costumer.properties");
// 2. 给Properties赋值
expression = properties.getProperty("custome.selector.expression");
expression2 = properties.getProperty("custome.selector.expression2");
}catch (Exception e){
System.out.println(e.getMessage());
}
}
/**
* 实现获取要导入类的字节码
* 需求:
* 导入的过滤规则TypeFilter为aspectj表达式
* @param importingClassMetadata
* @return
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 1. 定义扫描包的名称
String[] basePackages = null;
// 2. 判读 @Import注解的类上有没有@ComponentScan注解
if(importingClassMetadata.hasAnnotation(ComponentScan.class.getName())){
// 3. 取出ComponentScan注解的属性(basePackage/value)
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
// 4. 取出basePackages属性的值
assert attributes != null;
basePackages = (String[])attributes.get("basePackages");
}
// 5. 判读是否有此注解,是否指定了包扫描的信息, 如果没有此注解,则扫描
// @Import 类所在包及其子包
if(basePackages == null || basePackages.length == 0){
String basePackage = null;
try {
// 6. 取出 @Import 所在包的包名
basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 7. 把包名填充到basePackages中
basePackages = new String[]{basePackage};
}
// 8.创建类路径扫描器 false 表示 不使用默认扫描规则
ClassPathScanningCandidateComponentProvider scan = new ClassPathScanningCandidateComponentProvider(false);
// 9. 创建类型过滤器
TypeFilter typeFilter = new AspectJTypeFilter(expression, CustomSelector.class.getClassLoader());
TypeFilter typeFilter2 = new AspectJTypeFilter(expression2, CustomSelector.class.getClassLoader());
// 10. 把类型过滤器加入到扫描器中
scan.addIncludeFilter(typeFilter);
scan.addIncludeFilter(typeFilter2);
// 11. 定义要扫描类的全限定类名
final Set<String> classes = new HashSet<String>();
// 12.填充集合的类型
for (String aPackage : basePackages) {
Set<BeanDefinition> beanDefinitions = scan.findCandidateComponents(aPackage);
for (BeanDefinition beanDefinition : beanDefinitions) {
classes.add(beanDefinition.getBeanClassName());
}
}
return classes.toArray(new String[0]);
}
}
代码解析:
1. @Import 自定义扫描规则首先需要实现 ImportSelector 接口, 进入接口可以发现该接口只有一个方法,参数是@Import 注解下bean的元信息。
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
2. 自定义扫规则时,需要判断是否有@ComponentScan注解,如果有,则可以通过元信息,获取扫描的包名作为自定义扫描规则的包否则扫描 @Import 类所在包及其子包。需要注意的获取@ComponentScan的包名是使用的是basePackages,其实这里使用value属性是一样的,通过源码我们可以知道其实他们互为别名。
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}
3. 自定义过滤规则是使用的是ASPECJ方式,需要在pom.xml中引入相应的坐标。同时ASPECJ的表达式,通过读取配置文件获取。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>