- xml配置文件加载的容器;
- 通过注解加载的容器;
xml容器用如下方式获得:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
通过XML添加的所有组件只会都在该容器中,用注解容器是拿不到对应的实例对象。同理,用注解添加的组件也只会在注解容器中。
注解容器通过如下方式获得:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( MainConfig.class, OtherConfig.class, ImportConfig.class);
如果想精确的控制可以调用AnnotationConfigApplicationContext的无参构造器,然后手动调用里面的方法;如下:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig.class); context.register(MainConfig.class); context.register(MainConfig.class); context.refresh();
分步调用可以在register()执行完后进行额外的处理在调用refresh()。后面说到这些额外的处理。
注入组件的四种方式
- @Configuration + @Bean
导入的第三方包里面的组件。因为我们无法直接在三方代码上加@Component。 - @ComponentScans + @ComponentScan +@Component
直接在代码上添加如上注解,导入自己写的类。 - @Import 快速给容器中导入一个组件
a. @Import(WantImportClass.class);容器中自动注册这个组件,id默认是全类名
b. 协议ImportSelector:返回需要导入的组件的全类名数组;
c. 协议ImportBeanDefinitionRegistrar:手动注册bean到容器中 - 使用Spring提供的 FactoryBean(工厂Bean);
a. 默认获取到的是工厂bean调用getObject创建的对象
b. 要获取工厂Bean本身,我们需要给id前面加一个&colorFactoryBean
方式一 @Configuration @Bean
该方式主要用来导入三方的类作为Bean添加IoC容器中。因为我们自己的类通常是在代码上添加@Component和@ComponentScan直接把类注入到IoC容器中。使用时通常有几个Config类,用来描述Bean组件。注意,需要手动调用AnnotationConfigApplicationContext的register方法将配置类加载到IoC容器中,当然调用Context的有参构造器也可以。
@Configuration
@Configuration继承自@Component,说明用该注解的修饰类本身也会作为组件添加的IoC容器中。其作用范围是@Target({ElementType.TYPE}),也就是不能修饰方法上,通常用于修饰类,并在该配置类中声明要加载的bean。使用如下:
@Configurationpublic class MainConfig { @Bean(value = "tom") public Person person01 (){ return person02(); } @Bean(value = "jack") public Person person02 (){ return new Person("jack",20); }}
注意,如果类上没有@Configuration,Bean会议'lite Mode'的模式加载到IoC容器中。在lite Mode中tom和jack是不同的实例对象。有@Configuration注解,tom和jack则是同一个实例对象。 详见官方文档
@Bean
@Bean的作用范围是@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),通常情况下用于修饰有对象返回值的方法。 @Bean常用三个属性:
- value或name;
使用:@Bean("person")。如果value为空,默认用方法名作为id。 - initMethod;
- destroyMethod;
initMethod和destroyMethod这两个方法是用来指定bean的初始化之后和销毁之前调用的方法。会在Bean的生命周期在讨论。
方式二 @ComponentScans + @ComponentScan +@Component
该方式主要是将自己代码中的类注入成bean组件。
@ComponentScans + @ComponentScan
扫描制定路径下的所有类,将带有@Component注解的类注入成Bean组件。这两个注解的使用范围都是ElementType.TYPE,通常都是加在类上,Spring建议同时加上@Configuration注解使用。示例:
//告诉Spring这是一个配置类@Configuration @ComponentScans( value = { @ComponentScan(value={ "com.springDemo.Bean", "com.springDemo.Controller"} includeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}), @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}), @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class}) }, useDefaultFilters = false) })public class MainConfig { // other bean ...}
@ComponentScans的value是一个@Component的数组,用以描述多个组件扫描的规则。 @ComponentScan主要有以下四个属性:
- value : 指定要扫描的包,值是String数组;
- excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件;
- includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件;
- useDefaultFilters
默认只指定value的值,会将指定包中所有带@Component的类注入成Bean。如果不想用默认的扫描规则需要useDefaultFilters = false。同时指定excludeFilters或includeFilters。
@Filter
过滤规则使用@Filter注解。该注解用以详细描述过滤的规则,有以下5个值。常用的是FilterType.CUSTOM。这里不对过滤规则多详细介绍。
- FilterType.ANNOTATION:按照注解
- FilterType.ASSIGNABLE_TYPE:按照给定的类型;
- FilterType.ASPECTJ:使用ASPECTJ表达式
- FilterType.REGEX:使用正则指定
- FilterType.CUSTOM:使用自定义规则
@Component
该注解的范围是ElementType.TYPE,通常只作用在类上。使用如下:
// @Component("customAnimalName")@Componentpublic class Animal { // ... }
通常不需要指定Value的值,bean的id默认是类名。value值,用以改变bean id。
方式三 @Import
使用Import管理配置类
假设有N个配置类,我们可以通过在某一个配置类中用@Import其他所有配置类,这样在AnnotationConfigApplicationContext注册时就只要注册一个配置类即可,不需要记住那么多配置类。 使用如下:
@Configurationpublic class ConfigA { @Bean public A a() { return new A(); }}@Configuration@Import(ConfigA.class)public class ConfigB { @Bean public B b() { return new B(); }}
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); // now both beans A and B will be available... A a = ctx.getBean(A.class); B b = ctx.getBean(B.class);}
使用Import直接导入类作为Bean
使用如下:
//@Import导入组件,id默认是组件的全类名@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})public class MainConfig {}
这种方式不太建议,因为直接用@Import注册组件很难对组件做自定义设置,比如初始化方法,生命周期的管理,是单例还是多实例组件。
ImportSelector
实现接口ImportSelector,在实现类中返回要添加Bean的全类名,就会把类注入到Ioc容器中。在selectImports方法中可以做注入组件的逻辑判断,但是不能对组件的生命周期做业务处理。 示例:
//自定义逻辑返回需要导入的组件public class MyImportSelector implements ImportSelector { //返回值,就是到导入到容器中的组件全类名 //AnnotationMetadata:当前标注@Import注解的类的所有注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // TODO Auto-generated method stub //importingClassMetadata //方法不要返回null值 return new String[]{"com.spring.bean.Blue","com.spring.bean.Yellow"}; }}
// 导入自定义组件@Configuration@Import({MyImportSelector.class})public class MainConfig {// ...}
ImportBeanDefinitionRegistrar
实现接口ImportBeanDefinitionRegistrar,在registerBeanDefinitions方法中拿到registry对象,调用registry.registerBeanDefinition()方法将Bean对象注入到IoC容器中。这种方式比ImportSelector方法更灵活一点,可以对Bean注入做更灵活的控制。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有需要添加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean definition = registry.containsBeanDefinition("com.spring.bean.Red"); boolean definition2 = registry.containsBeanDefinition("com.spring.bean.Blue"); if(definition && definition2){ //指定Bean定义信息;(Bean的类型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } }}
// 导入自定义组件@Configuration@Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})public class MainConfig {// ...}
注意虽然Import了MyImportBeanDefinitionRegistrar和MyImportSelector,但这两个类的Bean组件。
方式四 FactoryBean工厂注入Bean
如果一个Bean特别复杂,需要做很多处理,则可以用FactoryBean来完成。该接口的实现类的不同方法共同定义了一个Bean对象。使用有两步:
- 实现FactoryBean;
- 使用@Bean注入;
使用如下:
- 实现FactoryBean;
//创建一个Spring定义的FactoryBeanpublic class ColorFactoryBean implements FactoryBean { //返回一个Color对象,这个对象会添加到容器中 @Override public Color getObject() throws Exception { // TODO Auto-generated method stub System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class> getObjectType() { // TODO Auto-generated method stub return Color.class; } //是单例? //true:这个bean是单实例,在容器中保存一份 //false:多实例,每次获取都会创建一个新的bean; @Override public boolean isSingleton() { // TODO Auto-generated method stub return false; }}
- 注入Bean
@Configurationpublic class MainConfig @Bean public ColorFactoryBean colorFactoryBean(){ return new ColorFactoryBean(); }}
这种方式即注入了Color Bean对象,同时也注入了ColorFactoryBean对象。两个Bean对象获取如下:
// 获取 Color BeanObject bean1 = applicationContext.getBean("colorFactoryBean");// 获取 ColorFactoryBeanObject bean2 = applicatio
原文链接:https://www.jianshu.com/p/332818c34861