直接切入正题,本文适合用过了解过相关Spring,SpringBoot的童靴
SpringBoot提供了三大特性:
组件自动装配:Web MVC,Web Flux,JDBC等
嵌入式Web容器:Tomcat,Jetty以及Undertow
生产准备特性:指标,健康检查,外部化配置(不用写代码来调节用户的行为如容器的端口server:8081)等
组件自动装配
概念
SpringBoot中默认没有激活①,激活后同时需要相应的配置来进行映射②,该文件即可以是框架实现也可以用户自定义实现,最后他的实现的文件③
①激活:@EnableAutoConfiguration
②配置:/META-INF/spring.factories 相对于ClassPath的目录(META-INF指一个源信息目录(META为源的意思,factoriesactories类似于工厂的机制,即key,value的形式))
③实现:XXXAutoConfiguration (如JDBCAutoConfiguration等等)
样例
https://start.spring.io/ 来创建一个工程
①在SpringBoot启动项中我们只能看到@SpringBootApplication,我们进去一探究竟,这里使用了模式注解后面会进行介绍
@SpringBootApplication
public class ImoocApplication {
public static void main(String[] args) {
SpringApplication.run(ImoocApplication.class, args);
}
}
点进@SpringBootApplication
②③我们搜索下spring.factories
可以看到springBoot中各种模块的装配,各个模块的value命名规则相同
spring启动的时候会读取配置文件装配上各个bean,比如WebMvcAutoConfiguration,可以看到因为没有引入依赖这个,所以这个工程不会被装配起来(需要引入starter包)。所以官网上建立的工程直接启动的时候就会直接结束了因为不是Web工程
嵌入式Web容器
Web Servlet :Tomcat,Jetty和Undertow Servlet容器
Web Reactive:Netty Web Server Reactive容器
在SpringBoot编程中,两种容器的选择其实没有太大的区别
生产准备特性
主要为一些功能性的特性。WebFlux,WebMVC,JDBC都是为开发做准备的,为生产做准备的如下:
指标:/actuator/metrics 监控和管理应用的一些特性如metrics信息(如CPU,内存,磁盘利用率)
健康检查:/actuator/health 如磁盘DB等的一些检查
外部化配置:/actuator/configprops 通过修改配置文件来改变应用的行为,这个有点像实际项目中,很多参数调节为可配的(如yaml,properties文件里的参数,通过读取文件内容动态修改程序的规则),SpringBoot该功能源于Spring Framework,只是名字改了下
--------------------------------------------------------------------------
接下来我们来讨论下SpringBoot的Web应用
传统的Servlet的应用,首先是一些相关的组件,然后如何将组件注册进来,在SpringBoot中我们允许把Servlet变为一个bean注入进来
Servlet组件:Servlet,Filter,Listener(事件监听者模式的实现)
Servlet注册:Servlet注解(Servlet3.0提供的特性),Spring Bean,RegistrationBean
异步非阻塞:异步Servlet(Servlet3.0提供的特性),非阻塞Servlet(Servlet3.1提供的特性)
传统的Servlet的应用
传统Servlet应用需要引入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
注:SpringBoot的自动装配是以Spring-Starter的方式来进行的,其中都为org.springframework.boot
我们引入进包后,可以发现依赖中有子依赖,加入web后可以看到装配的bean可用了
因为当你的依赖进来时,ClassPath上会有相应的类,条件满足自动装配,启动服务,我们也会看到Tomcat相关日志,这也与上面红色表示中引入Tomcat包有关联
2020-06-13 16:41:57.244 INFO 158980 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-06-13 16:41:57.378 INFO 158980 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2020-06-13 16:41:57.427 INFO 158980 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-06-13 16:41:57.430 INFO 158980 --- [ main] c.e.I.ImoocSpringProjectApplication : Started ImoocSpringProjectApplication in 2.039 seconds (JVM running for 2.43)
Servlet注册
我们在SpringBoot中使用传统Servlet定义方式,主要分三个步骤
实现:实现类为MyServlet,需要继承HttpServlet,并覆盖doGet方法。且Servlet3.0需要加上@WebServlet注解来进行导入
URL映射:@WebServlet中的urlPatterns参数
注册:@ServletComponentScan,basePackages填写类的package信息
@WebServlet(urlPatterns = "/test")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello World");
}
}
@SpringBootApplication
@ServletComponentScan(basePackages = "com.example.ImoocSpringProject.web.servlet")
public class ImoocApplication {
public static void main(String[] args) {
SpringApplication.run(ImoocApplication.class, args);
}
}
前端请求
异步非阻塞
异步Servlet
注意事项点:
(1)使用javax.servlet.ServletRequest#startAsync()
javax.servlet.AsyncContext
(2)@WebServlet中需要标注asyncSupported参数才可支持异步,否则前端报错
(3)在获取异步上下文asyncContext后需要告诉他结束否则前端超时
@WebServlet(urlPatterns = "/test",asyncSupported = true)
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
AsyncContext asyncContext=req.startAsync();
asyncContext.start(()->{
try {
resp.getWriter().println("Hello World");
//触发完成,否则前端访问不成功会超时
asyncContext.complete();
} catch (IOException e) {
e.printStackTrace();
//增加日志说明
}
});
}
}
其他不变运行结果相同
--------------------------------------------------------------------------
接下来我们来讨论下SpringBootWeb应用下的MVC
SpringWebMVC应用
Web MVC视图:模板引擎(如Thymeleaf),内容协商,异常处理(SpringBoot中的异常与Spring有所区别)
Web MVC REST:以json或xml为例下的资源服务(rest操作符如get/post请求下后面问题),资源跨域,服务发现等
WebMVC视图
涉及两个接口View和ViewResolver
ViewResolver意思为视图处理器,处理器有一个处理方法resolveViewName,入参如viewName视图名称和local位置。
返回值为View视图,而作为视图只关心它的渲染render,渲染则需要上下文变量Map model,入参request,出参则为输出的内容response,具体实现还是由render去实现
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
public interface View {
...
void render(@Nullable Map model, HttpServletRequest request, HttpServletResponse response)throws Exception;
}
每一种模板引擎(Thymeleaf,Freemarker,JSP),对应不同的View和ViewResolver实现
当一个应用中有多个模板引擎,这时就会涉及内容协商(ContentNegotiationConfigurer,ContentNegotiationStrategy,ContentNegotiatingViewResolver),内容协商会帮你选择最优的模板引擎进行渲染
处理中的异常会用
@ExceptionHandler:(如SpringBoot中会使用HandlerExceptionResolver来处理异常功能,其中该注解是由其子类ExceptionHandlerExceptionResolver来实现的)
BasicErrorController:(如页面上的404返回就是该类参与)
Web MVC REST
首先是资源服务的处理@RequestMapping,@ResponseBody,@RequestBody
@RequestMapping在spring4.3之后又有了相应专门的拓展
其次是资源跨域
两种方案@CrossOrigin,WebMvcConfurer#addCorsMappings
SpringBoot重拾来自Spring Framework方法
最后服务发现
如使用IEDA中启动SpringBoot项目可以搜索“Mapped”,可以发现相应url映射,即服务的发现mapping
--------------------------------------------------------------------------
接下来我们来讨论MVC下的核心架构
如
(1)DispatcherServlet(它会把不同的请求转发到对应的controller中),(2)HandlerMapping(与controller中的映射即通过HandlerMapping,hander即方法,mapping即方法上唯一的url映射),
(3)HandlerAdapter(用来转换,即将上面方法抽象的东西,转化为具体内部实现来进行映射)
(4)ViewResolver(处理完后返回model-and-View的东西,给ViewResolver处理)
最后生成视图或者rest应用第四部后面会发生变化如返回json
--------------------------------------------------------------------------
接下来我们来讨论Spring Web Flux 应用
这个特性是Spring5开始支持,是对Servlet的一个补充,从传统同步阻塞式编程变为异步非阻塞。
这里会涉及到一些基础:
Reactor基础:java Lambda,Mono,Flux
Web Flux核心:Web MVC注解(可以与其兼容如@Controller,@RequestMapping等),函数式声明(用condition进行判断丰富拓展),异步非阻塞(Servlet3.1+,提高系统的吞吐量)
使用场景:页面渲染和REST应用
这里我们给出一个老外测试的性能案例
https://blog.ippon.tech/spring-5-webflux-performance-tests/
Reactive并没有提升多大性能,因为Reactive提升系统的吞吐量,不代表快,后面会介绍。
实践
想要使用WebFlux等,你可能需要切换Web容器,自定义Reactive Web Server等,我们从基础开始
(1)Tomcat→Jetty
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
启动Application
(2)Servlet→WebFlux
需要去掉之前的web-starter,添加webflux-starter
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
--------------------------------------------------------------------------
接下来我们来讨论数据相关的特性
关系型数据
JDBC:数据源,JDBCTemplate,自动装配
JPA(java持久成api):实体映射关系,实体操作,自动装配
事务:Spring事务抽象,JDBC事务处理,自动装配
JDBC
依赖也是starter
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
jdbc包导入注意,在spring2.0之后jdbc会使用性能非常好的连接池HikariCP,大家可以感兴趣了解下
数据源接口是java中特性 javax.sql.DataSource,JdbcTemplate其实是jdbc中模板的实现,我们以JdbcTemplate的自动装配为例:
@ConditionalOnClass为前提条件,因为JdbcTemplate依赖于DataSource所以看源码,我们会发现@AutoConfigureAfter,在datasource注入之后
结合之前所说在spring.factories中会存在相应的AutoConfiguration
启动项目,会发现一些错误,当引入该模块你需要连接数据库,比如它说的embedded database嵌入式数据库放在classpath下面;或者传统Jdbc的方式
JPA
依赖中jpa与jdbc不冲突,因为它需要jdbc的dataSource(dataSource会在DataSourceAutoConfiguration上自动装配,然后产生数据库连接池,然后保存,此阶段jpa也是需要的)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
jpa中的一些实现如Hibernate,因为jpa只是一套规范标准,具体实现提供方如Hibernate,同时你也能看到间接引用了jdbc,此时会重复
实体映射关系中与数据库相似,数据库通常是外界或者表的关系进行关联,在JPA中叫做实体,其操作通过javax.persistence.EntityManager进行实现,如实现基本的一些增删改查的一些接口实现
public interface EntityManager {
void persist(Object var1); //类似于存
T merge(T var1); //类似于更新void remove(Object var1); //类似于删除 T find(Class var1, Object var2); //类似于查找,var1即bean,var2即查找的主键或者某个字段 T find(Class var1, Object var2, Map<String, Object> var3); T find(Class var1, Object var2, LockModeType var3); T find(Class var1, Object var2, LockModeType var3, Map<String, Object> var4); T getReference(Class var1, Object var2);
...
}
自动装配则使用HibernateJpaAutoConfiguration,依赖DataSourceAutoConfiguration
事务
依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
dependency>
Spring事务的抽象实现接口为PlatformTransactionManager,该接口主要提供两种事务的抽象,第一个Jta分布式事务,DataSource数据库事务
自动装配是在TransactionAutoConfiguration中。同时他需要类@ConditionalOnClass(PlatformTransactionManager.class),该类在我们上面引用的tx jar包中
--------------------------------------------------------------------------
接下来我们来讨SpringBoot论拓展功能
SpringBoot应用中,从下面三个点分析
SpringApplication:失败分析,应用特性,事件监听(重点)
SpringBoot配置:外部化配置(主要依赖Spring Framework中api),Profile,配置属性
SpringBoot Starter:Starter开发
这里提一下SpringApplication中有一种Fluent API的特性,在SpringBoot启动项中我们使用Builder模式进行重建。
@SpringBootApplication(scanBasePackages = "com.example")
public class DemoApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(DemoApplication.class)
.run(args);
//SpringApplication.run(DemoApplication.class, args);
}
}
Fluent API即当我们不想要web工程时可以.web()进行选择,如不使用NONE,REACTIVE模式等。需要什么则进行.添加
同时SpringBoot的配置属性中外部化配置在2.0中变为使用ConfigurationProperty,在传统的key,value下引入了新的配置属性origin,即属性的来源,用来跟踪配置从哪里来,因为SpringBoot中配置非常多,为了解决配置是从具体什么地方来的
@Profile即代表条件的意思,能力较弱,不如使用@Conditional
配置属性PropertySources,是Spring能力;会有两种实现方式接口和注解
--------------------------------------------------------------------------
接下来我们来讨SpringBoot运维管理
SpringBoot Actuator
端点的方式:各类Web EndPoints和JMX EndPoints
健康检查:Health,HealthIndicator
指标:内建Metrics,自定义Metrics
需要引入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
具体参考SpringBoot文档
后记:后面会分别对这些中重点的部分进行详细的探讨