现有的SSM(spring+springMVC+mybatis)框架存在的问题
Spring Boot是由Pivotal团队提供的全新框架
,其设计目的是用来简化Spring应用的 初始搭建以及开发过程
。该框架使用了特定的方式来进行配置
,从而使开发人员不 再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应 用开发领域(rapid application development)成为领导者。
Spring Boot 全新框架 作用: 简化spring应用初始搭建和开发过程
如何简化: 开发人员使用springboot只要基于特定方式进行配置 简化spring使用
SpringBoot 微框架: 5分钟 完成之前ssm中环境
springboot(微框架) = springmvc(控制器) + spring core(项目管理)
创建完整的独立的Spring应用程序
spring springmvc 只有一个容器嵌入的Tomcat,无需部署WAR文件
springboot 内嵌tomcat 应用跑在内嵌服务器简化Maven配置,自动配置Spring Springmvc,没有XML配置
几个依赖
springboot 项目中必须在**src/main/resources中放入application.yml(.properties)核心配置文件 名字必须为:application
springboot 项目中必须在**src/main/java中所有子包之外构建全局入口类型,xxApplication,入口类一个springboot项目只能有一个
约定的项目结构:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.4version>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
引入配置文件application.yml
src/main/resources/application.yml
server:
port: 8181 #自定义访问的端口号
servlet:
context-path: /boot #项目的根目录
编写入口类
@SpringBootApplication
public class Boot01Application {
//main方法里面必须有SpringApplication.run()方法,且参数为该入口类的class对象和main的参数args
public static void main(String[] args) {
SpringApplication.run(Boot01Application.class, args);
}
}
启动项目测试运行
没有报错的话表示项目环境搭建正常可以继续进行下一步操作
建立并创建控制器Controller
/**
* @author Liu
*/
@RestController
public class HelloController {
@RequestMapping("hello")
public String hello(){
System.out.println("hello");
return "hello";
}
}
访问项目
项目的路径为http://localhost:8181/boot/hello
http://localhost:(配置的端口号) / (配置的项目路径) / (访问路径)
测试访问
这里第一个简单的hello world就搭建完成了,比传统的ssm开发简洁了不少
入口类 SpringApplication
@SpringBootApplication 注解等价于:
说明: 在实际开发过程中生产环境和测试环境有可能是不一样的 因此将生产中的配置和测试中的配置拆分开,是非常必要的在springboot中也提供了配置文件拆分的方式. 这里以生产中项名名称不一致为例:
生产中项目名为: boot_prod
开发中项目名为: boot_dev
端口同时为: 8080
application.yml
#主配置文件 书写公共配置
server:
port: 8181 #自定义访问的端口号
#在主配置文件中,指定哪一个环境生效
spring:
profiles:
active: dev #指定那个环境配置生效 dev为环境配置文件的简名
application-dev.yml
#development:开发环境
server:
servlet:
context-path: /boot_dev
application-prod.yml
#product 生产环境的配置文件
server:
servlet:
context-path: /boot_prod #项目访问路径
测试路径:
说明: 往往在企业级开发过程中为SpringBoot应用启动时指定一个完整外部配置也是经常用到的,在SpringBoot中也提供这个方式来启动项目如
–spring.config.location=C:\Users\ASUS\IdeaProjects\JavaWeb\review\review\boot-01\src\main\resources\application-local.yml
在springboot中可以管理单个对象可以直接使用spring框架中注解形式创建。
@Component
通用的对象创建注解
@Controller
用来创建控制器对象@Service
用来创建业务层对象@Repository
用来创建DAO层对象 如何在springboot中像spring框架一样通过xml创建多个对象,在SpringBoot中也提供了相同注解如**@Configuration + @Bean
**注解进行创建
@Configuration
代表这是一个spring的配置类相当于Spring.xml配置文件@Bean
用来在工厂中创建这个@Bean注解标识的对象
管理复杂对象的创建
/**
* @author Liu
* SpringBoot配置类
*/
@Configuration
public class Beans {
// 复杂对象的创建
@Bean
public Calendar calendar(){
return Calendar.getInstance();
}
}
使用复杂对象还是通过@AutoWired注入属性
@SpringBootTest
class Boot01ApplicationTests {
@Autowired
private Calendar calendar;
@Test
void contextLoads() {
System.out.println("hello world");
System.out.println(calendar.getTime());
//hello world
//Mon Mar 21 13:41:30 CST 2022
}
}
@Configuration 用来在工厂中一次性创建多个对象
@Component 用来创建单个对象
实例:
注入基本类型、数组、日期类型、String
配置文件:
#基本数据类型 + string + date
name: 张三
age: 21
sex: true
price: 22.22
birthday: 2000/11/29 05:20:00 #spring中的日期格式为
#数组类型
array: arr1,arr2,arr3,arr4
list: list1,list2,list3,list4
map: "{'aa':'1234','bb':'2344'}"
测试:
@SpringBootTest
class Boot01ApplicationTests {
//基本数据类型 + string + date
@Value("${name}")
private String name;
@Value("${age}")
private Integer age;
@Value("${sex}")
private Boolean sex;
@Value("${price}")
private Double price;
@Value("${birthday}")
private Date birthday;
//数组类型
@Value("${array}")
private String[] array;
@Value("${list}")
private List<String> list;
@Value("#{${map}}")
private Map<String,String> map;
@Test
void di(){
System.out.println("基本数据类型 + string + date");
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("sex = " + sex);
System.out.println("price = " + price);
System.out.println("birthday = " + birthday);
System.out.println("数组类型");
System.out.println("array = ");
for (String s : array) {
System.out.println(s);
}
System.out.println("list = " + list);
System.out.println("map = " + map);
// 基本数据类型 + string + date
// name = 张三
// age = 21
// sex = true
// price = 22.22
// birthday = Wed Nov 29 05:20:00 CST 2000
// 数组类型
// array =
// arr1
// arr2
// arr3
// arr4
// list = [list1, list2, list3, list4]
// map = {aa=1234, bb=2344}
}
}
注意map集合的取值为 @Value("#{${map}}")
注意:这种方式需要提供set方法
#对象方式注入
user:
id: 1
username: 李四
age : 23
birthday : 2020/02/02
#对象属性集合类型
users:
- { id: 1, name: zhangsan, age: 20 ,birthday: 2000/11/16 }
- { id: 2, name: lisi, age: 21 ,birthday: 2000/12/11 }
- { id: 3, name: wangwu, age: 22 ,birthday: 2000/11/29 }
@SpringBootTest
@Component //必须提供component
@ConfigurationProperties(prefix = "user") //修饰范围在类上,用来指定前缀的属性,注入到对象中的属性名一致的属性当中
@Setter//必须提供属性的set方法
public class Test1 {
private Integer id;
private String username;
private Integer age;
private Date birthday;
private List <User> users;
@Test
void test(){
System.out.println("id = " + id);
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println("birthday = " + birthday);
users.forEach(user -> System.out.println(user));
// id = 1
// username = 李四
// age = 23
// birthday = Sun Feb 02 00:00:00 CST 2020
// User(id=1, name=zhangsan, age=20, birthday=Thu Nov 16 00:00:00 CST 2000)
// User(id=2, name=lisi, age=21, birthday=Mon Dec 11 00:00:00 CST 2000)
// User(id=3, name=wangwu, age=22, birthday=Wed Nov 29 00:00:00 CST 2000)
}
}
引入依赖构建自定义的元数据,否则idea会提示异常
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
jsp相关
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
dependency>
引入jsp运行插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
配置springmvc视图解析器
spring:
#配置视图解析器 视图前缀后缀
mvc:
view:
prefix: / # /代表访问项目中webapp中页面
suffix: .jsp
修改jsp无需重启应用
server:
servlet:
jsp:
init-parameters:
development: true
webapp的目录结构
两种方式
druid数据源、mybatis-springboot、mysql
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.4version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
说明:由于springboot整合mybatis版本中默认依赖mybatis 因此不需要额外引入mybati版本,否则会出现冲突
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource #指定数据源 为druid
driver-class-name: com.mysql.jdbc.Driver #指定驱动
url: jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=UTF-8 #url
username: root #username
password: 200011 #password
#mybatis相关配置
mybatis:
#注册mapper
mapper-locations: classpath:com/boot/mapper/*.xml
#起别名
type-aliases-package: com.boot.entity
扫描注册Dao
@SpringBootApplication
@MapperScan("com.boot.dao")
public class Boot01Application {
public static void main(String[] args) {
SpringApplication.run(Boot01Application.class, args);
}
}
实体类创建
/**
* @author Liu
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private Boolean gender;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
}
Dao接口 与 Mapper
//@Mapper
public interface UserDao {
void save(User user);
List<User> findAll();
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.boot.dao.UserDao">
<insert id="save" keyProperty="id" useGeneratedKeys="true" parameterType="user">
insert into user values(#{id},#{name},#{birthday},#{gender})
insert>
<select id="findAll" resultType="user">
select * from user
select>
mapper>
Service ServiceImpl
/**
* @author Liu
*/
public interface UserService {
void save(User user);
List <User> findAll();
}
@Service
@Transactional(rollbackFor = {Exception.class})
public class UserServiceImpl implements UserService{
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save(User user) {
userDao.save(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = {Exception.class})
public List <User> findAll() {
return userDao.findAll();
}
}
Controller层
@Controller
@RequestMapping("/users")
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping("save")
public String save(User user){
try {
userService.save(user);
return "redirect:/users/findAll";
} catch (Exception e) {
e.printStackTrace();
return "redirect:/index.jsp";
}
}
@RequestMapping("findAll")
public String findAll(Model model){
List <User> users = userService.findAll();
users.forEach(System.out :: println);
model.addAttribute("users",users);
return "success";
}
}
往往在开发过程中业务代码课程非常复杂频繁启动服务器测试,非常麻烦!这个时候使用本地测试就是一个很好的解决方案,springboot也提供了本地测试解决方案!
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
@SpringBootTest
@SpringBootTest
class Boot01ApplicationTests {
@Autowired
private Calendar calendar;
@Test
void contextLoads() {
System.out.println("hello world");
System.out.println(calendar.getTime());
//hello world
//Mon Mar 21 13:41:30 CST 2022
}
}
为了进一步提高开发效率,springboot为我们提供了全局项目热部署,日后在开发过程中修改了部分代码以及相关配置文件后,不需要每次重启使修改生效,在项目中开启了springboot全局热部署之后只需要在修改之后等待几秒即可使修改生效。
每次构建项目都需要引入该依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
只需要设置一次
Preferences | Build, Execution, Deployment | Compiler -> 勾选上 Build project automatically 这个选项
Main变成了restartedMain表示热部署生效
springboot框架 集成日志 logback 日志
常用处理java的日志组件 slf4j,log4j,logback,common-logging 等
All < Trace <
DEBUG < INFO < WARN < ERROR
< Fatal < OFF
日志级别由低到高:
日志级别越高输出的日志信息越多
注意:SpringBoot框架中默认根日志为INFO
logging:
level:
root: info #指定根日志级别(一般不推荐修改根日志,输出信息太多,推荐使用子日志)
com.boot.dao: debug #指定某个包中日志
file:
name: 日志.log #指定日志名称
path: C:\Users\ASUS\IdeaProjects\JavaWeb\review\review\boot-01\日志 #指定日志文件目录
@RestController
public class HelloController {
//声明日志成员
private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
@RequestMapping("/hello")
public String hello(){
System.out.println("======hello world=======");
logger.debug("DEBUG,{}","信息");
logger.info("INFO,{}","信息");
logger.warn("WARN,{}","信息");
logger.error("ERROR,{}","信息");
return "hello";
}
}
注意需要安装lombok插件
springboot是对原有项目中spring框架和springmvc的进一步封装,因此在springboot中同样支持spring框架中AOP切面编程,不过在springboot中为了快速开发仅仅提供了注解方式的切面编程.
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
需要在配置类里面配置切面
代码测试
/**
* @author Liu
*/
@Configuration
@Aspect
public class MyAspect {
private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
/**
* execution (* com.boot.service.*.*(..)) 表示用在service包下的所有方法上 返回值为任意 参数为任意
* @param joinPoint
*/
@Before("execution (* com.boot.service.*.*(..))")
public void before(JoinPoint joinPoint){
System.out.println("前置通知");
System.out.println(joinPoint.getTarget());
System.out.println(joinPoint.getSignature());
System.out.println(Arrays.toString(joinPoint.getArgs()));
}
@After("execution (* com.boot.service.*.*(..))")
public void after(JoinPoint joinPoint){
System.out.println("后置通知");
System.out.println(joinPoint.getTarget());
System.out.println(joinPoint.getSignature());
System.out.println(Arrays.toString(joinPoint.getArgs()));
}
@Around("execution (* com.boot.service.*.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("进入环绕通知");
Object proceed = null;
try {
proceedingJoinPoint.getTarget();//目标对象
proceedingJoinPoint.getSignature();//方法签名
proceedingJoinPoint.getArgs();//方法参数
proceed = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("目标方法执行之后回到环绕通知");
return proceed;
}
// 进入环绕通知
// 前置通知
// com.boot.service.UserServiceImpl@7b0ad3bc
// void com.boot.service.UserServiceImpl.save(User)
// [User(id=null, name=刘凯旋, gender=true, birthday=Sat Mar 26 00:00:00 CST 2022)]
// 后置通知
// com.boot.service.UserServiceImpl@7b0ad3bc
// void com.boot.service.UserServiceImpl.save(User)
// [User(id=56, name=刘凯旋, gender=true, birthday=Sat Mar 26 00:00:00 CST 2022)]
// 目标方法执行之后回到环绕通知
}
注意
用户访问当前系统,将自己本地计算机中文件通过浏览器上传到当前系统所在的服务器过程中称之为文件的上传
文件上传: 用户将自己计算机中文件 上传到 项目所在服务器过程、文件服务器、OSS 称之为文件上传
<body>
<h1>测试文件上传页面h1>
<%--
1. 表单提交方式必须是post
2. 表单的enctype属性必须为multipart/form-data
3. 后台接受变量名字要与文件选择name属性一致
--%>
<form action="${pageContext.servletContext.contextPath}/file/upload" method="post" enctype="multipart/form-data">
请选择上传的文件<input name="file" type="file"><br>
<input type="submit" value="点击上传">
form>
body>
#指定路径
file:
upload:
dir: C:\Users\ASUS\Desktop\文件
download:
dir: C:\Users\ASUS\Desktop\文件
private static final Logger log = LoggerFactory.getLogger(FileController.class);
//指定路径注入上传
@Value("${file.upload.dir}")
private String realPath;
@RequestMapping("newupload")
public String fileUploadNew(MultipartFile multipartFile , HttpServletRequest request){
log.debug("hello");
System.out.println("hello");
try {
//将上传的文件保存在目录中
multipartFile.transferTo(new File(realPath,multipartFile.getOriginalFilename()));//文件上传
} catch (IOException e) {
e.printStackTrace();
}
//重定向到上传页面
return "redirect:../index.jsp";
}
spring:
#修改文件上传大小限制
servlet:
multipart:
max-file-size: 1024MB #用来指定服务端最大文件大小
max-request-size: 1024MB #用来控制文件上传大小的限制
文件下载: 将服务器某个资源文件下载到用户本地计算机过程称之为文件下载
<h1>测试文件下载h1>
<a href="${pageContext.servletContext.contextPath}/file/download?fileName=你好.txt">你好.txta>
@Controller
public class downFileController {
@Value("${file.download.dir}")
private String realPath;
@RequestMapping("download")
public void downLoad(String name, HttpServletResponse response) throws IOException {
File file = new File(realPath, name);
FileInputStream inputStream = new FileInputStream(file);
response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(name,"UTF-8"));
ServletOutputStream outputStream = response.getOutputStream();
// int len = 0;
// byte[] b = new byte[1024];
//
// while (true){
// len = inputStream.read(b);
// if(len == -1) {
// break;
// }
// outputStream.write(b,0,len);
// }
// inputStream.close();
FileCopyUtils.copy(inputStream,outputStream);
}
}
Spring Boot拦截器(Interceptor)详解_hresh的博客-CSDN博客_spring拦截器
**拦截器(Interceptor)**同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
你可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置…
在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或多个)。
Spring Interceptor是一个非常类似于Servlet Filter 的概念 。
Interceptor 作用
如果你需要自定义 Interceptor 的话必须实现
org.springframework.web.servlet.HandlerInterceptor
接口或继承org.springframework.web.servlet.handler.HandlerInterceptorAdapter
类,并且需要重写下面下面 3 个方法:
public class MyInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("========1=======");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("========4=======");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("========6=======");
}
}
public class MyInterceptor2 implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(MyInterceptor2.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("=======2======");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("=======3======");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("=======5======");
}
}
实现WebMvcConfigurer接口并重写addInterceptors方法
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor("拦截器")
// .addPathPatterns("拦截路径")
// .excludePathPatterns("排除路径")
// .order("指定执行顺序")
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/file/down")
.order(1);
registry.addInterceptor(new MyInterceptor2())
.addPathPatterns("/**")
.excludePathPatterns("/file/down")
.order(2);
}
}
@RestController
public class HelloController {
//声明日志成员
private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
@RequestMapping("/hello")
public String hello(){
System.out.println("======2=====");
return "hello";
}
/*
======1=====
======2=====
=====3=====
=====4=====
*/
}
注意:order用来执行多个拦截器的执行顺序,order书写是自然数,按照自然数顺序执行
在pom文件中设置打包方式为war包
war
配置入口类
@SpringBootApplication
@MapperScan("com.boot.dao")
//1.继承SpringBootServletInitializer
//2.覆盖configure方法
public class Boot01Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Boot01Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Boot01Application.class);
}
}
在插件中指定入口类
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
<jvmArguments>-Dfile.encoding=UTF-8jvmArguments>
<mainClass>com.baizhi.Boot01ApplicationmainClass>
configuration>
plugin>
plugins>
build>
排除内嵌的tomcat
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
<scope>providedscope>
dependency>
打包测试
注意:
设置打包方式为jar
jar
注意:默认方式也是jar
执行打包
测试访问
C:\Users\ASUS\IdeaProjects\JavaWeb\review\review\boot-01\target>java -jar boot-01-0.0.1-SNAPSHOT.jar
注意:springboot部署jsp时,插件版本必须指定为1.4.2版本,并进行jsp打包配置才可以,其他版本均不支持!!!
修改插件版本
在项目的pom.xml配置文件中build标签中修改
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>1.4.2.RELEASEversion>
plugin>
plugins>
指定jsp打包配置
在项目的pom.xml配置文件中build标签中加入配置
<resources>
<resource>
<directory>src/main/webappdirectory>
<targetPath>META-INF/resourcestargetPath>
<includes>
<include>**/**include>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/**include>
includes>
<filtering>falsefiltering>
resource>
resources>
重新打包测试
Thymeleaf是一个用于web和独立环境的现代服务器端Java模板引擎。
——摘自官网https://www.thymeleaf.org/
Thymeleaf
是跟Velocity、FreeMarker类似的模板引擎,它可以完全替代JSP,相较与其他的模板引擎相比, Thymeleaf在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
spring:
thymeleaf:
cache: false #关闭缓存
prefix: classpath:templates/
suffix: .html
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(){
System.out.println("hello");
return "index";
}
}
测试访问…
前提:使用时必须在头部加入命名空间
使用时必须在页面中加入thymeleaf如下命名空间:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
model.addAttribute("name","张三"); 或 request.setAttribute("name","小黑");
<span th:text="${name}"/> --->获取数据
model.addAttribute("name","张三");
model.addAttribute("username","小陈");
<span th:text="${name}"/>
<span th:utext="${name}"/>
将数据赋值给表单元素
<input type="text" th:value="${username}"/>
# 总结
- 1.使用 th:text="${属性名}" 获取对应数据,获取数据时会将对应标签中数据清空,因此最好是空标签
- 2.使用 th:utext="${属性名}" 获取对应的数据,可以将数据中html先解析在渲染到页面
- 3.使用 th:value="${属性名}" 获取数据直接作为表单元素value属性
model.addAttribute("user",new User("21","xiaochen",23,new Date()));
id:<span th:text="${user.id}">span>
name:<span th:text="${user.name}">span>
age:<span th:text="${user.age}">span>
bir: <span th:text="${user.bir}">span> ==== <span th:text="${#dates.format(user.bir, 'yyyy-MM-dd HH:mm')}">span> 日期格式化
model.addAttribute("user",new User("21","xiaochen",23,new Date()));
<span th:if="${user.age} eq 23">
青年
span>
<span th:if="${user.age == 23}">
青年
span>
# 运算符
gt:great than(大于)>
ge:great equal(大于等于)>=
eq:equal(等于)==
lt:less than(小于)<
le:less equal(小于等于)<=
ne:not equal(不等于)!=
<ul th:each="user:${users}">
<li th:text="${user.id}">li>
<li th:text="${user.name}">li>
<li th:text="${user.age}">li>
<li th:text="${#dates.format(user.bir,'yyyy-MM-dd')}">li>
ul>
<ul th:each="user,userStat:${users}">
<li><span th:text="${userStat.count}"/>-<span th:text="${user.id}"/>li> 获取遍历次数 count 从1开始 index 从0开始
<li><span th:text="${userStat.odd}"/>-<span th:text="${user.name}"/>li> 获取当前遍历是否是奇数行
<li><span th:text="${userStat.even}"/>-<span th:text="${user.age}"/>li> 获取当前遍历是否是偶数行
<li><span th:text="${userStat.size}"/>-<span th:text="${user.bir}"/>li> 获取当前集合的总条数
ul>
# 使用thymeleaf模板项目中静态资源默认放在resources路径小static目录中
项目中放入对应静态资源
页面中引入
<link rel="stylesheet" th:href="@{/css/index.css}">
<script th:src="@{/js/jquery-min.js}">script>
注意: @{/}代表通过thymeleaf语法动态获取应用名
<script>
const ctx = '[[@{/}]]';
</script>
注意:[[书写thymeleaf语法]],这里[[]]是thymeleaf内嵌表达式
REST全称是Representational State Transfer
,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:“我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。” 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。
RestFul: 是一种以网络为基础构架一种架构风格 一个架构符合Rest设计原则和约束成这个架构为RestFul。
Rest 词: 更新技术 组件 服务 让我们web请求能够利用web中标准 和 能力 更好描述架构
REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。
Restful 一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
web开发: 实现所有功能
restful: 软件设计风格 标准 简洁 层次 优雅
rest设计原则 和 约束架构称之为restFul
http://127.0.0.1/item/queryUser.action?id=1 查询,GET
http://127.0.0.1/item/saveUser.action 新增,POST
http://127.0.0.1/item/updateUser.action 更新,PUT
http://127.0.0.1/item/deleteUser.action?id=1 删除,DELETE
**注意:**传统的操作是没有问题的,大神认为是有问题的,有什么问题呢?你每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser ,修改的时候用了updateUser,其实完全没有这个必要,我使用了get请求,就是查询.使用post请求,就是新增的请求,PUT就是修改,delete就是删除,我的意图很明显,完全没有必要做描述,这就是为什么有了restful.
【GET】 /users # 查询用户信息列表
【GET】 /users/1001 # 查看某个用户信息
【POST】 /users # 新建用户信息
【PUT】 /users/1001 # 更新用户信息(全部字段)
【PATCH】 /users/1001 # 更新用户信息(部分字段)
【DELETE】 /users/1001 # 删除用户信息
# 1.使用名词而不是动词
- 不要使用:
如:
/getAllUsers get /users get /users/002
/createNewUser post /users
/deleteAllUser delete /users/001
# 2.Get方法和查询参数不应该涉及状态改变
- 使用PUT, POST 和DELETE 方法 而不是 GET 方法来改变状态,不要使用GET 进行状态改变
# 3.使用复数名词
- 不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。
如:
/cars 而不是 /car
/users 而不是 /user
/products 而不是 /product
/settings 而不是 /setting
/orders 而不是 /order
# 4. 使用子资源表达关系
- 如果一个资源与另外一个资源有关系,使用子资源:
如:
GET /cars/711/drivers/ 返回 car 711的所有司机
GET /cars/711/drivers/4 返回 car 711的4号司机
GET /users/11/pets 返回 user 11的所有宠物
GET /users/11/pets/2 返回 user 11的2号宠物
# 5.使用Http头声明序列化格式
- 在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定
如:
Content-Type 定义请求格式
Accept 定义系列可接受的响应格式
# 6.为集合提供过滤 排序 选择和分页等功能
- Filtering过滤:使用唯一的查询参数进行
GET /cars?color=red 返回红色的cars
GET /cars?seats<=2 返回小于两座位的cars集合
- Sorting排序:允许针对多个字段排序
GET /cars?sort=-manufactorer,+model
这是返回根据生产者降序和模型升序排列的car集合
- Field selection
移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。
GET /cars?fields=manufacturer,model,id,color
- Paging分页
使用 limit 和offset.实现分页,缺省limit=20 和offset=0;
GET /cars?offset=10&limit=5
为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.
链接到下一页或上一页可以在HTTP头的link规定,遵循Link规定:
Link: ; rel="next",; rel="last",; rel="first",; rel="prev",
# 7.版本化你的API 支付宝 v1 v2 v3
- 使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5.
一般在Url后面使用?v
/blog/api/v1
# 8. 使用Http状态码处理错误
- 如果你的API没有错误处理是很难的,只是返回500和出错堆栈不一定有用
- Http状态码提供70个出错,我们只要使用10个左右:
`200 – OK – 一切正常
`201 – OK – 新的资源已经成功创建
`204 – OK – 资源已经成功删除
`304 – Not Modified – 客户端使用缓存数据
`400 – Bad Request – 请求无效,需要附加细节解释如 "JSON无效"
`401 – Unauthorized – 请求需要用户验证
`403 – Forbidden – 服务器已经理解了请求,但是拒绝服务或这种请求的访问是不允许的。
`404 – Not found – 没有发现该资源
`422 – Unprocessable Entity – 只有服务器不能处理实体时使用,比如图像不能被格式化,或者重要字段丢失。
`500 – Internal Server Error – API开发者应该避免这种错误。
使用详细的错误包装错误: 状态码 数据 header头信息
{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}
/**
* @author Liu
*/
@RestController
@RequestMapping("users")
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 保存用户
*
* @param user
* @return
*/
@PostMapping
public ResponseEntity <Void> saveUser(@RequestBody User user) {
System.out.println(user);
try {
userService.save(user);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
/**
* 删除用户
*
* @param id
* @return
*/
@DeleteMapping("{id}")
public ResponseEntity<Void> deleteUser(@PathVariable("id") Integer id) {
try {
userService.delete(id);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
/**
* 更新用户
*
* @param user
* @return
*/
@PutMapping
public ResponseEntity<Void> updateUser(@RequestBody User user) {
userService.update(user);
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
/**
* 用户详情
*
* @param id
* @return
*/
@GetMapping("{id}")
public ResponseEntity<User> findUserById(@PathVariable("id") Integer id) {
User user = userService.findById(id);
if (user == null) {
return new ResponseEntity<User>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
/**
* 用户列表
*
* @return
*/
@GetMapping
public ResponseEntity<List <User>> users() {
List<User> users = null;
try {
users = userService.findAll();
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<List<User>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
}
当控制器中的任意一个方法出现异常时,如果该控制器的方法没有处理异常的机制,就会自动进入resolveException方法,处理异常
@Component
public class GlobalExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
return new ModelAndView("500");
}
}
/**
* 全局异常处理之RestFul
*/
@ControllerAdvice
public class GlobalExceptionHandlerRest {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResponseEntity<String> exceptionHandler(Exception e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CORS跨域
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
。
它允许浏览器向跨源服务器,发出XMLHttpRequest(ajax)请求,从而克服了AJAX只能同源使用的限制
。
同源策略
同源策略
[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。 同源策略是浏览器安全的基石。源
源
[origin]就是协议、域名和端口号。例如:http://www.baidu.com:80这个URL。同源
若地址里面的协议、域名和端口号均相同
则属于同源。
*同源举例*
- 例如判断下面的URL是否与 http://www.a.com/test/index.html 同源
http://www.a.com/dir/page.html --------->同源
http://www.child.a.com/test/index.html ->不同源,域名不相同
https://www.a.com/test/index.html ------>不同源,协议不相同
http://www.a.com:8080/test/index.html -->不同源,端口号不相同
哪些操作不受同源限制
,
,
,
等。哪些操作受到同源限制
出现错误:Access-Control-Allow-Origin
@RestController
@RequestMapping("demos")
@CrossOrigin
public class DemoController {
@GetMapping
public String demos() {
System.out.println("========demo=======");
return "demo ok";
}
}
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用
corsConfiguration.addAllowedHeader("*"); // 2允许任何头
corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等)
source.registerCorsConfiguration("/**", corsConfiguration);//4处理所有请求的跨域配置
return new CorsFilter(source);
}
}
Jasypt 也即Java Simplified Encryption是Sourceforge.net上的一个开源项目。在当地时间11月23号的通告中,Jasypt 1.4的新特征包括:加密属性文件(encryptable properties files)、Spring Framework集成、加密Hibernate数据源配置、新的命令行工具、URL加密的Apache wicket集成以及升级文档。
根据Jasypt文档,该技术可用于加密任务与应用程序,例如加密密码、敏感信息和数据通信、创建完整检查数据的sums. 其他性能包括高安全性、基于标准的加密技术、可同时单向和双向加密的加密密码、文本、数字和二进制文件。Jasypt也可以与Acegi Security整合也即Spring Security。Jasypt亦拥有加密应用配置的集成功能,而且提供一个开放的API从而任何一个Java Cryptography Extension都可以使用Jasypt。
Jasypt还符合RSA标准的基于密码的加密,并提供了无配置加密工具以及新的、高可配置标准的加密工具。
引入依赖
<dependency>
<groupId>com.github.ulisesbocchiogroupId>
<artifactId>jasypt-spring-boot-starterartifactId>
<version>2.0.0version>
dependency>
编写配置
jasypt:
encryptor:
algorithm: PBEWithMD5AndDES #加密方法
#一般通过程序传递密钥
password: fhajfuifew788fcksjbv7823wg #加密密钥
通过程序方式传递密钥
-Djasypt.encryptor.password=fhajfuifew788fcksjbv7823wg
加密解密
@Autowired
private StringEncryptor encryptor;
@Test
void contextLoads() {
System.out.println(encryptor.encrypt("root"));
// dZ6c01yHE12laTC+9/7LaA==
System.out.println(encryptor.encrypt("200011"));
// aXsQC6VwA3IkUGAgg03/bw==
System.out.println(encryptor.encrypt("localhost"));
System.out.println(encryptor.decrypt("dZ6c01yHE12laTC+9/7LaA=="));
System.out.println(encryptor.decrypt("aXsQC6VwA3IkUGAgg03/bw=="));
}
使用
mysql:
host: ENC(vz5962h9bO2TnxVRwygaRlcytCVaLEaX)
spring:
thymeleaf:
cache: false #true为缓存开启,开发过程中建议false
prefix: classpath:/templates/ #指定模板位置
suffix: .html #指定后缀
#配置数据库
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://${mysql.host}:3306/springdb?characterEncoding=UTF-8
username: ENC(5oInGtk4Bd3JnXjOKxqM2Q==)
password: ENC(WwK3Y72l6r8rfYUNQXT4jQ==)