SpringBoot深入理解运用

SpringBoot的优缺点

优点

  • 创建独立Spring应用
  • 内嵌web服务器
  • 自动starter依赖,简化构建配置
  • 自动配置Spring以及第三方功能
  • 提供生产级别的监控,健康检查及外部化配置
  • 无代码生成,无需编写xml

缺点

  • 人称版本帝,迭代快,需要时刻关注变化
  • 封装太深,内部原理复杂,不容易精通

SpringBoot是整合Spring技术栈的一站式框架

SpringBoot是简化Spring技术栈的快速开发脚手架

SpringBoot四大核心

  1. 自动装配

  2. 起步依赖

  3. Actuator

  4. 命令行界面

    国内开发常用的是自动装配和起步依赖

idea生成springboot项目

idea选择Spring Initializr模块即可

新建的包或者类一定要在启动类的同级或者子级目录下,不然不会被识别

小插曲(修改logo)

在资源目录Resource下新建banner.txt文件,并如下复制

${AnsiColor.BRIGHT_YELLOW}

//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            佛祖保佑       永不宕机     永无BUG                  //

${AnsiColor.BRIGHT_RED}
Application Version: ${application.version}${application.formatted-version}
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}

${AnsiColor.BRIGHT_RED}:设置控制台中输出内容的颜色,可以自定义,具体参考org.springframework.boot.ansi.AnsiColor

${application.version}:用来获取MANIFEST.MF文件中的版本号,这就是为什么要在Application.java中指定 SpringVersion.class

{application.formatted-version}:格式化后的{application.version}版本信息

${spring-boot.version}:Spring Boot的版本号

{spring-boot.formatted-version}:格式化后的{spring-boot.version}版本

https://www.bootschool.net/ascii在线生成工具

properties文件

springBoot项目建立成功后会自动生成一个application.properties核心配置文件(但是SpringBoot建议使用yml/yaml文件),一个项目中只能有一个核心配置文件

properties的书写格式为 key = value

#设置内嵌tomcat端口号
server.port=8081
#设置上下文根(也就是web访问的根目录),必须斜杠开头
server.servlet.context-path=/test

application.yml /application.yaml

server:
  port: 8081
  servlet:
    context-path:  /test

多环境配下的配置文件

在工作中开发的环境有:开发环境 测试环境 准生产环境 生产环境

如果我们每到一个环境修改一次核心配置文件,会带来不必要的麻烦以及工作量,因此需要多环境下的配置文件

(格式:application-xxx.properties / application-xxx.yaml )

properties和yaml文件方法一致

开发环境配置文件

application-dev.properties

#开发环境配置文件
server.port=8081
server.servlet.context-path=/dev

测试环境配置文件

application-test.properties

#测试环境配置文件
server.port=8082
server.servlet.context-path=/test

准生产环境

application-uat.properties

#准生产环境
server.port=8083
server.servlet.context-path=/uat

生产环境

application-product.properties

#生产环境配置文件
server.port=8084
server.servlet.context-path=/product

主核心配置文件切换环境(application.properties)

#spring核心配置文件
#激活使用的配置文件
spring.profiles.active=dev

SpringBoot自定义配置

简单数值

​ 一个一个获取,不能出现变量重名现象,因此出现映射对象

properties格式

#开发环境配置文件
server.port=8081
server.servlet.context-path=/dev

#自定义值
name=zhangsan
age=18
# 会出现未定义错误:
# Cannot resolve configuration property 'name’
# Cannot resolve configuration property 'age'
#取出该值 @Value("${name}}")

yaml格式

server:
  port: 8081
  servlet:
    context-path: /dev

name: zhangsan
age: 17

Controller

@Controller
public class IndexController {
    @Value(value = "${name}")
    private String name;
    @Value("${age}")
    private int age;
    @RequestMapping("/user")
    @ResponseBody()
    public String getMes(){
        Map map = new HashMap();
        map.put("name:",name);
        map.put("age:",age);
        return map.toString();
    }
}

映射对象

步骤1:书写配置文件

#student对象
student.name=studentNO1
student.age=16

#user对象
user.name=userNO1
User.age=18

#映射同一个对象,注意点:前缀必须存在,前缀必须统一(不统一不属于同一个类),不然后续的 @ConfigurationProperties(prefix = "user") 无法确定
Student:
  name: studentNO2
  age: 16
User:
  name: userNO2
  age: 18

步骤2:注册一个类

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component  //将此类交由Spring容器管理
@ConfigurationProperties(prefix = "user") //设置该类为配置类,
public class User {
    //设置该类为配置类时注意两点:
    // 1.必要设置前缀参数,会报必须设置prefix
    // 2.前缀参数一定不能和类名一致,会报前缀参数格式不标准
    String name;
    int age;
    ...省略get\set\toString
}

步骤3:Cotroller层

@Controller
public class UserController {
    @Autowired  //自动注入
    private User user;
    @RequestMapping("/test")
    public @ResponseBody String getMes(){
        return "user-name:"+user.getName()+"user-age:"+user.getAge();
    }
}

去除红色警告

出现Spring Boot Configuration Annotation Processor not configured红色警告

导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <version>2.5.5</version>
</dependency>

整合JSP

步骤一

在mian文件中新建webapp文件夹(此时该文件夹是普通文件夹,需要设置),打开项目接口(快捷键:Ctrl+Alt+Shift+S) 选中Modules模块,点击项目下的web,设置它的web资源文件夹(Web Resource Directory),点击加号添加,找到自己新建的webapp文件夹添加后确定

步骤二

引入SpringBoot内嵌Tomcat对jsp的解析依赖,不添加解析不了jsp

若只展示jsp页面,只添加一下一个依赖,若有其他需求,自行搜索

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <version>10.1.0-M6</version>
</dependency>

步骤三:

SpringBoot项目默认推荐使用的前端引擎时thymeleaf,现在我们需要使用springboot集成jsp,手动指定jsp最后编译的路径

而且springboot集成jsp编译jsp的路径是springboot规定好的位置META-INF/resource

<resources>
    <resource>
        <!--源文件夹-->
        <directory>src/main/webapp</directory>
       <!-- 指定编译到META-INF/resource-->
        <targetPath>META-INF/resource</targetPath>
        <!--指定源文件夹中的哪个资源要进行编译-->
        <includes>
            <include>*.*</include>
        </includes>
    </resource>
</resources>

步骤四:

配置视图解析器(在application)

#配置视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

步骤五:

@Controller
public class JSPController {
    @RequestMapping("/jsp")
    public ModelAndView getMes(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("message","hello,我已经使用SPringboot集成了jsp!");
        mv.setViewName("index");
        return mv;
    }
    @RequestMapping("/jsp1")
    public String getMes(Model model){
        model.addAttribute("message","hello,JSP");
        return "index";
    }
}

逆向工程

步骤一

导入相关依赖:

<!-- Mysql驱动-->
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
 </dependency>
 <!--mybatis整合SpringBoot框架的起步依赖:需要添加版本号的原因
 是mybatis整合SpringBoot框架,而不是SpringBoot框架整合Mybatis-->
 <dependency>
     <groupId>com.github.dreamroute</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.0.0</version>
 </dependency>

步骤二

使用Mybatis提供的逆向工程生成实体bean,映射器文件,Dao接口

1.什么是逆向工程

mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的代码(mapper.java,mapper.xml、po…)

由数据库的表生成java代码。

2 下载逆向工程中需要的jar

mybatis-generator-core-1.3.2.jar

当然还需要一些其他的jar,比如连接mysql的jar

mysql-connector-java-5.1.28-bin.jar

3 使用方法

以下介绍两种方法(推荐第二种)

3.1 通过java project运行逆向工程

使用java程序方式,不依赖开发工具。

步骤1:添加生成代码配置文件 generatorConfig.xml

内容如下:

 1 <?xml version="1.0" encoding="UTF-8"?>  
 2 <!DOCTYPE generatorConfiguration  
 3   PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"  
 4   "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">  
 5 <generatorConfiguration>  
 6     <context id="testTables" targetRuntime="MyBatis3">  
 7     <commentGenerator>  
 8         <!-- 是否去除自动生成的注释 true:是 : false:否 -->  
 9         <property name="suppressAllComments" value="true" />  
10     </commentGenerator>  
11     <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->  
12     <jdbcConnection driverClass="com.mysql.jdbc.Driver"  
13            connectionURL="jdbc:mysql://192.168.1.78:3306/fdd_yun" userId="admin" password="Admin_12345">  
14     </jdbcConnection>  
15     <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"  
16     connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg"  password="yycg">  
17     </jdbcConnection> -->  
18     <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和  
19     NUMERIC 类型解析为java.math.BigDecimal -->  
20     <javaTypeResolver>  
21         <property name="forceBigDecimals" value="false" />  
22     </javaTypeResolver>  
23   
24     <!-- targetProject:生成PO类的位置 -->  
25     <javaModelGenerator targetPackage="com.fdd.newhouse.po"  
26     targetProject=".\src">  
27         <!-- enableSubPackages:是否让schema作为包的后缀 -->  
28         <property name="enableSubPackages" value="false" />  
29         <!-- 从数据库返回的值被清理前后的空格 -->  
30         <property name="trimStrings" value="true" />  
31     </javaModelGenerator>  
32   
33      <!-- targetProject:mapper映射文件生成的位置 -->  
34     <sqlMapGenerator targetPackage="com.fdd.newhouse.mapper"  
35     targetProject=".\src">  
36         <!-- enableSubPackages:是否让schema作为包的后缀 -->  
37         <property name="enableSubPackages" value="false" />  
38     </sqlMapGenerator>  
39   
40     <!-- targetPackage:mapper接口生成的位置 -->  
41     <javaClientGenerator type="XMLMAPPER" targetPackage="com.fdd.newhouse.mapper"  
42     targetProject=".\src">  
43         <!-- enableSubPackages:是否让schema作为包的后缀 -->  
44         <property name="enableSubPackages" value="false" />  
45     </javaClientGenerator>  
46     <!-- 指定数据库表 -->  
47     <table tableName="yun_customer"></table>  
48     <table tableName="yun_guide_record"></table>  
49     </context>  
50 </generatorConfiguration>  

需要修改的为生成po,mapper的包路径,需要指定的数据库表

步骤2:编写运行方法 run

 1 import java.io.File;  
 2 import java.util.ArrayList;  
 3 import java.util.List;  
 4   
 5 import org.mybatis.generator.api.MyBatisGenerator;  
 6 import org.mybatis.generator.config.Configuration;  
 7 import org.mybatis.generator.config.xml.ConfigurationParser;  
 8 import org.mybatis.generator.internal.DefaultShellCallback;  
 9   
10 public class Start {  
11   
12     public static void main(String[] args) throws Exception{  
13   
14         List<String> warnings = new ArrayList<String>();  
15         boolean overwrite = true;  
16         File configFile = new File("generatorConfig.xml");  
17         ConfigurationParser cp = new ConfigurationParser(warnings);  
18         Configuration config = cp.parseConfiguration(configFile);  
19         DefaultShellCallback callback = new DefaultShellCallback(overwrite);  
20         MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);  
21         myBatisGenerator.generate(null);  
22     }  
23 }  

步骤3:执行main方法

步骤4:拷贝

需要将生成工程中所生成的代码拷贝到自己的工程中。

3.2通过maven方式运行

步骤1:在pom中的bulid标签下添加

  <plugin>
               <!-- mybaties逆向工程插件-->
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <!--配置文件的位置,在项目下-->
                    <configurationFile>GeneratorConfig.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.30</version>
                    </dependency>
                </dependencies>
            </plugin>

步骤二:添加生成代码配置文件 generatorConfig.xml放到src/main/resource下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- 指定本地驱动,可全路径
    <classPathEntry location="classpath:mysql-connector-java-5.1.30.jar" />
     -->
    <!--配置table表信息内容体,tatgetRuntime指定Mybatis3版本-->
    <context id="testTables" targetRuntime="MyBatis3">
    <commentGenerator>
        <!-- 是否去除自动生成的注释 true:是 : false:否
              抑制生成注释由于生成的都是英文的,可以不让他生成        -->
        <property name="suppressAllComments" value="true" />
    </commentGenerator>
    <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
    <!-- mysql -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                    connectionURL="jdbc:mysql://localhost:3306/school" userId="root" password="root">
    </jdbcConnection>
    <!-- oracle
    <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
    connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg"  password="yycg">
    </jdbcConnection> -->
    <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
    NUMERIC 类型解析为java.math.BigDecimal -->
    <javaTypeResolver>
        <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>
    <!-- targetProject:生成POJO类的位置 -->
    <javaModelGenerator targetPackage="com.stu.pojo"
                        targetProject=".\src\main\java">
        <!-- enableSubPackages:是否让schema作为包的后缀 -->
        <property name="enableSubPackages" value="false" />
        <!-- 从数据库返回的值被清理前后的空格 -->
        <property name="trimStrings" value="true" />
    </javaModelGenerator>
    <!-- targetProject:mapper映射文件生成的位置 -->
    <sqlMapGenerator targetPackage="com.stu.mapper"
                     targetProject=".\src\main\java">
        <!-- enableSubPackages:是否让schema作为包的后缀 -->
        <property name="enableSubPackages" value="false" />
    </sqlMapGenerator>
    <!-- targetPackage:mapper接口生成的位置 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.stu.mapper"
                         targetProject=".\src\main\java">
        <!-- enableSubPackages:是否让schema作为包的后缀 -->
        <property name="enableSubPackages" value="false" />
    </javaClientGenerator>
    <!-- 指定数据库表 -->
    <!--数据库表-->
    <!--tableName对应数据库表明  domainObjectName对应对象名-->
    <table tableName="teacher" domainObjectName="Teacher"
           enableCountByExample="false"
           enableUpdateByExample="false"
           enableDeleteByExample="false"
           enableSelectByExample="false"
           selectByExampleQueryId="false" >
        <property name="useActualColumnNames" value="false"/>
    </table>
</context>
</generatorConfiguration>

步骤3…执行maven命令(或者直接点击pom文件新添加的插件新生成的插件)

mvn mybatis-generator:generate

覆盖之前写的文件,则如下:

mvn -Dmybatis.generator.overwrite=true mybatis-generator:generate

逆向生成的xml解析

<?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.stu.mapper.TeacherMapper">
  <resultMap id="BaseResultMap" type="com.stu.pojo.Teacher">
    <!--id 标签只能修饰主键字段-->
    <!--result除了主键以外的字段-->
    <!--
         column 数据库的字段名
         property 映射对象的属性名称
         jdbcType 列中数据库中字段的类型(可以省略)
    -->
    <!--
        resultMap作用:
        1.当数据库中的字段名与实体类的属性名不一致时,可以进行替换
        2.当前查询的结果没有对象一个表的时候,可以自定义一个结果集(如引用类型)
    -->
    <id column="tid" jdbcType="INTEGER" property="tid" />
    <result column="name" jdbcType="VARCHAR" property="name" />
  </resultMap>


  <!--
        sql语句片段,将公共的部分抽取出来(查询时不建议使用select * ,效率慢)
        通过include标签引用sql语句片段,如下
  -->
  <sql id="Base_Column_List">
    tid, name
  </sql>


  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    /*根据tid查询tid name,此处使用sql语句片段  */
    <include refid="Base_Column_List" />
    from teacher
    where tid = #{tid,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from teacher
    where tid = #{tid,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.stu.pojo.Teacher">
    insert into teacher (tid, name)
    values (#{tid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.stu.pojo.Teacher">
    insert into teacher
        /*trim拼接sql语句,prefix前缀,suffix后缀,suffixOverrides去除多余的符号*/
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="tid != null">
        tid,
      </if>
      <if test="name != null">
        name,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="tid != null">
        #{tid,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.stu.pojo.Teacher">
    update teacher
    <set>
      <if test="name != null">
        name = #{name,jdbcType=VARCHAR},
      </if>
    </set>
    where tid = #{tid,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.stu.pojo.Teacher">
    update teacher
    set name = #{name,jdbcType=VARCHAR}
    where tid = #{tid,jdbcType=INTEGER}
  </update>
</mapper>

整合Mybatis

步骤一:

使用逆向工程或则自己手动创建pojo实体类、mapper.java、mapper.xml

步骤二:

资源过滤问题

      <resources>
          <resource>
              <!-- 设定主资源目录  -->
              <directory>src/main/java</directory>
              <!-- 只处理如下配置中包含的资源类型 -->
              <includes>
                  <include>**/*.properties</include>
                  <include>**/*.xml</include>
              </includes>
              <!-- 不处理如下配置中包含的资源类型(剔除下如下配置中包含的资源类型)-->
              <excludes>
                  <exclude>**/*.yaml</exclude>
              </excludes>
              <!-- 是否对主资源目录开启资源过滤 -->
              <filtering>true</filtering>
          </resource>
		 <resource>
              <!--输出目录,默认为/target/classes-->
              <!-- <targetPath>${project.build.outputDirectory}</targetPath>-->
              <!-- 设定主资源目录  -->
              <directory>src/main/resources</directory>
              <!-- 只处理如下配置中包含的资源类型 -->
              <includes>
                  <include>*</include>
              </includes>
              <!-- 是否对主资源目录开启资源过滤 -->
              <filtering>false</filtering>
          </resource>

      </resources>

步骤三:导入相关依赖

<!-- Mysql驱动-->
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
 </dependency>
 <!--mybatis整合SpringBoot框架的起步依赖:需要添加版本号的原因
 是mybatis整合SpringBoot框架,而不是SpringBoot框架整合Mybatis-->
 <dependency>
     <groupId>com.github.dreamroute</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.0.0</version>
 </dependency>

第四步:编写相应代码

controller层

package com.stu.controller;
import com.stu.pojo.Teacher;
import com.stu.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MybatisController {
    @Autowired  //自动装配teacherService
    private TeacherService teacherService;
    @RequestMapping("/mybatis")
    public @ResponseBody Object getMes(Integer id){
        Teacher teacher = teacherService.queryTeacherById(id);
         return teacher;
    }
}

service层

package com.stu.service;

import com.stu.mapper.TeacherMapper;
import com.stu.pojo.Teacher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TeacherServiceImp implements TeacherService{
    @Autowired
    private TeacherMapper teacherMapper;
    //注入
    @Override
    public Teacher queryTeacherById(Integer id) {
        return teacherMapper.selectByPrimaryKey(id);
    }
}
package com.stu.service;
import com.stu.pojo.Teacher;
public interface TeacherService{
    Teacher queryTeacherById(Integer id);
}

Mapper层(Dao层)

package com.stu.mapper;

import com.stu.pojo.Teacher;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.stereotype.Component;


@Mapper //扫描Mapper(Dao)接口到spring容器  不理解为什么这添加的Mapper、MapperScan都失效 ,只能在入口类那设置MapperScan
public interface TeacherMapper {
    int deleteByPrimaryKey(Integer tid);

    int insert(Teacher record);

    int insertSelective(Teacher record);

    Teacher selectByPrimaryKey(Integer tid);

    int updateByPrimaryKeySelective(Teacher record);

    int updateByPrimaryKey(Teacher record);
}

Springboot入口类(启动类)

package com.stu;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.stu.mapper") //开启扫描mapper包下的mapper和子包(子目录)的mapper类,不用一个一个的去添加mapper注解
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

第五步:在application.properties指定数据库

事务支持

在方法上面添加@Transactional注解即可

@Service
public class TeacherServiceImp implements TeacherService{
    @Autowired
    private TeacherMapper teacherMapper;
    //注入
    @Transactional
    @Override
    public Teacher queryTeacherById(Integer id) {
        return teacherMapper.selectByPrimaryKey(id);
    }
}

Mapper配置文件存放的位置

方式一:默认的位置,与mapper类同文件夹(java/main/mapper)

需要资源过滤,无法识别java下的配置文件

方式二:存放在resources目录下

不需要资源过滤,但是需要在application.properties中指定mapper配置文件的目录

#指定MyBatis映射方法
mybatis.mapper-locations=classpath:mapper/*.xml

注意:多检查target目录是否与项目目录一致

常用注解:

@Controller

在控制层类上加@Controller,说明该类为控制类(本质仍然是Competent)

@RequestMapping(value="/test")

在在控制层方法上添加,表示请求路径为test,该方法支持get请求和post请求,默认get请求

  • @RequestMapping(value="/test",method=RequestMethod.GET)

    在在控制层方法上添加,表示请求路径为test,该方法只支持get请求,请求方式不对则会报405错误

    相当于:@GetMapping(value="")

    该注解通过在查询数据的时候使用 —》查询

  • @RequestMapping(value="/test",method=RequestMethod.POST)

    在在控制层方法上添加,表示请求路径为test,该方法只支持post请求,请求方式不对则会报405错误

    相当于:@PostMapping(value="")

    该注解通过在新增数据的时候使用 —》新增

  • @RequestMapping(value="/test",method=RequestMethod.DELETE)

    在在控制层方法上添加,表示请求路径为test,该方法只支持delete请求,请求方式不对则会报405错误

    相当于:@DeleteMapping(value="")

    该注解通过在删除数据的时候使用 —》删除

  • @RequestMapping(value="/test",method=RequestMethod.PUT)

    在在控制层方法上添加,表示请求路径为test,该方法只支持put请求,请求方式不对则会报405错误

    相当于:@PutMapping(value="")

    该注解通过在修改数据的时候使用 —》修改

优点:通过求情得知是什么操作

@ResponseBody

在在控制层方法上添加或则public后添加,表明该方法返回的是json对象,不添加默认视图文件

@RestController

相当于控制层类加上@Controller+所有方法加@ResponseBody ,意味着当前控制层所有的方法返回的都是JSON对象

Restful风格

REST (英文: Representational State Transfer,简称REST)
一种互联网软件架构设计的风格,但它那不是标准,它只是提出了- -组客户端和服务器交互时的架构理念和设计原则,基于这种理念和原则设计的接口可以更简洁,更有层次, REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。任何的技术都可以实现这种理念,如果- -个架构符合REST原则,就称它为RESTFul架构
比如我们要访问一一个http接口: http://localhost:8080/boot/order?id=1021&status=1
采用RESTFul风格则http地址为: http://localhost:8080/boot/order/1021/1

SpringBoot开发RESTFul

spring boot开发RESTFul主要时几个注解实现的

(1)@PathVariable

​ 获取url中的数据

该注解是实现RESTFul最主要的一个注解

package com.stu.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RESTFul {
    @RequestMapping("/restful/{name}/{id}")
    public String getMes(@PathVariable("name") String name, @PathVariable("id") int id){
        return "name:"+name+"---id:"+id;
    }

}

当代码如下时

package com.stu.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RESTFul {
    @RequestMapping("/restful/{name}/{id}")
    public String getMes1(@PathVariable("name") String name, @PathVariable("id") int id){
        return "name:"+name+"---id:"+id;
    }
    @RequestMapping("/restful/{name}/{tid}")
    public String getMes2(@PathVariable("name") String name, @PathVariable("tid") int tid){
        return "name:"+name+"---tid:"+tid;
    }

}

运行报错::java.lang.IllegalStateException

分析:以上代码由于请求得参数得类型都一致,无法识别,会出现请求路径模糊错误

通常在RESTful风格中方法的请求方式会按照增删查改的请求方式区分

当出现请求冲突时:

  • 会按照增删查改的请求方式区分
  • 修改请求路径

注意:RESTful风格请求路径中使用名词,不要出现动词!

@ConfigurationProperties与@Value的比较

yaml properties

@ConfigurationProperties @Value
属性注入 批量注入配置文件中的属性 需要一个一个的指定
松散绑定 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  • cp只需要写一次即可,value则需要每个字段都添加
  • 松散绑定:在配置文件yaml中写的last-name,就相当于lastName一样,- 后面跟着的字母默认大写,这就是松散绑定
  • JSR303数据校验,这个就是我们可以在字段上增加一层过滤器验证 , 可以保证数据的合法性
  • 复杂类型封装,yaml中可以封装对象,使用@value就不支持

结论:

  • 配置yaml/yml和配置properties都可以获取到值,强烈推荐yaml/yml
  • 如果我们在某个业务中,只需要获取文件的某个值,可以使用一下@value
  • 如果说,我们专门编写了一个javaBean来和配置文件进行映射,就可以直接用@ConfigurationProperties,不要犹豫

JSR303校验

@NotNull(message=“名字不能为空”)

private String userName;

@max(value=120,message=“年龄最大不能超过120”)

private int age;

@Email(message=“邮箱格式错误”)

private String email;

空检查

@Null 验证对象是否为null

@NotNull 验证对象是否部位null,无法检查长度为0的字符串

@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格。

@NotEmpty 检查约束元素是否为NULL或者是EMPTY

Boolean检查

@AssertTrue 验证Boolean对象是否为true

@AssertFalse 验证Boolean对象是否为false

长度检查

@Size(min=,max=) 验证对象(Array、Collection、Map、String)长度是否在给定范围之内

@Length(min=,max=) 验证字符串长度是否在指定的区间

日期检查

@Past 验证Date和Calendar对象是否在当前时间之前

@Future 验证Date和Calendar对象是否在当前时间之后

@Pattern 验证String对象是否符合正则表达式的规则

yaml文件识别的优先级

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

SpringBoot实现java工程(非Web工程)

方式一:

直接获取ConfigurableApplicationContext

方式二:

实现CommandLineRuner,重写run方法

关闭Springboot的logo

package com.stu;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.stu.mapper")
public class DemoApplication {

    public static void main(String[] args) {
        
       // SpringApplication.run(DemoApplication.class, args);
        //获取入口SpringBoot类
        SpringApplication springApplication = new SpringApplication(DemoApplication.class);
        //设置它的属性
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

SpringBoot拦截器

步骤:

  1. 定义一个拦截器类,实现HandlerInterceptor接口
  2. 创建一个配置类(即相当于SpringMVC配置文件使用mvc:interceptors标签

拦截器类

package com.stu.interceptor;

import com.stu.pojo.Teacher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //编写拦截器规则
        //从session中获取用户信息
        System.out.println("————————————拦截器进入————————————");
        Teacher teacher = (Teacher) request.getSession().getAttribute("teacher");
        //判断是否登录
        if(null==teacher){
            //未登录,不放行,跳转页面
            System.out.println("————————————已被拦截,进行跳转————————————");
            response.sendRedirect(request.getContextPath()+"/teacher/error");
            return false;
        }
        //放行
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

配置类

package com.stu.config;

import com.stu.interceptor.UserInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration //定义此类为配置类(相当于之前的xml配置文件)
public class InterceptorConfig implements WebMvcConfigurer {
    //继承WebMvcConfigurer接口并重写addInterceptors方法,用于添加拦截器
    //相当于 mvc:interceptors
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //相当于 mvc:interceptor  具体哪个拦截器
        //bean class
        //addPathPatterns拦截路径,excludePathPatterns排除路径

        String[] addPathPatterns ={
                "/teacher/**"
        };
        String[] excludePathPatterns ={
                "/teacher/login","/teacher/out","/teacher/error"
        };
        registry.addInterceptor(new UserInterceptor()).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);

    }
}

测试

package com.stu.controller;


import com.stu.pojo.Teacher;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@Controller
@RequestMapping("/teacher")
public class InterceptorController {
    //用户登录的请求
    @RequestMapping("/login")
    public @ResponseBody String login(HttpServletRequest request){
        Teacher teacher = new Teacher();
        teacher.setTid(101);
        teacher.setName("zhang");
        request.getSession().setAttribute("teacher",teacher);
        return "Login Success!";
    }
    //用户登录后才可访问的请求
    @RequestMapping("/show")
    public @ResponseBody String show(){
        return "Show Message!";
    }

    //用户不登陆也可以访问的请求
    @RequestMapping("/out")
    public @ResponseBody String out(){
        return "out show";
    }

    //错误登录跳转,
    @RequestMapping("/error")
    public @ResponseBody String error(){
        return "Login ERROR!";
    }


}

SpringBoot过滤器

两种方式

  • 方式一:注解——> @WebFilter(urlPatterns = “/myfilter”) 、@ServletComponentScan(basePackages = “com.stu.filter”)

    filter类

    package com.stu.filter;
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    //定义访问路径
    @WebFilter(urlPatterns = "/myfilter")
    public class MyFilter implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("-------------进入过滤器-----------");
            servletRequest.setCharacterEncoding("utf-8");
            servletResponse.setCharacterEncoding("utf-8");
            //没有doFilter,程序将会停留
            filterChain.doFilter(servletRequest,servletResponse);
    
        }
    }
    

    控制层

    package com.stu.controller;
    
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class FilterController {
        @RequestMapping("/myfilter")
        public @ResponseBody String getMes(){
            return "已经经过过滤器的过滤了!";
        }
    }
    

    启动类

    package com.stu;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.Banner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    @SpringBootApplication
    @ServletComponentScan(basePackages = "com.stu.filter")
    public class DemoApplication {
        public static void main(String[] args) {
           SpringApplication.run(DemoApplication.class, args);
        }
    }
    
  • 方式二:通过配置类注册组件

    filter类

    package com.stu.filter;
    
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class MyFilter1  implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("-------------进入过滤器-----------");
            servletRequest.setCharacterEncoding("utf-8");
            servletResponse.setCharacterEncoding("utf-8");
            //没有doFilter,程序将会停留
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    

    配置类

    package com.stu.config;
    
    import com.stu.filter.MyFilter1;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration  //定义此类为配置类
    public class FilterConfig {
        @Bean
        public FilterRegistrationBean filterRegistrationBean(){
            //注册过滤器
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter1());
            //请求路径
            filterRegistrationBean.addUrlPatterns("/user/myfilter");
            return filterRegistrationBean;
        }
    
    }
    

小插曲:Springboot使用servlet(继承的是HttpServlet)和使用拦截器方法一样

Thymeleaf模板引擎

认识 Thymeleaf

Thymeleaf是一个流行的模板引擎,该模板引擎采用Java语言开发模板引擎是一个技术名词,是跨领域跨平台的概念,在Java语言体系下有模板引擎,
在C#、PHP语言体系下也有模板引擎,甚至在JavaScript中也会用到模板引擎技术,Java生态下的模板引擎有Thymeleaf 、Freemaker、 Velocity、 Beetl (国产)等。Thymeleaf对网络环境不存在严格的要求,既能用于Web环境下,也能用于非Web环境下。在非Web环境下,他能直接显示模板上的静态数据;在Web环境下,它能像Jsp一样从后台接收数据并替换掉模板_上的静态数据。它是基于HTML的,以HTML标签为载体,Thymeleaf要寄托在HTML标签下实现。SpringBoot集成了Thymeleaf 模板技术,并且Spring Boot官方也推荐使用Thymeleaf来替代JSP技术,Thymeleaf 是另外的一种模板技 术,它本身并不属于Spring Boot, Spring Boot
只是很好地集成这种模板技术,作为前端页面的数据展示,在过去的JavaWeb开发中,我们往往会选择使用Jsp去完成页面的动态渲染,但是jsp需要翻译编译运行,效率低

官网:https://www.thymeleaf.org/

创建方式

方式一:

导入依赖

在html中设置命名空间

方式二:

idea选择Spring Initializr模块,后选择web 选择thymeleaf 即可

初次体验:获取后台数据

eg:

 <h2 th:text="${data}">展示要显示的内容</h2>

热部署

关闭缓存

1.设置thymeleaf模板引擎的缓存,设置为false关闭,默认为true开启

#设置thymeleaf模板引擎的缓存,设置为false关闭,默认为true开启
spring.thymeleaf.cache=false

#设置thymeleaf模板引擎的前后缀(可选项)
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

2.设置项目的Run/Debug Configurations的两个选项On ‘Update’ action 和 On frame deactiveation 都选择 Update resource

标准变量表达式

<body>
    <h1>标准变量表达式:${}  (推荐)</h1>
    用户编号:<span th:text="${user.id}"></span><br>
    用户姓名:<span th:text="${user.username}"></span><br>
    用户年龄:<span th:text="${user.age}"></span><br>
    用户地址:<span th:text="${user.address}"></span><br>
    
    <!--*{}必须使用th:object属性来绑定这个对象
		在子标签中使用*来代替绑定的对象${user} -->
    <h1>选择变量表达式:*{}  (不推荐)</h1>
    <div th:object="${user}">
        用户编号:<span th:text="*{id}"></span><br>
    	用户姓名:<span th:text="*{username}"></span><br>
    	用户年龄:<span th:text="*{age}"></span><br>
    	用户地址:<span th:text="*{address}"></span><br>
    </div>
    
    
    <h1>标准变量表达式与选择变量表达式的混合使用(不推荐)</h1>
    用户编号:<span th:text="*{user.id}"></span><br>
    用户姓名:<span th:text="*{user.username}"></span><br>
    用户年龄:<span th:text="*{user.age}"></span><br>
    用户地址:<span th:text="*{user.address}"></span><br>
</body>

URL路径表达式

<body>
    <h1>URL路径表达式:@{....} </h1>
    <h2>a标签的绝对路径(没有参数)</h2>
    <a href="http://www.baidu.com">传统写法:跳转到百度</a>
    <a th:href="@{http://www.baidu.com}">路径表达式:跳转百度</a>
    <a href="@{http://locahost:8081/user/detail}">传统写法: 跳转至 /user/detail</a>
    <a th:href="@{http://locahost:8081/user/detail}">路径表达式: 跳转至 /user/detail</a>
    
    <h2>a标签的相对路径(没有参数)【 开发中推荐使用 】</h2>
    <a th:href="@{/test/user/detail}">跳转至 /user/detail</a>
    
    <h2>a标签的相对路径(带参数:后台获取的参数值)</h2>
    <!--  /test1?id=100&username=LiSi&age=18 -->
    <a th:href="@{'/test1?id='+${id}+'&username='+${username}+'&age='+${age}}">【不推荐】路径表达式: 跳转至 /user/detail</a>
    
    <a th:href="@{'/test1(id=${id},username=${username},age=${age})}">【推荐】路径表达式: 跳转至 /user/detail</a>
    <!-- 在这里面()就相当于设置参数 -->
    
</body>

常见属性

格式 :th:属性名= XXX

<input type="text" th:id=id th:name=name />

注意,并不是所有的属性都要添加Thymeleaf表达式,静态数据,不需要和后端挂钩的数据不用添加

循环遍历集合

th:each=“变量名,集合状态名:${被遍历的集合}”

Map

<div th:each="userMap,userMapStat:${userMaps}">
    <span th:text="${userMapStat.cont}">计数</span>
    <span th:text="${userMapStat.index}">计下标</span>
    <span th:text="${userMap.key}">获取key</span>
    <span th:text="${userMap.value}">获取value</span>
    <span th:text="${userMap.value.id}">获取value中的id</span>
    <span th:text="${userMap.value.name}">获取value中的name</span>
    <span th:text="${userMap.value.address}">获取value中的address</span>
</div>

Array/List

<div th:each="user,userStat:${userArray}">
    <span th:text="${userStat.cont}">计数</span>
    <span th:text="${userStat.index}">计下标</span>
    <span th:text="${user.id}">获取key</span>
    <span th:text="${user.name}">获取value</span>
    <span th:text="${user.address}">获取value中的id</span>
    <span th:text="${user.phone}">获取value中的name</span>
</div>

复杂集合遍历

要求:List中方Map,Map里面又放了List,Map里面每个List都放了3个user对象

@RequestMapping(value = "/each/al1" )
public String eachAll (Model model) {
//1ist -> Map -> List -> User 
List<Map<Integer, List<User>>> myList = new ArrayList<MapsIntegecm ListsUser?>>();
    for(inti=e;i<2;i++){
        Map<Integer, List<User>> myMap = new HashMap<Integer. List<User>>();
        for(intj=e;j<2;j++){
            List<User> myUserList = new ArrayList<User>();
            for(intk=e;k<3;k++){
                User user = new User();
                user.setId(k);
                user.setNick("张三"+ k);
                user.setPhone( "1350000000"+ k);
                user.setAddress("广州市"+ i);
                myUserList.add(user);
            }
            myMap.put(j, myUserList);
        }
    }
}

< !DOCTYPE html> 
<html lang="en" xmIns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>循环遍历复杂的集合</title>
</head>
<body>
    <h2>循环遍历复杂集合:list -> Map -> list -> User</h2>
    <div th:each="myListMap:${myList}">
        <div th:each=" myListMap0bj:${myListMap}">
            Map集合的key:<span th:text="${myListMapobj.key}"></ span> 
            <div th:each="myListMapObjList:${myListMap0bj.value}">
                <span th:text="${myListMapobjList.id}"></span>
                <span th:text="${myListMapObjList.nick}"></span> 
                <span th:text="${myListMapobjList.phone}"></span>
                <span th:text= "${myListMapObjList.address}"></span>
            </div>
        </div>
    </div>
</body>
< /html》

条件判断

<h1>th:if用法:如果满足条件显示(执行),否则相反</h1>
<div th:if="${sex eq 1}"></div>
<div th:if="${sex eq 0}"></div>
<h1>th:unless用法:与th:if用法相反,即条件判断取反</h1>
<div th:unless="${sex ne 1}"></div>

<h1>th:unless用法:与th:if用法相反,即条件判断取反</h1>
<div th:unless="${sex ne 1}"></div>
<h1>th:switch/th:case用法</h1>
<div th: switch="${productType}">
<span th:case="0">产品0</ span>
<span th:case="1">产品1</span>
<span th:case="*">无此产品</ span>
</div>

内敛表达式(th:inline):

th:inline有三个取值类型(text, javascript和none), 值为none什么都不做,没有效

(1)内敛文本(th:inline=“text”)

<div th:inline="text">
   数据:[[${data}]]
</div>

通常我们使用内敛表达式时,通常时在body标签里指定

<body th:inline="text">
    <span>[[${data}]]</span>
    <span>[[${data}]]</span>
</body>

(2)内敛脚本(th:inline"javascript")

使用场景是在JavaScript中

<!--javascript获取后端数据 -->
<script type="text/javascript" th:inline"javascript">
	function showData(){
        alert([[${data}]]);
    }
</script>

<button th:onclick="showData()">展示数据</button>
</button>

字面量

<head>
<meta charset="UTF-8">
<title>字面量</title>
< /head> 
<body>
<h1>文本字面量,用单引号'.... '的字符串就是字面量</h1>
<a th:href="@{'/user/detail?sex='+${sex}}">查看性别</a>
<span th:text="Hel1o"></span>
<h1>数字字面量</h1>
今年是<span th: text="2020">1949</span><br/>
20年后是<span th:text="2020+20">1969</span><br/>
<h1>boolean字面量< /h1>
<div th:if="${flag}">
执行成功
</div>
<div th:if="${!flag}">
不成功
</div>

字符串拼接

<html lang="en" xmIns:th= "http://www.thymeleaf.org">
<head> 
<meta charset="UTF-8">
<title>字符串拼接</title>
</head>
<body>
<span th:text="''+${totalRows}+'' +${totalPage}+'页,当前第'+${currentPage}+'"
<h1>使用更优雅的方式来拼接字符串:|要 拼接的字符串内容|</h1>
<span th:text="|共${totalRows }条${totalPage}页,当前第${currentPag)页"></span>
</body>
</html>

运算符

<body>
<!--
三元运算:表达式?"正确结果":"错误结果"
算术运算:+ - * / %
关系比较: >,<,>=,<= (gt,lt,ge,le)
相等判断: ==,!= (eq,ne)
-->
<h1>三元运算符表达式?正确:错误</h1>
<div th:text="${sex eq 1 ? '':''}"></div>
<div th:text="${sex == 1 ? '':''}"></div>
<h1>算术运算</h1>
20+5=<span th:text= "20+5"></span><br/> 
20-5=<span th:text= "20-5"></span><br/>
20*5=<span th:text= "20*5"></ span><br/> 
20/5=<span th:text= "20/5"></ span><br/>
20%3=<span th:text= "20%3"></span><br/>
<h1>关系比较</h1>
5>2为<span th:if="5 gt 2"></span><br/>
52<span th:unless="5 lt 2"></span><br/>
1>=1<span th:if="1 ge 1"></span><br/>
    
<h1>相等判断</h1>
<span th:if="${sex == 1}"></span>
<span th:if="${sex eq 1}">男</ span>
<span th:unless="${seX ne 1}">女</ span>

</body>
</html>

表达式基本对象

模板引擎提供了一组内置的对象,这些内置的对象可以直接在模板中使用,这些对象由#号开始引用,我们比较常用的内置对象

#request #session

<h1>从SESSION中获取值</h1>
<span th:text="${#session.getAttribute('data')}"></span><br/>
<span th: text="${#httpSession.getAttribute('data')}"></span><br/>
<span th:text="${session.data}"></span><br/>


<script type="text/javascript" th:inline="javascript">
// http://localhost: 8080/ springboot/user/detail
//获取协议名称
var scheme = [[${#request.getScheme()]];
//获取服务器名称
var serverName
[[${#request.getServerName()}]];
//获取服务器端口号
var serverPort = [[${#request.getServerPort()}]];
//获取上下文根
var contextPath = [[${#request.getContextPath()}]];
var allPath = scheme + "://" + serverName + ":" + serverPort+contextPath;
alert(allPath) ;

var requestURL = [ [${#httpServletRequest.requestURL}]];
var queryString = [[${#httpServletRequest.queryString}]];
var requestAddress = requestURL + "?" +queryString;
alert(requestAddress);
</script>

功能表达式对象(了解)

模板引擎提供的一组功能性内置对象,可以在模板中直接使用这些对象提供的功能方法工作中常使用的数据类型,如集合,时间,数值,可以使用ThymelJeaf的提供的功能性对象来处理它们
内置功能对象前都需要加#号,内置对象一般都以s结尾,

官方手册: http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
#dates: java.util.Date对象的实用方法:

<span th:text="${#dates.format(curDate, yyyy-MM-dd HH:mm:ss )}"></span>

#calendars:和dates类似,但是java.util.Calendar 对象;

#numbers:格式化数字对象的实用方法;

#strings:字符串对象的实用方法: contains, startsWith, prepending/appending等

#objects:对objects操作的实用方法;

#bools:对布尔值求值的实用方法;

#arrays:数组的实用方法;

#lists: list 的实用方法,比如

<span th:text="${#lists.size(datas)}"></span>

#sets: set的实用方法;

#maps: map的实用方法;

#aggregates:对数组或集合创建聚合的实用方法;

源码分析

想了解springboot源码,先理解一下几个源码中常出现的底层注解

底层涉及的常用注解

@Configuration

配置类注解,应用于类上 ,表示该类为一个配置类,等同于配置文件,配置文件能做的该类也能做

配置文件中用bean标签

配置类中用@bean注解

  • @Bean表示给容器中添加一个组件
  • 以方法名作为组件的id
  • 返回类型就是组件类型
  • 返回的值就是组件在容器中的实例

配置类

@configuration() //告诉SpringBoot这是一个配置类==配置文件
pub1ic class MyConfig {
@Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是 组件在容器中的实例
public User user01(){
    return new User("zhangsan",18);
}
@Bean("tom")
public Pet tomcatPet(){
   return new Pet("tomcat");
}

启动类MainApplication.java

//@SpringBootApplication起几必325069908 
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.stu.boot" )
//修改默认的扫描位置,SpringBoot默认的是扫描的是启动类(主程序类)的同级或子孙级的组件
public class MainApplication {
public static void main(String[] args) {
    //1.返回我们IOC容器
    ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
    //2、查看容器里面的组件
    String[] names = run. getBeanDefiniti onNames();
    for (String name : names) {
        System. out . println(name);
    }
    //3.从容器中获取组件
    Pet tom01 = run. getBean("tom", Pet.class);
    Pet tom02 = run. getBean("tom", Pet.class);
    System.out.println("组件:"+(tom01 == tom02));  
    //true 表明注册的组件默认为单实例的
    
    MyConfig bean = run.getBean(MyConfig.class);
    System.out.println(bean);
}
}

配置类(本身也是组件)里面使用@Bean标注在方法上给容器注册组件,默认是单实例的

@Configuration 在2.0版本添加了一个proxyBeanMethods(代理bean的方法),默认为true

boolean proxyBeanMethods() default true;

@Configuration( proxyBeanMethods = true)

@configuration(proxyBeanMethods = ture) //告诉SpringBoot这是一个配置类==配置文件
pub1ic class MyConfig {
/**
*proxyBeanMethods = ture
*外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
*/
@Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是 组件在容器中的实例
public User user01(){
    return new User("zhangsan",18);
} 
@Bean("tom")
public Pet tomcatPet(){
   return new Pet("tomcat");
}

  • @Configuration( proxyBeanMethods = true)获取代理对象调用方法
  • 设置为true即获取的是代理对象,springbooot总会检查这个组件是否在容器存在,保持组件单实例

@Configuration( proxyBeanMethods = false)

  • 不保存代理对象
  • 每一次调用都会产生一个新的对象

这两种即为Full模式(@Configuration( proxyBeanMethods = true))与Lite模式(@Configuration( proxyBeanMethods = false))

解决场景:组件依赖

proxyBeanMethods设置true时可以实现组件依赖,false不存在组件依赖,因为每一次调用都重新获取对象

A 对象中 存在B对象(A依赖B)

获取A对象,A调用获取B对象的方法

true时:B是容器中的对象(组件)

false时:B是调用创建的对象,容器中不存在

实战场景:

  • 当组件不存在相互依赖时,使用Lite模式(也称为轻量级模式),即proxyBeanMethods设置为false,这样就不会扫描其他组件,减少判断,启动和加载就比较快
  • 当组件之间存在依赖关系,方法会被调用得到之前单例模式组件,用Full模式

@import

给容器中自动创建导入的类型的组件

默认组件的名字就是全类名

配置类

/* @Import( {User.class, DBHelper.class})
给容器中自动创建出这两个类型的组件
*/
@Import({User.class, DBHelper. class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {
    @Bean
    public User user01(){
    return new User("zhangsan",18);
    } 
    @Bean("tom")
    public Pet tomcatPet(){
   return new Pet("tomcat");
    }
}

测试

//获取组件
String[] beanNamesForType = run.getBeanNamesForType(User.class);
System. out. println("======");
for (String S : beanNamesForType) {
System.out.println(s);
}
DBHelper bean1 = run.getBean(DBHelper.class);
System.out.println(bean1);

@Conditional

条件装配:满足Conditional

Conditional是一个根注解,派生了很多的注解每一个注解都有不同的功能

  • ConditionalOnRepositoryType

  • ConditionalOnWarDeployment (org springframewo

  • ConditionalOnBean (常用)

    当容器中存在指定的组件时可以做某些事

  • ConditionalOnSingleCandidate

    当我们容器中指定的组件只有一个实例,或则一个主实例(@proxy)

  • ConditionalOnMissingBean (常用)

    当容器中存在指定的组件时不可以做某些事

  • ConditionalOnPropety

    当项目中配置了某一个配置文件时做什么

  • ConditionalOnMissingClass

    当容器中不存在某一个类事可以做某些事

  • ConditionalOnResource

    当项目的内路径存在某一个资源时可以做某些事

  • ConditionalOnJava

    当我们某一些是指定的某一个java版本号时可以做什么

  • ConditionalOnWebApplication

    当应用是一个web项目是可以做什么

  • ConditionalOnNotWebApplication

    当应用不是一个web项目是可以做什么

  • ConditionalOnJndi

  • ConditionalOnExpression

  • ConditionalOnCloudPlatform

  • ConditionalOnEnabledResourceChain

  • ConditionalOnClass

    当容器中存在某一个类事可以做某些事

  • Profile

检查容器中是否注册了组件

boolean tom = run.containsBean("tom");
System.out.print1n("容器中Tom组件: "+tom); //false tom组件不存在
boolean user01 = run.containsBean("user01") ;
System.out.println("容器中user01组件:"+user01); //true user01组件存在

使用条件注解

@Import({User.class, DBHelper. class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {
    @Conditional(name="tom") //存在名为tom的组件,则能在容器中添加user01组件,否则失败
    @Bean
    public User user01(){
    return new User("zhangsan",18);
    } 
    //@Bean("tom")
    public Pet tomcatPet(){
   return new Pet("tomcat");
    }
}
boolean user01 = run.containsBean("user01") ;
System.out.println("容器中user01组件:"+user01); //false user01组件不存在,由于条件不满足,不执行注册组件

其他条件注释类比学习

@ImportResource

@ConfigurationProper

底层源码分析

待续。。。。。。。。


版权声明:本文为qq_45738291原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>