浅谈Spring中的一些技术

一、前言

在学习Spring或者SpringBoot原理时,心里要始终想着一件事情:只有把对象(也可以称之为组件、bean)放入到Spring的IOC容器中,才能使用Spring为我们提供的一系列强大功能。

二、一些说明

在学习Spring或者SpringBoot的过程中,我们经常听到 <组件>、<bean>这两个词,那么这两个词指代什么呢?个人的一些浅显理解:你就把这两个词理解成普通的Java对象就可以,比如你自己写的一个User类(你创建一个User对象,把其添加到Spring的IOC容器中,这个User对象就可以称之为一个User组件,或者称之为一个bean组件),你创建了第三方的某个类的对象,然后把其放入Spring的IOC容器中,就可以这样说:我在容器中放入了某个类的一个组件。

三、添加组件(bean)到容器中

如何把自己写的对象(或称之为组件,或称之为bean)或者第三方组件放入到IOC容器中呢?有四种方式:

3.1 包扫描 + 组件标注注解

即在某个配置类(配置类指的是标注了@Configuration注解的类)上标注@ComponentScan(basePackages={"包名1","包名2"}), 然后再类上标注@Component、@Repository、@Service、@Controller等注解的方式。

注意:此种方式局限于想要把自己写的类添加到IOC容器中。

3.2 @Bean

在配置类中使用@Bean的方式给容器中导入一个组件。使用这种方式可以把第三方包中的组件导入到IOC容器中,让IOC容器帮我们管理第三方的组件,当需要使用第三方组件时,再从容器中获取就好了。

3.3 @Import

3.3.1 @Import导入普通组件

@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。比如你创建了一个com.example.User类,使用@Import(value={User.class})就可以给容器中导入一个User组件。

3.3.2 @Import(value={xxxImportSelector.class})

可以实现ImportSelector接口,然后实现接口中的 selectImports方法,让这个方法返回需要导入容器中的组件的全限定类名数组。然后使用 @Import(value = {MyImportSelector.class})的方式可以一次性给容器中导入多个组件。

3.3.3 @Import(value={xxxImportBeanDefinitionRegistrar.class})

可以实现 ImportBeanDefinitionRegistrar 接口,然后实现接口中的 registerBeanDefinitions 方法,在方法的实现中手动注册 BeanDefinition 到IOC容器中。然后使用 @Import(value = {MyImportBeanDefinitionRegistrar.class})的方式给容器中导入组件。

3.4 使用FactoryBean

使用Spring提供的 FactoryBean(工厂Bean):

1)、默认获取到的是工厂bean调用getObject创建的对象

2)、要获取工厂Bean本身,我们需要给id前面加一个&前缀

二、单例还是多例

导入到容器中的组件,是单例还是多例呢?默认存放在Spring IOC容器中的组件都是单例的,当然我们也可以控制放入IOC容器中的组件是否单例还是多例。比如在使用 @Bean给容器中放入组件时,可以使用 @Scope(value = "singleton") 或者 @Scope(value = "prototype") 控制你要放入到IOC容器中的组件是否为单例。

三、Bean的生命周期

把组件放入到IOC容器中,就意味着我们把一个对象交给了Spring 的IOC容器管理。每一个组件在IOC容器中都是有生命周期的。组件的生命周期概况起来有如下四个阶段:

复制代码
bean的对象创建(调用bean的构造方法创建对象) -> 给bean进行属性赋值 -> 后置处理器的前置处理方法 -> 初始化 -> 后置处理器的后置处理方法 -> 销毁bean

bean对象创建,主要分为单实例bean的创建和多实例bean的创建:

1)单实例bean:在容器启动时创建所有的单实例bean;

2)多实例bean:在每次获取bean组件时,都会创建新的bean。

bean的属性赋值(见第四小节)

后置处理器的前置处理方法 -> 初始化 -> 后置处理器的后置处理方法 -> 销毁bean

复制代码
针对bean的初始化前后,还可以做一些其他工作,有BeanPostProcessor来完成.

BeanPostProcessor.postProcessBeforeInitialization (在初始化方法执行之前进行后置处理工作
<调用初始化方法>
BeanPostProcessor.postProcessAfterInitialization  (在初始化方法执行之后进行后置处理工作
<销毁bean>

其中bean的初始化和销毁方式有如下几种:

复制代码
(a) 通过@Bean注解指定 initMethod和destroyMethod 
(b) 让bean实现 InitializingBean 和 DisposableBean 接口
(c) JRS250提供的注解:
     @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
     @PreDestroy:在容器销毁bean之前通知我们进行清理工作 

四、属性赋值

在给组件中的属性赋值时,我们可以使用@Value的方法,@Value可以写普通字符串,SpEL表达式,可以从配置文件中通过${xxx}的方式取值,需要把配置文件通过 @PropertySource引入进来,使配置文件中k/v键值对被Spring读取到运行时环境变量里面;

五、组件之间的依赖注入

组件之间会有依赖关系,比如xxxService依赖xxxDao组件,可以Spring或Java规范为我们提供的组件注入功能,如@Autowired + @Qualifier指定要注入的组件,Java规范里面的@Resource和@Inject注解。

疑问1:为什么标注了@Autowired注解之后,组件就自动给其属性进行了赋值呢?这就是组件注入的后置处理器(xxxPostProcessor)在底层进行工作。

疑问2:Spring在底层给IOC容器中注入了一些有用的组件,如果我们想把这些底层组件注入到我们自定义的组件中该怎么做呢?可以让自定义的组件实现Spring给我们提供的各种xxxAware接口,然后Spring在把我们自定义的组件添加到容器中的时候,会应用xxxAware对应的后置处理器,把其底层的组件注入到我们自定义的组件中。

疑问3:我还想让一些组件在特定条件下才注入到容器中,而不是无脑往容器中注入,该怎么办呢?可以使用@Import的,指定自定义的实现了ImportSelector接口的类,在自定义的实现类中进行进行条件控制,从而控制是否要导入哪些组件,还可以通过指定@Condition注解,需要我们自己实现 Condition接口,在接口中判断是否要导入某些组件。

相关推荐
魔道不误砍柴功几秒前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_234几秒前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨4 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java