前言
serverlet
- 服务器端小程序,第一代java web开发技术,基于java实现了一套用于动态网站的API
- Tomcat\Jetty\Undertow都是Servelet容器,用来管理Servelet类
jsp
- 在html页面中嵌入java代码,用于java web应用程序的用户界面部分,通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页
- serverlet将html代码以字符串的形式向外输出,编写html文档就是在拼接字符串
- 第二代java web技术,是对serverlet的封装
war包和jar包
- JAR文件的目的是把类和相关的资源封装到压缩的归档文件中
- 对于WAR文件来说,一个WAR文件代表了一个Web应用程序,它可以包含 Servlet、HTML页面、Java类、图像文件,以及组成Web应用程序的其他资源,而不仅仅是类的归档文件
Maven
- Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理
spring boot特点
- 简化xml配置
- 内嵌serverlet容器
Tomcat
- Apache是web服务器,Tomcat是应用(java)服务器,它只是一个servlet容器,是Apache的扩展
- Apache是普通服务器,本身支支持html普通网页,可以通过cgi扩展支持php,可以与tomcat连通
- tomcat用于执行jsp和serlet,核心组件包括
- Web 容器:完成 Web 服务器的功能
- Servlet 容器:名字为 catalina,用于处理 Servlet 代码
- JSP 容器:用于将 JSP 动态网页翻译成 Servlet 代码
Spring
IOC
- IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。
- 在配置文件(例如 Bean.xml)中,对各个对象以及它们之间的依赖关系进行配置;
- 我们可以把 IoC 容器当做一个工厂,这个工厂的产品就是 Spring Bean;
- 容器启动时会加载并解析这些配置文件,得到对象的基本信息以及它们之间的依赖关系;
- IoC 利用 Java 的反射机制,根据类名生成相应的对象(即 Spring Bean),并根据依赖关系将这个对象注入到依赖它的对象中。
- IoC 思想基于 IoC 容器实现的,IoC 容器底层其实就是一个 Bean 工厂。Spring 框架为我们提供了两种不同类型 IoC 容器,它们分别是 BeanFactory 和 ApplicationContext
- 控制反转核心思想就是由 Spring 负责对象的创建。在对象创建过程中,Spring 会自动根据依赖关系,将它依赖的对象注入到当前对象中,这就是所谓的“依赖注入”
属性注入
- 通过配置文件加setter注入
Bean的声明周期
- Bean 的实例化
- Bean 属性赋值
- Bean 的初始化
- Bean 的使用
- Bean 的销毁
后置处理器
- postProcessBeforeInitialization() 方法:在 Bean 实例化、属性注入后,初始化前调用。
- postProcessAfterInitialization() 方法:在 Bean 实例化、属性注入、初始化都完成后调用。
自动装配
-
把 Spring 在 Bean 与 Bean 之间建立依赖关系的行为称为“装配”
-
自动装配而这一过程是在完全不使用任何
和 元素 ref 属性的情况下进行的,分为byName和byType <bean id="employee" class="net.biancheng.c.Employee" autowire="byName">
基于注解的自动装配
-
引入依赖
-
开启组件扫描
-
使用注解定义 Bean
-
依赖注入
AOP面向切面编程
- 在A操作之后,B操作之前的通用型操作,通常用来进行事务和日志
- 术语:连接点,切入点,切面,通知,目标
基于注解的AspectJ AOP
- 启用 @AspectJ 注解支持
- 定义切面 @Aspect:类
- 定义切点 @Pointcut:方法
- 定义通知
Spring事务
- ACID特性
- 开启注解事务
- 使用 @Transactional 注解
Spring MVC
MVC框架
Spring MVC常用组件
Spring MVC项目
- 浏览器发送一个请求,若请求地址与 web.xml 中配置的前端控制器(DispatcherServlet)的 url-pattern 相匹配,则该请求就会被前端控制器 DispatcherServlet 拦截;
- 前端控制器(DispatcherServlet )会读取 SpringMVC 的核心配置文件,通过组件扫描获取到所有的控制器(Contorller);
- 将请求信息和控制器中所有控制器方法标识的 @RequestMapping 注解的 value、method 等属性值进行匹配。若匹配成功,则将请求交给对应的 @RequestMapping 注解所标识的控制器方法处理;
- 处理请求的方法会返回一个字符串类型的视图名称,该视图名称会被 Spring MVC 配置文件中配置的视图解析器(ViewResolver)解析真正的视图(View)对象,最终展示给客户端。
package net.biancheng.c.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/")
public String sayHello() {
//视图名,视图为:视图前缀+index+视图后缀,即 /WEB-INF/template/index.html
return "index";
}
@RequestMapping("/login")
public String welcome() {
//视图名,视图为:视图前缀+login+视图后缀,即 /WEB-INF/template/login.html
return "login";
}
@RequestMapping("/register")
public String success() {
//视图名,视图为:视图前缀+register+视图后缀,即 /WEB-INF/template/register.html
return "register";
}
}
SpringMVC执行流程
@RequestMapping注解
-
使用方式
- 修饰方法:要访问该方法,请求路径必须与这个value值相同
- 修饰类:要访问类中的任意方法,都要带上这个父路径
-
属性
-
value属性:用于设置请求地址
-
name属性:用于注释
-
method属性:method 属性的取值是一个 RequestMethod 类型的数组,表示一个控制器方法支持多种方式的请求,常用的请求方式有 GET、POST、DELETE、PUT 等。如果一个控制器方法没有设置 @RequestMapping 注解的 method 属性,则说明该控制器方法支持全部请求类型,可以处理所有类型的请求。
-
params属性:取值是一个字符串类型的数组,用于指定请求中的参数,只有当请求中携带了符合条件的参数时,控制器方法才会对该请求进行处理
@RequestMapping(value = "/testParam", params = {"name=z5onk0", "url=http://www.baidu.com"}) @ResponseBody public String testParam() { return "success"; } //以上代码表示,只有当请求中同时携带 name 和 url 两个请求参数,且参数值必须分别为 “z5onk0” 和“http://www.baidu.com”时,控制器方法 testParam() 才会对该请求进行处理 。
-
headers属性:取值是一个字符串类型的数组,用于设置请求中请求头信息,只有当请求中携带指定的请求头信息时,控制器方法才会处理该请求
-
Spring MVC获取请求参数
-
通过HttpServletRequest
@RequestMapping("/getRequestParam") public String requestParam(HttpServletRequest request) { String name = request.getParameter("name"); String url = request.getParameter("url"); System.out.println("name:" + name); System.out.println("url:" + url); return "index"; }
-
通过形参获取:在 Controller 的控制器方法中设置与请求参数同名的形参,以获取请求中携带的参数。需要注意这种方式是无视参数的数据类型的,我们可以在控制器方法中使用 String 字符串类型的形参接收所有的请求参数;并且不适用于参数过多的情况
@RequestMapping("/test") public String test(String name, String language) { System.out.println("a:" + a); System.out.println("b:" + b); return "success"; }
-
通过实体类对象获取(推荐):在 Controller 控制器方法的形参中设置一个实体类形参,如果请求参数的参数名与实体类中的属性名一致,那么 Spring MVC 会自动将请求参数封装到该实体类对象中
-
在net.biancheng.c.entity 包下,创建一个名为 User 的实体类
package net.biancheng.c.entity; public class User { private String UserId; private String UserName; private Integer age; public String getUserId() { return UserId; } public void setUserId(String userId) { UserId = userId; } public String getUserName() { return UserName; } public void setUserName(String userName) { UserName = userName; } @Override public String toString() { return "User{" + "UserId='" + UserId + '\'' + ", UserName='" + UserName + '\'' + ", age=" + age + '}'; } }
-
创建一个名为 UserController 的 Controller 类
package net.biancheng.c.controller; import net.biancheng.c.entity.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class UserController { /** * 通过实体类获取请求参数 * * @param user * @return */ @RequestMapping("/getUser") public String getUser(User user) { System.out.println("userId:" + user.getUserId()); System.out.println("userName:" + user.getUserName()); System.out.println("password:" + user.getPassword()); return "success"; } }
-
Spring MVC域对象共享
-
Controller 在接收到 Model 层返回的模型数据后,下一步就是将模型数据通过域对象共享的方式传递给 View 视图进行渲染,最终返回给客户端展示
-
域对象是服务器在内存上创建的一块存储空间,主要用不同动态资源之间的数据传递和数据共享。在 Spring MVC 中,常用的域对象有 request 域对象、session 域对象、application 域对象等
-
方式:
-
使用 Servlet API 向 request 域对象中共享数据
@RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request) { request.setAttribute("testScope", "hello,Servet API"); return "success"; }
-
使用 ModelAndView 向 request 域对象中共享数据
-
model 负责数据共享,而 view 则主要用于设置视图,实现页面的跳转
@RequestMapping("/testModelAndView") public ModelAndView testModelAndView() { /** * ModelAndView有Model和View的功能 * Model主要用于向请求域共享数据 * View主要用于设置视图,实现页面跳转 */ ModelAndView mav = new ModelAndView(); //向请求域共享数据 mav.addObject("testScope", "hello,ModelAndView"); //设置视图,实现页面跳转 mav.setViewName("success"); return mav; }
-
-
使用 Map 向 request 域对象中共享数据
@RequestMapping("/testMap") public String testMap(Map<String, Object> map) { map.put("testScope", "hello,Map"); return "success"; }
-
-
实例
-
定义user和product实体类;定义userDao和productDao类,将多个user和product放入字典和列表中,并暴露方法;定义userService和productService,对Dao暴露的方法进行封装
-
定义LoginController
@Controller public class LoginController { @Autowired private UserService userService; @RequestMapping("/user") public String sayHello() { return "user"; } @RequestMapping("/login") public String login(User user, HttpServletRequest request) { User user2 = userService.getUserByUserName(user.getUserName()); if (user2 != null && user2.getPassword().equals(user.getPassword())) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user2); return "redirect:/getProductList"; } request.setAttribute("msg", "账号或密码错误!"); return "user"; } }
-
定义ProductController
@Controller public class ProductController { @Autowired private ProductService productService; @RequestMapping("/getProductList") public ModelAndView getProductList() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("productList"); List<Product> productList = productService.getProductList(); modelAndView.addObject(productList); return modelAndView; } @RequestMapping("/getProduct") public String getProduct(Integer productId, Model model) { Product productById = productService.getProductById(productId); model.addAttribute("product", productById); return "product"; } }
-
Spring MVC视图和视图解析器
- 无论控制器方法的返回值是哪种类型,Spring MVC 内部最终都会将它们封装成一个 ModelAndView 对象
- 仅仅是一个 String 类型的逻辑视图名(View Name)而已,例如“success”、“index”等。这种情况下,Spring MVC 就需要借助 ViewResolver(视图解析器)将 ModelAndView 对象中逻辑视图名解析为真正的 View 视图对象,然后才能响应给客户端展示
- 需要在视图解析器中配置视图前缀和视图后缀,用来转化控制器返回的字符串
Spring MVC请求转发与重定向
-
请求转发
-
当控制器方法中所设置的逻辑视图名称以“forward:”为前缀时,该逻辑视图名称不会被 Spring MVC 配置的视图解析器解析,而是会将前缀“forward:”去掉,以剩余部分作为最终路径通过转发的方式实现跳转
-
通过String类型的返回值实现转发
@RequestMapping("/testDispatcher") public String testDispatcher() { return "forward:/login"; } //即路径“/testDispatcher”请求最终会被转发到 “/login”上进行处理
-
通过 ModelAndView 实现转发
@RequestMapping("/testDispatcher") public ModelAndView testDispatcher() { ModelAndView modelAndView = new ModelAndView(); //设置逻辑视图名 modelAndView.setViewName("forward:/login"); return modelAndView; }
-
-
重定向
- 当控制器方法中所设置的视图名称以“redirect:”为前缀时,该视图名称不会被 Spring MVC 配置的视图解析器解析,而是会将前缀“redirect:”去掉,以剩余部分作为最终路径通过重定向的方式实现跳转
- 通过String类型的返回值实现转发
- 通过 ModelAndView 实现转发
- 重定向和转发的区别:转发不会改变导航栏里的url
Spring MVC实现RESTful
-
通过 @RequestMapping +@PathVariable 注解的方式,来实现 RESTful 风格的请求
-
通过@RequestMapping 注解的路径设置
@RequestMapping("/testRest/{id}/{username}", method = RequestMethod.DELETE)
-
通过 @PathVariable 注解绑定参数
@RequestMapping("/testRest/{id}/{username}") public String testRest(@PathVariable("id") String id, @PathVariable("username") String username) { System.out.println("id:" + id + ",username:" + username); return "success"; }
Spring MVC类型转化器
-
作用是在控制器方法对请求进行处理前,先获取到请求发送过来的参数,并将其转换为控制器方法指定的数据类型,然后再将转换后的参数值传递给控制器方法的形参,这样后台的控制器方法就可以正确地获取请求中携带的参数了
-
Spring MVC 对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换
-
对于一些较为复杂类型的转换,例如 String 转换 Date 类型,以及开发人员自定义格式的数据的转换等,就需要我们根据自身的需求开发自定义类型转换器来转换
/** * 自定义日期转换器 */ public class MyDateConverter implements Converter<String, Date> { private String datePatten = "yyyy-MM-dd"; @Override public Date convert(String source) { System.out.println("前端页面传递过来的时间为:" + source); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePatten); try { return simpleDateFormat.parse(source); } catch (ParseException e) { throw new IllegalArgumentException("无效的日期格式,请使用正确的日期格式" + datePatten); } } } //还需要将其注册到Spring MVC的核心配置文件中,才能生效
Spring MVC格式化器
-
Spring 提供了一个 Formatter
接口, T 表示目标数据类型,它被称为格式化转换器;特殊数据都要经过一定的格式化处理才能够正常使用 -
同Converter的区别:Formatter 的源类型必须是 String 类型,而 Converter 的源类型可以是任意数据类型
-
定义格式化器需要重写parse和print方法
-
日期格式化
-
DateTimeFormat 注解可对 java.util.Date、java.util.Calendar、java.long.Long 等时间类型的数据进行标注
-
控制器中:
@DateTimeFormat(pattern = "yyyy-MM-dd") private Date date;
-
模板中:
<tr> <td>日期:</td> <td th:text="${#dates.format(market.getDate(),'yyyy-MM-dd')}"></td> </tr>
-
-
数值格式化
-
@NumberFormat 注解可以用来格式化任何数字基本类型
-
注解为货币类型
@NumberFormat(style = NumberFormat.Style.CURRENCY) private BigDecimal money;
-
JSON数据交互
-
JSON转换注解
-
实例
-
controller
/** * 查看或回显商品信息,get:查看商品信息 update:为修改页回显的商品信息 */ @ResponseBody @RequestMapping("/product/{productId}") public Product getProduct(@PathVariable("productId") String productId) { Product product = productDao.getProductById(productId); return product; } /** * 新增商品 * * @param product * @return */ @ResponseBody @RequestMapping(value = "/product", method = RequestMethod.POST) public Product addProduct(@RequestBody Product product) { productDao.addProduct(product); return product; }
-
product_list.html
-
主页面添加表格用来展示商品信息
<tbody th:id="tb">
-
通过ajax返回商品信息
$(function () { $.ajax({ //请求路径 url: "http://localhost:8080/springmvc-json-demo/getProductList1", //请求类型 type: "get", //定义发送请求的数据格式为JSON字符串 contentType: "application/json;charset=utf-8", //定义回调响应的数据格式为JSON字符串,该属性可以省略 dataType: "json", //成功响应的结果 success: function (data) { if (data != null) { var html = ""; for (var i = 0; i < data.length; i++) { var product = data[i]; html += "<tr>" + "<td>" + product.productId + "</td>" + "<td>" + product.productName + "</td>" + "<td>" + product.price + "</td>" + "<td>" + product.stock + "</td>" + "<td><button onclick='lookPage(" + product.productId + ")'>查看商品</button>" + "<button onclick='editPage(" + product.productId + ")';>修改商品</button>" + "<button onclick='deletePro(" + product.productId + ")';>删除商品</button></td>" + "</tr>" } $("#tb").html(html); } } }); })
-
首先通过css将增删查改的div块设置为不可见
<style type="text/css"> #addWindow, #editWindow { display: none; position: absolute; top: 25%; left: 25%; width: 30%; height: 40%; padding: 20px; border: 3px solid #ccc; background-color: white; z-index: 2; overflow: auto; } </style>
-
每个商品信息栏后有增删查改,通过click方法,将addWindow/editWindow设置为可见
$(document).ready(function () { //点击新增商品,弹出新增商品弹窗 $("#btn").click(function () { $("#addWindow").slideDown(300); //slideDown方法以滑动方式显示被选元素 $("#backGround").show(); });
-
addWindow用来展示增加商品的页面
<div id="addWindow"> <label id="x" style="position: absolute;top:2px;left: 95%;font-size: 25px;">x</label> <h4>新增商品</h4> <form th:action="@{/product}" method="post"> <table id="table-box" style="margin: auto"> <tr> <td>商品 ID:</td> <td><input type="text" id="productId" name="productId" required></td> </tr> <tr> <td>商品名称:</td> <td><input type="text" id="productName" name="productName" required></td> </tr> <tr> <td colspan="2" align="center"><input id="addPro" type="submit" value="新增商品"> <input type="reset" id="reset" value="重置"> </td> </tr> </table> </form> </div> <div id="backGround"></div>
-
填完商品信息,点击提交后,通过jquery click调用add方法,向后端请求数据,返回的数据append到商品详情页
$("#addPro").click(function () { add(); $("#addWindow").slideUp(300); $("#backGround").hide(); $("#reset").trigger("click") }); $.ajax({ //请求路径 url: "http://localhost:8080/springmvc-json-demo/product", //请求类型 type: "post", data: JSON.stringify({ productId: productId, productName: productName, }), //定义发送请求的数据格式为JSON字符串 contentType: "application/json;charset=utf-8", //定义回调响应的数据格式为JSON字符串,该属性可以省略 dataType: "json", //成功响应的结果 success: function (data) { if (data != null) { var product = data; var html = "<tr>" + "<td>" + product.productId + "</td>" + "<td>" + product.productName + "</td>" + "<td><button onclick='lookPage(" + product.productId + ")'>查看商品</a>" + "</tr>"; $("#tb").append(html); } }
-
-
Spring MVC拦截器
- 定义拦截器
- 通过实现 HandlerInterceptor 接口,重写其方法,来实现对拦截器类的定义
- 配置拦截器
- 通过
子标签配置全局拦截器 - 通过 子标签配置全局拦截器
- 通过mvc:interceptor子标签对拦截路径进行配置
- 通过
- 通过拦截器实现用户登录权限验证
异常处理
- DefaultHandlerExceptionResolver默认处理器
- ResponseStatusExceptionResolver,被用来解析 @ResponseStatus 注解标注的自定义异常
- ExceptionHandlerExceptionResolver 可以在控制器方法出现异常时,调用相应的 @ExceptionHandler 方法
文件上传
- 编写form表单
- 配置文件解析器(MultipartResolver )
- 编写控制器方法
创建Spring Boot项目
-
使用Maven创建
-
在poc.xml中添加如下配置
- 父工程依赖
<!--父工程,有这个项目即为springboot项目--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent>
- Spring Web依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
-
reload项目,安装Spring Boot依赖
-
创建启动应用,创建控制器
-
默认端口8080,可在src/main/resources下的appilication.properties文件中修改端口号
server.port=8090
starter启动器
-
引入starter依赖,自动解决web所有配置
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
-
Spring Boot项目的父级依赖,被称为版本仲裁中心,对项目内常用依赖进行统一管理
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath/> </parent>
Spring Boot配置绑定
JavaBean类
-
实现了Serializable接口,实例可被序列化
-
属性必须私有
-
实现了getter和setter方法
-
其它程序可以通过反射技术实例化JavaBean对象,并可获取其属性
-
配置绑定就是把配置文件中的值与 JavaBean 中对应的属性进行绑定
@ConfigurationProperties
-
在application.yml中添加对象及其属性
person: lastName: 张三 age: 18 boss: false birth: 1990/12/12 maps: { k1: v1,k2: 12 } //绑定字典 lists: //绑定列表 ‐ lisi ‐ zhaoliu dog: //绑定实例 name: 迪迪 age: 5
-
创建一个JavaBean类
- 类前需要增加
@Component和@ConfigurationProperties(prefix = "person")
注解- 只有在容器中的组件,才会拥有 SpringBoot 提供的强大功能。如果我们想要使用 @ConfigurationProperties 注解进行配置绑定,那么首先就要保证该对 JavaBean 对象在 IoC 容器中,所以需要用到 @Component 注解来添加组件到容器中。
- JavaBean 上使用了注解 @ConfigurationProperties(prefix = "person") ,它表示将这个 JavaBean 中的所有属性与配置文件中以“person”为前缀的配置进行绑定。
- 类中需要针对要传入的每个属性,定义get和set,以及构造方法
- 类前需要增加
-
对JavaBean类实例化前加一个注解
@Autowired
,即可完成配置绑定
@Value
- 当只需要读取配置文件中的某一个配置时,可以通过 @Value 注解获取
- 在类中声明每个私有属性前,加注解
@Value("${person.lastName}")
- 其余不变
@PropertySource
-
写一个单独的配置文件中,并在对应的 JavaBean 上使用 @PropertySource 注解指向该配置文件
-
resources目录下创建person.properties文件,写入属性值
-
创建JavaBean类前,加入注解:
@PropertySource(value = "classpath:person.properties")//指向对应的配置文件 @Component @ConfigurationProperties(prefix = "person")
IOC和DI
控制反转
- 把原本调用者通过代码实现的对象的创建,反转给 IoC 容器来帮忙实现,是面向对象编程中的一种设计原则,可以用来降低代码中的耦合度
- 本质是把对象的创建以及对象之间的调用过程,都交给Spring管理
- 底层原理包括xml解析、工厂模式、反射机制
- IOC容器的代表是BeanFactory及AppicationContext
基于XML配置文件方式实现控制反转
-
参考https://zhuanlan.zhihu.com/p/413580900
-
在xml配置文件中使用bean标签创建对象
-
注入属性
- 方式一:通过set方法注入
- 方式二:通过有参构造注入
-
注入bean
- 注入外部bean
- 注入内部bean
bean的生命周期
-
从创建到销毁
1、通过构造器创建bean实例(默认无参构造器) 2、为bean的属性注入值或对其他bean的引用(调用set方法) 3、把bean的实例传递给bean后置处理器方法 4、调用bean的初始化方法(需要进行配置) 5、把bean的实例传递给bean后置处理器方法 6、bean创建完毕,可以使用了 7、容器关闭的时候,调用bean的销毁方法(需要进行配置)
-
可以自定义初始化、销毁方法,添加后置处理器,精准控制bean的创建
事务管理
- 参考https://www.cnblogs.com/sharpest/p/7995203.html
- 事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。
- 首先使用注解 @EnableTransactionManagement 开启事务支持
- 然后在目标方法上添加注解 @Transactional 便可
单元测试
- 参考https://blog.csdn.net/wangxi06/article/details/114630426
Service层的单元测试
@SpringBootTest
:获取启动类,加载配置,寻找主配置启动类@RunWith(SpringRunner.class)
:让JUnit运行Spring的测试环境,获得Spring环境的上下文的支持@Test
:注解待测试方法,同时可以加下面两个注解@Transactional
:开启事务功能@Rollback()
: 事务回滚,默认是true
Controller层的单元测试
- 用到MockMvc,它使得你无需启动项目工程就能测试这些接口,MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境
@SpringBootTest
>:获取启动类,加载配置,寻找主配置启动类(被 @SpringBootApplication 注解的)@RunWith(SpringRunner.class)
>:让JUnit运行Spring的测试环境,获得Spring环境的上下文的支持@AutoConfigureMockMvc
:用于自动配置MockMvc,配置后MockMvc类可以直接注入,相当于new MockMvc@Before
:初始化方法 ,对于每一个测试方法都要执行一次
让XML配置文件生效
@ImportResource 导入 Spring 配置文件
- 在主启动程序类上使用 @ImportResource 注解,将 Spring 配置文件 beans.xml 加载到项目中
全注解方式加载 Spring 配置
- 删除主启动类的 @ImportResource 注解
- 使用
@Configuration
注解定义一个注解类,相当于 Spring 的配置文件 - 配置类中包含一个或多个被 @Bean 注解的方法,该方法相当于 Spring 配置文件中的
标签定义的组件 - 方法名是组件 id(相当于
标签的属性 id)
Profile
多Profile方式
- Spring Boot 的配置文件共有两种形式:.properties 文件和 .yml 文件
- 在 helloworld 的 src/main/resources 下添加 4 个配置文件:
- application.properties:主配置文件
- application-dev.properties:开发环境配置文件
- application-test.properties:测试环境配置文件
- application-prod.properties:生产环境配置文件
- 在主配置文件中激活某环境的配置文件,也可在命令行中激活
外部配置文件
-
命令行中指定外部配置文件
java -jar {JAR} --spring.config.location={外部配置文件全路径}
配置文件加载优先级
-
以下是常用的 Spring Boot 配置形式及其加载顺序(优先级由高到低):
- 命令行参数
- 来自 java:comp/env 的 JNDI 属性
- Java 系统属性(System.getProperties())
- 操作系统环境变量
- RandomValuePropertySource 配置的 random.* 属性值
- 配置文件(YAML 文件、Properties 文件)
- @Configuration 注解类上的 @PropertySource 指定的配置文件
- 通过 SpringApplication.setDefaultProperties 指定的默认属性
-
命令行启动spring boot
java -jar springbootdemo-0.0.1-SNAPSHOT.jar --server.port=8081 --server.servlet.context-path=/bcb --server.port:指定服务器端口号; --server.servlet.context-path:指定上下文路径(项目的访问路径)
自动配置的原理
- Spring Boot 自动化配置也是基于 Spring Factories 机制实现的
- Spring Boot 启动时,会利用 Spring-Factories 机制,将这些 xxxAutoConfiguration 实例化并作为组件加入到容器中,以实现 Spring Boot 的自动配置
自动配置的生效和修改该
- spring.factories 文件中的所有自动配置类(xxxAutoConfiguration),都是必须在一定的条件下才会作为组件添加到容器中,配置的内容才会生效
- 这些限制条件在 Spring Boot 中以 @Conditional 派生注解的形式体现
Spring Boot日志
统一日志框架
- 分为日志门面(日志抽象层)和日志实现
- Spring boot用的是SLF4J和Logback
- 通常一个完整的应用下会依赖于多种不同的框架,而且它们记录日志使用的日志框架也不尽相同
- 当我们引入了依赖了其他日志框架的第三方框架(例如 Hibernate)时,只需要把这个框架所依赖的日志框架排除,即可实现日志框架的统一
日志配置及输出
-
五种优先级
package net.biancheng.www; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringbootLoggingApplicationTests { Logger logger = LoggerFactory.getLogger(getClass()); /** * 测试日志输出 * SLF4J 日志级别从小到大trace>debug>info>warn>error */ @Test void logTest() { //日志级别 由低到高 logger.trace("trace 级别日志"); logger.debug("debug 级别日志"); logger.info("info 级别日志"); logger.warn("warn 级别日志"); logger.error("error 级别日志"); } }
-
在 application.properties 中,修改 Spring Boot 日志的默认配置
#日志级别 logging.level.net.biancheng.www=trace #使用相对路径的方式设置日志输出的位置(项目根目录目录\my-log\mylog\spring.log) #logging.file.path=my-log/myLog #绝对路径方式将日志文件输出到 【项目所在磁盘根目录\springboot\logging\my\spring.log】 logging.file.path=/spring-boot/logging #控制台日志输出格式 logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n #日志文件输出格式 logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} === - %msg%n
Spring Boot静态资源映射
- 默认提供三种静态资源映射规则:
- WebJars 映射
- 默认资源映射
- 静态首页(欢迎页)映射
Webjars映射
- 通常我们会将这些 Web 前端资源拷贝到 Java Web 项目的 webapp 相应目录下进行管理,但是 Spring Boot 项目是以 JAR 包的形式进行部署的,不存在 webapp 目录
- WebJars 可以完美的解决上面的问题,它可以 Jar 形式为 Web 项目提供资源文件
- 方法:
- 访问webjar官网,找到需要的前端资源的pom依赖
- 将依赖加入pom.xml
- jar包引入到当前项目路径下的
/META-INF/resources/webjars/
- 访问
/webjars/**
下的所有请求,都会取上面路径下查找前端资源
默认静态资源映射
- 以下路径被称为静态资源文件夹,按优先级从高到底:
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
静态首页映射
- 静态资源文件夹下的所有 index.html 被称为静态首页或者欢迎页,当我们访问“/”或者“/index.html”时,都会跳转到静态首页
Thymeleaf
- 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎,可集成到Web框架
- 与其它模板引擎相比,Thymeleaf 最大的特点是,即使不启动 Web 应用,也可以直接在浏览器中打开并正确显示模板页面
简介
- Thymeleaf 是新一代 Java 模板引擎,支持 HTML 原型,其文件后缀为“.html”,
- 因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;
- 当通过 Web 应用程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示
语法规则
-
在html标签中声明名称空间
xmlns:th="http://www.thymeleaf.org"
-
标准表达式语法
-
变量表达式,使用 ${} 包裹的表达式被称为变量表达式,具有以下功能:
-
获取对象的属性和方法
${person.lastName}
-
使用内置的基本对象,常用的内置基本对象为:
- #ctx :上下文对象;
- #vars :上下文变量;
- #locale:上下文的语言环境;
- #request:HttpServletRequest 对象(仅在Web 应用中可用);
- #response:HttpServletResponse 对象(仅 Web 应用);
- #session:HttpSession 对象(仅Web 应用可用);
- #servletContext:ServletContext 对象(仅Web 应用)
-
使用内置的工具对象
-
-
选择变量表达式
-
当使用 th:object 存储一个对象后,我们可以在其后代中使用选择变量表达式(*{...})获取该对象中的属性
<div th:object="${session.user}" > <p th:text="*{fisrtName}">firstname</p> </div>
-
-
链接表达式
-
不管是静态资源的引用,还是 form 表单的请求,凡是链接都可以用链接表达式 (@{...})
-
链接表达式的形式结构如下:
- 无参请求:@{/xxx}
- 有参请求:@{/xxx(k1=v1,k2=v2)}
-
使用链接表达式引入 css 样式表
<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
-
-
-
th属性:Thymeleaf 还提供了大量的 th 属性,这些属性可以直接在 HTML 标签中使用
Thymeleaf 公共页面抽取
-
头部导航栏、侧边菜单栏和公共的 js css 等,我们一般会把这些公共页面片段抽取出来,存放在一个独立的页面中,然后再由其他页面根据需要进行引用
-
抽取公共页面
-
使用 Thymeleaf 提供的 th:fragment 属性为这些抽取出来的公共页面片段命名,如commons,html:
<div th:fragment="fragment-name" id="fragment-id"> <span>公共页面片段</span> </div>
-
-
引用公共页面
-
使用以下3个属性,将公共页面片段引入到当前页面
- th:insert:将代码块片段整个插入到使用了 th:insert 属性的 HTML 标签中;
- th:replace:将代码块片段整个替换使用了 th:replace 属性的 HTML 标签中;
- th:include:将代码块片段包含的内容插入到使用了 th:include 属性的 HTML 标签中。
-
例如在fragment.html中引入commons.html
<!--th:insert 片段名引入--> <div th:insert="commons::fragment-name"></div> <!--th:insert id 选择器引入--> <div th:insert="commons::#fragment-id"></div>
-
-
传递参数
-
传入参数
<!--th:insert 片段名引入--> <div th:insert="commons::fragment-name(var1='insert-name',var2='insert-name2')"></div> <!--th:insert id 选择器引入--> <div th:insert="commons::#fragment-id(var1='insert-id',var2='insert-id2')"></div>
-
使用参数
<!--使用 var1 和 var2 声明传入的参数,并在该片段中直接使用这些参数 --> <div th:fragment="fragment-name(var1,var2)" id="fragment-id"> <p th:text="'参数1:'+${var1} + '-------------------参数2:' + ${var2}">...</p> </div>
-
Spring Boot整合Thymeleaf
引入依赖
-
pom.xml中添加Thymeleaf的Starter依赖
<!--Thymeleaf 启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
创建模板文件
- Spring Boot 通过 ThymeleafAutoConfiguration 自动配置类对 Thymeleaf 提供了一整套的自动化配置方案
- Thymeleaf 模板的默认位置在 resources/templates 目录下,默认的后缀是 html,即只要将 HTML 页面放在“classpath:/templates/”下,Thymeleaf 就能自动进行渲染
示例
-
创建hello.html
<!DOCTYPE html> <!--导入thymeleaf的名称空间--> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--th:text 为 Thymeleaf 属性,用于获取指定属性的值--> <h1 th:text="'欢迎来到'+${name}"></h1> </body> </html>
-
新建一个控制类 HelloController,并通过参数 map 传递数据到前台页面中
package net.biancheng.www.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import java.util.Map; @Controller public class HelloController { @RequestMapping("/hello") public String hello(Map<String, Object> map) { //通过 map 向前台页面传递数据 map.put("name", "编程帮(www.biancheng.net)"); return "hello"; } }
拦截器
-
定义拦截器:实现HandlerInterceptor 接口
-
注册拦截器
@Configuration public class MyMvcConfig implements WebMvcConfigurer { ...... @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()); } }
-
指定拦截规则
@Slf4j @Configuration public class MyMvcConfig implements WebMvcConfigurer { ...... @Override public void addInterceptors(InterceptorRegistry registry) { log.info("注册拦截器"); registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //拦截所有请求,包括静态资源文件 .excludePathPatterns("/", "/login", "/index.html", "/user/login", "/css/**", "/images/**", "/js/**", "/fonts/**"); //放行登录页,登陆操作,静态资源 } }
数据源
- 默认HikariCP,可添加Druid,用来做连接池