组件扫描策略注解 @ComponentScan
事实上,我们可以在定义MainConfig 方法中添加 @ComponentScan 注解来定义扫描的信息,比如,排除的Bean等等
@Configuration// 配置组件扫描信息的包名@ComponentScan(value = "com.zhoutao123.spring",// 定义排除的Bean的过滤器,排除Controller注解的Bean以及BookService类型的BeanexcludeFilters = {@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代表匹配过滤*/@Overridepublic 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 注释的的原信息*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 获取BeanFactoryConfigurableListableBeanFactory 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来注解使用 无参构造注入器 注入对象,比如下面两种方式结果是一样
@Beanpublic 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 {@Overridepublic 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 {@Overridepublic 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> {@Overridepublic Size getObject() throws Exception {return new Size();}@Overridepublic Class<?> getObjectType() {return Size.class;}// 是否是单例模式@Overridepublic boolean isSingleton() {return false;}}// 注入 FactoryBean 对象@Beanpublic 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;
