小工具      在线工具  汉语词典  dos游戏  css  js  c++  java

Spring Boot web开发实战

# Spring Boot,spring boot,java,spring,后端 额外说明

收录于:17天前

Json接口开发

使用Spring开发项目并向前端返回数据时,需要使用第三方json工具包将java数据类型转换为json。对于其他前端数据类型,Spring Boot中默认引入了jackson工具包,直接返回java对象,会自动转换为json。

但需要在@ResController下或者使用@ResponseBody注解。

自定义Filter(过滤器)

项目中会使用Filter来记录调用日志、排除有XSS威胁的字符、进行权限验证等。Spring Boot自动添加了OrderedCharacterEncodingFilter和HiddenHttpMethodFilter,我们可以自定义Filter。

主要有两个步骤来实现:

  1. 实现Filter接口并实现Filter方法
  2. 添加@Configuration 注解,将自定义Filter加入过滤链,或者使用@Order@ServletComponentScan将过滤器加到IoC容器
//自定义过滤器,只用@Order和@ServletComponentScan将filter加到ioc容器
@Order(1)
@WebFilter(urlPatterns = "/*")
public class filter1 implements Filter {
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
        System.out.println("执行了Filter1");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

//@ServletComponentScan注解在主程序上
@SpringBootApplication
@ServletComponentScan
public class SpringbootApplication{
    

    public static void main(String[] args) {
    
        SpringApplication.run(SpringbootApplication.class, args);
    }
}
//创建配置类实现fileter注入
@WebFilter("/first/*")
public class Filter2 implements Filter {
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
        System.out.println("注入的bean执行了");
    }
}

@Configuration
public class indexFilter {
    
    @Bean
    public FilterRegistrationBean MyFilter(){
    
        FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
        Filter2 filter=new Filter2();
        filterRegistrationBean.setFilter(filter);

        ArrayList<String> urlList=new ArrayList();
        urlList.add("/index/*");
        filterRegistrationBean.setUrlPatterns(urlList);
        return filterRegistrationBean;
    }
}

//该方法按配置顺序执行
//直接在Configuration配置类中bean注入
@Bean
    public Filter0 FilterTest(){
    
        Filter0 filter0=new Filter0();
        return filter0;
    }

自定义监听器

spring监听机制是对java提供的监听机制的封装。 java事件的监听机制有以下几类:

  • Event:继承java.util.EventObject类的对象
  • 事件源Source:任意Object对象
  • Listener监听器:实现java.util.EventListenter接口的对象

应用程序运行时

当应用程序运行时,应用程序事件应按以下顺序发送:

  1. 除了注册侦听器和初始化程序之外,还会在运行开始时但在任何处理之前发送 ApplicationStartingEvent。
  2. 当上下文中使用的环境已知但在创建上下文之前,将发送 ApplicationEnvironmentPreparedEvent。
  3. ApplicationPreparedEvent 在刷新开始之前、bean 定义加载之后发送。
  4. 在刷新上下文之后、调用任何应用程序和命令行运行程序之前发送 ApplicationStartedEvent。
  5. 在调用任何应用程序和命令行运行程序后发送 ApplicationReadyEvent。它表明应用程序已准备好处理请求。
  6. 如果启动时发生异常,则会发送 ApplicationFailedEvent。

应用程序运行前

  • 应用程序上下文初始化器
  • SpringApplicationRunListener
    在这里插入图片描述

从上到下依次是:项目启动中,环境开始准备,环境准备完毕,上下文开始加载,上下文加载完成,项目启动完成,项目启动失败的项目声明周期函数

上面要生效需要配置,再spring boot项目中的resources目录下创建MATA-INF/spring.factores并配置:

org.springframework.contex.ApplicationContextInitializer=exanple.text.listenerMyApplicationContextInitializer


//前面是监听器,后面是自定义的实现类
//注意都要写全限定名

//SpringApplicationRunListener的配置一样,再实现类重写方法实现监听

//SpringApplicationRunListener需要重写有参构造方法,参数为事件源和agrs的String数组

前两种接口的三种实现方式:
自定义接口:

public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
    
         System.out.println("-----MyApplicationContextInitializer initialize-----");
     }
 }
  1. 主要主要功能添加
@SpringBootApplication
public class MySpringBootApplication {
    
    public static void main(String[] args) {
    
        SpringApplication application = new SpringApplication(MySpringBootApplication.class);
        application.addInitializers(new MyApplicationContextInitializer());
        application.run(args);
    }
}
  1. 默认配置文件配置
context.initializer.classes=org.springframework.boot.demo.common.MyApplicationContextInitializer 
  1. SpringBoot的SPI扩展——在META-INF/spring.factories中配置
org.springframework.context.ApplicationContextInitializer=org.springframework.boot.demo.common.MyApplicationContextInitializer

//需要在主函数加载配置文件
  • CommandLineRunner
    接口偶一个run方法,当项目启动后执行run方法。
  • ApplicationRunner
    接口偶一个run方法,当项目启动后执行run方法。

这两个接口可以使用@Component注解到IoC容器中。

单元测试

使用@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTest(classes = Application.class)

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringbootupdateApplication.class)
public class Test {
    


    @org.junit.Test
    public void method1(){
    
        System.out.println("hello");
    }

}

Thymeleaf模板引擎

Thymeleaf支持html原型,然后在html标签中添加额外的属性,实现模板+数据展示。浏览器在解释html时会忽略未定义的标签属性,因此Thymeleaf的模板可以静态运行;当数据返回到页面时,Thymeleaf标签会动态替换静态内容,使得页面动态显示。

标准表达式语法

它们分为四类:

  1. 变量表达式
  2. 选择或星号表达式
  3. 文字国际化表达
  4. 网址表达

变量表达式
变量表达式即 OGNL 表达式或 Spring EL 表达式,它们将以HTML标签的一个属性来表示:

<span th:text="${book.author.name}">  
<li th:each="book : ${books}">  

选择(星号)表达式
选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行,如下:

<div th:object="${book}">  
  ...  
  <span th:text="*{title}">...</span>  
  ...  
</div>  

文字国际化表达
文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用 Key 索引 Value,还可以提供一组参数

#{main.title}  
#{message.entrycreated(${entryId})}  
<!--可以在模板文件中找到这样的表达式代码-->
<table>  
  ...  
  <th th:text="#{header.address.city}">...</th>  
  <th th:text="#{header.address.country}">...</th>  
  ...  
</table>  

网址表达
URL 表达式指的是把一个有用的上下文或回话信息添加到 URL,这个过程经常被叫做 URL 重写:@{/order/list}
URL还可以设置参数:@{/order/details(id=${orderId})}

<form th:action="@{/createOrder}">  
<a href="main.html" th:href="@{/main}">

变量表达式和星号表达有什么区别吗?
如果不考虑上下文的情况下,两者没有区别;星号语法评估在选定对象上表达,而不是整个上下文。什么是选定对象?就是父标签的值,如下:

<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

这完全等同于:

<div th:object="${session.user}">
  <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

当然,美元符号和星号语法可以混合使用:

 <div th:object="${session.user}">
	  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
  	  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
      <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
  </div>

常用th标签都有那些?

关键词 功能 案件
th:id 替换ID <input th:id="'xxx' + ${collect.id}"/>
th:文本 文本替换 <p th:text="${collect.description}">description</p>
th:utext 支持html文本替换 <p th:utext="${htmlcontent}">conten</p>
th:对象 替换对象 <div th:object="${session.user}">
th:值 财产转让 <input th:value="${user.name}" />
th:与 变量赋值操作 <div th:with="isEven=${prodStat.count}%2==0"></div>
th:风格 设置风格 th:style="'display:' + @{(${sitrue} ? 'none' : 'inline-block')} + ''"
th:onclick 点击事件 th:onclick="'getCollect()'"
th:每个 财产转让 tr th:each="user,userStat:${users}">
th:如果 分析条件 <a th:if="${userId == collect.userId}" >
th:除非 与th:if判断相反 <a th:href="@{/login}" th:unless=${session.user != null}>Login</a>
th:href 链接地址 <a th:href="@{/login}" th:unless=${session.user != null}>Login</a> />
th:开关 与 th:case 一起使用的多重选择 <div th:switch="${user.role}">
th:案例 th:switch 的一个分支 <p th:case="'admin'">User is an administrator</p>
th:片段 布局标签定义了一个代码片段,方便在其他地方引用 <div th:fragment="alert">
th:包括 布局标签,将内容替换到导入的文件中 <head th:include="layout :: htmlhead" th:with="title='xx'"></head> />
th:替换 布局标签,将整个标签替换到导入的文件中 <div th:replace="fragments/header :: title"></div>
th:已选择 选定的选择框已选定 th:selected="(${xxx.id} == ${configObj.dd})"
th:源代码 图片地址介绍 <img class="img-responsive" alt="App Logo" th:src="@{/img/logo.png}" />
细线 可以使用变量来定义js脚本 <script type="text/javascript" th:inline="javascript">
th:动作 表格提交地址 <form action="subscribe.html" th:action="@{/subscribe}">
th:删除 删除属性 <tr th:remove="all">1.all:删除包含标签和所有的孩子。2.body:不包含标记删除,但删除其所有的孩子。3.tag:包含标记的删除,但不删除它的孩子。4.all-but-first:删除所有包含标签的孩子,除了第一个。5.none:什么也不做。这个值是有用的动态评估。
th:属性 设置标签属性,多个属性可以用逗号分隔 比如th:attr="src=@{/image/aa.jpg},title=#{logo}",此标签不太优雅,一般用的比较少。

几种常用的使用方法

赋值、字符串连接

<p th:text="${collect.description}">description</p>
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

<span th:text="|Welcome to our application, ${user.name}!|">

条件判断If/Unless

<a th:if="${myself=='yes'}" > </i> </a>
<a th:unless=${session.user != null} th:href="@{/login}" >Login</a>

for循环

<tr th:each="collect,iterStat : ${collects}"> 
   <th scope="row" th:text="${collect.id}">1</th>
   <td >
      <img th:src="${collect.webLogo}"/>
   </td>
   <td th:text="${collect.url}">Mark</td>
   <td th:text="${collect.title}">Otto</td>
   <td th:text="${collect.description}">@mdo</td>
   <td th:text="${terStat.index}">index</td>
</tr>

网址

<!-- Will produce 'http://localhost:8080/standard/unread' (plus rewriting) -->
 <a th:href="@{/standard/{type}(type=${type})}">view</a>

<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

更改内部CSS

<div th:style="'background:url(' + @{
       /<path-to-image>} + ');'"></div>

<div class="media-object resource-card-image" th:style="'background:url(' + @{
       (${
       collect.webLogo}=='' ? 'img/favicon.png' : ${
       collect.webLogo})} + ')'" ></div>

内联js

<script th:inline="javascript">
/*<![CDATA[*/
...
var username = /*[[${sesion.user.name}]]*/ 'Sebastian';
var size = /*[[${size}]]*/ 0;
...
/*]]>*/
</script>

嵌入变量
在这里插入图片描述

dates

${#dates.format(date, 'dd/MMM/yyyy HH:mm')}
${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')}
${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')}
${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')}

/*
 * Create a date (java.util.Date) object for the current date and time
 */
${#dates.createNow()}

/*
 * Create a date (java.util.Date) object for the current date (time set to 00:00)
 */
${#dates.createToday()}
strings
${#strings.isEmpty(name)}
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
${#strings.setIsEmpty(nameSet)}

/*
 * Check whether a String starts or ends with a fragment
 * Also works with arrays, lists or sets
 */
${#strings.startsWith(name,'Don')}                  // also array*, list* and set*
${#strings.endsWith(name,endingFragment)}           // also array*, list* and set*

/*
 * Compute length
 * Also works with arrays, lists or sets
 */
${#strings.length(str)}

/*
 * Null-safe comparison and concatenation
 */
${#strings.equals(str)}
${#strings.equalsIgnoreCase(str)}
${#strings.concat(str)}
${#strings.concatReplaceNulls(str)}

/*
 * Random
 */
${#strings.randomAlphanumeric(count)}

使用 Thymeleaf 布局
Spring Boot 2.0 将布局单独提取了出来,需要单独引入依赖:thymeleaf-layout-dialect。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>nz.net.ultraq.thymeleaf</groupId>
    <artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
定义代码片段

<footer th:fragment="copy"> 
&copy; 2019
</footer>
在页面任何地方引入:

<body>
    <div th:insert="layout/copyright :: copyright"></div>
    <div th:replace="layout/copyright :: copyright"></div>
</body>
th:insert 和 th:replace 区别,insert 只是加载,replace 是替换。Thymeleaf 3.0 推荐使用 th:insert 替换 2.0 的 th:replace。

返回的 HTML 如下:

<body> 
   <div> &copy; 2019 </div> 
  <footer>&copy; 2019 </footer> 
</body>
下面是一个常用的后台页面布局,将整个页面分为头部,尾部、菜单栏、隐藏栏,点击菜单只改变 content 区域的页面

<body class="layout-fixed">
  <div th:fragment="navbar"  class="wrapper"  role="navigation">
	<div th:replace="fragments/header :: header">Header</div>
	<div th:replace="fragments/left :: left">left</div>
	<div th:replace="fragments/sidebar :: sidebar">sidebar</div>
	<div layout:fragment="content" id="content" ></div>
	<div th:replace="fragments/footer :: footer">footer</div>
  </div>
</body>
任何页面想使用这样的布局值只需要替换中见的 content 模块即可

<html xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">
 <body>
    <section layout:fragment="content">
  ...
也可以在引用模版的时候传参

<head th:include="layout :: htmlhead" th:with="title='Hello'"></head>
layout 是文件地址,如果有文件夹可以这样写fileName/layout:htmlhead,htmlhead 是指定义的代码片段 如th:fragment="copy"

其他模板引擎

Spring boot整合myabtis

引入mybatis-spring-boot-starter

完全注解开发

添加依赖项:

<dependencies>
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>2.0.0</version>
	</dependency>
     <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

application.properties 添加相关配置

mybatis.type-aliases-package=com.neo.model

spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

Spring Boot会自动加载spring.datasource.*相关配置,数据源会自动注入到sqlSessionFactory中,sqlSessionFactory会自动注入到Mapper中。

//在启动类中添加对 mapper 包扫描@MapperScan
@SpringBootApplication
@MapperScan("com.neo.mapper")
public class MybatisAnnotationApplication {
    

	public static void main(String[] args) {
    
		SpringApplication.run(MybatisAnnotationApplication.class, args);
	}
}

//或者直接在 Mapper 类上面添加注解@Mapper

开发映射器

public interface UserMapper {
    
	
	@Select("SELECT * FROM users")
	@Results({
    
		@Result(property = "userSex",  column = "user_sex", javaType = UserSexEnum.class),
		@Result(property = "nickName", column = "nick_name")
	})
	List<UserEntity> getAll();
	
	@Select("SELECT * FROM users WHERE id = #{id}")
	@Results({
    
		@Result(property = "userSex",  column = "user_sex", javaType = UserSexEnum.class),
		@Result(property = "nickName", column = "nick_name")
	})
	UserEntity getOne(Long id);

	@Insert("INSERT INTO users(userName,passWord,user_sex) VALUES(#{userName}, #{passWord}, #{userSex})")
	void insert(UserEntity user);

	@Update("UPDATE users SET userName=#{userName},nick_name=#{nickName} WHERE id =#{id}")
	void update(UserEntity user);

	@Delete("DELETE FROM users WHERE id =#{id}")
	void delete(Long id);

}

@Select 是查询类的注解,所有的查询均使用这个
@Result 修饰返回的结果集,关联实体类属性和数据库字段一一对应,如果实体类属性和数据库属性名保持一致,就不需要这个属性来修饰。
@Insert 插入数据库使用,直接传入实体类会自动解析属性到对应的值
@Update 负责修改,也可以直接传入对象
@delete 负责删除

使用#符号和$符号的区别:

// This example creates a prepared statement, something like select * from teacher where name = ?;
@Select("Select * from teacher where name = #{name}")
Teacher selectTeachForGivenName(@Param("name") String name);

// This example creates n inlined statement, something like select * from teacher where name = 'someName';
@Select("Select * from teacher where name = '${name}'")
Teacher selectTeachForGivenName(@Param("name") String name);
//测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
    

	@Autowired
	private UserMapper userMapper;

	@Test
	public void testInsert() throws Exception {
    
		userMapper.insert(new User("aa1", "a123456", UserSexEnum.MAN));
		userMapper.insert(new User("bb1", "b123456", UserSexEnum.WOMAN));
		userMapper.insert(new User("cc1", "b123456", UserSexEnum.WOMAN));

		Assert.assertEquals(3, userMapper.getAll().size());
	}

	@Test
	public void testQuery() throws Exception {
    
		List<User> users = userMapper.getAll();
		System.out.println(users.toString());
	}
	
	
	@Test
	public void testUpdate() throws Exception {
    
		User user = userMapper.getOne(30l);
		System.out.println(user.toString());
		user.setNickName("neo");
		userMapper.update(user);
		Assert.assertTrue(("neo".equals(userMapper.getOne(30l).getNickName())));
	}
}

极简 xml 开发

xml 版本保留了映射文件的旧传统。接口层只需要定义空方法,系统会根据方法名自动在映射文件中找到对应的Sql。

mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml 指定了 Mybatis 基础配置文件和实体类映射文件的地址 

mybatis-config.xml配置:

<configuration>
	<typeAliases>
		<typeAlias alias="Integer" type="java.lang.Integer" />
		<typeAlias alias="Long" type="java.lang.Long" />
		<typeAlias alias="HashMap" type="java.util.HashMap" />
		<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
		<typeAlias alias="ArrayList" type="java.util.ArrayList" />
		<typeAlias alias="LinkedList" type="java.util.LinkedList" />
	</typeAliases>
</configuration>

映射文件xml配置:

mapper namespace="com.neo.mapper.UserMapper" >
    <resultMap id="BaseResultMap" type="com.neo.entity.UserEntity" >
        <id column="id" property="id" jdbcType="BIGINT" />
        <result column="userName" property="userName" jdbcType="VARCHAR" />
        <result column="passWord" property="passWord" jdbcType="VARCHAR" />
        <result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/>
        <result column="nick_name" property="nickName" jdbcType="VARCHAR" />
    </resultMap>
    
    <sql id="Base_Column_List" >
        id, userName, passWord, user_sex, nick_name
    </sql>

    <select id="getAll" resultMap="BaseResultMap" >
       SELECT 
       <include refid="Base_Column_List" />
	   FROM users
    </select>

    <select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap" >
        SELECT 
       <include refid="Base_Column_List" />
	   FROM users
	   WHERE id = #{id}
    </select>

    <insert id="insert" parameterType="com.neo.entity.UserEntity" >
       INSERT INTO 
       		users
       		(userName,passWord,user_sex) 
       	VALUES
       		(#{userName}, #{passWord}, #{userSex})
    </insert>
    
    <update id="update" parameterType="com.neo.entity.UserEntity" >
       UPDATE 
       		users 
       SET 
       	<if test="userName != null">userName = #{userName},</if>
       	<if test="passWord != null">passWord = #{passWord},</if>
       	nick_name = #{nickName}
       WHERE 
       		id = #{id}
    </update>
    
    <delete id="delete" parameterType="java.lang.Long" >
       DELETE FROM
       		 users 
       WHERE 
       		 id =#{id}
    </delete>
</mapper>

编写Mapper层的代码

public interface UserMapper {
    
	
	List<UserEntity> getAll();
	
	UserEntity getOne(Long id);

	void insert(UserEntity user);

	void update(UserEntity user);

	void delete(Long id);

}

配置类

  • 配置类定义

Spring Boot 支持基于 Java 的配置。尽管可以将 SpringApplication 与 XML 源一起使用,但我们通常建议您的主要源是单个 @Configuration 类。通常,定义 main 方法的类是 @Configuration 的主要候选者。

  • 导入其他配置类

无需将所有@Configuration放入一个类中。 @Import注解可用于导入其他配置类。或者,您可以使用@ComponentScan自动选择所有Spring组件,包括@Configuration类。

  • 导入 XML 配置

可以使用@ImportResource注释加载XML配置文件

  • 自动配置

通过向@Configuration类之一添加@EnableAutoConfiguration或@SpringBootApplication注释来选择加入自动配置。
自动配置是非侵入性的。在任何时候,您都可以开始定义自己的配置以替换自动配置的特定部分。例如,如果添加自己的DataSource bean,则默认的嵌入式数据库支持会退回。

  • 禁用特定的自动配置类

如果您发现正在应用不需要的特定自动配置类,则可以使用 @EnableAutoConfiguration 的排除属性禁用它们,如以下示例所示:

@Configuration
@EnableAutoConfiguration(exclude={
     DataSourceAutoConfiguration.class})
public class MyConfiguration {
     
}

Spring Beans和依赖注入

Bean 及其注入的依赖项可以使用任何标准 Spring 框架技术在 Spring Boot 中定义。为了简单起见,我们经常使用@ComponentScan(来查找你的bean)和使用@Autowired(来进行构造函数注入)

按照上面的建议构建代码(在根包中定位应用程序类),您可以添加@ComponentScan而不带任何参数。所有应用程序组件(@Component、@Service、@Repository、@Controller 等)都会自动注册为 Spring Bean。

热交换

  • 属性默认值

Spring Boot 支持的多个库使用缓存来提高性能。例如,模板引擎会缓存已编译的模板,以避免重复解析模板文件。此外,Spring MVC 可以在提供静态资源时向响应添加 HTTP 缓存标头。

虽然缓存在生产中非常有用,但在开发过程中可能会适得其反,使您无法看到刚刚在应用程序中所做的更改。因此,spring-boot-devtools 默认禁用缓存选项。

缓存选项通常通过 application.properties 文件中的设置进行配置。例如,Thymeleaf 提供了 spring.thymeleaf.cache 属性。 spring-boot-devtools 模块不需要手动设置这些属性,而是自动应用适当的开发时配置。

  • 自动重启

每当类路径上的文件发生更改时,使用 spring-boot-devtools 的应用程序都会自动重新启动。在 IDE 中工作时,这可能是一个有用的功能,因为它为代码更改提供了非常快速的反馈循环。默认情况下,类路径上指向文件夹的任何条目都将受到监视以了解更改。请注意,某些资源(例如静态资产和视图模板)不需要重新启动应用程序。

外部配置

您可以使用属性文件、YAML 文件、环境变量和命令行参数来外部化配置。属性值可以使用@Value注解直接注入到bean中,通过Spring的Environment抽象访问,或者通过@ConfigurationProperties绑定到结构化对象。

如果 spring.config.location 包含目录(而不是文件),则它们应该以 / 结尾(并且在运行时,从 spring.config.name 生成的名称,包括配置文件特定的文件名,在加载之前附加)。 spring.config.location 中指定的文件按原样使用,不支持特定于配置文件的变体,并且会被任何特定于配置文件的属性覆盖。

以相反的顺序搜索配置位置。默认情况下,配置位置为classpath:/、classpath:/config/、file:./、file:./config/。结果搜索顺序如下:

file:./config/
file:./
classpath:/config/
classpath:/

配置文件特定的属性:
除了application.properties文件之外,还可以使用以下命名约定来定义特定于配置文件的属性:application-{profile}.properties。Environment有一组默认配置文件(默认情况下为[default]),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,则会加载application-default.properties中的属性。

这一组配置文件用于满足不同生产环境下的需求,特定于配置文件的属性从标准application.properties的相同位置加载,特定于配置文件的文件始终覆盖非特定文件,无论特定于配置文件的文件是在打包的jar内部还是外部。如果需要改变环境的化需要显示激活配置文件对默认覆盖:spring.profiles.active=true

异常处理即错误页面的定制

开发和使用过程中难免会遇到各种错误,通过自定义控制器返回具体的错误和页面。

@ControllerAdvice注释的类,以自定义要为特定控制器和/或异常类型返回的JSON文档,该类中可以直接返回一个统筹的404错误或使用@ExceptionHandler处理特定错误。

//类使用了@ControllerAdvice注解来修饰,
// 其会被<context:component-scan>扫描,
// 这使得该类中使用@ExceptionHandler注解修饰的方法都被应用到所有请求处理方法上
// 也就是所有请求处理方法抛出的异常都将由该类中对应的@ExceptionHandler注解修饰的方法处理.
@ControllerAdvice
public class GlobalExceptionHandler
{
    
	// 处理Exception类型异常
	@ExceptionHandler(value = Exception.class)
	public ModelAndView globalErrorHandler(Exception e) throws Exception
	{
    
		ModelAndView mav = new ModelAndView();
		mav.addObject("ex", e);
		mav.setViewName("error");
		return mav;
	}
	// 处理OrderException自定义异常
	@ExceptionHandler(value = OrderException.class)
	// 返回的结果将会被封装成JSON数据,并返回给客户端
	@ResponseBody
	public Object OrderErrorHandler(Exception e) throws Exception
	{
    
		// 创建返回对象Map并设置属性,会被@ResponseBody注解转换为JSON返回
		Map<String, Object> map = new HashMap<>();
		map.put("code", 100);
		map.put("message", e.getMessage());
		map.put("data", "请求失败");
		return map;
	}
}

使用@ExceptionHandler在控制器定义异常

@Controller
public class TestController
{
    
	@GetMapping("/test")
	public String test() throws Exception
	{
    
		// 模拟异常,调用本类中定义的异常处理方法
		@SuppressWarnings("unused")
		int i = 5 / 0;
		return "success";
	}
	// 在异常抛出的时候,Controller会使用@ExceptionHandler注解的方法去处理异常
	// value=Exception.class表示处理所有的Exception类型异常。
	@ExceptionHandler(value = Exception.class)
	public ModelAndView testErrorHandler(Exception e)
	{
    
		ModelAndView mav = new ModelAndView();
		mav.setViewName("error");
		mav.addObject("ex", e);
		return mav;
	}
}

跨域问题CORS

跨源资源共享 (CORS) 是大多数浏览器实现的 W3C 规范,它允许您以灵活的方式指定授权哪些跨源请求。

在Spring Boot应用程序中使用带有 注释的控制器方法CORS配置@CrossOrigin不需要任何特定配置。


// 允许所有域发送过来的请求
@CrossOrigin(maxAge = 3600)
@Controller
public class CrossOriginController
{
    
	// 只允许origins属性中指定的域的请求
	@CrossOrigin(origins = "http://localhost:8080/VariableTest")
	@GetMapping(value = "/welcome")
	public String welcome()
	{
    
		System.out.println("处理跨域请求");
		return "welcome";
	}
}

全局 CORS 配置还可以通过使用自定义 addCorsMappings(CorsRegistry) 方法注册 WebMvcConfigurer bean 来定义:

@Configuration
public class MyConfiguration {
    

	@Bean
	public WebMvcConfigurer corsConfigurer() {
    
		return new WebMvcConfigurer() {
    
			@Override
			public void addCorsMappings(CorsRegistry registry) {
    
				registry.addMapping("/api/**");
			}
		};
	}
}

Cookie和Session

通过@CookieValue来获取cookie的值


@Controller
public class CookieValueController
{
    
	// 测试@CookieValue注解
	// 该方法映射的请求为 /cookieValueTest
	@GetMapping(value = "/cookieValueTest")
	// 将cookie中的JSESSIONID值赋值给方法的参数sessionId
	public void cookieValueTest(
			@CookieValue(value = "JSESSIONID", defaultValue = "") String sessionId)
	{
    
		System.out.println("通过@CookieValue获得JSESSIONID: " + sessionId);
	}
}

通过@SessionAttribute获取session值,通过HttpSession对象存session。当然也可以全部使用HttpServletResquest和HttpServletResponse来存取session。

Spring Boot单元测试

Spring Boot提供了@SpringBootTest注释,当您需要Spring引导功能时,可以将其用作标准spring-test @ContextConfiguration注释的替代。注释的工作原理是 通过SpringApplication创建测试中使用的ApplicationContext。

如果您使用 JUnit 4,请不要忘记将 @RunWith(SpringRunner.class) 添加到您的测试中,否则该注释将被忽略。

@RunWith(SpringRunner.class)
@SpringBootTest
public class RandomPortTestRestTemplateExampleTests {
    

	@Autowired
	private TestRestTemplate restTemplate;

	@Test
	public void exampleTest() {
    
		.....
	}

}
. . .

相关推荐

额外说明

[Java|多线程与高并发]wait和notify方法详解

文章目录 1.前言 2.wait和notify的基本使用 3. notifyAll方法 4. wait和sleep方法的对比 5. 总结 1.前言 在Java多线程环境中,线程之间是抢占式执行的,线程的调度是随机的.这就很难受了. 在很多情况下我们希望线

额外说明

GatewayWorker-thinkphp-小程序 wss:// 配置 宝塔

废话不多说 测试 结果 composer require workerman/workerman TP 根目录 #!/usr/bin/env php <?php define('APP_PATH', __DIR__ . '/applicat

额外说明

go 图片转base64

func main() { srcByte, err := ioutil.ReadFile(`F:\mnt\15793397311298498081.png`) if err != nil { log.Fatal(err) } res

额外说明

信号处理(二)音频信号的分帧, 加窗

1 .语音信号的三个参数 语音信号有三个重要的参数:声道数、取样频率和量化位数。 声道数:单声道或者双声道 采样频率:一秒钟对声音采样的次数,例如10000HZ代表一秒钟将信号分解为10000份,当采样数量非常高的时候,我们人眼看起来就是连续的。(实际是

额外说明

Spring Boot整合Swagger

-作者简介:练习时长两年半的Java up主 -个人主页:程序员老茶 - ps:点赞-是免费的,却可以让写博客的作者开心好久好久- -系列专栏:Java全栈,计算机系列(火速更新中) - 格言:种一棵树最好的时间是十年前,其次是现在 -动动小手,点个关注

额外说明

机器学习必修课 - 如何处理缺失数据

运行环境:Google Colab 处理缺失数据可简单分为两种方法:1. 删除具有缺失值的列 2. 填充 !git clone https://github.com/JeffereyWu/Housing-prices-data.git 下载数据集 im

额外说明

解决docker启动目录的权限问题

方案一: 给指定文件所在的目录加足够的权限 chmod -R 775 目录 方案2:再次确认挂载的文件路径是否写错了,这个是最常见的

额外说明

2022.5.17 关于JAVA 类和对象(基础知识)

目录 类  this 关键字 构造方法 类 我们知道 int byte char double 等等都是基本的类型,此时我们想自定义一个类呢? 举例1: //创建一个用来洗衣服的机器的类 class WashMachine{ //以下定义的为 属

额外说明

48个国际音标发音图解暨口形及发音方法

引用 超然 的 48个国际音标 引用 【★英语国际音标★学习MV】英语音标发音大全(10) ^_^     四十八个音素发音图解暨口形及发音方法           四十八个音素发音图解暨口形及发音方法    1./ /美式读音符号     / /英式读

ads via 小工具