FilterType是一个枚举类,默认是ANNOTATION,注解方式
修改BeanConfig代码,增加includeFilters(),只扫描com.citi包下面的@Controller,@Service注解标识的Bean,一定不要忘记useDefaultFilters = false
@Configuration @ComponentScan(basePackages = "com.citi", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}) }, useDefaultFilters = false) public class BeanConfig { } 复制代码
执行测试,控制台输出只包含了@Controller,@Service注解的Bean,@Componet和@Repository注解标识的类并没有被容器管理
Plus:为什么使用includeFilters时,要配置useDefaultFilters = false?
当useDefaultFilters = true时,进入到registerDefaultFilters()方法中
该方法中将@Component注解的Bean都加入到includeFilters中,而@Controller,@Service,@Repository都是基于@Component的注解,所有useDefaultFilters = true会导致设置是扫描@Controller和@Service注解的Bean不生效
使用excludeFilters(),将BeanConfig中includeFilters改为excludeFilters(),并且useDefaultFilters = true,执行测试,可以看出只包含了除了@Controller,@Service标注的Bean之外的其他Bean的实例化对象
使用FilterType枚举类中的ASSIGNABLE_TYPE进行过滤Bean,修改BeanConfig为如下,表示触PersonController和PersonService之外的其他Bean
@Configuration @ComponentScan(basePackages = "com.citi", excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {PersonController.class, PersonService.class}) }, useDefaultFilters = true) public class BeanConfig { } 复制代码
使用自定义的FilterType,在config包下新增CustTypeFilter,继承TypeFilter
public class CustTypeFilter implements TypeFilter { /** * @param metadataReader 读取到当前正在扫描的类的信息 * @param metadataReaderFactory 可以获取到其他任何类的信息 * @return * @throws IOException */ @Override 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(); // 扫描的类 System.out.println("------>" + className); // 过滤条件,return true表示过滤掉符合这个条件的Bean if (className.contains("service")){ return true; } return false; } } 复制代码
除了@ComponentScan之外还可以添加@ComponentScans,也就是多个@ComponentScan
使用了两个@ComponentScan注解,一个是去除自定义的Bean,即bean id包含“service”字符串的Bean,另外一个使用Annotation 类型的Filter,去除@Controller注解标记的Bean
@ComponentScans({@ComponentScan(basePackages = "com.citi", excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {CustTypeFilter.class}) }, useDefaultFilters = true), @ComponentScan(basePackages = "com.citi", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}) }, useDefaultFilters = true)}) public class BeanConfig { } 复制代码
这里控制台应该输出包含除了PeresonController和PersonService外的实例化对象,根据控制台打印可以看出配置的两个@ComponentScan都没有生效,这里还有待继续探索,
Section 04 - Bean的单实例和多实例
IoC容器不管是使用注解还是xml方式,默认都是单实例的,IoC容器初始化的时候就会被创建,多实例仅当bean被使用的时候才会创建 修改BeanConfi为
@Configuration public class BeanConfig { @Bean public Person person(){ Person person = new Person(); person.setName("Stark"); person.setAge(40); return person; } } 复制代码
新增测试类SingleOrMultInstanceTest,执行testSingleBean()方法
public class SingleOrMultiInstanceTest { @Test public void testSingleBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); Person person = (Person) context.getBean("person"); Person person1 = (Person) context.getBean("person"); System.out.println("是否为单实例:" + (person == person1)); } } 复制代码
查看输出结果,通过比较从容器中获取的两个bean是否为一个bean,默认是单实例
@Scope注解可以配置Bean为单实例或者多实例 @Scope源码
修改BeanConfig
@Configuration public class BeanConfig { @Bean @Scope("prototype") public Person person(){ Person person = new Person(); person.setName("Stark"); person.setAge(40); return person; } } 复制代码
执行测试,此时已经为多实例
Section 05 - 懒加载@Lazy
修改BeanConfig,增加@Lazy注解,懒加载指针多单实例Bean,因为单实例是在容器初始化是就创建对象,增加@Lazy注解后,当调用getBean()获取实例化对象时容器才会实例化Bean 首先将BeanConfig中@Scope注解注释
@Configuration public class BeanConfig { //@Lazy @Bean //@Scope("prototype") public Person person(){ System.out.println("Bean被实例化"); Person person = new Person(); person.setName("Stark"); person.setAge(40); return person; } } 复制代码
新增LazyLoadTest,testLazyLoad()方法中只包含容器初始化的代码,执行该方法
public class LazyLoadTest { @Test public void testLazyLoad(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); //Person person = (Person) context.getBean("person"); //System.out.println(person); } } 复制代码
查看控制台,打印了Bean被实例化,说明容器初始化是就执行了Bean实例化操作
放开BeanConfig中的@Lazy注解,再次执行,Bean没有被实例化
在LazyLoadTest类中的testLazyLoad()方法中增加获取Bean的代码
public class LazyLoadTest { @Test public void testLazyLoad(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); Person person = (Person) context.getBean("person"); System.out.println(person); } } 复制代码
再次执行,结合上一次的执行结果,说明增加了@Lazy注解后,容器初始化是不在实例化Bean,而是在getBean时才会实例化Bean