Spring Boot笔记(全)

前言

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

    image-20221127175122128

  • 依赖注入

    image-20221127175151111

AOP面向切面编程

  • 在A操作之后,B操作之前的通用型操作,通常用来进行事务和日志
  • 术语:连接点,切入点,切面,通知,目标

基于注解的AspectJ AOP

  • 启用 @AspectJ 注解支持
  • 定义切面 @Aspect:类
  • 定义切点 @Pointcut:方法
  • 定义通知

Spring事务

  • ACID特性
  • 开启注解事务
  • 使用 @Transactional 注解

Spring MVC

MVC框架

image-20221113210005188

Spring MVC常用组件

image-20221113203426305

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执行流程

image-20221113210321765

@RequestMapping注解

  • 使用方式

    • 修饰方法:要访问该方法,请求路径必须与这个value值相同
    • 修饰类:要访问类中的任意方法,都要带上这个父路径
  • 属性

    • value属性:用于设置请求地址

    • name属性:用于注释

    • method属性:method 属性的取值是一个 RequestMethod 类型的数组,表示一个控制器方法支持多种方式的请求,常用的请求方式有 GET、POST、DELETE、PUT 等。如果一个控制器方法没有设置 @RequestMapping 注解的 method 属性,则说明该控制器方法支持全部请求类型,可以处理所有类型的请求。

    • params属性:取值是一个字符串类型的数组,用于指定请求中的参数,只有当请求中携带了符合条件的参数时,控制器方法才会对该请求进行处理

      image-20221113211532567
      @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 则主要用于设置视图,实现页面的跳转

        image-20221114200112232
        @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转换注解

    image-20221115100808169
  • 实例

    • 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依赖

  • 创建启动应用,创建控制器

    image-20221109202548432
  • 默认端口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 配置形式及其加载顺序(优先级由高到低):

    1. 命令行参数
    2. 来自 java:comp/env 的 JNDI 属性
    3. Java 系统属性(System.getProperties())
    4. 操作系统环境变量
    5. RandomValuePropertySource 配置的 random.* 属性值
    6. 配置文件(YAML 文件、Properties 文件)
    7. @Configuration 注解类上的 @PropertySource 指定的配置文件
    8. 通过 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 表单的请求,凡是链接都可以用链接表达式 (@{...})

      • 链接表达式的形式结构如下:

        • 无参请求:@
        • 有参请求:@
      • 使用链接表达式引入 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 接口

    image-20221127175827279

  • 注册拦截器

    @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,用来做连接池
posted @ 2022-11-27 18:14  z5onk0  阅读(34)  评论(0编辑  收藏  举报