1、使用@Configuration和@Bean给容器中注册组件
1.1、xml配置文件方法1.2、注解配置 2、使用@ComponentScan自动扫描组件并指定扫描规则
2.1、xml配置文件方式2.2、注解方式
2.2.1、扫描规则:excludeFilters2.2.2、扫描规则:includeFilters2.2.3、配置多个ComponentScan 3、自定义TypeFilter指定@ComponentScan注解的过滤规则4、使用@Scope注解设置组件的作用域
4.1、单实例和多实例情况下,对象是何时创建的? 5、@Lazy-bean如何实现懒加载?
1、使用@Configuration和@Bean给容器中注册组件首先创建一个Person类:
public class Person {
private String name;
private Integer age;
......
1.1、xml配置文件方法
创建一个beans.xml,在配置文件中指定
测试时,使用ClassPathXmlApplicationContext()方法传入xml配置文件的路径来获取一个ApplicationContext对象:
@Test
public void test1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object person = applicationContext.getBean("person");
System.out.println(person);
// Person{name='zhangsan', age=18}
}
1.2、注解配置
创建一个注解类MainConfig,相当于一个配置文件,采用@Configuration注解。
在类中创建一个方法,采用@Bean注解,类型为返回值类型,id默认是用方法名作为id,也可以给@Bean注解传入一个参数,作为id:
//配置类==配置文件
@Configuration //告诉spring,这是一个配置类
public class MainConfig {
//给容器中注册一个Bean;类型为返回值类型,id默认是用方法名作为id
@Bean("person") //可以传入参数,作为id
public Person person01(){
return new Person("lisi", 20);
}
}
获取Bean时,使用AnnotationConfigApplicationContext()方法传入注解类的class:
@Test
public void test2(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person);
// Person{name='lisi', age=20}
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String string :
beanNamesForType) {
System.out.println(string);
// person
}
}
2、使用@ComponentScan自动扫描组件并指定扫描规则
创建BookService、BookController、BookDao类,并分别使用@Service、@Controller、@Repository注解:
@Service
public class BookService {
}
@Controller
public class BookController {
}
@Repository
public class BookDao {
}
2.1、xml配置文件方式
2.2、注解方式
在注解类中添加@ComponentScan注解,并提供value属性的值:
//配置类==配置文件
@Configuration //告诉spring,这是一个配置类
@ComponentScan(value = {"bean", "config", "controller", "dao", "service"})
public class MainConfig {
//给容器中注册一个Bean;类型为返回值类型,id默认是用方法名作为id
@Bean("person") //可以传入参数,作为id
public Person person01(){
return new Person("lisi", 20);
}
}
输出一下所有定义的Bean的名字:
@Test
public void test3(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name :
definitionNames) {
System.out.println(name);
}
}
org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig bookController bookDao bookService person2.2.1、扫描规则:excludeFilters
看一下@ComponentScan注解的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@documented
//可重复的
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class extends ScopemetadataResolver> scopeResolver() default AnnotationScopemetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**
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);
//如果类名称包含"er"就扫描
if (className.contains("er")){
return true;
}
return false;
}
}
//配置类==配置文件
@Configuration //告诉spring,这是一个配置类
@ComponentScan(
value = {"bean", "config", "controller", "dao", "service"},
includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
},
useDefaultFilters = false
//excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}
)
//@ComponentScan value:指定要扫描的包
//@ComponentScan excludeFilters = ComponentScan.Filter[]:排除哪些包
//@ComponentScan includeFilters = ComponentScan.Filter[]:只要哪些包
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定类型
//FilterType.ASPECTJ:按照ASPECTJ表达式
//FilterType.REGEX:按照正则表达式
//FilterType.CUSTOM:按照自定义规则
@ComponentScans(
value = {
@ComponentScan(
value = {"bean", "config", "controller", "dao", "service"},
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
useDefaultFilters = false
//excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}
)
}
)
public class MainConfig {
//给容器中注册一个Bean;类型为返回值类型,id默认是用方法名作为id
@Bean("person") //可以传入参数,作为id
public Person person01(){
return new Person("lisi", 20);
}
}
--->bean.Person --->config.MyTypeFilter --->controller.BookController --->dao.BookDao --->service.BookService org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig person myTypeFilter bookController bookService
4、使用@Scope注解设置组件的作用域
创建一个配置类MainConfig2:
@Configuration
public class MainConfig2 {
//默认是单实例
@Bean("person")
public Person person(){
return new Person("张三", 5);
}
}
默认情况下是**单实例的,即不管多少次getBean()**,获取到的都是那一个new Person("张三", 5):
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
//默认是单实例
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
System.out.println(bean == bean2); //true
}
可以使用@Scope注解为其配置实现单实例还是多实例,查看@Scope源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
发现有四个取值:
- ConfigurableBeanFactory.SCOPE_PROTOTYPE, prototype:多实例ConfigurableBeanFactory.SCOPE_SINGLETON, singleton:单实例(默认值)org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST, request:同一个请求创建一个实例org.springframework.web.context.WebApplicationContext.SCOPE_SESSION, session:同一个session创建一个实例
@Configuration
public class MainConfig2 {
//默认是单实例
@Scope("prototype")
@Bean("person")
public Person person(){
return new Person("张三", 5);
}
}
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
//默认是单实例
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
System.out.println(bean);
System.out.println(bean2);
System.out.println(bean == bean2); //false
}
Person{name='张三', age=5}
Person{name='张三', age=5}
false
在xml配置文件中可以使用
4.1、单实例和多实例情况下,对象是何时创建的?
先来看单实例情况,在return new Person("张三", 5);调用创建对象的方法之前添加一个System.out.println("给容器中添加Pserson...");语句:
@Configuration
public class MainConfig2 {
//默认是单实例
@Scope //调整作用域
@Bean("person")
public Person person(){
System.out.println("给容器中添加Pserson...");
return new Person("张三", 5);
}
}
在测试代码中,先来创建IOC容器,并没有从容器中拿出对象:
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//String[] definitionNames = applicationContext.getBeanDefinitionNames();
//
//默认是单实例
System.out.println("IOC容器创建完成");
//Object bean = applicationContext.getBean("person");
//Object bean2 = applicationContext.getBean("person");
//System.out.println(bean);
//System.out.println(bean2);
//System.out.println(bean == bean2);
}
给容器中添加Pserson... IOC容器创建完成
打印出给容器中添加Pserson...。因此,可以推断:单实例(默认值),IOC容器启动时会调用方法创建对象,并放到IOC容器中。以后每次获取就从容器中拿,因此是单实例。
再看多实例情况:
@Configuration
public class MainConfig2 {
//默认是单实例
@Scope("prototype") //调整作用域
@Bean("person")
public Person person(){
System.out.println("给容器中添加Pserson...");
return new Person("张三", 5);
}
}
依然只创建IOC容器:
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//String[] definitionNames = applicationContext.getBeanDefinitionNames();
//
//默认是单实例
//System.out.println("IOC容器创建完成");
//Object bean = applicationContext.getBean("person");
//Object bean2 = applicationContext.getBean("person");
//System.out.println(bean);
//System.out.println(bean2);
//System.out.println(bean == bean2);
}
并没有任何输出。因此可以推断:多实例,创建IOC 容器时,并不会创建对象。
开启获取对象,在获取对象前添加System.out.println("IOC容器创建完成");:
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//String[] definitionNames = applicationContext.getBeanDefinitionNames();
//
//默认是单实例
System.out.println("IOC容器创建完成");
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
//System.out.println(bean);
//System.out.println(bean2);
//System.out.println(bean == bean2);
}
IOC容器创建完成 给容器中添加Pserson... 给容器中添加Pserson...
获取了两次对象,输出了两次给容器中添加Pserson...。因此,可以推断:多实例,IOC容器启动并不会调用方法创建对象放到IOC容器中,以后每次获取时才会调用方法创建对象。
5、@Lazy-bean如何实现懒加载?
懒加载是针对单实例bean:
单实例bean默认在容器启动时创建对象;懒加载:容器启动时不创建对象,第一次使用(获取)bean时创建对象,并初始化。
@Scope//("prototype") //调整作用域
@Lazy //懒加载
@Bean("person")
public Person person(){
System.out.println("给容器中添加Pserson...");
return new Person("张三", 5);
}
测试时,先不获取对象:
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//String[] definitionNames = applicationContext.getBeanDefinitionNames();
//
//默认是单实例
System.out.println("IOC容器创建完成");
//Object bean = applicationContext.getBean("person");
//Object bean2 = applicationContext.getBean("person");
//System.out.println(bean);
//System.out.println(bean2);
//System.out.println(bean == bean2);
}
IOC容器创建完成
只打印出IOC容器创建完成。并没创建对象。
获取对象,并判断对象是否相等:
@Test
public void test4(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//String[] definitionNames = applicationContext.getBeanDefinitionNames();
//
//默认是单实例
System.out.println("IOC容器创建完成");
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
System.out.println(bean);
System.out.println(bean2);
System.out.println(bean == bean2);
}
IOC容器创建完成
给容器中添加Pserson...
Person{name='张三', age=5}
Person{name='张三', age=5}
true
可以看到,虽然获取了两次对象,但只打印了一次给容器中添加Pserson...,说明只调用了一次创建对象的方法,因此,两个对象相等。



