Java经典框架之SpringBoot

news2024/10/5 17:12:36

SpringBoot

Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。
  

课程内容的介绍

1. SpringBoot基础
2. SpringBoot高级 
  

一、SpringBoot基础

1. SpringBoot概念
官网:https://spring.io/
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
SpringBoot是由Pivotal团队在2013年开始研发、2014年4月发布第一个版本的全新开源的轻量级框架。它基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。
  
SpringBoot所具备的特征有:
(1)可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;
(2)内嵌Tomcat或Jetty等Servlet容器;
(3)提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;
(4)尽可能自动配置Spring容器;
(5)提供准备好的特性,如指标、健康检查和外部化配置;
(6)绝对没有代码生成,不需要XML配置。
  
SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置。开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。
SpringBoot应用系统开发模板的基本架构设计从前端到后台进行说明:前端常使用模板引擎,主要有FreeMarker和Thymeleaf,它们都是用Java语言编写的,渲染模板并输出相应文本,使得界面的设计与应用的逻辑分离,同时前端开发还会使用到Bootstrap、AngularJS、JQuery等;在浏览器的数据传输格式上采用Json,非xml,同时提供RESTfulAPI;SpringMVC框架用于数据到达服务器后处理请求;到数据访问层主要有Hibernate、MyBatis、JPA等持久层框架;数据库常用MySQL;开发工具推荐IntelliJIDEA。
  
2. SpringBoot项目构建
SpringBoot项目的构建方式本身是非常简单的,实现的方式也有多种。
  
2.1 手动创建
我们通过基本的Maven项目来手动配置一个SpringBoot。
    
2.1.1 创建MAVEN项目
创建一个普通的Maven项目即可。

    
2.1.2 添加依赖
创建一个SpringBoot的Web项目,我们需要添加对应的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bobo</groupId>
    <artifactId>SpringBootDemo01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 1.添加SpringBoot的依赖 -->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.8.RELEASE</version>
    </parent>

    <dependencies>
        <!-- 2. 添加SpringMVC的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>


</project>
    
2.1.3 创建启动类
我们要启动当前项目需要创建一个Java启动类。
package com.bobo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SpringBoot项目的启动类
 */
@SpringBootApplication
public class AppStart {

    /**
     * 程序启动的入口
     * @param args
     */
    public static void main(String[] args) {
        SpringApplication.run(AppStart.class,args);
    }
}
  
2.1.4 启动程序
执行我们的主方法即可。

访问出现404,说明服务启动成功,只是请求访问的资源不存在。

   
2.2 在线构建
我们也可以通过SpringBoot提供的在线地址创建我们的SpringBoot项:https://start.spring.io/

  
在线生成我们的SpringBoot项目,解压后可以直接导入。
 
2.3 IDEA直接创建
IDEA工具可以直接通过在线创建的工具来直接生成,帮助我们简化了下载解压缩的步骤。

到此创建完成。
  
3. SpringBoot基本使用
3.1 自定义控制器
我们创建的是一个基于SpringBoot的WEB项目,那么怎么处理客户端提交的请求呢?这时我们可以直接在启动器所在的子目录下创建对应的Controller即可。
package com.bobo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/*@Controller
@ResponseBody*/
@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello ...);
        return "Hello ...";
    }
}
    
访问即可

       
为什么能够扫描到这个@Controller注解,根本原因是在我们的启动器中的那个@SpringBootApplication注解,这个注解本身是一个组合注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 以上四个是JAVA中提供的元注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
  

   
3.2 静态资源
在SpringBoot项目中默认的静态资源【html,css,js,图片....】是放置在resource/static目录中。

    
效果

  
3.3 定制Banner
如果我们想要修改服务启动时的那个banner图标,我们只需要在resource目录下创建一个banner.txt文件,然后将我们要显示的信息写入即可http://patorjk.com/software/taag/#p=display&f=Graffiti&t=HELLO
  

  

    
当然我们也可以不显示Banner信息。
package com.bobo;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @SpringBootApplication 组合注解
 * @ComponentScan 可以直接扫描路径
 *                如果没有指定要扫描的特定的路径,
 *                那么默认的是会把当前注解所在的类的包及其子包作为扫描路径
 */
@SpringBootApplication
public class SpringBootDemo03Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(SpringBootDemo03Application.class);
        app.setBannerMode(Banner.Mode.OFF); // 关闭掉Banner
        app.run(args);
    }

}
  
3.4 属性文件
在resource目录下的application.properties文件。
   
3.4.1 默认设置
我们可以通过application.properties文件来修改系统默认的属性,比如修改Tomcat相关配置信息。
server.port=8082
server.servlet.context-path=/springboot
   
3.4.2 自定义属性
我们可以在application.properties文件中自定义属性,供我们在代码中使用。
   
自定义属性
# 默认属性修改
server.port=8082
server.servlet.context-path=/springboot
# 自定义属性
user.userName=admin
user.realName=波波
user.address=湖南长沙
  
获取自定义属性
package com.bobo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/*@Controller
@ResponseBody*/
@RestController
public class HelloController {

    @Value(value = "${user.userName}")
    private String userName;
    @Value(value = "${user.realName}")
    private String realName;
    @Value(value = "${user.address}")
    private String address;

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello ..."+ userName + "  " + realName + " " + address);
        return "Hello ...";
    }
}
     
出现乱码的情况

  
解决方法

    
修改了乱码后的效果

    
3.4.3 yml文件
yml是我们在配置系统属性或者自定义属性的另外一种方式。
user.hello.username=a
user.hello.password=123
user.hello.address=cs
user.hello.age=1
  
改成yml
user:
    hello:
        username:a
        password:123
        address:cs
        age:1
  
3.5 日志
SpringBoot中支持 JavaUtil Logging, Log4J, Log4J2和Logback作为日志框架,而在SpringBoot中默认支持的是Logback作为日志框架。
    
简单配置
# 日志配置
#logging.file.path=d:/tools/log
logging.file.name=d:/tools/log/log.log
logging.level.org.springframework.web=DEBUG
  

     
引入日志文件的扩展配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--日志文件主目录:这里${user.home}为当前服务器用户主目录-->
    <property name="LOG_HOME" value="${user.home}/log"/>
    <!--日志文件名称:这里spring.application.name表示工程名称-->
    <springProperty scope="context" name="APP_NAME" source="spring.application.name"/>

    <!--默认配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!--配置控制台(Console)-->
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <!--配置日志文件(File)-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--设置策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件路径:这里%d{yyyyMMdd}表示按天分类日志-->
            <FileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</FileNamePattern>
            <!--日志保留天数-->
            <MaxHistory>15</MaxHistory>
        </rollingPolicy>
        <!--设置格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!-- 或者使用默认配置 -->
            <!--<pattern>${FILE_LOG_PATTERN}</pattern>-->
            <charset>utf8</charset>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 多环境配置 按照active profile选择分支 -->
    <springProfile name="dev">
        <!--root节点 全局日志级别,用来指定最基础的日志输出级别-->
        <root level="DEBUG">
            <appender-ref ref="FILE"/>
            <appender-ref ref="CONSOLE"/>
        </root>

        <!-- 子节点向上级传递 局部日志级别-->
        <logger level="WARN" name="org.springframework"/>
        <logger level="WARN" name="com.netflix"/>
        <logger level="DEBUG" name="org.hibernate.SQL"/>
    </springProfile>
    <springProfile name="prod">
        <!--root节点 全局日志级别,用来指定最基础的日志输出级别-->
        <root level="INFO">
            <appender-ref ref="FILE"/>
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>
</configuration>
    
引入

   
3.6 Profile
项目开发中会出现开发环境的切换,为了更好的处理我们可以通过Profile来实现,之前在Spring的阶段就已经给大家介绍过了Profile,但是实现相对复杂了点,在SpringBoot中也提供了对Profile的支持,而且更加简化,创建对应的属性文件。
  
开发环境
user.host=192.168.100.120
   
生产环境
user.host=192.168.111.123
  
文件名称的命名规则是 application-环境.properties
  

  
要让哪个文件生效,我们只需要在application.properties文件中指定即可。
  

  
测试效果
  

  
3.7 静态资源文件
在SpringBoot项目中默认的存放路径是在static目录下,但是实际开发的时候有可能我们需要调整资源的目录。
   
在main目录下创建一个webapp目录 设置类型为 ResourceRoot。

   
可以直接访问
  

  
自定义目录
有些情况下我们需要将特定的目录作为我们存放静态资源文件的目录。
## 设置自定义的路径
spring.mvc.static-path-pattern=/**
## 覆盖掉默认的配置录
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,class path:/sfile/
   

  

   
3.8 Servlet操作
我们在项目开发过程中可以要碰到直接操作Servlet的情况,这时我们应该怎么去实现。
  
3.8.1 Servlet
第一种方式
定义Servlet
package com.bobo.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name="firstServlet",urlPatterns = "/first")
public class FirstServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("--firstServlet -- doGet 方法");
        PrintWriter writer = resp.getWriter();
        writer.write("success");
        writer.flush();
        writer.close();
    }
}
     
在启动类中添加扫描的注解。
@SpringBootApplication
// 在SpringBoot项目启动的时候会扫描 @WebServlet注解
@ServletComponentScan
public class SpringbootDemo06Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemo06Application.class, args);
    }
}
   
测试访问

      
第二种方式
创建Servlet,不用添加WebServlet注解。
package com.bobo.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public class SecondServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("--secondServlet -- doGet 方法");
        PrintWriter writer = resp.getWriter();
        writer.write("success");
        writer.flush();
        writer.close();
    }
}
     
在启动类中注入ServletRegistrationBean对象。
@SpringBootApplication
// 在SpringBoot项目启动的时候会扫描 @WebServlet注解
@ServletComponentScan
public class SpringbootDemo06Application {

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

    @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new SecondServlet());
        bean.addUrlMappings("/second");
        return bean;
    }
}
  
测试

    
3.8.2 Filter
第一种方式
创建过滤器
@WebFilter(urlPatterns = "/first")
public class FirstFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("FirstFilter before");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("FirstFilter end");
    }
}
   
在启动器中添加注解。

  
测试

  
第二种方式
在该过滤器中我们不用添加对应的注解。
package com.bobo.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;


public class SecondFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("SecondFilter before");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("SecondFilter end");
    }
}
   
启动器中注入 注册器。
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean bean = new FilterRegistrationBean(new SecondFilter());
        bean.addUrlPatterns("/second");
        return bean;
    }
  
测试

  
3.8.3 Listener
第一种方式
package com.bobo.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class FirstListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("FirstListener ... 初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("FirstListener ... 销毁");
    }
}
  
启动器中注解

  
测试效果

  
第二种方式
package com.bobo.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;


public class SecondListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("SecondListener ... 初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("SecondListener ... 销毁");
    }
}
    @Bean
    public ServletListenerRegistrationBean servletListenerRegistrationBean(){
        return new ServletListenerRegistrationBean(new SecondListener());
    }
  
测试

 
3.9 文件上传
3.9.1 表单页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户管理</title>
</head>
<body>

    <h1>文件上传案例:</h1>
    <form action="/user/upload" method="post" enctype="multipart/form-data" >
        <label>账号:</label><input type="text" name="username"><br>
        <label>头像:</label><input type="file" name="upload"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>
  
3.9.2 控制器
package com.bobo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@RestController
@RequestMapping("/user")
public class UserContoller {

    @RequestMapping("/upload")
    public String fileUpload(String username, MultipartFile upload) throws IOException {
        System.out.println(username + " " + upload.getOriginalFilename());
        upload.transferTo(new File("d:/tools/",upload.getOriginalFilename()));
        return "success";
    }


}
  
3.9.3 属性文件设置
server.port=8082

spring.servlet.multipart.enabled=true
# 设置单个文件上传的大小
spring.servlet.multipart.max-file-size=20MB
# 设置一次请求上传文件的总的大小
spring.servlet.multipart.max-request-size=200MB
  
3.9.4 测试

  

 
上传成功的文件

       
4.SpringBoot基本应用
4.1 Freemaker
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
   
http://freemarker.foofun.cn/
  
JavaEE中的两种开发方式
前后端不分离
        要求程序员要掌握js,为了简化页面开发,引入页面模板,页面模板整体上来说又可以分为两大类。
   
前端模板
前端模板就是后缀为html的模板,代表就是Thymeleaf,这种模板有一个好处就是不需要服务端解析就能直接在浏览器中打开。
   
后端模板
必须经过服务端解析才能被浏览器展示出来的模板。
        JSP
        Freemarker
        velocity
  

前后端分离
前后端分离的时候,后端纯粹只是接口,没有任何页面。所有的页面由前端完成,前端会使用相关的模板。
        Vue
        AngularJS
        React
  
4.2 整合Freemaker
4.2.1 添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  
4.2.2 配置
我们在属性文件中设置视图解析器的前后缀。
spring.freemarker.suffix=.ftl
   
4.2.3 创建Freemaker文件
然后我们在系统的模板文件中创建Freemaker文件,注意该文件为一个后缀为 .ftl 的文件。
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello Freemark ...</h1>
    </body>
</html>
  
4.2.4 控制器
因为在 template 目录下的文件是没法直接访问的,而且我们也需要先在服务端获取数据绑定数据后再在页面模板文件中呈现,所以请求先到控制器,然后通过模板引擎解析模板文件生成具体的HTML页面响应客户。
@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/query")
    public String query(){
        System.out.println("query ....");
        return "user";
    }
}
   
4.2.5 测试
直接启动服务,访问看效果。

  

  
这就表示整合成功了。
   

4.3 Freemaker的基本应用
接下来介绍下如果在Freemaker中绑定数据。
  
4.3.1 绑定单个数据
我们在Model中绑定的是单个数据,怎么在ftl文件中绑定呢?
    /**
     * 基本数据类型
     * 自定义数据类型
     * 数据容器
     * @param model
     * @return
     */
    @RequestMapping("/query")
    public String query(Model model){
        System.out.println("query ....");
        model.addAttribute("userName","波波老师");
        model.addAttribute("age",18);
        model.addAttribute("address","湖南长沙");
        model.addAttribute("flag",true);
        model.addAttribute("birth",new Date());
        return "user";
    }
     
字符和数字类型我们可以通过EL表达式直接取出来。
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello Freemark ...</h1>
        ${userName}<br>
        ${age}<br>
    </body>
</html>
   
boolean不能直接转换为string类型。

  
这时我们要通过内部的转换函数来处理。
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello Freemark ...</h1>
        ${userName}<br>
        ${age}<br>
        ${address}<br>
        ${flag?string("真","假")}<br>
    </body>
</html>
      

      
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello Freemark ...</h1>
        ${userName}<br>
        ${age}<br>
        ${address}<br>
        ${flag?string("真","假")}<br>
        ${birth}<br>
    </body>
</html>
      
时间类型也需要转换。

   
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello Freemark ...</h1>
        ${userName}<br>
        ${age}<br>
        ${address}<br>
        ${flag?string("真","假")}<br>
        ${birth?string("yyyy-MM-dd")}<br>
    </body>
</html>
     

     
4.3.2 单个数据处理
我们服务端绑定的单个数据,比如字符串或者数字,我们可能需要对这些数据做出调整,比如数字要四舍五入,字符串我们需要截取等操作。
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello Freemark ...</h1>
        ${userName}<br>
        ${age}<br>
        ${address}<br>
        ${flag?string("真","假")}<br>
        ${birth?string("yyyy-MM-dd")}<br>
        <hr>
        <#-- 注释符 -->
        <#assign x=3.1415>
        <#assign y=6>
        <!--
            mN:小数部分最小N位
            MN:小数部分最大N位
         -->
        x=${x}<br>
        y=${y}<br>
        #{x;M2}<br><!-- 3.14 -->
        #{x;m2}<br><!-- 3.14 -->
        #{y;M2}<br><!-- 6 -->
        #{y;m2}<br><!-- 6.00 -->
    <body>
</html>
     

  
字符串拼接处理
<#assign hello="hello freemarker" >
<#-- 字符串拼接 -->
HELLO-${hello}<br>
<#-- EL表达式中的常量表示 -->
${'HELLO|'+hello}<br>
<#-- 常量中使用数据 -->
${'HELLO*${hello}'}<br>
${userName}----${hello}<br>
${userName+'-->' + hello}<br>
   

  
字符串截取
${hello}<br>
${hello[1]}<br>
${hello[4]}<br>
${hello[1..6]}<br>
${hello[3..]}<br>
  

     
4.3.3 自定义对象
@RequestMapping("/query1")
public String query1(Model model){
    User user = new User(666,"admin","123456");
    model.addAttribute("user",user);
    return "user1";
}
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <#-- 自定义对象 -->
        ${user.id}<br>
        --> <#--${user[id]}<br>-->
        ${user.userName}<br>
        -->${user['userName']}<br>
        ${user.password}<br>
    </body>
</html>
   

      
4.3.4 集合对象
    @RequestMapping("/query1")
    public String query1(Model model){
        User user = new User(666,"admin","123456");
        model.addAttribute("user",user);
        Map<String,Object> map = new HashMap<>();
        map.put("user",user);
        List list = Arrays.asList("张三","李四","王五");
        List list1 = Arrays.asList("1111","2222","3333");
        model.addAttribute("list",list);
        model.addAttribute("list1",list1);
        model.addAttribute("map",map);
        return "user1";
    }
   

    
4.3.5 算数运算
算数运算包含基本的四则运算和求模运算,运算符有:
加法: +
减法: -
乘法: *
除法: /
求模 (求余): %
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>算术运算符:</h1><br>
        ${99+100*30}<br>
        ${99/7}<br>
        ${(99/7)?int}<br>
        ${55%3}<br>
    </body>
</html>
  

     
4.3.6 比较运算符

  
4.3.7 逻辑操作
常用的逻辑操作符:
逻辑 或: ||
逻辑 与: &&
逻辑 非: !
    
逻辑操作符仅仅在布尔值之间有效,若用在其他类型将会产生错误导致模板执行中止。
  
4.3.8 内置函数
内建函数就像FreeMarker在对象中添加的方法一样。 要防止和实际方法和其它子变量的命名冲突,则不能使用点 (.),这里使用问号 (?)来和父对象分隔开。 比如,想要保证 path 有起始的 / ,那么可以这么来写: path?ensure_starts_with(’/’)。 path 后的Java对象(通常就是 String) 并没有这样的方法,这是FreeMarker添加的。为了简洁,如果方法没有参数, 那么就可以忽略 (),比如想要获取 path 的长度,就可以写作:path?length, 而不是 path?length()。

     
更多内置函数见此:http://freemarker.foofun.cn/ref_builtins.html
   
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
    <h1>算术运算符:</h1><br>
        ${99+100*30}<br>
        ${99/7}<br>
        ${(99/7)?int}<br>
        ${55%3}<br>
    <h1>内建函数:</h1>
    <#assign hello="Hello FreeMarker">
    <#assign page="<span style='color:red'>HELLO</span>">
    ${hello}<br>
    ${page}<br>
    ${page?html}<br>
    ${hello?upper_case}<br>
    ${hello?lower_case}<br>
    ${now?date}<br>
    ${now?datetime}<br>
    ${now?time}<br>
    </body>
</html>
  

     
4.3.9 分支和循环
if语句,switch语句已经循环语句,基本的语法格式和我们在Java中使用的是一样的。我们只需要注意下在具体的使用格式上。
<html>
    <head>
        <title>Freemaker</title>
        <meta charset="UTF-8">
    </head>
    <body>
    <h1>算术运算符:</h1><br>
        ${99+100*30}<br>
        ${99/7}<br>
        ${(99/7)?int}<br>
        ${55%3}<br>
    <h1>内建函数:</h1>
    <#assign hello="Hello FreeMarker">
    <#assign page="<span style='color:red'>HELLO</span>">
    ${hello}<br>
    ${page}<br>
    ${page?html}<br>
    ${hello?upper_case}<br>
    ${hello?lower_case}<br>
    ${now?date}<br>
    ${now?datetime}<br>
    ${now?time}<br>
    <hr>
        <#assign age = 18 >
        <#if age == 18>
            等于18
            <#elseif age gt 18 >
            大于18
            <#else >
            小于18
        </#if>

        null的判断:<br>
        <#assign  mypage="a">
    <#-- ?? 检测值是否存在 -->
        <#if mypage??>
            mypage存在
            <#else >
            mypage不存在
        </#if>
        <br>
        <#assign i=3>
        <#switch i>
            <#case 1>
               ok
                <#break >
            <#case 2>
               ok2
                <#break >
            <#case 3>
                ok3
                <#break >
            <#default >
                ok4

        </#switch>

    <#list list as obj>
        <#if obj=='李四'>
            <#break >
        </#if>
        ${obj}<br>
    </#list>
    <#assign  aaa=555>
    <#-- !的使用--><br>
    ${aaa!"666"}<!-- 如果aaa存在就显示aaa本来的值,如果aaa不存在就显示666 --><br>

    </body>
</html>
   

   
5.综合案例
前面介绍了Freemaker,这节介绍SpringBoot整合MyBatis,同时结合Freemaker展现数据。
   
5.1 项目创建
添加相关的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bobo</groupId>
    <artifactId>springboot-demo09</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-demo09</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.14</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
     
添加相关的配置文件
server.port=8082

# 配置JDBC的相关信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/logistics?
characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

# 配置连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

# 配置MyBatis的package 设置别名
mybatis.type-aliases-package=com.bobo.pojo
     
创建实体对象
package com.bobo.pojo;

public class User {
    private String user_id    ;
    private String user_name  ;
    private String real_name  ;
    private String password   ;
    private String email      ;
    private String phone      ;
    private String u1         ;
    private String u2         ;

    public String getUser_id() {
        return user_id;
    }

    public void setUser_id(String user_id) {
        this.user_id = user_id;
    }

    public String getUser_name() {
        return user_name;
    }

    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }

    public String getReal_name() {
        return real_name;
    }

    public void setReal_name(String real_name) {
        this.real_name = real_name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getU1() {
        return u1;
    }

    public void setU1(String u1) {
        this.u1 = u1;
    }

    public String getU2() {
        return u2;
    }

    public void setU2(String u2) {
        this.u2 = u2;
    }
}
   
5.2 查询用户信息
创建接口。
package com.bobo.mapper;

import com.bobo.pojo.User;

import java.util.List;

public interface UserMapper {

    List<User> query();

}
      
创建映射文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bobo.mapper.UserMapper">
    <select id="query" resultType="user">
        select * from t_user
    </select>
</mapper>
   
属性文件中添加Mapper映射文件的路径。

  
创建Service
package com.bobo.service;

import com.bobo.pojo.User;

import java.util.List;

public interface IUserService {

    List<User> query();

}
package com.bobo.service.impl;

import com.bobo.mapper.UserMapper;
import com.bobo.pojo.User;
import com.bobo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper mapper;

    @Override
    public List<User> query() {
        return mapper.query();
    }

}
   
创建控制器
package com.bobo.controller;

import com.bobo.pojo.User;
import com.bobo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService service;

    @RequestMapping("/query")
    public String query(Model model){
        List<User> list = service.query();
        model.addAttribute("list",list);
        return "/user";
    }

}
  
属性文件中配置Freemaker的后缀。

  
创建Freemaker模板文件,并且展示数据。
<html>
    <head>
        <title>用户管理</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>用户管理</h1>
        <table>
            <tr>
                <th>编号</th>
                <th>账号</th>
                <th>姓名</th>
                <th>邮箱</th>
                <th>电话</th>
                <th>操作</th>
            </tr>
            <#list list as user>
                <tr>
                    <td>${user.user_id}</td>
                    <td>${user.user_name}</td>
                    <td>${user.real_name!""}</td>
                    <td>${user.email!""}</td>
                    <td>${user.phone!""}</td>
                    <td>...</td>
                </tr>
            </#list>
        </table>
    </body>
</html>
   
启动操作之前我们需要添加 MyBatis接口的扫描路径。

  
访问测试

   
5.3 添加用户
<html>
    <head>
        <title>用户管理</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>用户管理</h1>
        <form action="/user/userUpdate" method="post" >
            <input type="hidden" name="user_id" value="${user.user_id}">
            <label>账号</label><input type="text" name="user_name" value="${user.user_name}"><br>
            <label>姓名</label><input type="text" name="real_name" value="${user.real_name!""}"><br>
            <label>邮箱</label><input type="text" name="email" value="${user.email!""}"><br>
            <label>电话</label><input type="text" name="phone" value="${user.phone!""}"><br>
            <input type="submit" value="提交">
        </form>
    </body>
</html>
    
5.4 更新用户
<html>
    <head>
        <title>用户管理</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>用户管理</h1>
        <form action="/user/userUpdate" method="post" >
            <input type="hidden" name="user_id" value="${user.user_id}">
            <label>账号</label><input type="text" name="user_name"value="${user.user_name}"><br>
            <label>姓名</label><input type="text" name="real_name"value="${user.real_name!""}"><br>
            <label>邮箱</label><input type="text" name="email"value="${user.email!""}"><br>
            <label>电话</label><input type="text" name="phone"value="${user.phone!""}"><br>
            <input type="submit" value="提交">
        </form>
    </body>
</html>
     
5.5 删除用户
<html>
    <head>
        <title>用户管理</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>用户管理</h1>
        <h2>
            <a href="/user/dispatchUpdate">添加用户</a>
        </h2>
        <table>
            <tr>
                <th>编号</th>
                <th>账号</th>
                <th>姓名</th>
                <th>邮箱</th>
                <th>电话</th>
                <th>操作</th>
            </tr>
            <#list list as user>
                <tr>
                    <td>${user.user_id}</td>
                    <td>${user.user_name}</td>
                    <td>${user.real_name!""}</td>
                    <td>${user.email!""}</td>
                    <td>${user.phone!""}</td>
                    <td>
                        <a href="/user/dispatchUpdate?id=${user.user_id}">更新</a>
                        <a href="/user/deleteUser?id=${user.user_id}">删除</a>
                    </td>
                </tr>
            </#list>
        </table>
    </body>
</html>
   
6. Thymeleaf
Thymeleaf是SpringBoot中推荐使用的前端模板框架。所以比较重要。
  
6.1 SpringBoot整合
创建一个SpringBoot项目,然后添加对应的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bobo</groupId>
    <artifactId>springboot-demo10</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-demo10</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- 使用Thymeleaf需要添加的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
     
创建Thymeleaf文件,Thymeleaf的后缀就是html,我在template目录下直接创建一个html页面即可,但是为了能够使用Thymeleaf中的标签提示,我们添加对应的xmlns即可。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>
    <h1>Hello Thymeleaf</h1>

</body>
</html>
  
添加跳转的控制器。
@Controller
public class UserController {

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello ....");
        return "/user";
    }
}
   
启动服务测试

  
访问成功,说明整合搞定。
  
6.2 Thymeleaf基本使用
Thymeleaf表达式只能放置在Thymeleaf的自定义属性中(html标签中)。
  
6.2.1 变量输出
控制器中绑定数据。
@RequestMapping("/hello")
public String hello(Model model){
    System.out.println("hello ....");
    model.addAttribute("hello","Hello Thymeleaf");
    model.addAttribute("msg","hahaha");
    model.addAttribute("now",new Date());
    model.addAttribute("flag",true);
    model.addAttribute("age",18);
    return "/user";
}
  
模板文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>
    <h1>Hello Thymeleaf</h1>
    <label th:text="hello"></label><br>
    <label th:text="${hello}"></label><br>
    <label th:text="${now}"></label><br>
    <label th:text="${flag}"></label><br>
    <label th:text="${age}"></label><br>
    <h2>th:value的使用</h2>
    <input type="text" value="aaa"><br>
    <input type="text" th:value="${msg}"><br>
</body>
</html>
   
显示效果

  
6.2.2 内置函数
我们通过上面的案例发现显示Model中的数据很方便,但是显示的数据的格式可能不满足我们的需求,这时我们需要调整就需要借助内置的函数来帮助我们实现,我们主要介绍字符串和时间相关的函数。
  
注意点
1. 调用内置函数对象一定要使用#
2. 大部分的内置函数都以 s 结尾, 比如 strings numbers dates
  
字符串的处理
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>
    <h1>string类型介绍</h1>
    hello:<span th:text="${hello}"></span><br>
    hello是否为空:<span th:text="${#strings.isEmpty(hello)}"></span><br>
    hello字符串是否包含"th":<span th:text="${#strings.contains(hello,'th')}">
</span><br>
    hello字符串是否包含"Th":<span th:text="${#strings.contains(hello,'Th')}">
</span><br>
    hello以H开头:<span th:text="${#strings.startsWith(hello,'H')}"></span><br>
    hello以a开头:<span th:text="${#strings.startsWith(hello,'a')}"></span><br>
    hello以H结尾:<span th:text="${#strings.endsWith(hello,'H')}"></span><br>
    hello以a结尾:<span th:text="${#strings.endsWith(hello,'a')}"></span><br>
    hello的长度:<span th:text="${#strings.length(hello)}"></span><br>
    hello都大写:<span th:text="${#strings.toUpperCase(hello)}"></span><br>
    hello都小写:<span th:text="${#strings.toLowerCase(hello)}"></span><br>
</body>
</html>
   

  
日期时间类型的处理
<h1>日期时间处理</h1>
时间:<span th:text="${now}"></span><br>
时间:<span th:text="${#dates.format(now)}"></span><br>
时间:<span th:text="${#dates.format(now,'yy/MM/dd')}"></span><br>
时间:<span th:text="${#dates.format(now,'yy/MM/dd hh:ss:mm')}"></span><br>
时间:<span th:text="${#dates.format(now,'yy/MM/dd HH:ss:mm')}"></span><br>
年份:<span th:text="${#dates.year(now)}"></span><br>
月份:<span th:text="${#dates.month(now)}"></span><br>
日期:<span th:text="${#dates.day(now)}"></span><br>
本周的第几天:<span th:text="${#dates.dayOfWeek(now)}"></span><br>
小时:<span th:text="${#dates.hour(now)}"></span><br>
  

  
6.2.3 条件判断
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>

    <h1>条件判断</h1>
    <h2>if语句</h2>
    <span th:if="${sex} == '男'">男</span>
    <span th:unless="${sex} =='男'">女</span>
    <br>
    <!-- and or not -->
    <span th:if="${flag or false}">
        or的使用11
    </span>
    <span th:unless="${flag or false}">
        or的使用12
    </span>
    <br>
    <span th:if="${flag and false}">
        and的使用21
    </span>
    <span th:unless="${flag and false}">
        and的使用22
    </span>
    <br>
    <span th:if="${not flag}">
        not的使用11
    </span>
    <span th:unless="${not flag}">
        not的使用22
    </span>
    <br>

    <!-- 三木运算符 -->
    <span th:text="true?'A':'B'"></span><br>

    <!-- switch语句 -->
    <hr>
    <div th:switch="${age}">
        <div th:case="17">
            17岁
        </div>
        <div th:case="18">
            18岁
        </div>
        <div th:case="19">
            19岁
        </div>
        <div th:case="*">其他...</div>
    </div>
</body>
</html>
    

  
6.2.4 循环语句
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>

    <h1>循环判断</h1>
    <div th:each="c : ${list1}">
        <span th:text="${c}"></span><br>
    </div>
    <hr>
    <div th:each="user : ${list2}">
        <span th:text="${user.id}"></span>&nbsp;&nbsp;
        <span th:text="${user.userName}"></span>&nbsp;&nbsp;
        <span th:text="${user.address}"></span><br>
    </div>
    <hr>
    <div th:each="m : ${map}">
        <!-- 每次循环获取的是一个KV对 -->
        <span th:text="${m.getKey() + ':' + m.getValue().getId()}"></span>
        <span th:text="${m.getKey() + ':' + m.getValue().getUserName()}"></span>
        <span th:text="${m.getKey() + ':' + m.getValue().getAddress()}"></span>
    </div>
    <hr>
    <div th:each="user,iter : ${list2}">
        <span th:text="${iter.count}"></span>&nbsp;&nbsp;
        <span th:text="${iter.index}"></span>&nbsp;&nbsp;
        <span th:text="${user.id}"></span>&nbsp;&nbsp;
        <span th:text="${user.userName}"></span>&nbsp;&nbsp;
        <span th:text="${user.address}"></span><br>
    </div>
</body>
</html>
   

  
6.2.5 域对象的操作
也就是我们怎么在Thymeleaf中获取三大作用域中绑定的数据。
@RequestMapping("/hello4")
public String hello4(HttpServletRequest request){
    request.setAttribute("req","request msg ...");
    request.getSession().setAttribute("sess","session msg ....");
    request.getServletContext().setAttribute("app","application msg ....");
    return "/user4";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>
    <h1>域对象使用</h1>
    <h2>request:</h2>
    <span th:text="${#httpServletRequest.getAttribute('req')}"></span><br>
    <span th:text="${#request.getAttribute('req')}"></span><br>
    <span th:text="${req}"></span><br>
    <h2>session:</h2>
    <span th:text="${#httpSession.getAttribute('sess')}"></span><br>
    <span th:text="${#session.getAttribute('sess')}"></span><br>
    <h2>servletContext:</h2>
    <span th:text="${#servletContext.getAttribute('app')}"></span><br>
</body>
</html>
  
效果

  
6.2.6 URL表达式
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf介绍</title>
</head>
<body>

    <h1>URL使用</h1>
    <a href="http://www.baidu.com">百度</a><br>
    <a th:href="@{http://www.baidu.com}">百度</a><br>
    <hr>
    <a th:href="@{/show}">相对路径</a><br>
    <a th:href="@{~/project2/app1}">相对于服务器的根</a><br>
    <a th:href="@{/show(id=1,name=aaa)}">相对路径--参数传递</a><br>
    <a th:href="@{/path/{id}/show(id=66,name=123)}">RestFul支持</a>
</body>
</html>
  

  
6.2.7 整合案例改造
我们可以将前面介绍的SpringBoot+MyBatis+Freemaker的案例改为SpringBoot+MyBatis+Thymeleaf的案例,涉及到的页面代码如下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户管理</h1>
<h2>
    <a th:href="@{/user/dispatchUpdate}">添加用户</a>
</h2>
    <table>
        <tr>
            <th>编号</th>
            <th>账号</th>
            <th>姓名</th>
            <th>邮箱</th>
            <th>电话</th>
            <th>操作</th>
        </tr>

        <tr th:each="user : ${list}">
            <td th:text="${user.user_id}"></td>
            <td th:text="${user.user_name}"></td>
            <td th:text="${user.real_name}"></td>
            <td th:text="${user.email}"></td>
            <td th:text="${user.phone}"></td>
            <td>
                <a th:href="@{/user/dispatchUpdate(id=${user.user_id})}">更新</a>
                <a th:href="@{/user/deleteUser(id=${user.user_id})}">删除</a>
            </td>
        </tr>

    </table>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form th:action="@{/user/userUpdate}" method="post" >
        <span th:if="${user}">
            <input type="hidden" name="user_id" th:value="${user.user_id}">
        </span>

        <label>账号</label><input type="text" name="user_name" th:value="${ user==null ?'':user.user_name}"><br>
        <label>姓名</label><input type="text" name="real_name" th:value="${user==null ?'':user.real_name}"><br>
        <label>邮箱</label><input type="text" name="email" th:value="${user==null ?'':user.email}"><br>
        <label>电话</label><input type="text" name="phone" th:value="${user==null ?'':user.phone}"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>
   

二、SpringBoot高级

1.热部署
为了提高我们的开发效率,我们可以放开IDEA中的SpringBoot项目的热部署操作。
  
1.1 放开配置
在IDEA中默认是没有放开热部署操作的,我们需要手动的放开设置。

  
1.2 注册
Control+shift+Alt+/ 会出现一个弹出界面。

  
然后选择Registry

  
1.3 添加devtools
<!--devtools 热部署的支持 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
  
2. 异常处理
2.1 自定义错误页面
SpringBoot默认的处理异常的机制:一旦程序出现了异常SpringBoot会想 /error 的url发送请求,在SpringBoot中提供了一个 BasicExceptionController来处理 /error 请求,然后跳转到默认显示异常的页面来展示异常信息。

  
如果我们需要将所有的异常统一跳转到我们自定义的错误页面,需要在src/main/resources/template 目录下创建一个 error.html页面,注意名称必须是 error.html。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>系统出错,请联系管理员....</h1>
    <span th:text="${exception}"></span>
</body>
</html>
  

  

  
2.2 @ExceptionHandle注解
package com.bobo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UserController {

    @RequestMapping("/show1")
    public String showInfo1(){
        String name = null;
        // 模拟 空指针异常
        name.length();
        return "index";
    }

    @RequestMapping("/show2")
    public String showInfo2(){
        int a = 1/0; // 默认算术异常
        return "index";
    }

    @ExceptionHandler(value = {NullPointerException.class})
    public ModelAndView nullPointerExceptionHandler(Exception e){
        ModelAndView mm = new ModelAndView();
        mm.addObject("error",e.toString());
        mm.setViewName("error1");
        return mm;
    }

    @ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView arithmeticException(Exception e){
        ModelAndView mm = new ModelAndView();
        mm.addObject("error",e.toString());
        mm.setViewName("error2");
        return mm;
    }
}
  
error1.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>系统出错,请联系管理员....nullPointerExceptionHandler</h1>
    <span th:text="${error}"></span>
</body>
</html>
   
error2.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>系统出错,请联系管理员....arithmeticException</h1>
    <span th:text="${error}"></span>
</body>
</html>
  
效果

  

  
2.3 @ControllerAdvice注解
上面的实现将控制器和异常处理的方法写在了一块,显然不太合理,这时我们可以通过@ControllerAdvice注解来实现解耦。
     
专门的异常处理类
package com.bobo.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

// @ControllerAdvice
public class GlobalException {
    //@ExceptionHandler(value = {NullPointerException.class})
    public ModelAndView nullPointerExceptionHandler(Exception e){
        ModelAndView mm = new ModelAndView();
        mm.addObject("error",e.toString());
        mm.setViewName("error1");
        return mm;
    }

    //@ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView arithmeticException(Exception e){
        ModelAndView mm = new ModelAndView();
        mm.addObject("error",e.toString());
        mm.setViewName("error2");
        return mm;
    }
}
  
控制器代码
package com.bobo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UserController {

    @RequestMapping("/show1")
    public String showInfo1(){
        String name = null;
        // 模拟 空指针异常
        name.length();
        return "index";
    }
    @RequestMapping("/show2")
    public String showInfo2(){
        int a = 1/0; // 默认算术异常
        return "index";
    }
}
   

   
2.4 SimpleMappingExceptionResolver
我们还可以通过SimpleMappingExceptionResolver来简化我们的异常处理。
package com.bobo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.Properties;

@SpringBootApplication
public class SpringbootDemo11Application {

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

    /**
     * 通过SimpleMappingExceptionResolver 设置 特定异常和 处理器的映射关系
     * @return
     */
    // @Bean
    public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.put("java.lang.NullPointerException","error1");
        properties.put("java.lang.ArithmeticException","error2");
        resolver.setExceptionMappings(properties);
        return  resolver;
    }
}
   
2.5 HandleExceptionResolver处理
我们上面讲的SimpleMappingExceptionResolver本质上就是实现HandleExceptionResolver的。

  
所以我们也可以自己来实现HandleExceptionResolver接口。
package com.bobo.exception;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyHandleExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView mm = new ModelAndView();
        if(e instanceof NullPointerException){
            mm.setViewName("error1");
        }else if(e instanceof  ArithmeticException){
            mm.setViewName("error2");
        }else{
            mm.setViewName("error");
        }
        return mm;
    }
}
   
3. 单元测试
为了提高在开发过程中的效率,我们可以通过SpringBoot中提供的单元测试来快速测试service和dao的业务逻辑。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
   
业务逻辑
package com.bobo.service.impl;

import com.bobo.service.IUserService;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

@Service
public class UserServiceImpl implements IUserService {


    @Override
    public List<String> query() {

        return Arrays.asList("张三","李四","王五");
    }
}
     
单元测试
package com.bobo;

import com.bobo.service.IUserService;
import net.bytebuddy.asm.Advice;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootDemo12ApplicationTests {

    @Autowired
    private IUserService service;


    @Test
    void contextLoads() {
        System.out.println("---->" + service.query());
    }

    @BeforeEach
    void before(){
        System.out.println("before ...");
    }

    @AfterEach
    void after(){
        System.out.println("after ...");
    }

}
  

    
4. 整合Shiro
4.1 项目准备
创建一个SpringBoot项目整合MyBatis,Thymeleaf,SpringMVC等并创建相关的配置文件和Service逻辑。
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.8</version>
        </dependency>
        <!--devtools 热部署的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    <dependencies>
  
属性配置文件
server.port=8082

# 配置JDBC的相关信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/logistics?
characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

# 配置连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

# 配置MyBatis的package 设置别名
mybatis.type-aliases-package=com.bobo.pojo

# 指定映射文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
   
通过MyBatis Generator自动生成持久层的相关的代码(t_user表)或者从之前的货运系统中拷贝对应的代码。

  
Service的逻辑实现
package com.bobo.service;

import com.bobo.pojo.User;

import java.util.List;

public interface IUserService {

    public User login(String userName);

    public List<User> query(User user);
}
package com.bobo.service.impl;

import com.bobo.mapper.UserMapper;
import com.bobo.pojo.User;
import com.bobo.pojo.UserExample;
import com.bobo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper mapper;

    @Override
    public User login(String userName) {
        User user =  new User();
        user.setUserName(userName);
        List<User> list = this.query(user);
        if(list != null && list.size() == 1){
            return list.get(0);
        }
        return null;
    }

    @Override
    public List<User> query(User user) {
        UserExample example = new UserExample();
        UserExample.Criteria criteria = example.createCriteria();
        if(user != null){
            if(!"".equals(user.getUserName()) && user.getUserName() != null){
                criteria.andUserNameEqualTo(user.getUserName());
            }
        }
        return mapper.selectByExample(example);
    }
}
  
到此准备完成。
  
4.2 Shiro整合
4.2.1 Shiro的依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
   
4.2.2 自定义Realm
package com.bobo.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private IUserService service;

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
       return null;
    }
}
  
4.2.3 Shiro的配置
package com.bobo.config;

import com.bobo.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Hash;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.apache.shiro.mgt.SecurityManager;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 配置凭证匹配器
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");
        matcher.setHashIterations(1024);
        return matcher;
    }

    /**
     * 注册自定义的Realm
     * @param hashedCredentialsMatcher
     * @return
     */
    @Bean
    public MyRealm myRealm(CredentialsMatcher hashedCredentialsMatcher){
       MyRealm realm = new MyRealm();
       realm.setCredentialsMatcher(hashedCredentialsMatcher);
       return realm;
    }

    /**
     * 注册SecurityManager对象
     * @return
     */
    @Bean
    public SecurityManager securityManager(Realm myRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm);
        return manager;
    }

    /**
     * 注册ShiroFilterFactoryBean
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager manager){
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        filter.setSecurityManager(manager);
        filter.setLoginUrl("/login.do");
        filter.setSuccessUrl("/success.html");
        filter.setUnauthorizedUrl("/refuse.html");
        // 设置过滤器
        Map<String,String> map = new HashMap<>();
        map.put("/css/**","anon");
        map.put("/img/**","anon");
        map.put("/js/**","anon");
        map.put("/login","anon");
        map.put("/login.do","authc");
        map.put("/**","authc");
        filter.setFilterChainDefinitionMap(map);
        return filter;
    }
}
  
4.2.4 测试
添加对应的测试文件

   
package com.bobo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {
    @RequestMapping("/login")
    public String goLoginPage(){
        return "login";
    }
}
package com.bobo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/query")
    public String query(){
        System.out.println("----user query----");
        return "user";
    }
}
  
4.3 认证实现
4.3.1 自定义Realm
在自定义Realm中完成认证逻辑。
package com.bobo.realm;

import com.bobo.pojo.User;
import com.bobo.service.IUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.SimpleByteSource;
import org.springframework.beans.factory.annotation.Autowired;

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private IUserService service;

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String userName = token.getUsername();
        User user = new User();
        user.setUserName(userName);
        // 账号验证
        user = service.login(userName);
        if(user == null){
            return null;
        }
        return new SimpleAuthenticationInfo(user,user.getPassword(),new SimpleByteSource(user.getU1()) ,"myRealm");
    }

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}
    
4.3.2 控制器
在控制器中完成认证失败的处理。
package com.bobo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class LoginController {

    @RequestMapping("/login")
    public String goLoginPage(){
        return "login";
    }
    
    @RequestMapping("/login.do")
    public String login(HttpServletRequest request){
        Object obj = request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
        System.out.println("认证错误的信息:" + obj);
        return "/login";
    }

    @RequestMapping("/logout")
    public String logout(){
        SecurityUtils.getSubject().logout();
        return "/login";
    }
}
   
4.3.3 登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>登录页面</h1>
    <form th:action="@{/login.do}" method="post">
        账号:<input type="text" name="username" th:value="'admin1'"><br>
        密码:<input type="password" name="password" th:value="'123'"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>
   
5. 授权操作
5.1 注解的使用
我们需要开启SpringMVC对注解的支持。
/**
* 开启对Shiro授权注解的支持
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(securityManager);
    return advisor;
}
   
在自定义Realm中添加权限。
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    User user = (User) principalCollection.getPrimaryPrincipal();
    System.out.println("获取授权的账号:" + user.getUserName());
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    info.addRole("role1");
    return info;
}
     

   
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>
   
启动访问

  

  

  
5.2 标签的使用
添加相关的依赖。
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
  
注入对象
@Bean
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}
   
在页面中实现处理
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
    xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户管理</h1>
    <shiro:authenticated>
        已登录:<shiro:principal property="userName"></shiro:principal>
    </shiro:authenticated>
    <a href="#" shiro:hasRole="role1">用户查询</a>
    <a href="#" shiro:hasRole="role1">用户添加</a>
    <a href="#" shiro:hasRole="role2">用户修改</a>
    <a href="#" shiro:hasRole="role2">用户删除</a>
</body>
</html>
  
访问效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1350635.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GraalVM Native学习及使用

概述 在开发Spring Boot 应用或者其他JAVA程序的过程中&#xff0c;启动慢、内存占用大是比较头疼的问题&#xff0c;往往需要更多的资源去部署&#xff0c;成本大幅提高。为了优化上述问题&#xff0c;常常使用优化程序、使用更小消耗的JVM、使用容器等措施。 现在有一个叫做…

权限修饰符和代码块

权限修饰符&#xff1a;是用来控制一个成员能够被访问的范围的。、 可以修饰成员变量、方法、构造方法、内部类。 权限修饰符的范围 权限修饰符的使用规则&#xff1a; 实际开发中&#xff0c;一般只用private和public 成员变量私有 方法公开 特例&#xff1a;如果方法中的…

Debezium日常分享系列之:向 Debezium 连接器发送信号

Debezium日常分享系列之&#xff1a;向 Debezium 连接器发送信号 一、概述二、激活源信号通道三、信令数据集合的结构四、创建信令数据集合五、激活kafka信号通道六、数据格式七、激活JMX信号通道八、自定义信令通道九、Debezium 核心模块依赖项十、部署自定义信令通道十一、信…

前端文件上传组件最全封装+删除+下载+预览

前言&#xff1a;使用的是若依的框架element uivue2封装的。如果有不对的地方欢迎指出。后台管理使用&#xff0c;文件需要上传。回显列表&#xff0c;详情也需要回显预览 // 开始封装组件&#xff1a;封装在 src/components/FileUpload/index.vue中 <template><div c…

诺派克ROPEX热封控制器维修RES-408/400VAC

德国希尔科诺派克ROPEX热封控制器维修型号包括&#xff1a;RES-401&#xff0c;RES-402&#xff0c;RES-403&#xff0c;RES-406&#xff0c;RES-407&#xff0c;RES-408&#xff0c;RES-409&#xff0c;RES-420&#xff0c;RES-440&#xff0c;MSW-2&#xff0c;PEX-W3&#x…

三层架构概述

三层架构就是把整个软件的代码分为三个层次&#xff0c;分层的目的是&#xff1a;规范代码&#xff0c;大型软件需要团队配合的时候问题就来了&#xff0c;由于每个程序员风格不一样&#xff0c;而开发软件大量的代码风格不统一就会造成后期调试和维护出现问题&#xff0c;然而…

Axure骚操作:【制作可暂停与不可暂停进度加载条】

目录 一、不可暂停进度条 1.1 前期准备 1.2 效果假想 1.3 适用场景 1.4 实现步骤 &#xff08;1&#xff09;除按钮外的元件设置隐藏 &#xff08;2&#xff09;给按钮添加交互 &#xff08;3&#xff09;给变量值文本标签添加交互 &#xff08;4&#xff09;给进度条矩…

计算机网络——传输层(五)

前言&#xff1a; 最重要的网络层我们已经学习完了&#xff0c;下面让我们再往上一层&#xff0c;对网络层的上一层传输层进行一个学习与了解&#xff0c;学习网络层的基本概念和网络层中的TCP协议和UDP协议 目录 ​编辑一、传输层的概述&#xff1a; 1.传输层&#xff1a; …

二叉树的前序遍历 、二叉树的最大深度、平衡二叉树、二叉树遍历【LeetCode刷题日志】

目录 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 方法二&#xff1a;传址调用记录节点个数 二、二叉树的最大深度 三、平衡二叉树 四、二叉树遍历 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 计算树的节点数: 函数TreeSize用于…

LLM之RAG理论(四)| RAG高级数据索引技术

本文将重新审视分块技术以及其他方法&#xff0c;包括查询增强、层次结构和知识图谱。 一、简单RAG架构快速概览 在2023年年初&#xff0c;我的主要关注点集中在Vector DB及其在更广泛的设计领域中的表现上。然而&#xff0c;随着2023年的收尾&#xff0c;这一领域出现了重大进…

Spring MVC - Controller的创建与使用

控制器Controller是处理器&#xff0c;是真正处理请求的组件 1 创建Controller 一般在src/main/java/com/qdu下建立一个controller包用来存放所有控制器。当创建一个控制器时&#xff0c;首先要记得使用Controller标记将该类注册成为一个控制器类。 然后在SpringMVCConfig类…

项目 杂碎 知识点 汇总!!!

Vue !!! setup生命周期 使用 nextTick &#xff01;&#xff01;获取节点 onMounted中可以使用JS&#xff0c;获取节点&#xff0c;setup生命周期无法获取节点 vue实现文本粘贴复制 Vue遍历对象 1、使用v-for指令&#xff1a;可以直接遍历对象的键和值 2、使用 Object.keys…

git基础概念和常用命令(日常开发收藏备用)

目录 ### 常用命令 ### 远程仓库与克隆 ### 分支管理 ### 子模块&#xff08;Submodule&#xff09; ### 其他高级操作 ### 交互式暂存&#xff08;Interactive Staging&#xff09; ### cherry-pick ### rebase ### reflog与reset ### 子树合并&#xff08;Subtree …

【Maven】<scope>provided</scope>

在Maven中&#xff0c;“provided”是一个常用的依赖范围&#xff0c;它表示某个依赖项在编译和测试阶段是必需的&#xff0c;但在运行时则由外部环境提供&#xff0c;不需要包含在最终的项目包中。下面是对Maven scope “provided”的详细解释&#xff1a; 编译和测试阶段可用…

Vue3全局属性app.config.globalProperties

文章目录 一、概念二、实践2.1、定义2.2、使用 三、最后 一、概念 一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。点击【前往】访问官网 二、实践 2.1、定义 在main.ts文件中设置app.config.globalPropertie import {createApp} from vue import ElementPl…

.NET国产化改造探索(二)、银河麒麟安装人大金仓数据库

随着时代的发展以及近年来信创工作和…废话就不多说了&#xff0c;这个系列就是为.NET遇到国产化需求的一个闭坑系列。接下来&#xff0c;看操作。 上一篇介绍了如何安装银河麒麟操作系统&#xff0c;这篇文章详细介绍下如何在银河麒麟操作系统上安装人大金仓数据库。 准备安…

LangChain.js 实战系列:搭配 LangSmith 实现调试、监控、测试

&#x1f4dd; LangChain.js 是一个快速开发大模型应用的框架&#xff0c;它提供了一系列强大的功能和工具&#xff0c;使得开发者能够更加高效地构建复杂的应用程序。LangChain.js 实战系列文章将介绍在实际项目中使用 LangChain.js 时的一些方法和技巧。 LangSmith 是 LangCh…

RK3568平台开发系列讲解(Linux系统篇)PWM系统编程

🚀返回专栏总目录 文章目录 一、什么是PWM二、PWM相关节点三、PWM应用编程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 PWM 的系统编程。 一、什么是PWM PWM,即脉冲宽度调制(Pulse Width Modulation)

2023年下半年信息系统项目管理师考试

目录 引言 结果 论文 案例分析 综合知识 总结 引言 2023年下半年参加了信息系统项目管理师考试&#xff0c;考试结果情理之中&#xff0c;意料之外。论文压线&#xff0c;综合和案例差一分。从个人参加考试的整个过程来看&#xff0c;属于历史性的突破。以本文&#xff…

稳定币记录

稳定币&#xff1a; 稳定币&#xff08;Stablecoin&#xff09;是一种加密货币&#xff0c;其设计目的是维持相对稳定的价值&#xff0c;通常与某种法定货币&#xff08;如美元、欧元&#xff09;或其他资产&#xff08;如黄金&#xff09;挂钩。稳定币通过将加密货币与相应的…