1、通过注解指定配置类和注册Bean
@Configuration
public class MainConfig {
@Bean
public Person person(){
return new Person("lisi",12);
}
}
@Configuration注解可以不加,创建ApplicationContext对象时传入的类即可被认定为容器配置类,Spring并不会自动扫描注解有@Configuration的类并将其视为容器类,@Bean注解的方法的返回值即为注册的Bean,Bean的id默认为方法名,id可通过@Bean注解的属性指定
public class MainTest {
public static void main(String args[]){
// ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// Person p = (Person) context.getBean("person");
// System.out.print(p);
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = context.getBean(Person.class);
System.out.print(person);
}
}
ClassPathXmlApplicationContext和AnnotationConfigApplicationContext均为ApplicationContext的实现类,前者用来加载spring的xml配置文件,后者用来加载spring的配置类,加载完spring的xml配置文件或配置类后spring容器就会创建对象,这两种方式会有些不同:使用xml配置的方式会默认调用指定Class的无参构造器,也可以制定构造器,属性的值是通过Setter方法注入的;而使用配置类时则会调用配置类中所有@Bean注解的方法,调用的构造器是在该方法中指明的。
2、通过注解指定容器类的扫描范围和扫描规则
@Configuration
@ComponentScan(value = "com.bdm", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
@Configuration
@ComponentScan(value = "com.bdm", useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
@ComponentScan注解的value属性指定了扫描的包,表示此包中使用了@Controller、@Service、@Repository、@Component等注解的类会被纳入Spring容器的管理;excludeFilters属性表示该包中哪些类别的类不会被纳入Spring容器管理,它的属性值是一个数组;includeFilters属性表示指定的包中哪些类别的类会被纳入到Spring容器管理,注意要同时设置useDefaultFilters的属性为false。
JDK1.8之后一个容器类可以使用多次@ComponentScan注解,JDK1.8之前则可以使用@ComponentScans实现这个需求
@Configuration
@ComponentScans(value = {@ComponentScan(value = "com.bdm", useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}), @ComponentScan(value = "com.bdm", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
如果通过@ComponentScan设置的包有包含关系时,同一个被纳入Spring容器的Class会创建多少个bean实例呢?答案是1个,因为通过包扫描的方式创建的bean实例的id是类名的首字母小写,spring在创建bean时会先判断该id的bean有无创建,已创建则不再创建,否则才创建;那如果除了包扫描中有该类,同时@Bean注解的方法中也有该Class的实例,这个时候会创建几个bean实例呢?这个要看@Bean注解的方法的方法名或者通过@Bean的name属性为该实例指定的id是否和类名的首字母小写后相同,相同则仅创建一个,否则创建多个,说明spring容器在创建bean时会先检查该id的bean是否已经存在,不存在则创建,否则不创建。
FilterType的值:
FilterType.ANNOTATION:指定注解注解的类
FilterType.ASSIGNABLE_TYPE:指定类型的类
FilterType.ASPECTJ:使用AspectJ表达式过滤
FilterType.REGEX:使用正则表达式过滤
FilterType.CUSTOM:自定义过滤规则
3、自定义过滤规则
自定义规则类需实现TypeFilter接口:
public class BdmTypeFilter implements TypeFilter{
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//注解
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
//路径
Resource resource = metadataReader.getResource();
//类名
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
if(className.contains("action"))
return true;
MetadataReader metadataReader1 = metadataReaderFactory.getMetadataReader("");
return false;
}
}
MetadataReader:获取正被扫描的类的信息,包括该类的注解信息、路径信息、类名信息等
MetadataReaderFactory:获取MetadataReader
使用自定义过滤规则:type=FilterType.CUSTOM
@Configuration
@ComponentScan(value="com.bdm",useDefaultFilters = false,includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BdmTypeFilter.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
则在指定包com.bdm及其子包下所有符合自定义规则(此处为类名中含有action字符串)的类将被纳入Spring容器管理
由此可以看出Spring容器在通过扫描的方式决定将哪些类纳入容器管理前会先扫描指定包下所有的类,再看这些类是否符合被纳入管理的条件(有没有指定注解、类名是否符合条件、路径是否符合条件等),所以如果在一个包下需要被Spring容器管理的类所占的比例比较小时最好不要使用扫描包的方式,而使用@Bean注解的方式性能会更好些,因为减少了遍历