作为一个秃头的开发者,在对方怼不过的时候,经常会被问到一个发自灵魂的拷问:你有对象吗?
不但有,哥们有好几个!甚至还有个叫春的管家
而在开发过程中让spring管理对象确实是很常见的,也非常方便,有哪些方式呢,我总结了以下几种,咱们具体说下。
总览
先讲一个比较骚的大杀器@Import
它主要是做拓展的时候使用,导入一个配置文件,经常搭配自定义注解使用。
1. @Import直接导入类
代码示例如下:
public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } /** * 直接使用@Import导入person类,然后尝试从applicationContext中取,成功拿到 **/ @Import(Person.class) public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } }
使用@Import导入了一个类,会自动把类放置到IOC容器中。
2. @Import 配合 ImportSelector使用
这种方式,平常用的比较少,不过在@Import注解的源码中,说的已经很清楚了,感兴趣的可以看下,我们实现一个ImportSelector的接口,然后实现其中的方法,进行导入。
@Import(MyImportSelector.class) public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } } class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.springboot.pojo.Person"}; } }
3. @Import 配合 ImportBeanDefinitionRegistrar使用
这种方式也不太常用,需要我们自己实现 ImportBeanDefinitionRegistrar 接口中的方法,具体代码如下
@Import(MyImportBeanDefinitionRegistrar.class) public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } } class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 构建一个beanDefinition, 关于beanDefinition我后续会介绍,可以简单理解为bean的定义. AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition(); // 将beanDefinition注册到Ioc容器中. registry.registerBeanDefinition("person", beanDefinition); } }
上述实现其实和Import的第二种方式差不多,都需要去实现接口,然后进行导入就行。
4. @Import 配合 DeferredImportSelector使用
这种方式和第二种差别不大。只是Spring的处理方式不一样,使用方式如下:
@Import(MyDeferredImportSelector.class) public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } } class MyDeferredImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 也是直接将Person的全限定名放进去 return new String[]{Person.class.getName()}; } }
再说两种常见的
@Componet 配合@ComponentScan
这种是用组件的方式来实现,这种也比较常见,在配置类上的@ComponentScan可以当做是一个扫描器,扫描带有@Componet注解的bean,然后加至容器中。 具体代码如下:
@Component public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } @ComponentScan(basePackages = "com.springboot.initbean.*") public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } }
@Configuration 配合@Bean
首先来说下最常用的,先用@Configuration来声明一个配置类,然后使用 @Bean 注解,这类似于SpringMVC中的配置,最常用,我就不多讲了,就是如下操作:
@Configuration public class MyConfiguration { @Bean public Person person() { Person person = new Person(); person.setName("spring"); return person; } }
最后说两种不太常用的:
1.使用FactoryBean接口
FactoryBean接口和BeanFactory可能有的新手会经常搞混,新手面试官也爱问这个问题,实际上他们的功能是不太一样的,区别的话其实从名字上就可以看出来,就比如,FactoryBean后缀为bean,所以它就是个bean,而BeanFactory后缀是Factory,它是IOC容器的顶级接口。
代码示例:
@Configuration public class Demo1 { @Bean public PersonFactoryBean personFactoryBean() { return new PersonFactoryBean(); } public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } } class PersonFactoryBean implements FactoryBean<Person> { /** * 直接new出来Person进行返回. */ @Override public Person getObject() throws Exception { return new Person(); } /** * 指定返回bean的类型. */ @Override public Class<?> getObjectType() { return Person.class; } }
上述代码,我使用@Configuration + @Bean的方式将 PersonFactoryBean 加入到容器中,这里注意,我的操作是,并没有向容器中注入 Person, 而是注入的 PersonFactoryBean 然后从容器中直接拿的Person。
2.使用 BeanDefinitionRegistryPostProcessor
这种方式也是利用了BeanDefinitionRegistry,因为Spring启动时会执行BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,它们的作用是可以调整IOC容器中的beanDefinition,从而影响后面Bean的初始化,从而影响功能。
代码示例如下:
public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor(); applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor); applicationContext.refresh(); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } } class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition(); registry.registerBeanDefinition("person", beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } }
我们可以手动向beanDefinitionRegistry中注册了person的BeanDefinition。然后成功将person加入到applicationContext中。