目录
SpringBoot、SpringMVC、SpringCloud区别
简介
Spring Boot 简化了基于Spring的应用开发,只需要“run”就能创建一个独立的、生产级别的Spring应用。Spring Boot为Spring平台及第三方库提供开箱即用的设置(提供默认设置),这样我们就可以简单的开始。多数Spring Boot应用只需要很少的Spring配置。
我们可以使用SpringBoot创建java应用,并使用java –jar 启动它,或者采用传统的war部署方式。
SpringBoot、SpringMVC、SpringCloud区别
- SpringBoot 是一个快速开发的框架,能够快速的整合第三方框架,简化XML配置,全部采用注解形式,内置Web容器,帮助开发者能够实现快速开发,SpringBoot的Web组件 默认集成的是SpringMVC框架。SpringBoot并不是微服务框架。
- SpringMVC是控制层。
-
SpringCloud依赖与SpringBoot组件,使用SpringMVC编写Http协议接口,同时SpringCloud是一套完整的微服务解决框架。
快速入门
1、用IntelliJ IDEA新建一个项目
2、编写HelloWorld
//1.在上加上RestController 表示修饰该Controller所有的方法返回JSON格式
//直接可以编写Restful接口
@RestController
//2.作用在于让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置
@EnableAutoConfiguration
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String index(){
return "Hello World";
}
}
//@SpringBootApplication 被 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解
// 所修饰,换言之 Springboot 提供了统一的注解来替代以上三个注解
//扫包范围:在启动类上加上@SpringBootApplication注解,当前包下或者子包下所有的类都可以扫到。
@SpringBootApplication
public class SpringbootHelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootHelloworldApplication.class, args);
}
}
访问页面:http://127.0.0.1:8080/helloWorld
Web开发
静态资源访问
Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则:
- /static
- /public
- /resources
-
/META-INF/resources
例如:我们可以在src/main/resources目录下创建static,在该位置放置一个图片文件。启动程序后,直接访问根路径加图片名称,就能显示图片。
模板引擎
在动态HTML实现上Spring Boot依然可以完美胜任,并且提供了多种模板引擎的默认配置支持,所以在推荐的模板引擎下,我们可以很快的上手开发动态网站。
Spring Boot提供了默认配置的模板引擎主要有以下几种:
- Thymeleaf
- FreeMarker
- Velocity
- Groovy
- Mustache
当你使用上述模板引擎中的任何一个,它们默认的模板配置路径为:src/main/resources/templates。
使用FreeMarker模板引擎渲染Web视图
1、pom文件引入
<!-- 引入freeMarker的依赖包. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2、在配置文件中增加freeMarker相关配置
spring.freemarker.allow-request-override=false
spring.freemarker.cache=true
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=classpath:/templates/
3、页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
${name}:欢迎你
</body>
</html>
4、后端代码
//1.在上加上RestController 表示修饰该Controller所有的方法返回JSON格式
//直接可以编写Restful接口
@RestController
//2.作用在于让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置
@EnableAutoConfiguration
public class HelloWorldController {
@RequestMapping("/helloWorld")
public ModelAndView index(Map<String,Object> map){
map.put("name","demo");
return new ModelAndView("index");
}
}
这里面有一个坑,由于加上了@RestController注解,所有方法都会返回JSON格式。如果此时直接返回页面的名称,会导致返回给浏览器一个json字符串,不会经过视图解析器。有两种解决方案如下:
1)将@RestController注解换成@Controller注解即可。
2)直接返回视图数据。返回值类型改成ModelAndView
使用JSP渲染Web视图
1、pom文件引入
<groupId>springboot</groupId>
<artifactId>springboot-jsp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringBoot 外部tomcat支持 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
</dependencies>
注意:打包类型一定选择war类型,否则找不到页面
2、 在配置文件中增加JSP相关配置
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
注意:不用把jsp页面放到resources下,需要新建目录
全局捕获异常
- @ExceptionHandler 表示拦截异常
- @ControllerAdvice 是 controller 的一个辅助类,最常用的就是作为全局异常处理的切面类
- @ControllerAdvice 可以指定扫描范围
- @ControllerAdvice 约定了几种可行的返回值,如果是直接返回 model 类的话,需要使用 @ResponseBody 进行 json 转换
//定义扫包范围
@ControllerAdvice(basePackages = "springboot.springboothelloworld")
public class GlobalExceptionHandler {
//拦截异常
@ExceptionHandler(RuntimeException.class)
// @ResponseBody 返回json格式
// modeAndView 返回页面
@ResponseBody
public Map<String, Object> errorResult() {
Map<String, Object> errorResultMap = new HashMap<String, Object>();
errorResultMap.put("errorCode", "400");
errorResultMap.put("errorMsg", "全局捕获异常系统错误!");
return errorResultMap;
}
}
模拟一个异常,前端页面就会返回json数据。
@RequestMapping("/helloWorld")
public ModelAndView index(Map<String,Object> map){
map.put("name","demo");
int i = 1 / 0;
return new ModelAndView("index");
}
数据访问
springboot整合mybatis
1、pom文件引入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入freeMarker的依赖包. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
2、 在配置文件中增加mybatis相关配置
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
3、后端相关代码
@RequestMapping("/findByName")
@ResponseBody
public User findByName(String name){
return userService.findByName(name);
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User findByName(String name){
User user = userMapper.findByName(name);
return user;
}
}
@Mapper
public interface UserMapper {
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
}
springboot整合多数据源
1、配置文件中新增两个数据源
spring.datasource.test1.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3306/test01?serverTimezone=UTC
spring.datasource.test1.username = root
spring.datasource.test1.password = root
spring.datasource.test2.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.test2.jdbc-url = jdbc:mysql://localhost:3306/test02?serverTimezone=UTC
spring.datasource.test2.username = root
spring.datasource.test2.password = root
2、 读取数据源配置文件
读取数据源1
@Configuration // 注册到springboot容器中
@MapperScan(basePackages = "springboot.springboothelloworld.test01", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSource1Config {
//配置test01数据库
//注入到Spring容器中
@Bean(name = "test1DataSource")
//prefix解析前缀
@ConfigurationProperties(prefix = "spring.datasource.test1")
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
//test1 sql会话工厂
@Bean(name = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// bean.setMapperLocations(
// new
// PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test1/*.xml"));
return bean.getObject();
}
//test1 事物管理
@Bean(name = "test1TransactionManager")
public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3、后端相关代码
注意:需要指定事务管理器
@Service
public class UserServiceTest01 {
@Autowired
private UserMapperTest01 userMapperTest01;
@Transactional(transactionManager = "test1TransactionManager")
public int insertUser(String name, Integer id) {
int insertUserResult = userMapperTest01.insert(name, id);
//验证事务是否成功
int i = 1 / id;
return insertUserResult;
}
}
public interface UserMapperTest01 {
// 查询语句
@Select("SELECT * FROM USER WHERE NAME = #{name}")
User findByName(@Param("name") String name);
// 添加
@Insert("INSERT INTO user(name, id) VALUES(#{name}, #{id})")
int insert(@Param("name") String name, @Param("id") Integer id);
}
springboot默认集成事物,只主要在方法上加上@Transactional即可。测试时,输入id为0时,数据库未插入数据,输入id为非0时,则插入成功,验证事务管理成功。数据源2同数据源1。
事务管理
使用springboot+jta+atomikos 分布式事物管理
1、pom文件引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
2、相关配置文件
# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test01?serverTimezone=UTC
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root
mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?serverTimezone=UTC
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root
mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
3、读取配置信息
读取数据源1
@Data
@ConfigurationProperties(prefix = "mysql.datasource.test1")
public class DBConfig1 {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
}
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "springboot.springboothelloworld.test01", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class MyBatisConfig1 {
// 配置数据源
@Bean(name = "testDataSource")
public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(testConfig.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(testConfig.getPassword());
mysqlXaDataSource.setUser(testConfig.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
// 将本地事务注册到 Atomikos全局事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("testDataSource");
xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
xaDataSource.setTestQuery(testConfig.getTestQuery());
return xaDataSource;
}
@Bean(name = "testSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "testSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
4、后端相关代码
@RequestMapping("/insertUserTest01AndTest02")
public int insertUserTest01AndTest02(String name, Integer id) {
return userServiceTest02.insertUserTest01AndTest02(name, id);
}
@Transactional()
public int insertUserTest01AndTest02(String name, Integer age) {
// 传统分布式事务解决方案 jta+atomikos 注册同一个全局事务中
// 第一个数据源
int insertUserResult01 = userMapperTest01.insert(name, age);
// 第二个数据源
int insertUserResult02 = userMapperTest02.insert(name, age);
int i = 1 / 0;
int result = insertUserResult01 + insertUserResult02;
// test01入库 test02回滚
return result;
}
此时就不需要制定事务管理器,因为都交到了全家事务中,只有一个事务管理器。
缓存
1、 pom文件引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2、新建ehcache.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="java.io.tmpdir/Tmp_EhCache" />
<!-- 默认配置 -->
<defaultCache maxElementsInMemory="5000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120"
memoryStoreEvictionPolicy="LRU" overflowToDisk="false" />
<!--
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<cache name="baseCache" maxElementsInMemory="10000"
maxElementsOnDisk="100000" />
</ehcache>
3、后端相关代码
@Mapper
//1.设置缓存名称
@CacheConfig(cacheNames = "userMapperCache")
public interface UserMapper {
//2.加上注解,将查询结果保存到缓存中
@Cacheable
@Select("SELECT * FROM USER WHERE id = #{id}")
User findById(@Param("id") Integer id);
}
需要注意的是:需要在springboot启动器上加入开启缓存注解
@SpringBootApplication
@EnableCaching // 开启缓存注解
public class SpringbootHelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootHelloworldApplication.class, args);
}
}
管理缓存
//将缓存管理器注入进来
@Autowired
private CacheManager cacheManager;
清除缓存
//清楚缓存
@RequestMapping("/remoKey")
public void remoKey() {
cacheManager.getCache("userMapperCache").clear();
}
性能优化
扫包优化
默认情况下,我们会使用 @SpringBootApplication 注解来自动获取应用的配置信息,但这样也会给应用带来一些副作用。使用这个注解后,会触发自动配置( auto-configuration )和 组件扫描 ( component scanning ),这跟使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 三个注解的作用是一样的。但是这样导致:
1、会导致项目启动时间变长。当启动一个大的应用程序,或将做大量的集成测试启动应用程序时,影响会特别明显。
2、会加载一些不需要的多余的实例(beans)。
3、会增加 CPU 消耗。
针对以上三个情况,我们可以移除 @SpringBootApplication 和 @ComponentScan 两个注解来禁用组件自动扫描,然后在我们需要的 bean 上进行显式配置:
//移除 @SpringBootApplication and @ComponentScan, 用 @EnableAutoConfiguration 来替代
//@SpringBootApplication
@Configuration
@EnableAutoConfiguration
public class SpringbootHelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootHelloworldApplication.class, args);
}
}
将Servlet容器变成Undertow
默认情况下,Spring Boot 使用 Tomcat 来作为内嵌的 Servlet 容器
可以将 Web 服务器切换到 Undertow 来提高应用性能。Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。Undertow 是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器。首先,从依赖信息里移除 Tomcat 配置:
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
然后添加 Undertow
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
JVM参数调优
这个根据服务器的内存大小,来设置堆参数。
-Xms :设置Java堆栈的初始化大小
-Xmx :设置最大的java堆大小
实例参数-XX:+PrintGCDetails -Xmx32M -Xms1M
调优策略:初始化堆内存和最大堆内存相等,这样可以减少垃圾回收次数
SpringBoot配置文件
- application.properties
- application.yml
YML 比properties配置文件更加节约 简约(结构)
server:
port: 8090
context-path: /springboot
自定义参数
name=lun
后端代码中直接就可以通过${ }拿到配置文件中的参数值
@Value("${name}")
private String name;
使用@Scheduled创建定时任务
@SpringBootApplication
//开启定时任务
@EnableScheduling
public class SpringbootHelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootHelloworldApplication.class, args);
}
}
@Component
public class ScheduledTasks {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate = 1000)
public void reportCurrentTime() {
System.out.println("现在时间:" + dateFormat.format(new Date()));
}
}