Bean 装配,从 Spring 到 Spring Boot
本文首发于我的个人博客,Bean装配,从Spring到Spring Boot ,欢迎访问!
本文旨在厘清从Spring 到Spring Boot过程中,Bean装配的过程。
自从用上Spring Boot,真的是一直用一直爽,已经完全无法直视之前Spring的代码了。约定大于配置的设计理念,使得其不需要太多的配置就能开箱即用。但是由于其便捷性,也就意味着掩盖了许多细节的部分,使得直接学习Spring Boot的开发者只会用,而不清楚内部的实现流程。最近刚好有空,重新回顾了一下Spring的相关内容,并且梳理了有关于Bean装配的一些用法,描述从过去的Spring开发,到现在的Spring开发 Boot在Bean装配上的变化和进步。
从SSM的集成谈到Bean的装配
在学习初期,我想每个人都会去看一些博客,例如“Spring Spring MVC Mybatis整合”。一步一步整合出一个ssm项目。那个时候的我是没有什么概念的,完全就是,跟着步骤走,新建配置文件,把内容贴进来,然后run,一个基本的脚手架项目就出来了。回顾一下,基本是以下几步:
1. 建立maven web项目,并在pom.xml中添加依赖。
2. 配置web.xml,引入spring-.xml配置文件。
3. 配置若干个spring-.xml文件,例如自动扫包、静态资源映射、默认视图解析器、数据库连接池等等。
4. 写业务逻辑代码(dao、services、controller)
后期可能需要用到文件上传了,再去xml中配置相关的节点。在开发中,基本也是遵循一个模式——三层架构,面向接口编程。类如果是Controller的就加一个@Controller;是Services的就加一个@Services注解,然后就可以愉快的写业务逻辑了。
Spring是什么?那个时候的理解,像这样子配置一下,再加上几个注解,就是Spring。或者说,Spring就是这么用的。
随着学习的深入,对Spring就有更深的理解了。在Spring当中,一切皆为Bean。可以说Bean是组成一个Spring应用的基本单位。Spring(这里是狭义的概念,指Spring Core)中最核心部分就是对Bean的管理。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.zjut.ssm.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="20971500"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="resolveLazily" value="true"/>
</bean>
</beans>
让我们再次看一下Spring MVC的配置文件,除了一些参数外,还有两个bean节点,注入InternalResourceViewResolver来处理视图,注入CommonsMultipartResolver来处理文件上传。这个时候,如果需要集成Mybatis一起工作,类似的,注入相关的Bean就可以了。Mybatis最核心的Bean就是SqlSessionFactory,通过创建Session来进行数据库的操作。在不使用Spring时,可以通过加载XML,读入数据库信息,进行创建。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
显然,上面的代码是不符合Spring的思想的。为了达到松耦合,高内聚,尽可能不直接去new一个实例,而是通过DI的方式,来注入bean,由Spring IoC容器进行管理。Mybatis官方给到一个MyBatis-Spring包,只需添加下面的Bean就可以组织Mybatis进行工作了(创建sqlSessionFactory,打开Session等工作),关于Mybatis的内容,这里不展开了。
<beans>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value=""/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value=""/>
</bean>
</beans>
那么,现在我们就知道了,通过XML配置Spring,集成各种框架的实质,就是Bean装配的。每一个框架都是由N多个Bean构成的,如果需要使用它,就必须根据框架的要求装配相应的Bean。装配成功的Bean由Spring IoC容器统一管理,就能够正常进行工作。
Bean的装配
具体Bean的装配方式,发展到现在也已经有很多种了,从过去的XML到Java Config,再到现在Spring Boot的Auto Configuration,是一种不断简化,不断清晰的过程。
Bean装配一般分为三步:注册、扫描、注入。
由XML到Java Config
XML配置Bean早已成为明日黄花了,目前更常见的是使用Java Config和注解来进行Bean的装配。当然,偶尔也能看到它的身影,例如用于ssm框架的集成的spring-*.xml。
Java Config的优势如下:
- Java是类型安全的,如果装配过程中如果出现问题,编译器会直接反馈。
- XML配置文件会随着配置的增加而越来越大,不易维护管理。
- Java Config更易于重构和搜索Bean。
因此下面主要讲都是基于Java的配置方法。基本流程如下:
// 注册
@Configuration
public class BeanConfiguration {
@Bean
public AtomicInteger count() {
return new AtomicInteger();
}
}
//或者
@Componment
public class Foo{}
// 扫描
@ComponentScan(basePackages={})
@Configuration
public class BeanConfiguration {}
// 注入
@Autowired
private AtomicInteger c;
下面详细展开。
Java Config注册Bean,主要分为两类,注册非源码的Bean和注册源码的Bean。
注册非源码的Bean
非源码的Bean,指的是我们无法去编辑的代码,主要是引入外部框架或依赖,或者使用Spring的一些Bean。这些Bean的配置一般采用Java文件的形式进行声明。
新建一个使用@Configuration修饰的配置类,然后使用@Bean修饰需要创建Bean的方法,具体的可指定value和name值(两者等同)。示例如下:
@Configuration
public class BeanConfiguration {
@Scope("prototype")
@Bean(value = "uploadThreadPool")
public ExecutorService downloadThreadPool() {
return Executors.newFixedThreadPool(10);
}
}
其中需要注意的是:
- Bean默认是以单例的形式创建的,整个应用中,只创建一个实例。如果需要,每次注入都创建一个新的实例,可添加注解
@Scope("prototype")。对于这个例子而言,默认创建的线程池是单例的,在应用的任何一个地方注入后使用,用到的都是同一个线程池(全局共享);加上@Scope("prototype")后,在每一个Controller中分别注入,意味着,每一个Controller都拥有各自的线程池,各自的请求会分别提交到各自的线程池中。 - 注入多个相同类型的Bean时,手动指定name或value值加以区分。或通过
@Primary,标出首选的Bean。
注册源码的Bean
源码的Bean,指的是我们自己写的代码,一般不会以@Bean的形式装配,而是使用另外一系列具有语义的注解。(@Component、@Controller、@Service、@Repository)添加这些注解后,该类就成为Spring管理的组件类了,列出的后三个注解用的几率最高,基本已经成为样板注解了,Controller类添加@Controller,Service层实现添加@Service。
下面展示一个例子,通过Spring声明自己封装的类。
@Scope("prototype")
@Component(value = "uploadThread")
public class UploadTask implements Runnable {
private List<ByteArrayInputStream> files;
private List<String> fileNameList;
private PropertiesConfig prop = SpringUtil.getBean(PropertiesConfig.class);
// 如果直接传入MutiPartFile,文件会无法存入,因为对象传递后spring会将tmp文件缓存清楚
public UploadThread(List<ByteArrayInputStream> files, List<String> fileNameList) {
this.files = files;
this.fileNameList = fileNameList;
}
@Override
public void run() {
for (int i = 0; i < files.size(); ++i) {
String fileName = fileNameList.get(i);
String filePath = FileUtils.generatePath(prop.getImageSavePath(),fileName);
FileUtils.save(new File(filePath), files.get(i));
}
}
}
接着上面的线程池讲,这里我们实现了一个task,用来处理异步上传任务。在传统JUC中,我们一般会这么写代码:
private ExecutorService uploadThreadPool = Executors.newFixedThreadPool(10);
uploadThreadPool.submit(new UploadTask(fileCopyList, fileNameList));
在Spring中,我觉得就应该把代码写的更Spring化一些,因此添加@Component使之成为Spring Bean,并且线程非单例,添加@Scope注解。重构后的代码如下:
@Resource(name = "uploadThreadPool")
private ExecutorService uploadThreadPool;
@PostMapping("/upload")
public RestResult upload(HttpServletRequest request) {
uploadThreadPool.submit((Runnable) SpringUtils.getBean("uploadThread", fileCopyList, fileNameList));
}
Bean的注入在下节会仔细讲。其实这样写还有零一个原因,非Spring管理的Bean一般是无法直接注入Spring Bean的。如果我们需要在UploadTask中实现一些业务逻辑,可能需要注入一些Services,最好的做法就是讲UploadTask本身也注册成Spring Bean,那么在类中就能够使用@Autowired进行自动注入了。
额外需要注意的是:由于线程安全的一些原因,线程类是无法直接使用@Autowired注入Bean的。一般会采用SpringUtils.getBean()手动注入。
自动扫描
在配置类上添加 @ComponentScan注解。该注解默认会扫描该类所在的包下所有的配置类,特殊的包可以配置basePackages属性。Spring扫描到所有Bean,待注入就可以使用了。
Bean的注入
对于一些不直接使用的Bean,注册到Spring IoC容器后,我们是不需要手动去注入的。例如前面提到Mybatis的三个Bean。我们只需要根据文档进行使用,创建Mapper接口,并使用@Mapper修饰,在调用具体的查询方法时,Mybatis内部会进行Bean的注入,Open一个Session进行数据库的操作。
对于我们需要使用到的Bean,就需要注入到变量中进行使用。常用Bean注入的方式有两种。
1.注解注入(@Autowired和@Resource)。
@Autowired是Bean注入最常用的注解,默认是通过byType的方式注入的。也就是说如果包含多个相同类型的Bean,是无法直接通过@Autowired注入的。这个时候需要通过@Qualifier限定注入的Bean。或者使用@Resource。@Resource是通过byName的方式注入,直接在注解上标明Bean的name即可。
public class Foo{
// 正常字段注入
@Autowired
private AtomicInteger c;
// 正常构造器注入
private final AtomicInteger c;
@Autowired
public Foo(AtomicInteger c){this.c = c;}
// 歧义加上@Qualifier
@Autowired
@Qualifier("count")
private AtomicInteger c;
// 歧义直接使用@Resource(与前一种等同)
@Resource("count")
private AtomicInteger c;
}
2.调用Application Context的getBean方法。
推荐使用注解注入,但是在默写特殊情况下,需要使用getBean()方法来注入Bean。例如之前讲到的多线程环境。具体实现可见附录,实现ApplicationContextAware,可以封装成一个工具类。
SSM集成的Java版
由于Java Config的优势,框架集成工作很多选择不用XML配置了。例如之前在xml中配置Spring MVC的ViewResolver,在Java中就可以这样去注入:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".jsp");
return resolver;
}
}
有兴趣的,可以自己去使用Java Config集成一下,其实需要配置的东西都是一致的,只是在Java中,有的是要注册对应的Bean,有的需要实现对应的接口罢了。由XML到Java,仅仅只是配置语言的改变,真正解放程序员,提高生产力是Spring Boot。基于约定大于配置的思路,通过Auto Configuration,大规模减少了一些缺省Bean的配置工作。
Spring Boot Magic
Auto Configuration
接下来我们看一下,基于Spring Boot的SSM集成需要配置的内容。
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: fur@6289
mybatis:
type-aliases-package: com.fur.mybatis_web.mapper
mapper-locations: classpath:mapping/*.xml
用过Spring Boot的都知道,上面的是Spring Boot的配置文件application.yml。只需要在pom.xml添加对应依赖,在yml里配置必要的信息。(框架再智能也不可能知道我们的数据库信息吧
Bean 装配,从 Spring 到 Spring Boot的更多相关文章
- 【Spring】Spring中的Bean - 5、Bean的装配方式(XML、注解(Annotation)、自动装配)
Bean的装配方式 简单记录-Java EE企业级应用开发教程(Spring+Spring MVC+MyBatis)-Spring中的Bean 文章目录 Bean的装配方式 基于XML的装配 基于注解 ...
- spring:spring再总结(ioc、aop、DI等)
IOC(Inversion of Control),即"控制反转",不是一种技术而是一种思想 1.IOC的理解 Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部 ...
- 什么是bean装配?
装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起.
- 什么是 bean 装配?
装配,或 bean 装配是指在 Spring 容器中把 bean 组装到一起,前提是容器需要 知道 bean 的依赖关系,如何通过依赖注入来把它们装配到一起.
- [原创]java WEB学习笔记103:Spring学习---Spring Bean配置:基于注解的方式(基于注解配置bean,基于注解来装配bean的属性)
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- [原创]java WEB学习笔记99:Spring学习---Spring Bean配置:自动装配,配置bean之间的关系(继承/依赖),bean的作用域(singleton,prototype,web环境作用域),使用外部属性文件
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- 【Spring】Spring的bean装配
前言 bean是Spring最基础最核心的部分,Spring简化代码主要是依赖于bean,下面学习Spring中如何装配bean. 装配bean Spring在装配bean时非常灵活,其提供了三种方式 ...
- Spring系列(三) Bean装配的高级技术
profile 不同于maven的profile, spring的profile不需要重新打包, 同一个版本的包文件可以部署在不同环境的服务器上, 只需要激活对应的profile就可以切换到对应的环境 ...
- spring Boot异步操作报错误: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.self.spring.springboot.Jeep' available
我也是最近开始学习Spring Boot,在执行异步操作的时候总是汇报如下的错误: Exception in thread "main" org.springframework.b ...
随机推荐
- 腾讯PCG(后台开发) 牛客网视频面试 一面
腾讯视频面试 作为一个小渣渣记录一下,腾讯是我一直想进的公司,但其实准备的时间不是很长,也不是科班还是存在很大的劣势,记录一下找工作的经历. 首先说一下,这是我第一次视频面试,还是蛮紧张的.不过面试官 ...
- Ajax前台返回JSON数据后再Controller中直接转换成类型使用,后台接收json转成实体的方法
之前写过一篇记录文章,写的是将一个比较复杂的数据结构在前台组合起来后传递到后台. 当时并不太了解@RequestBody,也并没有使用js提供的JSON.stringify()方法 所有都是自己写的, ...
- <lable>标签
最近用各种框架的时候,发现很多平常自己写代码没注意到的标签和用法,在这里记录一下. 其实是很多细节方面需要注意的写法. <label> 定义:为input元素定义标注 label标签不会向 ...
- 超越Storm,SparkStreaming——Flink如何实现有状态的计算
流式计算分为无状态和有状态两种情况.无状态计算观察每个独立的事件,Storm就是无状态的计算框架,每一条消息来了以后和前后都没有关系,一条是一条.比如我们接收电力系统传感器的数据,当电压超过240v就 ...
- C#开发BIMFACE系列14 服务端API之批量获取转换状态详情
系列目录 [已更新最新开发文章,点击查看详细] 上一篇<C#开发BIMFACE系列13 服务端API之获取转换状态>中介绍了根据文件ID查询单个文件的转换状态. 本文介绍批量获取转 ...
- HibernateSynchronizer的安装与使用
HibernateSynchronizer的作用是自动生成hibernate配置文件,即hibernate.cfg.xml文件,映射文件,Plain Object类文件和一些基础数据库操作文件. 安装 ...
- HDU6223——2017ICPC沈阳G Infinite Fraction Path
题意: 给定一个数字串,每个位子都能向(i*i+1)%n的位子转移,输出路径上,字典序最大的,长度为n的串. 参考:https://www.cnblogs.com/mountaink/p/954144 ...
- lightoj 1145 - Dice (I)(dp+空间优化+前缀和)
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1145 题解:首先只要是dp的值只和上一个状态有关系那么就可以优化一维,然后这题 ...
- JOBDU 1108 堆栈的使用
之所以把这道题目贴出来的原因,是因为真的有几个地方要注意的 题目1108:堆栈的使用 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:10763 解决:3119 题目描述: 堆栈是一种基本的 ...
- 牛客网暑期ACM多校训练营(第二场) 题解 A run 递推 dp
链接:https://www.nowcoder.com/acm/contest/140/A来源:牛客网 White Cloud is exercising in the playground. Whi ...