组件扫描策略注解 @ComponentScan
事实上,我们可以在定义MainConfig
方法中添加 @ComponentScan
注解来定义扫描的信息,比如,排除的Bean等等
@Configuration
// 配置组件扫描信息的包名
@ComponentScan(value = "com.zhoutao123.spring",
// 定义排除的Bean的过滤器,排除Controller注解的Bean以及BookService类型的Bean
excludeFilters = {
@Filter(classes = Controller.class, type = FilterType.ANNOTATION),
@Filter(classes = BookService.class, type = FilterType.ASSIGNABLE_TYPE),
}, includeFilters = {
// 定义包含过滤器,包含BookService类型的Bean,注意需要设置使用默认过滤器为false,否则不生效
@Filter(classes = BookService.class, type = FilterType.ASSIGNABLE_TYPE)
}, useDefaultFilters = false)
public class MainConfig {
//....
}
- 过滤类型的枚举包含以下方式
public enum FilterType {
ANNOTATION, // 按照注解的方式过滤
ASSIGNABLE_TYPE, // 按照类型的方式过滤
ASPECTJ, // 按照ASPECTJ方法过滤
REGEX, // 按照正则方法过滤Beand的类型
CUSTOM // 定义过滤,实现 org.springframework.core.type.filter.TypeFilter 接口
}
比如下面可以自定义一个过滤器
public class CustomTypeFilter implements TypeFilter {
/**
* @param metadataReader 读取当前正在扫描类的信息
* @param metadataReaderFactory 读取其他类的信息
* @return 返回True代表匹配过滤
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 当前类的原信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 类的注解的原信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取其他类型信息
metadataReaderFactory.getMetadataReader("TestClassName");
return false;
}
}
// 可以在@ComponentmScan中扫描
@ComponentScan(value = "com.zhoutao123.spring", excludeFilters = {
@Filter(classes = Controller.class, type = FilterType.ANNOTATION),
@Filter(classes = CustomTypeFilter.class, type = FilterType.CUSTOM)
})
作用域 @Scope
默认情况下,从上下文Context获取的Bean都是单例的,可以用下面的代码验证
Student bean = context.getBean(Student.class);
Student bean2 = context.getBean(Student.class);
assert bean == bean2;
一些情况下,我们希望每次获取都是一个新的Bean,这时候可以使用 @Scope
注解的方式来实现, @Scope
的value = ‘singleton’ 标识默认单例模式,value=’prototype’ 标识Bean 是多实例模式
- 单实例对象:在IOC容器创建的时候创建Bean
- 多实例对象: 在获取Bean的时候,在生成对象
@Scope(value="xxxx") // value取值可以是 singleton,prototype,request 以及 session
@Bean("测试Bean")
public Student student2() {
Student student = new Student();
student.setAge(18);
student.setLessonList(Arrays.asList("Math", "English"));
student.setName("燕归来兮");
return student;
}
// 断言测试不相等
Student bean = context.getBean(Student.class);
Student bean2 = context.getBean(Student.class);
assert bean != bean2;
懒加载 @Lazy
Bean 的懒加载模式,仅针对于单实例模式,Bean将程序第一次获取从上下文中的时候创建Bean对象,通常在有Bean注解的方法添加注解 @Lazy
即可。
@Lazy
@Bean("测试Bean")
public Student student() {
Student student = new Student();
student.setAge(18);
student.setLessonList(Arrays.asList("Math", "English"));
student.setName("燕归来兮");
return student;
}
条件注入 @Conditional
满足条件,则向容器中注入,不满足则不注入,从Spring4.0 开始实现的方式。自定义Condition需要实现 Condition
接口
public class CustomConditional implements Condition {
/**
* @param context 上下文
* @param metadata 注释的的原信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 获取环境信息
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name", "");
if (osName.contains("Windows")) {
return false;
}
return osName.contains("Linux");
}
在Bean上添加注解 @Conditional
注解即可实现动态注入, 标注在类上,具有影响类的全部方式的效果
@Conditional(value = CustomConditional.class)
@Bean("测试Bean")
public Student student() {
return new Student();
}
手动注入 @Import
使用 @Import
使用 @Bean
注解并不是唯一注入 Bean 的方式,在一些简单的的对象,通常可以直接使用@Import来注解使用 无参构造注入器
注入对象,比如下面两种方式结果是一样
@Bean
public Color(){
return new Color();
}
@Import({Color.class})
public MainCoinfig{}
使用 @ImportSelector
@Import
注解可以填写需要进行无参构造注入的Bean的class对象,同时也可以自定义一个 ImportSelector
对象或者 ImportBeanDefinitionRegistrar
来动态的注入所需要的Bean,定义一个CustomerImportSelector需要继承 org.springframework.context.annotation.ImportSelector
然后重写 selectImports
返回一个字符串数组,该数组包含需要Import的全类名,该对象可以返回空数组,但不能返回null,否则会报错 NullPointException
自定义ImportSelector 的方式如下
public class CustomerImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 演示获取到全类名
String clazzName = Size.class.getName();
return new String[]{clazzName};
}
}
// 在启动的配置类上添加@Import注解,@Import({Color.class, CustomerImportSelector.class})
使用 @ImportBeanDefinitionRegistrar
定义一个自定义的ImportBeanDefinitionRegistrar 需要继承 org.springframework.context.annotation.ImportBeanDefinitionRegistrar
并重写 registerBeanDefinitions
方法,示例代码展示的是如果存在名称为apple的Bean,则注册一个BeanName=size的新的Bean,示例代码如下:
public class CustomerImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
boolean hasAppleComponent = registry.containsBeanDefinition("apple");
if (hasAppleComponent) {
RootBeanDefinition definition = new RootBeanDefinition(Size.class);
registry.registerBeanDefinition("size",definition);
}
}
}
// 同样也需要在 @Import({ImportBeanDefinitionRegistrar.class})
使用 FactoryBean
同样的使用Spring提供的FactoryBean也可以手动注入Bean对象, 创建自定义的SpringFactoryBean 需要继承 org.springframework.beans.factory.FactoryBean
public class CustomerFactoryBean implements FactoryBean<Size> {
@Override
public Size getObject() throws Exception {
return new Size();
}
@Override
public Class<?> getObjectType() {
return Size.class;
}
// 是否是单例模式
@Override
public boolean isSingleton() {
return false;
}
}
// 注入 FactoryBean 对象
@Bean
public CustomerFactoryBean factoryBean(){
return new CustomerFactoryBean();
}
这里需要注意,虽然注入的 CustomerFactoryBean,但是从IOC 容器中获取到 BeanName = ‘factoryBean’ 其类型不是CustomerFactoryBean,而是这个FactoryBean的构造Bean,如果需要获取其对应的FactoryBean对象实例,则需要在BeanNam前面加上 ‘&’ 符号
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Object bean1 = context.getBean("factoryBean");
assert bean1 instanceof Size;
bean1 = context.getBean("&factoryBean");
assert bean1 instanceof CustomerFactoryBean;