@Configuration&@Bean给容器中注册组件
原来我们向容器中添加一个bean的时候,我们是这么做的
<bean id="person" class="main.java.pojo.Person">
<constructor-arg name="name" value="kevin"></constructor-arg>
<constructor-arg name="age" value="100"></constructor-arg>
</bean>
然后再在这样获取容器中的bean
ClassPathXmlApplicationContext classPathXmlApplicationContext1 = new ClassPathXmlApplicationContext("main/resource/beans.xml");
Person person1 =(Person) classPathXmlApplicationContext1.getBean("person");
我们也可以通过注解驱动+配置类的方式向容器中注册一个bean
1.编写MyConfig配置类
@Configuration //加入@Configuration 注解表示告诉spring这个类是一个配置类,spring容器在启动时会加载里边的信息
public class MyConfig {
@Bean //@Bean相当于我们原来xml配置中的<Bean>标签 id:默认是方法名,当然也可以自定义,类型是返回值类型,将方法的返回值放入到ioc容器中
public Person getPerson(){
return new Person("kevin",21);
}
}
2.测试
// 用到一个新的ApplicationContext的实现类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Person person = (Person) applicationContext.getBean("getPerson");
// 获取bean的id
String[] beanNames = applicationContext.getBeanNamesForType(Person.class);
for (String name : beanNames){
System.out.println(name);
}
System.out.println(person);
@ComponentScan-自动扫描组件&指定扫描规则
原来我们还可以配置一个包扫描,可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进Spring容器中管理
<!-- 包扫描 -->
<context:component-scan base-package="main.java.*"></context:component-scan>
现在也可以在以上配置类中加上注解配置
@ComponentScan(value = "main.java.*") //value:指定要扫描的包
加上controller层,service层测试:
// 获取ioc容器中的所有bean id
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for(String beanName : beanDefinitionNames ){
System.out.println(beanName);
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
personController
personDao
personService
getPerson
原来<context:component-scan base-package=“main.java.*”></context:component-scan>会提供两个子标签让我们根据需要向容器中导入相关组件
<context:include-filter>
<context:exclude-filter>
Use-dafault-filters=”false”的情况下:
<context:exclude-filter>指定的不扫描
<context:include-filter>指定的扫描
现在同样可以通过上述注解形式配置
@ComponentScans(value = {
@ComponentScan(value = "main.java.*",includeFilters ={
@ComponentScan.Filter(type = FilterType.ANNOTATION , classes = {Controller.class, Service.class})
},useDefaultFilters = false //禁用默认的扫描规则
)
})
//@ComponentScan value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
personController
personService
getPerson
@Scope-设置组件作用域 和 懒加载
@Scope("prototype")//默认是单实例
@Bean
public Person getPerson(){
System.out.println("创建对象");
return new Person("kevin",21);
}
测试:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Person person1 = (Person) applicationContext.getBean("getPerson");
Person person2 = (Person) applicationContext.getBean("getPerson");
// 获取bean的id
String[] beanNames = applicationContext.getBeanNamesForType(Person.class);
for (String name : beanNames){
System.out.println(name);
}
System.out.println(person1);
System.out.println(person2);
System.out.println(person1 == person2);
结果:
创建对象
创建对象
getPerson
Person{name='kevin', age=21}
Person{name='kevin', age=21}
false
注意点:
1.多实例情况下,ioc容器启动并不会去调用方法实例化对象放在容器中,而是每次获取时采取调用方法
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("ioc容器启动");
//我先注释掉调用代码
/*Person person1 = (Person) applicationContext.getBean("getPerson");
Person person2 = (Person) applicationContext.getBean("getPerson");
System.out.println(person1);
System.out.println(person2);
System.out.println(person1 == person2);*/
结果:
ioc容器启动
getPerson
从结果可以看到,ioc容器启动后只是知道了有这个bean,但是却没调用方法
2.单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿
//@Scope("prototype")//默认是单实例
结果:
创建对象
ioc容器启动
getPerson
如果我们希望在单实例情况下像多实例一样,当调用时才会去创建对象,这时候就需要用到懒加载
//@Scope("prototype")//默认是单实例
@Lazy
@Bean
public Person getPerson(){
System.out.println("创建对象");
return new Person("kevin",21);
}
结果;
ioc容器启动
getPerson
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化
@Conditional-按照条件注册
如果有任务要求我们在不同环境下加载不同的bean到容器中,比如在window环境下加载对象1,Linux环境下加载对象2,这是可以使用@Conditional注解加上相关类完成,@Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
@Conditional(value = {WinCondition.class})
@Bean
public Person getPerson(){
System.out.println("创建对象1");
return new Person("kevin1",21);
}
@Conditional(value = {LinuxConfition.class})
@Bean
public Person getPerson1(){
System.out.println("创建对象2");
return new Person("kevin2",2456);
}
编写相关类实现 Condition接口
public class LinuxConfition implements Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
// TODO是否window系统
//1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
//可以判断容器中的bean注册情况,也可以给容器中注册bean
boolean definition = registry.containsBeanDefinition("person");
if(property.contains("linux")){
return true;
}
return false;
}
}
另外一个同理,就省略了
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("ioc容器启动");
// 获取bean的id
String[] beanNames = applicationContext.getBeanNamesForType(Person.class);
for (String name : beanNames){
System.out.println(name);
}
结果:
ioc容器启动
getPerson1
@Import-给容器中快速导入一个组件
之前我们每次导入一个组件都需要使用@bean注解,其实,我们也可以使用这个注解实现快速导入第三包
新建一个color
public class Color {
}
使用@Import导入
@Import({Color.class})
结果:
myConfig
personController
personService
main.java.pojo.Color
@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
除此之外,还可以在@Import-使用ImportSelector
我们还可以使用ImportSelector导入color
@Import({MyImportSelector.class})
我们需要自定义导入规则
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// TODO Auto-generated method stub
//importingClassMetadata
//方法不要返回null值
return new String[]{"main.java.pojo.Color"};
}
}
结果还是跟上边一样
也可以在@Import-使用ImportBeanDefinitionRegistrar完成手工注册bean
@Import({MyImportBeanDefinitionRegistrar.class})
自定义的ImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 定义bean的注册信息
RootBeanDefinition beanDefinition = new RootBeanDefinition(Color.class);
// 注册bean
beanDefinitionRegistry.registerBeanDefinition("Color",beanDefinition);
}
}
结果还是一样
使用FactoryBean注册组件
public class PersonBeanFactory implements FactoryBean {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Object getObject() throws Exception {
return new Person("kekk",6465);
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
//是单例?
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
@Override
public boolean isSingleton() {
return false;
}
}