Bean注入的四种方式
发表于|更新于
|阅读量:
Bean注入的四种方式
xml 方式
依稀记得最早接触Spring
的时候,用的还是SSH
框架,不知道大家对这个还有印象吗?所有的bean
的注入得依靠xml
文件来完成。
它的注入方式分为:set
方法注入、构造方法注入、字段注入,而注入类型分为值类型注入(8种基本数据类型)和引用类型注入(将依赖对象注入)。
以下是set
方法注入的简单样例
1 2 3
| <bean name="teacher" class="cn.vipwen.domain.Teacher"> <property name="name" value="vipwen"></property> </bean>
|
对应的实体类代码
1 2 3 4 5 6 7 8
| public class Teacher {
private String name;
public void setName(String name) { this.name = name; } }
|
xml方式存在的缺点如下:
xml
文件配置起来比较麻烦,既要维护代码又要维护配置文件,开发效率低;
- 项目中配置文件过多,维护起来比较困难;
- 程序编译期间无法对配置项的正确性进行验证,只能在运行期发现并且出错之后不易排查;
- 解析
xml
时,无论是将xml
一次性装进内存,还是一行一行解析,都会占用内存资源,影响性能。
注解方式
随着Spring
的发展,Spring 2.5
开始出现了一系列注解,除了我们经常使用的@Controller、@Service、@Repository、@Component 之外,还有一些比较常用的方式,接下来我们简单了解下。
@Configuration + @Bean
当我们需要引入第三方的jar
包时,可以用@Bean
注解来标注,同时需要搭配@Configuration
来使用。
@Configuration
用来声明一个配置类,可以理解为xml
的``标签
@Bean
用来声明一个bean
,将其加入到Spring
容器中,可以理解为xml
的``标签
简单样例:将 RedisTemplate 注入 Spring
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet(); return redisTemplate; } }
|
@Import
我们在翻看Spring
源码的过程中,经常会看到@Import
注解,它也可以用来将第三方jar
包注入Spring
,但是它只可以作用在类上。
例如在注解EnableSpringConfigured
上就包含了@Import
注解,用于将SpringConfiguredConfiguration
配置文件加载进Spring
容器。
1 2
| @Import(SpringConfiguredConfiguration.class) public @interface EnableSpringConfigured {}
|
@Import
的value
值是一个数组,一个一个注入比较繁琐,因此我们可以搭配ImportSelector
接口来使用,用法如下:
1 2 3 4 5 6 7 8 9 10
| @Configuration @Import(MySelector.class) public class MyConfig {}
public class MySelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"org.springframework.data.redis.core.RedisTemplate"}; } }
|
其中selectImports
方法返回的数组就会通过@Import
注解注入到Spring
容器中。
无独有偶,ImportBeanDefinitionRegistrar
接口也为我们提供了注入bean
的方法。
1 2 3 4
| @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { ...... }
|
我们点击AspectJAutoProxyRegistrar
类,发现它实现了ImportBeanDefinitionRegistrar
接口,它的registerBeanDefinitions
方法便是注入bean
的过程,可以参考下。
如果觉得源代码比较难懂,可以看一下我们自定义的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration @Import(value = {MyImportBeanDefinitionRegistrar.class}) public class MyConfig {}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition tDefinition = new RootBeanDefinition(Teacher.class); registry.registerBeanDefinition("teacher", tDefinition); } } }
|
这样我们就把Teacher
类注入到Spring
容器中了。
FactoryBean
提到FactoryBean
,就不得不与BeanFactory
比较一番。
BeanFactory
: 是 Factory
, IOC
容器或者对象工厂,所有的Bean
都由它进行管理
FactoryBean
: 是Bean
,是一个能产生或者修饰对象生成的工厂 Bean
,实现与工厂模式和修饰器模式类似
那么FactoryBean
是如何实现bean
注入的呢?
先定义实现了FactoryBean
接口的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class TeacherFactoryBean implements FactoryBean<Teacher> {
@Override public Teacher getObject() throws Exception { return new Teacher(); }
@Override public Class<?> getObjectType() { return Teacher.class; }
}
|
然后通过 @Configuration + @Bean的方式将TeacherFactoryBean
加入到容器中
1 2 3 4 5 6 7
| @Configuration public class MyConfig { @Bean public TeacherFactoryBean teacherFactoryBean(){ return new TeacherFactoryBean(); } }
|
注意:我们没有向容器中注入Teacher
, 而是直接注入的TeacherFactoryBean
,然后从容器中拿Teacher
这个类型的bean
,成功运行。
BDRegistryPostProcessor
看到这个接口,不知道对于翻看过Spring
源码的你来说熟不熟悉。如果不熟悉的话请往下看,要是熟悉的话就再看一遍吧😃。
源码
1 2 3 4 5 6 7 8 9
| public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
@FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
|
BeanFactoryPostProcessor
接口是BeanFactory
的后置处理器,方法postProcessBeanFactory
对bean
的定义进行控制。今天我们重点来看看postProcessBeanDefinitionRegistry
方法:它的参数是BeanDefinitionRegistry
,顾名思义就是与BeanDefinition
注册相关的。
通过观察该类,我们发现它里边包含了registerBeanDefinition
方法,这个不就是我们想要的吗?为了能更好的使用该接口来达到注入bean
的目的,我们先来看看Spring
是如何操作此接口的。
看下invokeBeanFactoryPostProcessors
方法,会发现没有实现PriorityOrdered
和Ordered
的bean
(这种跟我们自定义的实现类有关)会执行以下代码。
1 2 3 4 5
| while (reiterate) { ...... invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); ...... }
|
进入该方法
1 2 3 4 5 6 7 8
| private static void invokeBeanDefinitionRegistryPostProcessors( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } }
|
会发现实现了BeanDefinitionRegistryPostProcessor
接口的bean
,其postProcessBeanDefinitionRegistry
方法会被调用,也就是说如果我们自定义接口实现该接口,它的postProcessBeanDefinitionRegistry
方法也会被执行。
实战
话不多说,直接上代码。自定义接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Teacher.class); registry.registerBeanDefinition("teacher", rootBeanDefinition); }
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {} }
|
启动类代码
1 2 3 4 5 6 7 8 9
| public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); MyBeanDefinitionRegistryPostProcessor postProcessor = new MyBeanDefinitionRegistryPostProcessor(); context.addBeanFactoryPostProcessor(postProcessor); context.refresh(); Teacher bean = context.getBean(Teacher.class); System.out.println(bean); }
|
启动并打印结果
1
| org.springframework.demo.model.Teacher@2473d930
|
发现已经注入到Spring
容器中了。