闲话
最近简单的学习了一下 springboot,记录下自己的一些学习心得,没有体系,没有深入讲解,基本只涉及到一些概念级,权当随笔记录。。他日翻看时,希望能有所帮助,肯定也有理解不到位之处,还请大家指正
为什么会有 spring
学习一项技术,我们首先需要弄明白,这项技术出现的意义是什么,它能够解决什么场景问题,知其然,必先知其所以然
- 一般大型的企业级java应用,都会包含很多的内容模块,包括各种接口、逻辑、页面、存储等,它们之间可能存在复杂的依赖关系,如何对他们进行统一的管理和调度是影响开发效率的重要因素
- 容器化管理我们的应用程序,是上述问题的通用解决方案:应用的开发针对 pojo、bean或者组件,然后交由容器去负责组装调用,实现解耦
- 在spring之前,java官方推荐的解决方案是 EJB,但是 EJB 是一个非常重型的框架,上手成本很高,对于中小型的企业应用支持并友好
- 于是民间组织在 EJB 的容器化管理的基础上,创建了 spring,相对来说更轻量级的开发框架,简单对比 EJB 的话,他俩的优缺点:
- EJB 面向的是组件级容器管理,spring 则是 bean 的管理,更细粒度,对于开发的理解更容易
- EJB 和 spring 都支持 ioc 和 aop,但是 spring 封装的功能更强大简单,早期spring只支持xml配置方式,ejb只支持注解方式,随着发展,两家各取所长,目前都支持 xml 和注解的方式
- EJB 对于事务的封装处理更强大,EJB 对于分布式应用的开发,具有天然的优势,因为它的最初设计就是为了解决分布式场景
- 对于第三方工具的集成能力,spring更强大一些
- spring 的出现,是为了简化 EJB 的使用复杂度,而EJB的出现是为了给大型企业级应用开发提供一个通用的
容器化管理组件、统一的数据存储api、事务和分布式等复杂业务的封装处理
等核心技术难点的解决方案,如果没有 EJB 和 spring,我们的应用都需要自己去考虑这些复杂场景的处理方案、以及很难处理复杂组件bean的依赖关系带来的开发测试难题 - spring 和 EJB 是两种解决方案,在选择时可以根据自己的需求进行选择,大型国企的应用使用 EJB 更合适,而一般的互联网项目适用 Spring 会更方便快捷
spring boot 和 spring
spring boot 是 spring 的一种简化版,spring 需要大量的配置才能够启动一个项目,而spring boot 只需要简单的配置几个 starter 即可
主要内容
数据库
基本概念
- jdbc:java database connectivity,java官方提供的各个数据库连接的统一标准,相当于java程序连接数据库的驱动,所有数据库厂商需要实现自己的数据库驱动,例如
com.mysql.jdbc
就是 mysql 实现了 jdbc 规范的驱动,开发人员引入这个包就可以通过统一的 jdbc 接口,与mysql服务器创建连接- orm:Object Relational Mapping,对象关系映射,orm 是一种思想,表示java对象与数据库表结构之间的一种转换关系
- jpa: Java Persistence API,是java的一种持久化规范,定义了一套实现orm思想的统一接口,可以认为它是 orm 的一种具体落地,而很多知名的持久化框架是基于这个规范,例如hibernate
hibernate 和 mybatis
- 这两个算是最知名的持久化框架,但是 hibernate 是基于 jpa 规范的一个标准 orm 框架,而 mybatis 不完全是一个 orm 框架
- hibernate 可以自动将java对象的变化,对应到 sql 的语句,进行增删改查,开发人员对于基本的需求都不需要写 sql 语句,会由 hibernate 自动生成,所以说它是一个标准 orm 框架;也是因为hibernate的话不需要自己写sql,因此如果想做查询的优化,也会更难
- mybatis 需要开发人员自己写 sql 语句去实现具体的 dao 操作,框架可以帮我们做的事情是把 dao 接口与 sql 语句映射起来,虽然开发人员要写 sql,但是不需要去处理 resultSet 这些结果,可以由查询结果自动转化为pojo类,同时提供一些缓存等辅助功能
- 简单总结的话,hibernate更笨重,但是功能强大,上手复杂,适合大型项目;mysql上手简单,便于做sql优化,但是提供的功能不多
http相关
http的基本原理就不多讲了,java原生封装的最常见的http开发方式是 jsp + servlet,然后将 servlet 打包成 war 包之后运行在web容器中,当然也可以使用内置的容器例如tomcat、jetty这些。容器的作用是管理每个servlet的调用关系,例如 abc/123 这个 uri 对应的是具体哪个 servlet
在web开发中,我们经常会使用一些http的开发框架,例如最经典的 struts 和 spring mvc,它们的作用和主要对比如下
- 框架可以帮我们封装好底层的映射关系,并且根据我们选择的容器自动转化为该容器的设置方式,例如在业务中,我们只需要编写某个接口的 uri 是 "test/123",那么我们容器具体选择tomcat 还是Jboss 等,可以交给框架帮我们处理映射
- 框架可以在底层封装一些统一的拦截处理方式,例如在收到请求后统一输出一个日志,以及处理后的一些跳转等,例如跳转返回一个jsp页面,不需要业务开发每次单独处理,只需要告诉框架返回哪个jsp即可
- 框架可以统一封装一些异常情况的处理,例如在报错后的跳转、日志输出或者统一返回格式等
- struts 和 spring mvc 的主要区别在于,struts是一个类级别的拦截,spring mvc是一个方法级别的拦截,同时spring mvc因为是spring的亲儿子,因此兼容更好
spring 5之后推出的web flux是一种新式的http编程方式,传说中的reactive编程风格,响应式http处理方式(原来针对HttpServletRequest 和 HttpServletResponse 的编程,现在针对 mono 和 flux),底层是 nio 的处理模式,理论来说性能更好,但是目前应用还不广泛。。目前主流还是使用 spring mvc 的阻塞式 io + tomcat 容器等,效率其实也够用,不够就加机器吧。。。
ioc容器
Inversion of Control
控制反转,原理部分可以参考这篇文章,讲的还是比较通俗易懂,下面简单罗列下 spring 中使用容器的几个常用的标注
- 定义bean的方式:@Bean @Component @Service @Repository @Controller,其中
@Bean
主要用在方法上,通过方法返回一个实例bean,可以使用@Scope
来定义bean的生成方式(Singleton是单例,Prototype是每次调用都会生成一个新的,Request是每个http请求产生一个新的等等。。),还可以使用@Lazy
定义一个bean为懒加载,用到的时候才去生成 - 注入的方式:@Autowired @Inject @Resource,可以使用 @Required 表示某个对象是否必须有,使用 @Qualifier 可以指定注入的bean的名字
- 正常情况下,是没办法拿到spring封装好的ApplicationContext去获取注入的bean的,如果有这种需求可以参考下面代码,定义一个 SpringUtils 之后,可以通过 getObject 方法去获取注入的bean
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContextParam) throws BeansException {
applicationContext = applicationContextParam;
}
public static Object getObject(String id) {
return applicationContext.getBean(id);
}
public static <T> T getObject(Class<T> tClass) {
return applicationContext.getBean(tClass);
}
}
aop相关
Aspect Oriented Programming,面向切面编程,是OOP之后的另一种编程思想,什么叫切面:例如所有http请求的处理结束后,这就是一个切面,我们可以在这个切面进行一些日志记录,这就是面向切面编程
- 面向切面编程的原理,基本都是底层封装了一套动态代理的框架,将切面的一些动作动态绑定到被调用的类和方法上,之后在每次绑定的点上,额外执行定义的切面动作
- AOP 最典型的应用在于 日志记录、异常处理封装、参数拦截校验、参数统一注入等
- 目前最常见的两种AOP框架,分别是 AspectJ 和 Spring AOP,他们的对比在可以参考这里,
特别解释一下:spring aop功能不如aspectJ强大,它有两种使用的方式,第一是使用xml进行定义,第二是使用 AspectJ 风格的注解定义
事务
事务指的是一笔业务的要不全部失败、要不全部成功,这里的成功失败都是相对于写入到数据库而言。
- spring 并没有直接实现数据库的事务管理,它只是封装了统一的接口及定义方式,然后具体的实现交给了Hibernate或者其他JPA框架去进行处理,它们会实现具体的 rollback 和 commit 方法,供 spring 的事务底层代码统一调用,关系图如下所示
- 在业务的具体使用中,需要首先将 DataSource 注入到 TransactionManagementConfigurer 中,开启事务后,使用 @Transactional 对 service 中的方法进行注解即可,那么该方法涉及到的注入了的 DataSource 就是事务安全的,但是内存级的数据不保证事务安全性
- spring 的事务和数据库的事务隔离级别是两个概念,需要区分
- mysql 数据库的 Myism 存储引擎是不支持事务的,如果数据表是 Myism 的存储引擎,无论如何设置 spring 的注解,都无法使用事务!!!
异步任务和定时任务
spring 封装了一套异步线程池和起定时任务的方式
- 异步任务,通过 @EnableAsync 注解来开启异步任务功能,可以定义一个 AsyncConfigurer 来具体设置异步任务池相关的一些配置
- 之后通过 @Async 标注某个方法是异步方法,在业务中,每次调用该方法的时候,都是扔到异步线程池去执行,而不会同步等待该方法的返回
- Spring 的异步框架,也封装了一些对于异步任务的监控、Future结果的获取等功能
- @EnableScheduling 开启定时任务功能,@Scheduled 注解可以定义具体的定时任务,类似于crontab的定义和使用方式,这部分可以被 quartz 代替
单元测试
Spring 的单元测试,封装了基于 JUnit 和 TestNG 两种的风格,但是对于 JUnit 的支持更加完善,推荐使用 JUnit
- 具体的使用方法可以参考这个网站
- SpringRunner 和 SpringJUnit4ClassRunner 是一样的,SpringBootTest是指定本测试类为SpringBoot的启动,类似于SpringBootApplication,而如果使用@ContextConfiguration 则是去指定本次测试主动去加载的beans,可以通过xml或者class等去指定,显然SpringBootTest使用更简单一些,但是笨重一点