SpringBoot

SpringBoot

  • pom.xml

每个springboot项目中都要引入父项目
在spring-boot-starter-parent 的 父项目 spring-boot-dependencies里有各种依赖的版本控制

<parent>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-parent</artifactId>

    <version>2.7.1</version>

</parent>

<!--
 在spring-boot-starter-parent 的 父项目 spring-boot-dependencies
 里有各种依赖的版本控制
 -->

<dependencies>
    <!-- spring-boot的场景启动器,关联该场景里的常用jar包 -->
    <!-- web场景启动器里 关联的有 日志,内嵌tomcat,json等 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-configuration-processor</artifactId>

        <optional>true</optional>

    </dependency>

    <!-- 配置属性类跟配置文件绑定的处理器 -->

    <dependency>
        <groupId>org.projectlombok</groupId>

        <artifactId>lombok</artifactId>

    </dependency>

</dependencies>

入门程序

spring-boot的场景启动器,关联该场景里的常用jar包
web场景启动器里 关联的有 日志,内嵌tomcat,json等

<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>

  • application.properties
server.port=8080
server.servlet.context-path=/ls
  • handler
@RestController //返回json
public class HelloHandler {
    @RequestMapping("/hello")
    public String hello(){
        return "springboot入门程序-hello!";
    }
}
  • 创建启动类/向导类/主程序
      • 		注意包结构 :主包结构下 创建
        
@SpringBootApplication//默认扫描所在包里的子包里的 各类注解
public class MyRunner {
    public static void main(String[] args) {
        SpringApplication.run(MyRunner.class, args);
    }
}
  • SpringBootApplication注解上 被SpringBootConfiguration 标注

  • SpringBootConfiguration 被 Configuration标注

  • Configuration 是 spring中的配置类,也是被扫描为组件的一个注解

  • Configuration注解上 被 Component 标注 会被扫描为spring容器里的组件/bean

  • 运行主程序,在浏览器输入localhost:8080/ls/hello即可访问

  • 添加spring-boot-maven-pluginpom.xml创建可执行jar

  1. 停止程序
  2. 添加插件
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

  1. 运行maven 的打包命令
  2. 将jar拷贝入桌面文件夹abc内 (模拟任意文件夹)
  3. 使用命令运行jar
java -jar sbt001-1.0-SNAPSHOT.jar
java -jar -Dserver.port=8090 sbt001-1.0-SNAPSHOT.jar

依赖管理

1.jar包版本

每个SringBoot项目都需要引入父项目

    <parent>
        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.6.6</version>

    </parent>

ctrl单击spring-boot-starter-parent查看父项目发现: 父项目 还有一个 父项目

  <parent>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-dependencies</artifactId>

    <version>2.6.6</version>

  </parent>

ctrl单击spring-boot-dependencies查看,发现几乎声明了开发中常用各种jar的版本号:

<properties>
    <activemq.version>5.16.1</activemq.version>

    <antlr2.version>2.7.7</antlr2.version>

    <appengine-sdk.version>1.9.86</appengine-sdk.version>

    <artemis.version>2.15.0</artemis.version>

    <aspectj.version>1.9.6</aspectj.version>

    ......
  • 版本号自动被父项目进行管理dependencyManagement ,在导包时可以不写版本号
  • 可以自行指定版本号
      <dependency>
            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.47</version>

        </dependency>

  • 可以在自己项目的pom中指定版本号属性
    <properties>
        <mysql.version>5.1.47</mysql.version>

    </properties>

2.场景启动器

springboot中有很多 形如: spring-boot-starter-XXX 的场景启动器 , 他会自动把XXX场景需要的所有jar包一同依赖进来.

<!-- 引入web场景相关的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

ctrl+单击 spring-boot-starter-web 可以看到 web场景的很多依赖被加入.

helloworld 的自动配置

  1. 自动引入并配置好了内嵌的tomcat
  2. 自动引入并配置好了web常见组件. 比如 示例里的中文没有乱码说明已经配置好了字符编码过滤器
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MainInitiator.class, args);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
    }

在打印的容器中组件名的时候,我们发现了: 各种viewResolver , multipartResolver,dispatcherServlet,characterEncodingFilter,jackson等组件.

  1. 默认的包结构 约定>配置 目前必须这么做.


主程序(向导/启动类)所在包下及其子包里 带有注解的组件 都才会被扫描到容器中.

  1. 各种配置都有默认配置

比如: tomcat的端口号8080
我们可以在resources目录下的 application.properties文件中进行修改

server.port=8088

在我们写的时候发现有快捷键提示. ctrl单击:server.port 跳转到配置属性类中: 转到setter方法

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    ......

@ConfigurationProperties :配置属性类 , 前缀是server, springboot会自动绑定到属性配置文件中
而配置属性类 的 信息 会被 系统读取 .

  1. 按需加载自动配置.

注: 在配置文件中 配置: debug=true 则可以在启动界面中看到 条件评估报告: 哪些场景装配了.哪些场景没装配,缺少了哪些条件等

容器功能里的几个注解

1.@Configuration @SpringBootConfiguration
@AutoConfiguration 配置类

2.@Import
@Import注解 可以给容器中导入某个组件

@Import(value = {Random.class, Date.class})
@Configuration
public class MyConfig {
    ...

默认的组件的名字 全路径类名 eg: java.util.Random
注: 一般的bean的id或名字 为 类名小写 比如 myRunner
3.@Conditional
满足指定的条件才生效. 可以添加到类或方法上
4.配置绑定

  • 配置文件里使用一些前缀 可以 直接绑定到配置属性类里的属性


  • 在 application.properties 中

  • handler

  • 成功运行

SpringInitializr

使用IDEA 的SpringInitializr 快速创建 springBoot应用 必须联网

  • 此处改为2.7.1,删除java17版本,使用java8

  • 目录结构

yml/yaml

  • YAML 的配置文件后缀为 .yml
  • 使用缩进和换行表示层级关系

yml

server:
  port: 8082 # 属性和属性值之间 使用 冒号 空格 隔开
  servlet:
    context-path: '/ls' # 字符串可以使用引号 或 不使用
cat:
  name: 'tomcat'
  weight: 2.2D
dog:
  nick: 旺财
  age: 5
#
beauty:
  name: '李思'
  age: 28
  model: true
  cat:
    name: '大花猫'
    weight: 3.3D
  birthday: 2000/01/12 # yml里日期格式必须为 yyyy/MM/dd
 
 #数组 list set 都有两种写法
  hobbys: ['冬泳','羽毛球','烹饪']
  foods:
    - 大白菜
    - 胡萝卜
    - 冬枣
  salarys: [1000.0F,555.5F,6666.8F]
 
 # map或对象 都有两种写法
  sizes:
    height: 177cm
    weight: 55.9kg
    eye: 4.9
# 对象可以用{}jason格式 也可使用缩进列出的格式
  dogs:
    big: [{'nick':'大黄','age':7},{'nick':'二黄','age':6},{'nick':'小黄','age':5}]
    small:
      - {'nick':'蝴蝶1号','age':1}
      - {'nick':'蝴蝶2号','age':2}
      - nick: '蝴蝶3号'
        age: 3

controller

@RestController
public class YMLController {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    @Autowired
    private Beauty beauty;
    
        @RequestMapping("/demo")
    public Map<String,Object> demo(){
        Map<String,Object> map = new HashMap<>();
        map.put("猫数据",cat);
        map.put("狗数据",dog);
        map.put("美女数据",beauty);
        return map;
    }
}

在浏览器访问: http://localhost:8082/ls/demo

SpringMVC自动配置

1: springboot引入web场景以后, 会自动配置一些 springmvc的配置, 能够应对绝大多数的web场景.
2: 如果我们想继续使用 springboot的默认配置 , 以及还能自己添加一些配置, 那么我们可以

  • 使用@Configration 和 @Bean 自己做配置类 ,添加组件
  • 或实现 WebMvcConfirgure接口, 重写方法或添加 一些组件进去.
  • 或使用mvc已经绑定的前缀的配置属性类 在 配置文件里更改默认配置

3: 如果我们想弃用springboot的mvc的所有默认配置, 全面接管mvc场景的配置, 使用**@Configration + @EnableWebMvc**

WebMvcAutoConfigration 自动配置类有一个条件是 : spring容器里不能有这个类型的组件: WebMvcConfigurationSupport

@EnableWebMvc 注解:
发现 自动向spring容器添加一个组件: DelegatingWebMvcConfiguration
DelegatingWebMvcConfiguration 是 WebMvcConfigurationSupport 的子类型

静态资源

A.默认的静态资源目录

在static下的index.html会被默认访问


改变默认的静态资源路径:

 # 配置springboot静态资源 存放的位置 为 类路径下的my文件夹下的(原来的static里的index.html就访问不了)
spring:
  web:
    resources:
      static-locations: ['classpath:/my/']
    # 配置springboot静态资源的 访问路径
  mvc:
    static-path-pattern: '/a/**'

B. WebJars 了解

除了前面提到的“标准”静态资源位置外,Webjars内容也有特殊情况。/webjars/**如果jar文件以Webjars格式打包,则从jar文件提供带有路径的所有资源。

在 webjars的网站上:https://www.webjars.org/ 搜索 jquery的jar包依赖 添加到 pom中
必须添加下面这个:

<dependency>
    <groupId>org.webjars</groupId>

    <artifactId>jquery</artifactId>

    <version>3.6.0</version>

</dependency>

<button id="btn" type="button">点击</button>

<script src="/ls/webjars/jquery/3.7.1/jquery.slim.js"></script>

<script>
    $("#btn").click(function () {
        alert(1+2+3+4);
    })
</script>

C.默认欢迎页面

Spring Boot支持静态和模板欢迎页面。它首先index.html在配置的静态内容位置中查找文件。如果找不到,它将寻找一个index模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。

模板欢迎页: 走控制层,经视图解析器得到的欢迎页.

①.静态欢迎页

在static目录下 放入 index.html , 不使用任何的自定义的: 静态资源访问前缀 的前提下,
则可以测试看到默认欢迎页.

②.controller处理 /index请求 转发到 欢迎页 后面说到模板时再说.

请求参数处理

A.restful风格

  • html
<form action="/ls/user" method="post">
    <input name="username" value="POST请求"/>
    <input type="submit" value="POST请求"/>
</form>

<form action="/ls/user" method="post">
    <input name="username" value="PUT请求"/>
    <input type="hidden" name="_method" value="PUT"/>
    <input type="submit" value="PUT请求"/>
</form>

<form action="/ls/user" method="post">
    <input name="username" value="DELETE请求"/>
    <input type="hidden" name="_method" value="DELETE"/>
    <input type="submit" value="DELETE请求"/>
</form>

  • yml
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true
  • controller
@GetMapping("/user")
public String get(){
    return "restful风格的get请求完成!";
}

@PostMapping("/user")
public String post(String username){
    return "restful风格的post请求完成! - "+username;
}

@PutMapping("/user")
public String put(String username){
    return "restful风格的put请求完成! - "+username;
}

@DeleteMapping("/user")
public String delete(String username){
    return "restful风格的delete请求完成! - "+username;
}

○ 让 mvc自动配置的restful过滤器失效,向容器里自己添加一个

@Configuration
public class MyConfig {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("mym");
        return hiddenHttpMethodFilter;
    }
}
<h3><a href="/ls/user">GET请求</a></h3>

<form action="/ls/user" method="post">
    <input name="username" value="POST请求"/>
    <input type="submit" value="POST请求"/>
</form>

<form action="/ls/user" method="post">
    <input name="username" value="PUT请求"/>
    <input type="hidden" name="mym" value="PUT"/> 使用mym
    <input type="submit" value="PUT请求"/>
</form>

<form action="/ls/user" method="post">
    <input name="username" value="DELETE请求"/>
    <input type="hidden" name="mym" value="DELETE"/>
    <input type="submit" value="DELETE请求"/>
</form>

B.自定义类型转换器

在WebMvcConfigurer接口中可以进行很多配置. 下面添加自定义类型转换器: (Converter只用于 请求参数绑定到handler方法入参, 不会对配置文件里的有作用.)

  • html
<h3><a href="/ls/user/2000-01-01">GET请求</a></h3>

  • 转换器
@Configuration
public class MyConfig implements WebMvcConfigurer {
    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String, Date>() {

            @Override
            public Date convert(String source) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                try {
                    Date date = sdf.parse(source);
                    return date;
                } catch (ParseException e) {
                    e.printStackTrace();
                    throw new RuntimeException("不符合转换的日期格式! source = "+source);
                }
            }
        });
    }
    
}
  • controller

数据访问

每一个XXXX-starter-XXX 都会有一个 XXXautoConfigration的类 做自动配置.一般自动配置类 还 绑定了在配置文件中的某个前缀

1.默认连接池是 Hikari 默认配置了JdbcTemplate

  • 添加 JDBC 启动器
    • 连接池的自动配置类 : DataSourceAutoConfigruation
  • 加入mysql的驱动 依赖
  • 修改配置文件
  • 在 test包下 进行测试 -
@Resource
private JdbcTemplate template;

@Test
void contextLoads() {
    String sql = "select count(*) cs from worker";
    Integer integer = template.queryForObject(sql, Integer.class);
    System.out.println(integer);
}

2.修改连接池为 Druid

  • 引入 druid的 starter
  • 修改配置文件

监控功能的访问路径 是 /druid/index.html

  • controller


druid里有内置的DataSource,可以自动注入

@Autowired
private DataSource dataSource;

@ResponseBody
@RequestMapping("/workers")
public List<Worker> workers(){
    JdbcTemplate template = new JdbcTemplate(dataSource);
    String sql = "select * from worker";
    List<Worker> workers = template.query(sql, new BeanPropertyRowMapper<>(Worker.class));
    return workers;
}

3.springboot的配置文件

1.可以放置 4个位置 加载顺序 由上到下

2.多配置文件形式

多配置文件形式 采用 application-xxx.yml xxx表示不同阶段
如: dev prod test


dev: 开发环境
prod: 生产环境
test: 测试环境


3.

  1. application.yml里 使用 - - - 多文档形式

     删除三个阶段的配置文件
    

日志

1.Log4j 介绍

    Log4j是Apache是apache旗下的一款开源的日志框架,通过在项目中使用Log4j,我们可以控制信息输出到控制台,文件、甚至是数据库中。我们可以控制每一条日志的输出格式。通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目调试。

2.Log4j组件

    log4j主要由Loggers(日志记录器),Appenders(输出器),和Layout(日志格式化器)。其中Loggers控制日志输出级别和日志是否输出;Appenders指定日志的输出方式(输出到控制台,文件等);Layout控制日志输出的格式。
Loggers:
    日志记录器,负责收集处理日志记录,实例的命名是类"XX"的全限定类名,Loggers的名字大小写铭感,其命名有继承机制:例如:name为org.apache.commons的logger会继承为name为org.apache的logger。

    Log4j中有一个特殊的logger叫做"root",它是所有logger的根。也就意味着其他所有的logger都会直接会间接的继承root。root的logger可以用Logger.getRootLogger()方法获取。但是,自log4j1.2版以来,Logger类已经取代了Category类,对于熟悉早期版本的log4j的来说,Logger类可以被视为Category的别名。
Appensers:
    Appensers用来指定日志输出到哪个地方,可以同时指定日志输出目的地,Log4j常用的输出目的地有一下几种:

ConsoleAppender: 将日志输出到控制台
FileAppender: 将日志输出到文件
DailyRollingFileAppender:将日志输出到一个文件,并且每天输出到一个新的文件。
RollingFileAppender:将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定的尺寸时,会自动把文件改名,同时产生一个新的文件。
JDBCAppender:将日志信息保存到数据库。

Layouts
    布局器Layouts用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。Log4j常用的Layouts:

HTMLLayout: 格式化日志输出HTML表格形式
SimpleLayout: 简单的日志输出格式化,打印的日志格式为(info - message)
PatternLayout:最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式。

3.案例



4.使用log4j & 日志适配

  • 使用log4j
    • 添加jar包,将lib文件夹添加为库
    • log4j.properties(建在src下)
#               级别 , 自定义的输出源
log4j.rootLogger=INFO,myConsole
# 输出源的配置
# 控制台输出
log4j.appender.myConsole=org.apache.log4j.ConsoleAppender
# 输出布局采用正则布局
log4j.appender.myConsole.layout=org.apache.log4j.PatternLayout
# 正则布局格式
log4j.appender.myConsole.layout.ConversionPattern=%p [%t] %l %m%n
  • java
public class Show {
    // 日志对象
    public static Logger logger = Logger.getLogger(Show.class);
    
    public static void main(String[] args) throws InterruptedException {
        int cnt = 0;
        for (int i = 1; i <= 100; i++) {
            try {
                int div = new Random().nextInt(3);// 0 1 2
                logger.info("次数: " + i);
                int result = 100 / div;
                //日志输出
                logger.info("结果: " + result);// slf4j里的日志输出可以使用{}占位符
            } catch (ArithmeticException e) {
                logger.error(e.getMessage());
                cnt++;
            }
            Thread.sleep(1300);
        }
        logger.info("出现异常次数 即 随机到0的次数 : " + cnt);

    }
}
  • 输出
  • 使用slf4j门面
    • jar
    • log4j.properties
#               级别 , 自定义的输出源
log4j.rootLogger=INFO,myConsole,myScrollFile
                           # myFile

#log4j.appender.myFile=org.apache.log4j.FileAppender
#log4j.appender.myFile.layout=org.apache.log4j.PatternLayout
#log4j.appender.myFile.layout.ConversionPattern=%p [%t] %l %m%n
#log4j.appender.myFile.File=logs/my.log
#log4j.appender.myFile.Append=true

log4j.appender.myScrollFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.myScrollFile.layout=org.apache.log4j.PatternLayout
log4j.appender.myScrollFile.layout.ConversionPattern=%p [%t] %l - %d - %m%n
log4j.appender.myScrollFile.File=logs/my.log
log4j.appender.myScrollFile.Append=true
log4j.appender.myScrollFile.DatePattern='.'yyyy-MM-dd-HH-mm
  • 在项目下建包logs
  • Show
public class Show {
    // 日志对象
    //public static Logger logger = Logger.getLogger(Show.class);
    public static Logger logger = LoggerFactory.getLogger(Show.class);

    public static void main(String[] args) throws InterruptedException {
        int cnt = 0;
        for (int i = 1; i <= 100; i++) {
            try {
                int div = new Random().nextInt(3);// 0 1 2
                logger.info("次数: " + i);
                int result = 100 / div;
                //日志输出
                logger.info("结果: {}",result);// slf4j里的日志输出可以使用{}占位符
            } catch (ArithmeticException e) {
                logger.error(e.getMessage());
                cnt++;
            }
            Thread.sleep(1300);
        }
        logger.info("出现异常次数 即 随机到0的次数 : {}",cnt);

    }
}
  • 输出
  • 在SpringBoot中使用logbak
    • 在resource下添加logbak-spring.xml
    • 在application.xml中添加
logging:
  config: 'classpath:logbak-spring.xml'

thymeleaf

springboot 默认不支持 JSP , 推荐使用thymeleaf.
使用详情参考使用文档

入门程序

  • pom.xml

  • 查看ThymeleafAutoConfiguration
  • 查看ThymeleafProperties
  • 在 ThHelloConytroller
@Controller
public class ThHelloController {

    @Autowired
    private Beauty beauty;

    @RequestMapping("/thindex")
    public String thindex(){
        return "thindex";
    }
}
  • thindex
<h2><a th:href="@{/thhello}">thymeleaf入门</a></h2>

  • ThHelloController
@Controller
public class ThHelloController {

    @Autowired
    private Beauty beauty;

    @RequestMapping("/thhello")
    public String thhello(ModelMap map){
        map.addAttribute("msg","thymeleaf入门程序");
        map.addAttribute("beauty",beauty);
        return "success";
    }
}
  • success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>

<body>
<h2>文本中直接获取数据是: [[${msg}]]</h2>

<h2>在标签里的数据是:<span th:text="${msg}"></span></h2>


<h2>美女名字: <span th:text="${beauty.name}"></span></h2>

<h2>美女年龄: <span th:text="${beauty.age}"></span></h2>

<h2>美女是不是模特: <span th:text="${beauty.model}"></span></h2>

<h2>美女生日: <span th:text="${beauty.birthday}"></span></h2>

<h2>美女的猫: <span th:text="${beauty.cat.name}"></span></h2>

<!-- 数组,list,set 遍历写法一致 -->
<p th:each="food,stat:${beauty.foods}">
    <span th:text="${stat.count}"></span> : <span th:text="${food}"></span>

</p>

<hr/>
<hr/>
<p th:each="entry:${beauty.sizes}">
    [[${entry.key}]] - <span th:text="${entry.value}"></span>

</p>

<hr/>
<hr/>
<div th:each="item:${beauty.dogs}">
    <span th:text="${item.key}"></span><br/>
    <p th:each="dog:${item.value}">
        <span th:text="${dog.nick}"></span> - <span th:text="${dog.age}"></span>

    </p>

</div>

<hr/>
<hr/>
<h2 th:if="${beauty.model}">美女是模特</h2>

<h2 th:unless="${beauty.age != 28}">大学老师</h2>

<hr/>
<hr/>
<div th:switch="${beauty.age}">
    <p th:case="20">小学生</p>

    <p th:case="24">大学生</p>

    <p th:case="28">博士生</p>

    <p th:case="32">院士</p>

</div>


</body>

</html>

几个基本功能

1. 新建项目-登录

  1. 使用初始化向导创建项目,勾选必要的场景依赖
  2. 将静态资源加入项目
  3. 将login.html放入templates目录下 并引入thymeleaf的命名空间 xmlns:th=“http://www.thymeleaf.org
  4. 创建 跳转登录页的controller 方法
@Controller
public class IndexController {
     /**
     * 跳转登录页
     * @return
     */
    @GetMapping(path = {"/","/login"})
    public String loginPage(){
        return "login";
    }
  1. 将index.html放入templates目录下 并引入thymeleaf的命名空间
  2. 修改login.html中登录表单的属性
    <form class="form-signin" th:action="@{/login}" method="post">
  1. 创建 处理登录 并 跳转首页的controller 方法
    @PostMapping(value = {"/login"})
    public  String indexPage(User user, HttpSession session,Model model){
        if (!StringUtils.isEmpty(user.getUsername())
            && !StringUtils.isEmpty(user.getPassword())){
            session.setAttribute("user", user);
        }else{
            model.addAttribute("msg", "用户名或密码不正确!");
            return "login";
        }
        return "redirect:/index.html";
    }

    @RequestMapping("/index.html")
    public  String redirectIndexPage(@SessionAttribute(name = "user",required = false) User user,Model model){
        if (user==null){
            model.addAttribute("msg", "未登录!请先登录!");
            return "login";
        }
        return "index";
    }

//这里为了 防止表单 重复提交.
  1. 设计bean User类型
@Data
@ToString
public class User {
    private String username;
    private String password;
}

  1. login.html中添加一个 回显 msg信息的标签
 <div class="login-wrap">
            <label class="text-danger" th:text="${msg}" />
            <input type="text" name="username" class="form-control" placeholder="用户名" autofocus>
            <input type="password" name="password" class="form-control" placeholder="密码">
     ... ...

2.抽取公共部分,让一个页面可以复用

  1. 创建table文件夹,存放所有表格页面

  1. 观察页面将 页面中公共的部分 抽取到 common.html中 ,使用thymeleaf语法可以让其他页面引用common.html中的 部分

  1. 在被抽取页面引用公共部分
    <!-- 引入相同的头部部分 -->
    <th:block th:include="common::commonHead"/>
    <!-- 引入相同的左侧导航栏 -->
    <th:block th:replace="common::commonLeftNav"></th:block>    

三种引入公共片段的方式的区别:

公共部分: 
<footer th:fragment="copy">
  &copy; 2011 The Good Thymes Virtual Grocery
</footer>


使用三种引入方式:
<body>

  <div th:insert="footer :: copy">2</div>

  <div th:replace="footer :: copy">2</div>

  <div th:include="footer :: copy">2</div>

  
</body>

结果:
<body>

  整个片段插入 到 当前标签里
  <div>
    <footer>
      &copy; 2011 The Good Thymes Virtual Grocery
    </footer>

  </div>

  整个片段替换 当前标签
  <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
  </footer>

   只有片段里的内容 替换到 当前标签内 
  <div>
    &copy; 2011 The Good Thymes Virtual Grocery
  </div>

  
</body>

3.拦截器(登录拦截)

1.创建拦截器实现类

/**
 * 在访问诸如 /tables/** 这种特殊路径下的内容时 需要有登录权限
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        if (user == null){
            log.warn("正在访问的资源为: {}",request.getRequestURI());
            //重定向到登录页面的handler方法 不放行
            response.sendRedirect(request.getContextPath()+"/login");
            return false;
        }
        return true;
    }
}

2.配置拦截器,将拦截器注入容器,并添加拦截规则

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LoginInterceptor loginInterceptor = new LoginInterceptor();
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") // 添加拦截路径
                .excludePathPatterns("/","/login","/js/**","/images/**",
                        "/fonts/**","/css/**","/error/**");// 排除拦截路径
    }

    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

4. 文件上传

  1. 添加上传表单页面 form_layouts.html 添加 thymeleaf的 命名空间
<form role="form" method="post" enctype="multipart/form-data"
                              th:action="@{/upload}">
                            <div class="form-group">
                                <label for="username">Username</label>

                                <input type="text" class="form-control" name="username" id="username" placeholder="username">
                            </div>

                            <div class="form-group">
                                <label for="password">Password</label>

                                <input type="password" class="form-control" name="password" id="password" placeholder="Password">
                            </div>

                            <div class="form-group">
                                <label for="headimg">头像</label>

                                <input type="file" id="headimg" name="headimg">
                                <p class="help-block">Example block-level help text here.</p>

                            </div>

                            <div class="form-group">
                                <label for="liveimg">生活照</label>

                                <input type="file" id="liveimg" name="liveimg" multiple>
                                <p class="help-block">Example block-level help text here.</p>

                            </div>

                            <div class="checkbox">
                                <label>
                                    <input type="checkbox"> Check me out
                                </label>

                            </div>

                            <button type="submit" class="btn btn-primary">Submit</button>

                        </form>







<script>
  $("#headimg").change(function(){
    let file = this.files[0];
    let url = URL.createObjectURL(file);
    $("#headimgShow").attr("src",url);
    $("#headimgShow").css("display","inline-block");
  })

  $("#liveimg").change(function(){
    $("#liveimgShowDiv").empty();
    let files = this.files;
    for (let i = 0; i < files.length ; i++) {
      let url = URL.createObjectURL(files[i]);
      let img = $("<img />");
      img.attr({"src":url,"width":50,"height":50});
      $("#liveimgShowDiv").append(img);
    }
  })





  1. common页面的 左侧栏中 修改访问 表单页面的超链接
<a th:href="@{/form_layouts}">表单布局</a>

  1. 添加controller 首先处理 跳转到 表单页面
@Controller
public class FileController {
    @GetMapping("/form_layouts")
    public String form_layouts(){
        return "form/form_layouts";
    }
    ... ...
  1. yaml中 配置 最大文件上传大小
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB
  1. 处理上传请求
   
    @PostMapping("/upload")
    public String uploadm(User user,
                          @RequestPart(name="headimg",required = false) MultipartFile headimg,
                          MultipartFile[] liveimg){
        log.info("上传信息:{}",user.toString());
        log.info("头像大小:{}",headimg.getSize());
        log.info("生活照数量:{}",liveimg.length);
        //保存文件和springMVC用法一致.
        return "redirect:/index.html";
    }

5.错误处理


1.自定义错误页面
可以在静态文件夹下创建 error文件夹 也可以在 模板文件夹下 创建 error文件夹,给页面 添加 thymeleaf的命名空间

2.在5xx页面上 可以使用 ${message}来获取 异常信息.
${status} 取响应状态码

3.异常处理自动配置原理
4.定义全局异常处理器 @ControllerAdvice + @ExceptionHandler 默认优先级高

@ControllerAdvice // 有@Component
@Slf4j
public class MyGlobalExceptionHandler {

    @ExceptionHandler({ArithmeticException.class,NullPointerException.class})
    //这是处理 异常的方法,用起来其实就是handlere方法
    public ModelAndView exceptionHandler(Exception e){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error/myxx");
        mv.addObject("status", "9527");
        mv.addObject("message","出现了算术或空指针异常");
        log.warn("全局异常处理器工作了,异常: {}",e.getMessage());
        return mv;
    }
}


5.自定义异常 @ResponseStatus 定义状态码 和 错误原因信息 (因为springboot 异常和错误要带着http响应状态码)

@ResponseStatus(code = HttpStatus.BAD_REQUEST,reason = "请求语法有问题")
public class CustomException extends Exception{
}

6.自定义异常解析器 默认优先级低 , 使用@order注解 设置优先级

@Component
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error/myxx");
        mv.addObject("status",9999);
        mv.addObject("message","异常解析器处理了!");
        return mv;
    }
}

6.国际化

1.提供国际化资源配置文件

title=默认标题
user=默认用户
pass=默认密码
title=登录
user=用户
pass=密码
title=Login
user=Username
pass=Password
  1. yaml配置指定资源配置文件位置
spring:
  messages:
    basename: ls

3.自定义国际化解析器

public class MyLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String lan = request.getParameter("lan");
        if (lan != null && lan.contains("_")){
            String[] data = lan.split("_");
            // data[0] zh  data[1] CN
            Locale locale = new Locale(data[0],data[1]);
            return locale;
        }
        // 返回默认国际化信息对象 或者 自定义一个
        return Locale.getDefault();
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

4.配置为组件, 组件名必须为 localeResolver

@Configuration
public class MyConfig implements WebMvcConfigurer {
 @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

5.thymeleaf里结合消息表达式一起使用

<a th:href="@{/index(lan='zh_CN')}">中文</a>

<a th:href="@{/index(lan='en_US')}">英文</a>

<h2 th:text="#{index.login}"></h2>

<h2 th:text="#{index.register}"></h2>

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值