<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
SpringBoot会将所有的功能场景,都变成一个个的启动器,要使用什么功能,就只需要找到对应的启动器。
比如spring-boot-starter-web, 他就会帮我们自动导入web环境所有的依赖
官网列出的所有启动器
@SpringBootApplication // 1.配置Spring和SpringMVC 2.包扫描
public class App {
public static void main(String[] args){
SpringApplication.run(App.class,args);
}
}
包括:
@SpringBootConfiguration 表明配置类,类似于Spring
中的aplicationContext.xml 文件
一般会创建一个专门的类用来配置bean,方便管理
@Configuration
public class MyConfig(){
}
@EnableAutoConfiguration 开启自动化配置
@ComponentScan 扫描当前类所在包下面内容,一般把项目启动类放在根包中
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
spring-boot-starter-web 依赖会默认使用Tomcat 作为Web 容器,可以在application.properties 中进行进一步配置
keytool -genkey -alias tomcathttps -keyalg RSA -keysize 2048 -keystore sang.p12 -validity 365
2. 在application.properties 中配置
然后就可以使用https访问了。
https://localhost:8081/chapter02/getHello
book.name=三国演义
book.author=罗贯中
book.price=30
//yml文件还能支持对象和列表,如:book,persons
book:
name: 水浒
author_name: 施耐庵
price: 30
persons:
- 武松
- 宋江
- chaogai
等同于
book: {name: 水浒传,author_name: 施耐庵,price: 30,persons: [武松,宋江,chaogai]}
@Component
@ConfigurationProperties(prefix = "book")
public class Book {
//@Value("楚门的世界")
//@Value("${book.name}")
//private String name;
private String name;
private String author;
private Float price;
private List<String> persons;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public List<String> getPersons() {
return persons;
}
public void setPersons(List<String> persons) {
this.persons = persons;
}
}
@RestController
public class BookController {
@Autowired
Book book;
@GetMapping("/book")
public Book book(){
return book;
}
}
@Component
//@ConfigurationProperties(prefix = "book")
@PropertySource(value = "classpath:casey.properties")
public class Book {
@Value("${name}")
private String name;
private String authorName;
private Float price;
private List<String> persons;
private Date time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public List<String> getPersons() {
return persons;
}
public void setPersons(List<String> persons) {
this.persons = persons;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
book:
name: 水浒${random.uuid} #水浒96cbd308-d338-4bca-8897-17380137bc4e
author_name: ${student.name:施耐庵}作者 #student.name不存在,取 施耐庵作者
price: ${random.int} #-698479567
persons:
- 武松
- 宋江
- chaogai
配置文件中:last-name
bean类中:lastName
@Pattern 验证 String 对象是否符合正则表达式的规则
@Component
@ConfigurationProperties(prefix = "book")
@Validated
public class Book {
@Email(message = "邮箱格式错误")
private String name;
private String authorName;
private int price;
private List<String> persons;
private Date time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public List<String> getPersons() {
return persons;
}
public void setPersons(List<String> persons) {
this.persons = persons;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) Validates that the annotated string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) 检查数字是否介于min和max之间.
@Range(min=10000,max=50000,message="range.bean.wage")
private BigDecimal wage;
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
spring:
profiles:
#表示使用配置文件application-dev.yml启动项目
active: dev
server:
port: 8080
server:
port: 8081
# tomcat配置
server:
port: 8081
#servlet:
# context-path: /chapter02
tomcat:
uri-encoding: utf-8
# https配置
ssl:
key-store: sang.p12
key-alias: tomcathttps
key-store-password: root123
#激活配置文件
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
● xxxAutoConfiguraion:向容器中自动配置组件
● xxxxProperties:自动配置类,装配配置文件中自定义的一些内容
参考:整合Thymeleaf
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-java8timeartifactId>
dependency>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>titletitle>
head>
<body>
<div th:text="${msg}">div>
body>
html>
@RequestMapping("/success")
public String success(Model model){
//存入数据
model.addAttribute("msg","Hello,Thymeleaf");
//classpath:/templates/success.html
return "success";
}
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<form class="form-signin" th:action="@{/user/login}">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign inh1>
<p th:text="${msg}" th:if="${not #strings.isEmpty(msg)}">p>
国际化
由于pom.xml中添加的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
其中默认加入了jackson-databind 作为JSON处理器,如上面例子中返回book对象就属于这种情况。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd hh:mm:ss");//日期格式
config.setCharset(Charset.forName("UTF-8"));//数据编码
config.setSerializerFeatures(
SerializerFeature.WriteClassName,//是否在生成的JSON 中输出类名
SerializerFeature.WriteMapNullValue,//是否输出value为null的数据
SerializerFeature.PrettyFormat,//生成的json格式化
SerializerFeature.WriteNullListAsEmpty,//空集合输出[]而非null
SerializerFeature.WriteNullStringAsEmpty//空字符串输出""而非null
);
converter.setFastJsonConfig(config);
converters.add(converter);
}
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
java中文件上传一般涉及两个组件:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFile" value="请选择文件">
<input type="submit" value="上传">
form>
body>
html>
package com.cewell.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* ClassName:FileUploadController
* Package:com.cewell.controller
* Description:文件上传
*
* @date:2019/11/26 18:24
* @author:[email protected]
*/
@RestController
public class FileUploadController {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
@PostMapping("/upload")
public String upload(MultipartFile uploadFile, HttpServletRequest req){
//第27代码表示规划上传文件的保存路径为项目运行目录下的uploadFile 文件夹,并在文件夹中通过日期对所上传的文件归类保存。
String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
String format = sdf.format(new Date());
File folder = new File(realPath + format);
//如果目录不存在,创建
if (!folder.isDirectory()) {
folder.mkdirs();
}
//给上传的文件重命名
String oldName = uploadFile.getOriginalFilename(); //得到上传时的文件名
//UUID.randomUUID().toString() 是JDK提供的一个自动生成主键的方法
//* 截取字符串substring()方法的用法
// 1.substring(int beginIndex); 截取开始索引beginIndex到结束
// "mybaby".substring(3) returns"aby"
// 2.substring(int beginIndex, int endIndex); 截取开始索引beginIndex到结束索引endIndex-1
// "hamburger".substring(3,8) returns "burge"
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."),oldName.length());
//UUID + 截取的文件后缀名
try {
//文件保存 地址,重命名的名字
uploadFile.transferTo(new File(folder,newName));
//生成上传文件的访问路径,并将访问路径返回。
String filePath =
req.getScheme() + "://" //协议
+ req.getServerName() + ":" //ip
+ req.getServerPort() + "/uploadFile/" //端口
+ format + newName;
return filePath;
} catch (IOException e) {
e.printStackTrace();
}
return "上传失败";
}
}
spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=0
spring.servlet.multipart.location=E:\\temp
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.resolve-lazily=false
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/uploadMultiple" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFiles" value="请选择文件" multiple/>
<input type="submit" value="上传"/>
form>
body>
html>
@PostMapping("/uploadMultiple")
public List<String> uploadMultiple(MultipartFile[] uploadFiles, HttpServletRequest req) {
//定义一个list用来存放三张图片上传之后的链接
List<String> filePathList = new ArrayList<String>();
for (int i = 0; i < uploadFiles.length; i++) {
//第27代码表示规划上传文件的保存路径为项目运行目录下的uploadFile 文件夹,并在文件夹中通过日期对所上传的文件归类保存。
String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
String format = sdf.format(new Date());
File folder = new File(realPath + format);
//如果目录不存在,创建
if (!folder.isDirectory()) {
folder.mkdirs();
}
//给上传的文件重命名
String oldName = uploadFiles[i].getOriginalFilename(); //得到上传时的文件名
//UUID.randomUUID().toString() 是JDK提供的一个自动生成主键的方法
//* 截取字符串substring()方法的用法
// 1.substring(int beginIndex); 截取开始索引beginIndex到结束
// "mybaby".substring(3) returns"aby"
// 2.substring(int beginIndex, int endIndex); 截取开始索引beginIndex到结束索引endIndex-1
// "hamburger".substring(3,8) returns "burge"
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."), oldName.length());
//UUID + 截取的文件后缀名
try {
//文件保存 地址,重命名的名字
uploadFiles[i].transferTo(new File(folder, newName));
//生成上传文件的访问路径,并将访问路径返回。
String filePath =
req.getScheme() + "://" //协议
+ req.getServerName() + ":" //ip
+ req.getServerPort() + "/uploadFile/" //端口
+ format + newName;
filePathList.add(filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
//return "上传失败";
return filePathList;
}
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter(); //取一个响应客户端的流对象,用来在客户端输出
out.write("上传文件大小超出限制"); //通过PrintWrite,以流方式输出,返回给浏览器
out.flush(); //清空缓冲区的数据流
out.close(); //关闭读写流
//flush()这个函数是清空的意思,用于清空缓冲区的数据流,进行流的操作时,数据先被读到内存中,然后再用数据写到文件中,
// 那么当你数据读完时,我们如果这时调用close()方法关闭读写流,这时就可能造成数据丢失,为什么呢,因为,读入数据完成时不代表写入数据完成,
// 一部分数据可能会留在缓存区中,为了理解这个问题,我们举一个例子:
//
//比如,在农村,几乎每家都有抽水机,抽水机的作用是什么呢,就是把水井里的水抽到水缸中,这时我们就会用水管连接抽水机和水缸(水管就好比是缓冲区),
// 当我们想把水井中的水都抽到水缸中时,我们就让抽水机工作抽水,如果我们发现水井里的水刚好抽完时,我们就会关掉抽水机的开关停止抽水,那么这时,
// 管道里就会遗留一部分水,抽水就是读数据,水缸进水就是写数据,水管充当缓存区的角色,不知道这样是不是具象化了呢
//
//那么这样一来我们如果中途调用close()方法,输出区也还是有数据的,就像水缸里有水,只是在缓冲区遗留了一部分,这时如果我们先调用flush()方法,
// 就会强制把数据输出,缓存区就清空了,最后再关闭读写流调用close()就完成了。
}
}
@ControllerAdvice
public class GlobalConfig {
//注解@ModelAttribute中的value属性表示这条返回数据的key,而方法的返回值是返回数据的value
@ModelAttribute(value = "info")
public Map<String,String> userInfo(){
HashMap<String,String> map = new HashMap<>();
map.put("writer","罗贯中");
map.put("bookName","三国演义");
return map;
}
}
@RestController
public class HelloController {
@GetMapping("/helloBook")
public String helloBook(Model model){
Map<String,Object> map = model.asMap();
return map.toString(); //{info={writer=罗贯中, bookName=三国演义}}
}
}
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
# 数据库配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/exercise?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: root123
//bean
public class Masterpiece {
private Integer id;
private String name;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
<mapper namespace="com.cewell.dao.MasterpieceMapper">
<resultMap id="BaseResultMap" type="com.cewell.bean.Masterpiece">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="author" jdbcType="VARCHAR" property="author"/>
resultMap>
<select id="getMasterpiece" resultMap="BaseResultMap">
select * from masterpiece
select>
mapper>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
resource>
resources>
redis-server.exe | redis-cli.exe |
---|---|
服务端 | 客户端 |
启动redis程序 | 进入redis的命令界面 |
必须启动 | 用jedis之类的操作redis的话就无须启动 |
有5种数据类型 :
import redis.clients.jedis.Jedis;
1>.Jedis直连
生成Jedis对象
Jedis执行命令
返回执行结果
关闭Jedis连接
Jedis jedis = new Jedis("127.0.0.1","6379");
jedis.set("hello","world");
String value = jedis.get("hello");
2>.使用Jedis连接池
从资源池借Jedis对象
Jedis执行命令
返回执行结果
归还Jedis对象给连接池
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig ,"12.0.0.1",6379);
Jedis jedis = null ;
try{
jedis = jedisPool.getResource();
}catch(Exception e ){
e.printStackTrace;
}finally{
}
(1)Redis、Jedis、SpringDataRedis及RedisTemplate关系
1>.Redis是一个基于内存的Key-Value数据库
2>.Jedis是Redis官方推出的面向Java的客户端Client,提供了很多接口和方法,可以让Java操作使用Redis
3>.SDR是Spring框架集成Redis操作的一个子框架,封装了Redis的很多命令;比Jedis多了自动管理连接池的特性
4>.RedisTemplate是SpringDataRedis中对JedisApi的高度封装
(2)jedis : 操作redis是很简单的,通过new的方式,获取jedis对象,然后增删改查(set,get,del,exists)等,简单来说就是代码的方式来实现操作的;
RedisTemplate : 也可以做到操作redis缓存, 使用spring注入方式的配置
一个比较原生,一个经过spring托管控制;RedisTemplate只做缓存,没有队列,相对jedis功能更少;
单线程使用RedisTemplate缓存即可,如果有多线程,需要使用jedis
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
spring:
# 配置Redis
redis:
# 基本连接信息
database: 0 #数据库索引,共16个,编号1-15
host: 127.0.0.1
port: 6379
password:
# 连接池信息
jedis:
pool:
#最大连接数
max-active: 8
#最大空闲连接数
max-idle: 8
#最大阻塞等待时间(负数表示没限制)
max-wait: -1ms
#最小空闲
min-idle: 0
//一个类只有实现了Serializable接口,它的对象才是可序列化的
//Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
public class Masterpiece implements Serializable {
private Integer id;
private String name;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "name: " + this.name + " " + "author: " + this.author;
}
}
@RestController
@RequestMapping("api")
@GetMapping("/getMasterpieceRedis")
public void getRedis(){
//向redis中存一条记录,再将其读取
ValueOperations<String,String> opsl = stringRedisTemplate.opsForValue(); //获取一个键值操作器
opsl.set("name","三国演义");
String name = opsl.get("name");
System.out.println(name);
//向redis中存一个对象,再将其读取
ValueOperations opsl2 = redisTemplate.opsForValue();
Masterpiece m = new Masterpiece();
m.setId(1);
m.setAuthor("罗贯中");
m.setName("三国演义");
opsl2.set("m",m);
Masterpiece masterpiece = (Masterpiece) opsl2.get("m");
System.out.println(masterpiece.toString());
}
}