一提到Spring
,大家最先想到的是啥?是AOP
和IOC
的两大特性?是Spring
中Bean
的初始化流程?还是基于Spring
的Spring Cloud
全家桶呢?
今天我们就从Spring
的IOC
特性入手,聊一聊Spring
中把Bean
注入Spring
容器的几种方式。
我们先来简单了解下IOC
的概念:IOC
即控制反转,也称为依赖注入,是指将对象的创建或者依赖关系的引用从具体的对象控制转为框架或者IOC
容器来完成,也就是依赖对象的获得被反转了。
xml 方式可以简单理解为原来由我们来创建对象,现在由
Spring
来创建并控制对象。
依稀记得最早接触Spring
的时候,用的还是SSH
框架,不知道大家对这个还有印象吗?所有的bean
的注入得依靠xml
文件来完成。
它的注入方式分为:set
方法注入、构造方法注入、字段注入,而注入类型分为值类型注入(8种基本数据类型)和引用类型注入(将依赖对象注入)。
以下是set
方法注入的简单样例
<bean name="teacher" class="org.springframework.demo.model.Teacher">
<property name="name" value="阿Q"></property>
</bean>
对应的实体类代码
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 之外,还有一些比较常用的方式,接下来我们简单了解下。
当我们需要引入第三方的jar
包时,可以用@Bean
注解来标注,同时需要搭配@Configuration
来使用。
-
@Configuration
用来声明一个配置类,可以理解为xml
的<beans>
标签 -
@Bean
用来声明一个bean
,将其加入到Spring
容器中,可以理解为xml
的<bean>
标签
简单样例:将 RedisTemplate 注入 Spring
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
......
return redisTemplate;
}
}
@Import
我们在翻看Spring
源码的过程中,经常会看到@Import
注解,它也可以用来将第三方jar
包注入Spring
,但是它只可以作用在类上。
例如在注解EnableSpringConfigured
上就包含了@Import
注解,用于将SpringConfiguredConfiguration
配置文件加载进Spring
容器。
@Import(SpringConfiguredConfiguration.class)
public @interface EnableSpringConfigured {}
@Import
的value
值是一个数组,一个一个注入比较繁琐,因此我们可以搭配ImportSelector
接口来使用,用法如下:
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"org.springframework.demo.model.Teacher","org.springframework.demo.model.Student"};
}
}
其中selectImports
方法返回的数组就会通过@Import
注解注入到Spring
容器中。
无独有偶,ImportBeanDefinitionRegistrar
接口也为我们提供了注入bean
的方法。
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
......
}
我们点击AspectJAutoProxyRegistrar
类,发现它实现了ImportBeanDefinitionRegistrar
接口,它的registerBeanDefinitions
方法便是注入bean
的过程,可以参考下。
如果觉得源代码比较难懂,可以看一下我们自定义的类
@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);
// 注册 Bean,并指定bean的名称和类型
registry.registerBeanDefinition("teacher", tDefinition);
}
}
}
这样我们就把Teacher
类注入到Spring
容器中了。
提到FactoryBean
,就不得不与BeanFactory
比较一番。
BeanFactory
: 是Factory
,IOC
容器或者对象工厂,所有的Bean
都由它进行管理FactoryBean
: 是Bean
,是一个能产生或者修饰对象生成的工厂Bean
,实现与工厂模式和修饰器模式类似
那么FactoryBean
是如何实现bean
注入的呢?
先定义实现了FactoryBean
接口的类
public class TeacherFactoryBean implements FactoryBean<Teacher> {
/**
* 返回此工厂管理的对象实例
**/
@Override
public Teacher getObject() throws Exception {
return new Teacher();
}
/**
* 返回此 FactoryBean 创建的对象的类型
**/
@Override
public Class<?> getObjectType() {
return Teacher.class;
}
}
然后通过 @Configuration + @Bean的方式将TeacherFactoryBean
加入到容器中
@Configuration
public class MyConfig {
@Bean
public TeacherFactoryBean teacherFactoryBean(){
return new TeacherFactoryBean();
}
}
注意:我们没有向容器中注入Teacher
, 而是直接注入的TeacherFactoryBean
,然后从容器中拿Teacher
这个类型的bean
,成功运行。
看到这个接口,不知道对于翻看过Spring
源码的你来说熟不熟悉。如果不熟悉的话请往下看,要是熟悉的话就再看一遍吧