SpringBoot完整版

SpringBoot

文章目录

1. SpringBoot核心功能

//1.1 独立运行spring项目,集成主流框架。

//1.2 内嵌Servlet容器,无需打成War包

//1.3 无代码生成和xml配置

//1.4 自动配置Spring

//1.5  提供starter简化Maven配置(自动依赖和版本控制)

//1.6 准生产的应用监控

//1.7 天然集成云计算

2. SpringBoot快速入门

2.1 环境准备

  • jdk 1.8
  • maven 3.5.4
  • idea 2019.1
  • springboot 2.1.6

2.2 创建Maven工程

2.2.1 继承父亲项目
<!--导入父工程依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>
2.2.2 导入依赖和插件
<!--依赖 spring-boot-starter-**(场景)-->
    <dependencies>
        <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>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.0</version>
    	    <!--只在编译阶段生效,不需要打入包中-->
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
    <!--插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2.3 编写代码

  • 主程序,一定写在主包下。
package com.jliul.boottest;

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

/**
 * @author jliu.l
 * @date 2020/10/10
 */
@SpringBootApplication
public class MySpringBootApplication {

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

  • Controller类
  • 在主程序MySpringBootApplication同级包或者子级包中创建TestController
package com.jliul.boottest.controller;

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

/**
 * @author jliu.l
 * @date 2020/10/10
 */
@RestController
public class TestController {

    @GetMapping(value = "/test/{name}")
    public String getName(@PathVariable(value = "name") String name){
        return name;
    }
}

  • 配置文件[可选]
  • 在resources下创建配置文件
    • application.properties
    • application.yaml(推荐)

运行结果

2.4 SpringBoot 三种启动方式

2.4.1 运行启动类的main方法

运行main方法即可

2.4.2 Maven命令

mvn spring-boot:run

2.4.3 采用jar包的方式运行
//将当前项目打包成jar文件。并通过 java -jar ***.jar文件
mvn package

打包

//进入target文件夹
cd target
//回退上一级
cd ..
//查看目录
dir

target

//启动springbootg
java -jar boottest01-1.0-SNAPSHOT.jar
//改变端口启动springboot
java -jar boottest01-1.0-SNAPSHOT.jar --server.port=8088

springboot

2.5 Spring Initializr 创建

1

2

3

4

3. 配置文件

3.1 yaml的语法

  • 大小写敏感。
  • 使用缩进表示层级关系。
  • 缩进时不允许使用Tab键,只允许使用空格。
  • 每个冒号后面一定要有一个空格。

3.2 添加自己的配置

application.yaml

server:
  port: 8080
student:
  name: jliul
  age: 18

一个一个属性读取

package com.jliul.boottest.entity;

/**
 * @author jliu.l
 * @date 2020/10/10
 * 一个一个属性读取
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Student {
    
    @Value(value = "${student.name}")
    //直接赋值
    //@Value(value = "jliul")
    private String name;
    @Value("${student.age}")
    private Integer age;
}

整体读取

package com.jliul.boottest.entity;

/**
 * @author jliu.l
 * @date 2020/10/10
 * 整体读取
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "student")
public class Student {
    private String name;
    private Integer age;
}

test.java

package com.jliul.boottest;

/**
 * @author jliu.l
 * @date 2020/10/10
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class StudentTest {
    @Autowired
    private Student student;

    @Test
    public void test01(){
        System.out.println(student);
    }
}

3.2.1 在新的配置文件中配置

新建 properties 文件

student

student.name=jliul
student.age=88

整体读取

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "student")
@PropertySource("classpath:student.properties")
public class Student {
    private String name;
    private Integer age;
}

3.3 通过配置文件 注入复杂的值

3.3.1 带有转义字符的字符串
name: "top.video.com is vary\ngood"
3.3.2 集合,数组,set
pets:
  - cat
  - dag
  - pig
      
//List
private List<String> pets;

4. 多环境配置

  • 开发环境,测试环境,生产环境

4.1 在resoureces目录下,创建多个application-环境名.yaml 文件

many

4.1.2 application.yaml 文件中配置激活文件
spring:
  profiles:
    active: test
4.1.3 生产的时候可以设置环境参数来切换环境
java -jar boottest01-1.0-SNAPSHOT.jar --spring.profiles.active=dev

5. Spring 的 bean 的配置方式

5.1 xml 配置 第一代

  • 新建 beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.jliul.boottest.entity.User"/>
</beans>
    @Autowired
    private User user;
    
    @Test
    public void test02(){
        System.out.println(user);
    }
//导入配置文件,写下主程序中
@ImportResource("classpath:beans.xml")

导入配置文件

5.2 注解配置成bean 第二代

//控制层 bean
@Controller
//业务层 bean
@Service
//Dao层 bean
@Repostory
//bean
@Conponent

5.3 配置类 第三代

注解

package com.jliul.boottest.config;

/**
 * @author jliu.l
 * @date 2020/10/10
 */
@Configuration
public class MyConfig {

    @Bean
    public User getUser(){
        return new User();
    }
}

6. 命令行

mvn package	//打包
mvn package -Dmaven.test.skip=true //跳过测试
mvn install	//打包并发布到本地仓库
mvn deploy	//打包 发布到本地 再到私服
mvn clean deploy //清除,发布到私服
@ConfigurationProperties(prefix = "student")
@PropertySource("classpath:student.properties")
@Configuration
@ImportResource("classpath:beans.xml")

7. Spring

7.1 spring是一个什么样的框架?

胶水框架:  整合 市面上几乎所有的优秀框架																														
管理框架:   bean[生命周期【出生,活着的状态,死亡】]												
//容器 和 bean
	spring 的容器		游泳池   bean  在 池子洗澡
	1:项目一启动 就要初始化 spring 的容器													
	2: spring 容器 在创建 bean  把bean 放在容器中												
	3: 要使用bean 的时候从容器中拿
        	单例
        	用完之后再放回去
	4: 销毁:  程序结束
        容器销毁  bean 销毁										
//aop
事务
@ResponseBody
//代理模式
委托人(本体)
    代理人
    
    静态代理:
    	//实现接口
    	1.代理人 和 委托人 实现同样的接口
    	//继承
    	2.代理人 是 委托人的 子类
    动态代理
    	//spring 默认的动态代理
    	1.jdk动态代理
    		jdk的代理工厂 生产 代理对象
    			和 委托人 实现了同一接口
    	2.cglib代理 //继承
    		cglib 代理工厂 生产 代理对象
    			是 委托人 类的子类
    	

8. Maven私服

8.1 maven私服的简介

	私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的用户使用。当Maven需要下载构件的时候,它从私服请求,如果私服上不存在该组件,则从外部远程仓库下载,缓存在私服上之后,再为Maven的下载请求提供服务。
    
    私服的好处:
    //1. 节省自己的外网带宽
    //2. 部署自己内部的第三方组件
    //3. 提高稳定性,增强控制
    //4. 降低中央仓库的负荷。
    //5. 加速maven构建

maven私服

8.2 Nexus

8.2.1 下载 解压 Nexus 私服
8.2.2 修改配置文件

\nexus-2.12.0-01-bundle\nexus-2.12.0-01\conf\nexus.properties

配置

8.2.3 安装启动
//cmd命令行
nexus install // 安装
nexus start  // 开启
nexus stop  //关闭

安装启动

8.2.4 访问 Nexus 服务站点
http://localhost:18081/nexus

admin	//账号
admin123	//密码

登录

8.2.5 仓库

ck

9. 私服上传下载

9.1 上传下载版本v1.0

  • maven setting 配置文件
	<profiles>
		<!-- 在已有的profiles标签中添加profile标签 -->
		<profile>    
			<id>myjdk</id>    
			<activation>    
				<activeByDefault>true</activeByDefault>    
				<jdk>1.8</jdk>    
			</activation>    
			<properties>    
				<maven.compiler.source>1.8</maven.compiler.source>    
				<maven.compiler.target>1.8</maven.compiler.target>
				<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> 
			</properties>    
		</profile>
	</profiles>
	<!-- 激活 -->
	<activeProfiles>
		<activeProfile>myjdk</activeProfile>
	</activeProfiles>
        
     <servers>
       <server> 
           <id>nexus-public</id> <!-- nexus的认证id -->
           <username>admin</username> <!--nexus中的用户名密码-->
           <password>admin123</password> 
       </server>
       <server> 
           <id>releases</id> <!-- nexus的认证id -->
           <username>admin</username> <!--nexus中的用户名密码-->
           <password>admin123</password> 
       </server>
       <server> 
           <id>snapshots</id> <!-- nexus的认证id -->
           <username>admin</username> <!--nexus中的用户名密码-->
           <password>admin123</password> 
       </server>
     </servers>
  • 上传 pom.xml
    <distributionManagement>
        <repository>
            <id>releases</id>
            <url>http://192.168.0.113:18081/nexus/content/repositories/releases</url>
        </repository>
        <snapshotRepository>
            <id>snapshots</id>
            <url>http://192.168.0.113:18081/nexus/content/repositories/snapshots</url>
        </snapshotRepository>
    </distributionManagement>
  • 下载 pom.xml
    <dependency>
          <groupId>org.example</groupId>
          <artifactId>myutils</artifactId>
          <version>1.0-SNAPSHOT</version>
    </dependency>

	<repositories>
        <repository>
            <id>nexus</id>
            <name>Nexus Repository</name>
            <url>http://192.168.0.113:18081/nexus/content/groups/public/</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>nexus</id>
            <name>Nexus Plugin Repository</name>
            <url>http://192.168.0.113:18081/nexus/content/groups/public/</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </pluginRepository>
    </pluginRepositories>
  • 使用下载的组件,配置类自动注入

peizhilei

  • 相关命令
mvn clean deploy	//上传

9.2 上传下载优化版本v1.1

// 1. 在组件开发方写配置类
// 2. 将组件重新上传到私服
		mvn clean deploy
// 3. 使用方导入配置类(在主程序)
         @Import(MyConfig.class)

import

9.3 上传下载优化版本v1.2

9.3.1 需求:
//根据使用组件的工程的需求 来配置
	我们在使用 utils 组件的工程中 能根据需求配置
9.3.2 组件开发配置
//在组件工程中 组件类 添加额外配置的 属性
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class DBUtils {

    private String username;
    private String password;
}
//在组件工程中添加配置类	@ConfigurationProperties
@Configuration
public class MyUtilsConfig {

    @Bean
    public MyUtils getMyUtils(){
        return new MyUtils();
    }

    @Bean
    @ConfigurationProperties(prefix = "db")  //谁用这个组件  谁就添加db 配置
    public DBUtils getDBUtils(){
        return new DBUtils();
    }
}

9.3.3 组件使用配置

//pom.xml 	导入依赖
<dependency>
     <groupId>org.example</groupId>
     <artifactId>myutils</artifactId>
     <version>1.0-SNAPSHOT</version>
</dependency>
// .yaml配置文件中添加 db 配置
server:
  port: 8088
db:
  username: jliul
  password: 7894
//启动类(主程序) 导入配置类
@Import(MyConfig.class)
//直接注入 组件使用
@Autowired
private MyUtils myUtils;

//测试
@Test
public void test03(){
    System.out.println(dbUtils);
}

10.SpringBoot整合Mybatis

10.1 新建 Springboot 工程

10.2 配置pom.xml

<?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.jliul</groupId>
    <artifactId>springboot_mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.16.RELEASE</version>
        <!--父项目的pom.xml文件的相对路径。
            默认值为../pom.xml。maven首先从当前构建项目开始查找父项目的pom文件,然后从本地仓库,
            最后从远程仓库。RelativePath允许你选择一个不同的位置。-->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <!--版本控制-->
    <properties>
        <java.version>1.8</java.version>
        <mybatis.version>2.1.3</mybatis.version>
        <druid.version>1.1.10</druid.version>
        <pagehelper.version>1.2.3</pagehelper.version>
    </properties>

    <!--依赖(web,test,mybatis,lombok,druid,pagehelper)-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--runtime:运行时范围-->
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <!--此功能/此依赖可选-->
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <!--runtime:测试范围-->
            <scope>test</scope>
        </dependency>

        <!-- druid alibaba -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--mybatis 分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>${pagehelper.version}</version>
        </dependency>
    </dependencies>

    <!--插件-->
    <build>
        <plugins>
            <!-- springboot 插件, mybatis 逆向工程插件(生成接口,实体类,.xml文件) -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.44</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <!--配置文件的路径-->
                	<configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

10.3 配置dbconfig.peoperties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/cs2003?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
jdbc.user=root
jdbc.password=root

# 生成的主包名
group.package=com.jliul.sbmtest
# 对应的数据库名
catalog.name=cs2003

10.4 配置generatorConfig.xml

<?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>
	<!--引入配置文件-->
	<properties resource="dbconfig.properties"/>
	<context id="testTables" targetRuntime="MyBatis3">
		<!--生成java文件的编码-->
		<property name="javaFileEncoding" value="UTF-8"/>
		<!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
		<property name="beginningDelimiter" value="`"/>
		<property name="endingDelimiter" value="`"/>
		<property name="lombok" value="Getter,Setter,ToString,Accessors"/>
		<!--注释-->
		<commentGenerator>
			<!-- 是否去除自动生成的注释 true:是 : false:-->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>
		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
		<jdbcConnection driverClass="${jdbc.driverClass}"
			connectionURL="${jdbc.url}"
			userId="${jdbc.user}"
			password="${jdbc.password}">
		</jdbcConnection>
	
		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
			NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成PO类的位置 -->
		<javaModelGenerator targetPackage="${group.package}.entity"
			targetProject="src/main/java">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
		<sqlMapGenerator targetPackage="mapper"
			targetProject="src/main/resources">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper(dao)接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="${group.package}.mapper"
			targetProject="src/main/java">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		<!-- 指定数据库表 -->
		<!-- % 代表全部 -->
		<table schema="" tableName="account"/>
	</context>
</generatorConfiguration>

双击

包图

10.5 Mapper层

package com.jliul.sbmtest.mapper;

import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.entity.AccountExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface AccountMapper {
    /**
     * 按条件记数
     * @param [example]
     * @return int
     */
    int countByExample(AccountExample example);

    /**
     * 根据特定限制条件删除,具体使用的方法和查询的时候是一样的。
     * @param [example]
     * @return int
     */
    int deleteByExample(AccountExample example);

    /**
     * 根据主键删除。
     * @param [id]
     * @return int 
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * insert会插入所有的信息,如果传入的对象某一属性为空,则插入空,如果数据库中设置了默认值,默认值就失效了。
     * @param [record] pojo
     * @return int
     */
    int insert(Account record);

    /**
     * 只会插入含有数据的属性,对于为空的属性,不予以处理
     * @param [record] pojo
     * @return int
     */
    int insertSelective(Account record);

    /**
     * 通过特定限制条件查询信息,example用于生成一个Criteria对象来设置查询条件
     * @param [example]
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    List<Account> selectByExample(AccountExample example);

    /**
     * 通过主键查询
     * @param [id]
     * @return com.jliul.sbmtest.entity.Account
     */
    Account selectByPrimaryKey(Integer id);

    int updateByExampleSelective(@Param("record") Account record, @Param("example") AccountExample example);

    /**
     * 根据特定的限制条件更新数据
     * @param [record, example]
     * @return int
     */
    int updateByExample(@Param("record") Account record, @Param("example") AccountExample example);

    /**
     * 通过ID更新值不为 null 的列。
     * @param [record]
     * @return int 
     */
    int updateByPrimaryKeySelective(Account record);

    /**
     * 通过ID更新数据
     * @param [record]
     * @return int 
     */
    int updateByPrimaryKey(Account record);
}

10.6 配置 application.yaml

# 端口号
server:
  port: 8090

# 数据源(druid)
spring:
  datasource:
    druid:
      url:  jdbc:mysql://127.0.0.1:3306/cs2003?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
      driver-class-name:  com.mysql.jdbc.Driver
      username: root
      password: "root"
      # 初始化连接数量(初始化时建立物理连接的个数)
      initial-size:  10
      # 最大连接池数量
      max-active:  50
      # 最小连接池数量
      min-idle:  10
      # 获取连接最大等待(超过)时间
      # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
      max-wait:  60000
      # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements:  true
      max-pool-prepared-statement-per-connection-size:  20
      # mysql 数据库的特征  会关闭已经连接了8个小时的连接
      # 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
      validation-query:  SELECT 1 FROM DUAL

# mybatis 分页插件
pagehelper:
  # 指定数据库
  helperDialect:  mysql
  # 默认是false。
  # 启用合理化时,如果pageNum<1会查询第一页。
  # 如果pageNum>pages(最大页数)会查询最后一页。
  # 禁用合理化时
  # 如果pageNum<1或pageNum>pages会返回空数据
  reasonable: true
  # 是否支持接口参数来传递分页参数,默认false
  supportMethodsArguments:  true
  # 为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
  # 其余参数详情请看官方文档:https://pagehelper.github.io/docs/howtouse/
  params: count=countSql

# mybtais 的其他配置
mybatis:
  # 扫描 .xml映射文件
  mapper-locations: classpath:mapper/*.xml
  configuration:
    # 开启驼峰映射配置
    # 指将带有下划线的表字段映射为驼峰格式的实体类属性。如:book_name
    map-underscore-to-camel-case: true

10.7 启动类(主程序)

package com.jliul.sbmtest;

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

/**
 * @author jliu.l
 * @date 2020/10/12
 *   注解:
 *      @MapperScan:扫描 ampper 接口
 */
@SpringBootApplication
@MapperScan("com.jliul.sbmtest.mapper")
public class BootMyApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootMyApplication.class,args);
    }
}

10.8 Entity层

package com.jliul.sbmtest.entity;

import lombok.Data;
/**
 * @author QzyLing
 */
@Data
public class Account {
    private Integer id;

    private String accno;

    private Integer password;

    private Double balance;

    private String name;
}

10.9 Service层

package com.jliul.sbmtest.service;

import com.github.pagehelper.PageInfo;
import com.jliul.sbmtest.entity.Account;
import java.util.List;

/**
 * @author jliu.l
 * @date 2020/10/12
 *  service接口
 */
public interface AccountService {

    /**
     * 根据id 查询Account
     * @param [id]
     * @return com.jliul.sbmtest.entity.Account
     */
    Account findAccountById(Integer id);

    /**
     * 查询卡号等于 参数 accno, 并且余额大于 参数 balance
     * @param [accno, balance]
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    List<Account> findAccountByInput(String accno, Double balance);

    /**
     * 查询所有Account
     * @param []
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    List<Account> findAll();

    /**
     * 查询所有,并分页
     * @param [pageno, pagesize]
     * @return com.github.pagehelper.PageInfo<com.jliul.sbmtest.entity.Account>
     */
    PageInfo<Account> findAllPaging(int pageno, int pagesize);
}
package com.jliul.sbmtest.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.entity.AccountExample;
import com.jliul.sbmtest.mapper.AccountMapper;
import com.jliul.sbmtest.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author jliu.l
 * @date 2020/10/12
 *  service实现类
 */
@Service
public class AccountServiceImpl implements AccountService {

    private AccountMapper accountMapper;

    @Autowired
    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;
    }

    /**
     * 根据id 查询Account
     *
     * @param id@return com.jliul.sbmtest.entity.Account
     */
    @Override
    public Account findAccountById(Integer id) {
        return accountMapper.selectByPrimaryKey(1);
    }

    /**
     * 查询卡号等于 参数 accno, 并且余额大于 参数 balance
     *
     * @param accno 卡号
     * @param balance 余额
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    @Override
    public List<Account> findAccountByInput(String accno, Double balance) {
        AccountExample example = new AccountExample();
        example.createCriteria()
                .andAccnoEqualTo(accno)
                .andBalanceGreaterThan(balance);

        List<Account> accounts = accountMapper.selectByExample(example);
        return accounts;
    }

    /**
     * 查询所有Account
     *
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    @Override
    public List<Account> findAll() {
        AccountExample example = new AccountExample();
        return accountMapper.selectByExample(example);
    }

    /**
     * 查询所有,并分页
     *
     * @param pageno 当前页
     * @param pagesize 每页数据量
     * @return com.github.pagehelper.PageInfo<com.jliul.sbmtest.entity.Account>
     */
    @Override
    public PageInfo<Account> findAllPaging(int pageno, int pagesize) {
        PageHelper.startPage(pageno, pagesize);
        AccountExample example = new AccountExample();

        List<Account> accounts = accountMapper.selectByExample(example);
        PageInfo<Account> pageInfo = new PageInfo<>(accounts);

        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("每页数量:" + pageInfo.getPageSize());
        System.out.println("当前页数量:" + pageInfo.getSize());
        System.out.println("总记录数:" + pageInfo.getTotal());
        System.out.println("总页数:" + pageInfo.getPages());
	    
        //此处有点问题,未解决(并不影响程序)
        System.out.println("结果集:" + pageInfo.getList());
        for (Account account : pageInfo.getList()) {
            System.out.println(account);
        }

        System.out.println("前一页:" + pageInfo.getPrePage());
        System.out.println("下一页:"+pageInfo.getNextPage());
        System.out.println("是否为第一页:"+pageInfo.isIsFirstPage());
        System.out.println("是否为最后一页"+pageInfo.isIsLastPage());
        System.out.println("是否有前一页:"+pageInfo.isHasPreviousPage());
        System.out.println("是否有下一页"+pageInfo.isHasNextPage());
        System.out.println("导航页码数:"+pageInfo.getNavigatePages());

        return pageInfo;
    }
}

10.10Controller层

package com.jliul.sbmtest.controller;

import com.github.pagehelper.PageInfo;
import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author jliu.l
 * @date 2020/10/12
 *  controller 层
 */
@RestController
public class AccountController {

    private AccountService accountService;

    @Autowired
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    @GetMapping("/account/{id}")
    public Account findAccountById(@PathVariable("id") Integer id){
        return accountService.findAccountById(id);
    }

    @GetMapping(value = "/account/{accno}/{balance}")
    public List<Account> findAccountByInput(@PathVariable("accno")String accno,@PathVariable("balance")Double balance){
        return accountService.findAccountByInput(accno, balance);
    }

    @GetMapping("/account")
    public List<Account> testFindAll(){
        return accountService.findAll();
    }

    @GetMapping("/accountTo/{pageno}/{pagesize}")
    public PageInfo<Account>  testFindAllPaging(@PathVariable("pageno")int pageno,@PathVariable("pagesize")int pagesize){
        return accountService.findAllPaging(pageno, pagesize);
    }
}

10.11测试

package com.jliul.sbmtest;

import com.github.pagehelper.PageInfo;
import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * @author jliu.l
 * @date 2020/10/12
 *  测试
 */
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {

    private AccountService accountService;

    @Autowired
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    @Test
    public void testFindAccountByInput(){

        List<Account> accountByInput = accountService.findAccountByInput("001", 1000D);
        for (Account account : accountByInput) {
            System.out.println(account);
        }
    }

    @Test
    public void testFindAll(){

        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
    }

    @Test
    public void testFindAllPaging(){
        PageInfo<Account> allPaging = accountService.findAllPaging(1, 2);
        for (Account account : allPaging.getList()) {
            System.out.println(account);
        }
    }
}

10.12 开启日志,控制台打印 SQL 语句

  • .yaml 文件
# 其他代码的日志 还是走默认的级别(info)
# 唯独com.qf.dbtest.mapper 这个包中的代码 走debug
logging:
  level:
    com.jliul.sbmtest.mapper: debug

10.13 数据库文件

/*
SQLyog Ultimate v12.09 (64 bit)
MySQL - 5.7.31-log 
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;

create table `account` (
	`id` int (10),
	`accno` varchar (54),
	`password` int (6),
	`balance` double ,
	`name` varchar (60)
); 
insert into `account` (`id`, `accno`, `password`, `balance`, `name`) values('1','001','123456','7400','金陵');
insert into `account` (`id`, `accno`, `password`, `balance`, `name`) values('2','001','123456','4600','刘林');
insert into `account` (`id`, `accno`, `password`, `balance`, `name`) values('3','002','123456','1000','李四');
insert into `account` (`id`, `accno`, `password`, `balance`, `name`) values('4','001','123456','1000','王五');
insert into `account` (`id`, `accno`, `password`, `balance`, `name`) values('5','005','123456','1000','陆航');

源码获取

11.restful

11.1 概念

// restful 
	restful 不是一种新的技术
        只是一种开发方式和设计风格
// 是一种开发风格,遵循此风格开发软件,符合REST风格,则是RESTFUL
请求类型URL功能意图
GET/users查询用户列表
POST/users创建(增加)一个用户
GET/users/id根据id查询一个用户
PUT/users/id根据id修改一个用户
DELETE/users/id根据id删除一个用户

11.2 关键

// 同样的url 可以访问不同的功能请求(方法)
	// 实现:
		通过不同的请求方式和地址 确定 意图
package com.jliul.sbmtest.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @author jliu.l
 * @date 2020/10/14
 *  注解:
 *      @RestController :
 *          相当于 @ResponseBody + @Controller合在一起的作用。
 *              支持 REST 风格
 *  可使用 Postman 工具测试
 */

@RestController
public class TestController {

    @GetMapping("/test")
    public String testGet(){
        return "test get";
    }

    @PostMapping("/test")
    public String testPost(){
        return "test post";
    }

    @PutMapping("/test")
    public String testPut(){
        return "test put";
    }

    @DeleteMapping("/test")
    public String testDelete(){
        return "test delete";
    }
}

11.3 企业级 restful 接口规范

// 给前后端开发人员 提供一套统一的开发规范 
	提升工作效率 降低沟通成本
        cookie数据存放在客户的浏览器上,session数据放在服务器上。
package com.jliul.sbmtest.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @author jliu.l
 * @date 2020/10/14
 *  注解:
 *      @RestController :
 *          相当于 @ResponseBody + @Controller合在一起的作用。
 *              支持 REST 风格
 *  可使用 Postman 工具测试
 */

@RestController
public class TestController {

    @GetMapping("/test")
    public String testGet(){
        return "test get";
    }

    @PostMapping("/test")
    public String testPost(){
        return "test post";
    }

    @PutMapping("/test")
    public String testPut(){
        return "test put";
    }

    @DeleteMapping("/test")
    public String testDelete(){
        return "test delete";
    }

    /**
     * 注解1:@RequestParam 用于将请求参数区数据映射到功能处理方法的参数上。
     * 注解2:@RequestHeader 用于将请求头数据映射到功能处理方法的参数上。
     * @param []
     * @return java.lang.String
     */
    @PostMapping("/testHeader")
    public String testHeader(@RequestParam(value = "bname") String bname, @RequestHeader(value = "hname") String hname){
        System.out.println(hname + " : " + bname);
        return "testHeader";
    }
}

12 登录取钱案例

12.1 创建统一格式返回

package com.jliul.sbmtest.common.result;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author jliu.l
 * @date 2020/10/13
 * RESTFULL 统一返回格式类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class R {
    //状态码
    private String code;
    //提示信息
    private String message;
    //返回数据
    private Object data;

    public R(ResponseEnum responseEnum){
        this.code = responseEnum.getCode();
        this.message = responseEnum.getMessage();
    }

    public R(ResponseEnum responseEnum,Object data){
        this.code = responseEnum.getCode();
        this.message = responseEnum.getMessage();
        this.data = data;
    }
}

12.1 创建业务异常类

package com.jliul.sbmtest.common.exception;

import com.jliul.sbmtest.common.result.ResponseEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author jliu.l
 * @date 2020/10/13
 * 本系统的业务异常类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AppException extends RuntimeException {
    private String code;
    private String message;

    public AppException(ResponseEnum responseEnum) {
        this.code = responseEnum.getCode();
        this.message = responseEnum.getMessage();
    }
}

12.3 枚举信息类

package com.jliul.sbmtest.common.result;

/**
 * @author jliu.l
 * @date 2020/10/13
 * 枚举枚举信息类
 */
public enum  ResponseEnum {
    SUCCESS("200","成功登录"),
    LOGIN_USER_NO_EXISTS("700","用户名不存在"),
    ACCOUNT_PASSWORD_WRONG("701","账号或者密码错误"),
    ACCOUNT_NO_EXISTS("702","账户不存在"),
    ACCOUNT_BALANCE_LESS("800","余额不足"),
    ACCOUNT_BALANCE_NULL("801","请输入正确取款金额"),
    NO_LOGININ("802","请先登录"),
    ACCOUNT_BALANCE_SUCCESS("200","取款成功");

    private String code; //状态码
    private String message; //状态信息

    ResponseEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

12.4 异常处理的增强类

package com.jliul.sbmtest.common.exception;

import com.jliul.sbmtest.common.result.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


/**
 * @author jliu.l
 * @date 2020/10/13
 * 异常处理的增强类
 * 自定义全局异常处理类
 * Controller 中抛出来的异常 不用处理,交给这个增强类来 处理
 */
@ControllerAdvice
public class MyExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyExceptionHandler.class);

    /**
     * 如果 Controller 中抛出的异常是 AppException , 就由这个方法来处理
     * @param [ex] 抛出来的异常对象
     * @return com.jliul.sbmtest.common.result.R
     */
    @ExceptionHandler(AppException.class)
    @ResponseBody
    public R doAppException(AppException ex){
        LOGGER.error(ex.getMessage());
        return new R(ex.getCode(), ex.getMessage(), null);
    }

    /**
     * 除了 AppException 异常之外的 异常 都交给这个方法处理
     * @param [ex]
     * @return com.jliul.sbmtest.common.result.R
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R doException(Exception ex){
        LOGGER.error(ex.toString());
        return new R("500", "发生未知异常,请联系管理员:jliul8712@qq.com", null);
    }
}

12.5 Service 层

12.2.1 接口
package com.jliul.sbmtest.service;

import com.github.pagehelper.PageInfo;
import com.jliul.sbmtest.entity.Account;

import java.math.BigDecimal;
import java.util.List;

/**
 * @author jliu.l
 * @date 2020/10/12
 *  service接口
 */
public interface AccountService {

    /**
     * 根据id 查询Account
     * @param [id]
     * @return com.jliul.sbmtest.entity.Account
     */
    Account findAccountById(Integer id);

    /**
     * 查询卡号等于 参数 accno, 并且余额大于 参数 balance
     * @param [accno, balance]
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    List<Account> findAccountByInput(String accno, BigDecimal balance);

    /**
     * 查询所有Account
     * @param []
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    List<Account> findAll();

    /**
     * 查询所有,并分页
     * @param [pageno, pagesize]
     * @return com.github.pagehelper.PageInfo<com.jliul.sbmtest.entity.Account>
     */
    PageInfo<Account> findAllPaging(int pageno, int pagesize);

    /**
     * 用户名 密码登录的方法
     * @param [accno, password]
     * @return com.jliul.sbmtest.entity.Account
     */
    Account login1(String accno,String password);

    /**
     * 取钱
     * @param [money, id]
     * @return int
     */
    void drawMoney(BigDecimal money, Integer id);
}
12.5.2 实现类
package com.jliul.sbmtest.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jliul.sbmtest.common.annotation.CheckLogin;
import com.jliul.sbmtest.common.exception.AppException;
import com.jliul.sbmtest.common.result.ResponseEnum;
import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.entity.AccountExample;
import com.jliul.sbmtest.mapper.AccountMapper;
import com.jliul.sbmtest.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;

/**
 * @author jliu.l
 * @date 2020/10/12
 *  service实现类
 */
@Service
public class AccountServiceImpl implements AccountService {

    private AccountMapper accountMapper;

    @Autowired
    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;
    }

    /**
     * 根据id 查询Account
     *
     * @param id@return com.jliul.sbmtest.entity.Account
     */
    @Override
    public Account findAccountById(Integer id) {
        return accountMapper.selectByPrimaryKey(id);
    }

    /**
     * 查询卡号等于 参数 accno, 并且余额大于 参数 balance
     *
     * @param accno 卡号
     * @param balance 余额
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    @Override
    public List<Account> findAccountByInput(String accno, BigDecimal balance) {
        AccountExample example = new AccountExample();
        example.createCriteria()
                .andAccnoEqualTo(accno)
                .andBalanceEqualTo(balance);

        return accountMapper.selectByExample(example);
    }

    /**
     * 查询所有Account
     *
     * @return java.util.List<com.jliul.sbmtest.entity.Account>
     */
    @Override
    public List<Account> findAll() {
        AccountExample example = new AccountExample();
        return accountMapper.selectByExample(example);
    }

    /**
     * 查询所有,并分页
     *
     * @param pageno 当前页
     * @param pagesize 每页数据量
     * @return com.github.pagehelper.PageInfo<com.jliul.sbmtest.entity.Account>
     */
    @Override
    public PageInfo<Account> findAllPaging(int pageno, int pagesize) {
        PageHelper.startPage(pageno, pagesize);
        AccountExample example = new AccountExample();

        List<Account> accounts = accountMapper.selectByExample(example);
        PageInfo<Account> pageInfo = new PageInfo<>(accounts);

        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("每页数量:" + pageInfo.getPageSize());
        System.out.println("当前页数量:" + pageInfo.getSize());
        System.out.println("总记录数:" + pageInfo.getTotal());
        System.out.println("总页数:" + pageInfo.getPages());

        System.out.println("结果集:" + pageInfo.getList());
        for (Account account : pageInfo.getList()) {
            System.out.println(account);
        }

        System.out.println("前一页:" + pageInfo.getPrePage());
        System.out.println("下一页:"+pageInfo.getNextPage());
        System.out.println("是否为第一页:"+pageInfo.isIsFirstPage());
        System.out.println("是否为最后一页"+pageInfo.isIsLastPage());
        System.out.println("是否有前一页:"+pageInfo.isHasPreviousPage());
        System.out.println("是否有下一页"+pageInfo.isHasNextPage());
        System.out.println("导航页码数:"+pageInfo.getNavigatePages());

        return pageInfo;
    }

    /**
     * 用户名 密码登录的方法
     *
     * @param accno 账号
     * @param password 密码
     * @return com.jliul.sbmtest.entity.Account
     */
    @Override
    public Account login1(String accno, String password) {
        // 1.用户名是否存在
        AccountExample example = new AccountExample();
        example.createCriteria()
                .andAccnoEqualTo(accno);
        //查询
        List<Account> accounts = accountMapper.selectByExample(example);
        if (accounts == null || accounts.size() == 0) {
            throw new AppException(ResponseEnum.LOGIN_USER_NO_EXISTS);
        }
        // 2.密码是否匹配
        Account account = accounts.get(0);
        if (!account.getPassword().equals(password)) {
            throw new AppException(ResponseEnum.ACCOUNT_PASSWORD_WRONG);
        }
        return account;
    }

    /**
     * 取钱
     *
     * @param money 取款金额
     * @param id id
     * @return int
     */
    @Override
    public void drawMoney(BigDecimal money, Integer id) {

        Account account = accountMapper.selectByPrimaryKey(id);
        //判断获取的accout是否为空
        if (account == null) {
            throw new AppException(ResponseEnum.ACCOUNT_NO_EXISTS);
        }
        //判断余额
        if (account.getBalance().compareTo(money)<0) {
            throw new AppException(ResponseEnum.ACCOUNT_BALANCE_LESS);
        }
        if (money.compareTo(BigDecimal.ZERO)< 0) {
            throw new AppException(ResponseEnum.ACCOUNT_BALANCE_NULL);
        }

        account.setBalance(account.getBalance().subtract(money));
        accountMapper.updateByPrimaryKey(account);
    }
}

12.6 Controller 层

package com.jliul.sbmtest.controller;

import com.alibaba.fastjson.JSON;
import com.jliul.sbmtest.common.annotation.CheckLogin;
import com.jliul.sbmtest.common.result.R;
import com.jliul.sbmtest.common.result.ResponseEnum;
import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;

/**
 * @author jliu.l
 * @date 2020/10/13
 */
@RestController
@CrossOrigin(origins = "*",maxAge = 3600)
public class LoginController {

    private AccountService accountService;

    @Autowired
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    /**
     * 登录1(用户名,密码)
     * @param [accno, password]
     * @return com.jliul.sbmtest.common.result.R
     */
    @PostMapping("/account/login1")
    public R login1(String accno,String password) {
        Account account = accountService.login1(accno, password);
        return new R(ResponseEnum.SUCCESS,account);
    }

    /**
     * 取钱
     * @param [money, accountInfo]
     * @return com.jliul.sbmtest.common.result.R
     */
    @PutMapping(value = "/account/drawmoney")
    @CheckLogin
    public R drawMoney(BigDecimal money,String accountInfo){
        System.out.println(money);
        Account account = JSON.parseObject(accountInfo, Account.class);
        accountService.drawMoney(money, account.getId());
        Account accountById = accountService.findAccountById(account.getId());
        return new R(ResponseEnum.ACCOUNT_BALANCE_SUCCESS,accountById);
    }
}

12.7 前端

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>登录</title>
	</head>
	<body>
		<p>用户名:<input type="text" id="accno" value="" /></p>
		<p>密码:<input type="password" id="password" /></p>
		<p><input type="button" id="btnlogin" value="login" /></p>
	</body>
	<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
	<script>
    $("#btnlogin").on("click",function(){
		
        // 发出ajax 请求
        let url='http://localhost:8090/account/login1'
        let pdata={
            accno: $("#accno").val(),
            "password": $("#password").val()
        }

        $.post(url,pdata,function (jsondata) {
			
            if(jsondata.code==='200'){
				sessionStorage.setItem("accountInfo",JSON.stringify(jsondata.data))
                window.location.href='index.html'
            }else{
                alert(jsondata.message)
            }
        },"json")
    })
	</script>
</html>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>取钱页面</title>
	</head>
	<body>
		<h2>取钱页面</h2>
		<hr />
		<p>金额:<input type="type" id="drawmoney" />
		<input type="button" id="btndraw" value="取钱" /></p>
	</body>
	<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
	<script type="text/javascript">
		$("#btndraw").on("click",function(){
			//ajax请求
			var url="http://localhost:8090/account/drawmoney"
			var data={
				money: $("#drawmoney").val(),
				accountInfo: sessionStorage.getItem("accountInfo")
			}
			
			$.ajax({
				url: url,
				type: "PUT",
				data: data,
				
				success: function(jsondata){
					if(jsondata.code === "200"){
						alert("取款成功! 剩余金额:"+jsondata.data.balance)
					}else{
						alert(jsondata.message);
					}
				}
			})
		})
	</script>
</html>

13. 跨域问题

13.1 跨域

跨域

// 为什么会出现跨域问题 ?
浏览器的同源策略限制。
    阻止一个域的JavaScript脚本和另外一个域的内容进行交互。
 // 什么是同源
    相同协议,主机,端口号
13.1.1什么是跨域
// 当一个请求url 的协议,域名,端口 与当前页面 url 任意一个不同,即跨域。
当前页面被请求的页面是否跨域原因
http://www.jliul.top/http://www.jliul.top/index.html同源
http://www.jliul.top/https://www.jliul.top/index.html跨域协议不同
http://www.jliul.top/http://www.bibi.top/跨域主域名不同
http://www.jliul.top/http://video.jliul.top/跨域子域名不同
http://www.jliul.top:8080/http://www.jliul.top:9001/跨域端口号不同

13.2 跨域解决

13.2.1 jsonp
// 有三个标签是允许跨域加载资源的
	<img src = *** />
        <link href = *** />
        <script src = *** />
// JSONP 原理
	利用 <script> 标签没有跨域的限制,可以得到从其他来源 动态产生的JSON 数据。JSON请求一定需要对方的服务器做支持才行。
// JSONP 缺点
     仅支持 get 方法,具有局限性。不安全 可能遭受 XXS 攻击。
13.2.2 cors
// CORS 需要浏览器和后端同时支持。

// 在控制层加上注解
@CrossOrigin(origins = "*",maxAge = 3600)
	// origins : 表示跨域的url,* 代表所有url。
	// maxAge : 缓存最大等待时间。

// 前端将Json 转化为 字符串
JSON.stringify(jsondata.data)

//将信息保存到客户端,并取出
sessionStorage.setItem("accountInfo",JSON.stringify(jsondata.data))
accountInfo: sessionStorage.getItem("accountInfo")
    
// json字符串 转 javaBean
JSON.parseObject(accountInfo, Account.class);

13.2.3 代理

14. AOP

14.1 aop

// Aop 核心概念
// 1. 连接点(joinpoint):
		被拦截到的点
// 2. 切入点(pointcut);
         被拦截的点
// 3. 通知(advice):
         拦截后要执行的代码
            after,before,round,throwing
14.2 新建 springboot 项目
// 添加 Aop 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
14.2.1 添加切点
package com.jliul.aoptest.service.impl;

import com.jliul.aoptest.comment.annotation.MyAnno;
import com.jliul.aoptest.service.TestService;
import org.springframework.stereotype.Service;

/**
 * @author jliu.l
 * @date 2020/10/14
 */
@Service
public class TestSeriveceImpl implements TestService {

    @Override
    @MyAnno("18")
    public void testAdd() {
        System.out.println("add");
    }

    @Override
    public void testUdp() {
        System.out.println("update");
    }
}
14.2.2 添加切面类 并写出增强逻辑
package com.jliul.aoptest.comment.aspect;

import com.jliul.aoptest.comment.annotation.MyAnno;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;


/**
 * @author jliu.l
 * @date 2020/10/14
 */
@Component
@Aspect
public class MyAspect {

    /**
     * 切入点之前的增强
     * @param [jp] 连接点
     * @return void
     */
    @Before("@annotation(com.jliul.aoptest.comment.annotation.MyAnno)")
    public void before01(JoinPoint jp){
        // 获取本体
        System.out.println(jp.getTarget().getClass());
        // 获取切点方法
        System.out.println(jp.getSignature().getName());
    }
}
14.2.3 配置切入点
// 通过表达式
@Before("execution(* com.jliul.aoptest.service.impl.*.*(..))")
// 通过注解
@Before("@annotation(com.jliul.aoptest.comment.annotation.MyAnno)")

//自定义注解
package com.jliul.aoptest.comment.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @author QzyLing
 */
@Retention(RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnno {

    String name() default "jliul";
    int age() default 18;
    String value();
}

15. SpringBoot整合AOP

15.1 写注解 CheckLogin

package com.jliul.sbmtest.common.annotation;

/**
 * @author QzyLing
 * 是否登录
 */
public @interface CheckLogin {
}

15.2 在需要验证登录的方法上 加上此注解

checkLogin

15.3 切面类与增强逻辑

package com.jliul.sbmtest.common.acpect;

import com.jliul.sbmtest.common.exception.AppException;
import com.jliul.sbmtest.common.result.ResponseEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @author jliu.l
 * @date 2020/10/14
 */
@Component
@Aspect
public class MyAspect {
    /**
     * 环绕的增强逻辑,有返回值
     * @param [pjp] 可执行的切入点,是JoinPoint 子类。
     * @return java.lang.Object
     */
    @Around("@annotation(com.jliul.sbmtest.common.annotation.CheckLogin)")
    public Object aroundCheckLogin(ProceedingJoinPoint pjp) throws Throwable {
        // 1.前置,进之前做什么
        // 获取request
        ServletRequestAttributes requestAttributes  =
                (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletRequest request = requestAttributes.getRequest();

        String accountInfo = request.getParameter("accountInfo");
        System.out.println(accountInfo);
        if (StringUtils.isEmpty(accountInfo)) {
            throw new AppException(ResponseEnum.NO_LOGININ);
        }

        // 放行(运行本体)
        Object result = pjp.proceed();
        //后置,出来后做什么

        return result;
    }
}
//前端有类似的过滤器 aop 的机制  

//不通过传参,获取request
ServletRequestAttributes requestAttributes  =
    (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();

16. 注解

16.1 自定义注解

zdy

// 1.注解中的属性(带有返回值的方法声明)															
											
// 2.关于注解中的属性的 类型的说明													
    8个基本数据类型												
    String												
    enum												
    Class					字节码							
    另外一个注解类型												
    以上几个类型的数组												

17. 获取注解的值

17.1 获取字节码对象

//获取类的字节码对象
Class<TestSeriveceImpl> testSeriveceClass = TestSeriveceImpl.class;

17.2 获取方法的字节码对象

// 获取方法的字节码对象
Method method = testSeriveceClass.getDeclaredMethod("testAdd");

17.3 获取方法上的注解的字节码对象

//获取方法上注解的字节码对象
MyAnno annotation = method.getAnnotation(MyAnno.class);

17.4 获取注解的值

if(annotation == null){
    System.out.println("没有此注解");
}

assert annotation != null;
System.out.println(annotation.value());
System.out.println(annotation.name());

17.5 优化版:aop,注解,反射

  1. 在切入点的方法上加上注解

zj

  1. 在切面定义,拥有该注解的方法,才是 切除点。

qd

  1. 进入增强方法后,通过反射获取 注解中的值
//获取切入点
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
// 获取method 上面的注解
MyAnno annotation = methodSignature.getMethod().getAnnotation(MyAnno.class);
System.out.println(annotation.value());

18. Swagger

// 1.Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
// 2.一般来说,接口需要两套:
接口

package com.jliul.sbmtest.controller.admin;

import com.alibaba.fastjson.JSON;
import com.jliul.sbmtest.common.annotation.CheckLogin;
import com.jliul.sbmtest.common.result.R;
import com.jliul.sbmtest.common.result.ResponseEnum;
import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.service.AccountService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

/**
 * @author jliu.l
 * @date 2020/10/15
 * admin 接口
 */
@RestController
@RequestMapping("/admin")
@Api(description = "Account 后台管理")
public class adminAccountController {
    private AccountService accountService;

    @Autowired
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    /**
     * 登录1(用户名,密码)
     * @param [accno, password]
     * @return com.jliul.sbmtest.common.result.R
     */
    @PostMapping("/account/login1")
    @ApiOperation("登录")
    public R login1(@ApiParam("账号") String accno, String password) {
        Account account = accountService.login1(accno, password);
        return new R(ResponseEnum.SUCCESS,account);
    }

    /**
     * 取钱
     * @param [money, accountInfo]
     * @return com.jliul.sbmtest.common.result.R
     */
    @PutMapping(value = "/account/drawmoney")
    @CheckLogin
    public R drawMoney(BigDecimal money, String accountInfo){
        System.out.println(money);
        Account account = JSON.parseObject(accountInfo, Account.class);
        accountService.drawMoney(money, account.getId());
        Account accountById = accountService.findAccountById(account.getId());
        return new R(ResponseEnum.ACCOUNT_BALANCE_SUCCESS,accountById);
    }
}
package com.jliul.sbmtest.controller.api;

import com.jliul.sbmtest.entity.Account;
import com.jliul.sbmtest.service.AccountService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author jliu.l
 * @date 2020/10/15
 * api 接口
 */
@RestController
@RequestMapping("/api")
@Slf4j
@Api(description = "Account 用户管理")
public class aipAccountController {
    private AccountService accountService;

    @Autowired
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    @GetMapping("/account/{id}")
    @ApiOperation("查看用户")
    public Account findAccountById(@ApiParam("用户id") @PathVariable("id") Integer id){
        log.info("controller info");
        log.debug("controller debug");
        return accountService.findAccountById(id);
    }
}

18.1 依赖

<!--swagger-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${swagger.version}</version>
    </dependency>
    
    <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>${swagger.version}</version>
</dependency>

18.2 配置

package com.jliul.sbmtest.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author jliu.l
 * @date 2020/10/15
 * http://localhost:8090/swagger-ui.html
 */
@Configuration
@EnableSwagger2 //允许使用swagger
public class SwaggerConfig {

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站接口定义")
                .version("1.0")
                .contact(new Contact("jiul.l", "http://www.jliul.top", "jliul8712@qq.com"))
                .build();
    }

    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统接口定义")
                .version("1.0")
                .contact(new Contact("jiul.l", "http://www.jliul.top", "jliul8712@qq.com"))
                .build();
    }

    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //只显示api路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/api/.*")))
                .build();

    }

    @Bean
    public Docket adminApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();

    }
}

18. 文档注解

// 1. 类上
@Api(description = "Account 后台管理")
// 2.方法上
@ApiOperation("登录")
// 3.属性
public R login1(@ApiParam("账号") String accno, String password) 

结果

19. 集成日志

19.1 日志

// 概述
1. 用于记录系统中发生的各种事件
2. 记录位置常有:控制台(开发),磁盘文件(准生产)等

// 作用
1.通过日志观察,分析项目的运行情况(项目维护)
2.通过日志分析用户的使用情况(大数据分析)

19.2 常见解决方案

log4j	[apache]
    logback	[springbot 默认]
    	// 以上均实现 slf4j 规范(接口)

19.3 日志级别

ALL,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF	[日志级别从低 ->]
	//默认情况下,springboot 从控制台打印出的日志级别只有INFO 及以上
   	
    //简单配置
    logging:
  		level:
    		root: info
    		com.jliul.sbmtest.mapper: debug

19.4 准生产级别配置

// 要求
1.开发日志:看 debug信息
2.生产日志:一般只要看到 error信息
19.4.1 配置文件
  1. logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="10 seconds">

    <contextName>logback</contextName>
    <property name="log.path" value="p:/project/demo_log" />

    <property name="CONSOLE_LOG_PATTERN"
              value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>

    <!--文件日志格式-->
    <property name="FILE_LOG_PATTERN"
              value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n" />

    <!--编码-->
    <property name="ENCODING"
              value="UTF-8" />

    <!--输出到控制台
        name: 名字任意取
        class:ch.qos.logback.core.ConsoleAppender
        filter:框架提供
            level:打印的最低日志级别
    -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!--日志级别-->
            <level>DEBUG</level>

        </filter>
        <encoder>
            <!--日志格式-->
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!--日志字符集-->
            <charset>${ENCODING}</charset>
        </encoder>
    </appender>

    <!--输出到文件
        name: 名字任意取
        class:ch.qos.logback.core.rolling.RollingFileAppender
        filter:框架提供
            onMatch   接受  onMismatch  拒绝   只打印info
    -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志过滤器:此日志文件只记录INFO级别的-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_info.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${ENCODING}</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志过滤器:此日志文件只记录WARN级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_warn.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志过滤器:此日志文件只记录ERROR级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_error.log</file>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <!--开发环境-->
    <springProfile name="dev">
        <!--可以灵活设置此处,从而控制日志的输出-->
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
        </root>
    </springProfile>

    <!--生产环境-->
    <springProfile name="prod">
        <!--可以灵活设置此处,从而控制日志的输出-->
        <root level="INFO">
            <appender-ref ref="INFO_FILE" />
            <appender-ref ref="WARN_FILE" />
            <appender-ref ref="ERROR_FILE" />
        </root>
    </springProfile>
</configuration>
  1. application-dev.yaml
logging:
  level:
    com.jliul.sbmtest.mapper: debug
  1. application.yaml
spring:
  profiles:
    active: dev

20 集成阿里云短信服务

20.1 准备工作

1. 注册阿里云的账号
2. 登录
3. 进入控制台
4. 选择短信服务
5. 国内消息
    a.申请签名
    b.申请短信模板
    c.申请密匙
    d.给新用户添加权限
6.阅读源码,发送第一条短信

发送

20.2 配置文件

yaml

20.3 读取配置信息的类

package com.jliul.sbmtest.config;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author jliu.l
 * @date 2020/10/15
 * SMS
 */
@Component
@ConfigurationProperties("aliyun.sms")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SmsConfig {

    private String regionId;
    private String signName;
    private String accessKeyId;
    private String accessSecret;
}

20.4 阿里云依赖

        <!--阿里云短信服务-->
        <dependency>
             <groupId>com.aliyun</groupId>
             <artifactId>aliyun-java-sdk-core</artifactId>
             <version>4.5.3</version>
        </dependency>

20.5 接口

package com.jliul.sbmtest.service;

import com.aliyuncs.exceptions.ClientException;

/**
 * @author jliu.l
 * @date 2020/10/15
 * 短信服务
 */
public interface SmsService {

    /**
     * 发送验证码
     * @param [phone, checkCode]
     * @return void
     */
    void sendCheckCode(String phone,String checkCode) throws ClientException;
}

20.6 实现类

package com.jliul.sbmtest.service.impl;

import com.alibaba.fastjson.JSON;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.jliul.sbmtest.common.exception.AppException;
import com.jliul.sbmtest.common.result.ResponseEnum;
import com.jliul.sbmtest.config.SmsConfig;
import com.jliul.sbmtest.service.SmsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * @author jliu.l
 * @date 2020/10/15
 * SMS impl
 */
@Service
@Slf4j
public class SmsServiceImpl implements SmsService{

    private SmsConfig smsConfig;
    @Autowired
    public void setSmsConfig(SmsConfig smsConfig) {
        this.smsConfig = smsConfig;
    }


    /**
     * 发送验证码
     *
     * @param phone 手机号
     * @param checkCode 验证码
     * @return void
     */
    @Override
    public void sendCheckCode(String phone, String checkCode) throws ClientException {

        DefaultProfile profile =
                DefaultProfile.getProfile(smsConfig.getRegionId(), smsConfig.getAccessKeyId(), smsConfig.getAccessSecret());
        //创建阿里云的客户端
        IAcsClient client = new DefaultAcsClient(profile);
        //请求对象
        CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");
        //地区
        request.putQueryParameter("RegionId", smsConfig.getRegionId());
        //手机号
        request.putQueryParameter("PhoneNumbers", phone);
        //签名
        request.putQueryParameter("SignName", smsConfig.getSignName());
        request.putQueryParameter("TemplateCode", "SMS_204750114");
        Map<String,String> noteMap = new HashMap<>(16);
        noteMap.put("code", checkCode);
        request.putQueryParameter("TemplateParam", JSON.toJSONString(noteMap));

        //发送短信
        CommonResponse response = client.getCommonResponse(request);

        //得到json字符串格式的响应结果
        String data = response.getData();
        System.out.println(data);

        //解析json字符串格式的响应结果
        //把阿里云响应给我们的json 字符串 转成map
        HashMap map = JSON.parseObject(data, HashMap.class);
        String code = (String) map.get("Code");
        String message = (String) map.get("Message");

        //配置参考:短信服务->系统设置->国内消息设置
        //错误码参考:
        //https://help.aliyun.com/document_detail/101346.html?spm=a2c4g.11186623.6.613.3f6e2246sDg6Ry
        //控制所有短信流向限制(同一手机号:一分钟一条、一个小时五条、一天十条)

        final String pf = "isv.BUSINESS_LIMIT_CONTROL";
        if (pf.equals(code)) {
            log.error("短信发送过于频繁 " + "【code】" + code + ", 【message】" + message);
            throw new AppException(ResponseEnum.SMS_SEND_ERROR_BUSINESS_LIMIT_CONTROL);
        }

        final String ok = "OK";
        if (!ok.equals(code)) {
            log.error("短信发送失败 " + " - code: " + code + ", message: " + message);
            throw new AppException(ResponseEnum.SMS_SEND_ERROR);
        }
    }
}

20.7 前端

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
</head>

<body>
    <h3>手机号快速登录</h3>
    <p>用户名:<input type="text" id="phone" value="" /></p>
    <p>验证码:<input type="text" id="checkCode" />
        <input type="button" id="btncode" value="发送验证码" />
    </p>
    <p><input type="button" id="btnlogin2" value="login2" /></p>
</body>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
<script>
    // 向手机发验证码
    $("#btncode").on("click", function() {
        var url = "http://localhost:8090/admin/account/getCheckCode";
        var data = {
            "pphone": $("#phone").val()
        }
        $.ajax({
            url: url,
            type: "GET",
            data: data,
            success: function(jsondata) {
                if (jsondata.code != "200") {
                    alert(jsondata.message)
                }
            }
        })
    })

    // 快速登录
    $("#btnlogin2").on("click", function() {
        var url = "http://localhost:8090/admin/account/login2/" + $("#phone").val() + "/" + $("#checkCode").val();

        $.ajax({
            url: url,
            type: "GET",
            success: function(jsondata) {
                if (jsondata.code === '200') {
                    // sessionStorage.setItem("accountInfo",JSON.stringify(jsondata.data))
                    window.location.href = 'index.html'
                } else {
                    alert(jsondata.message)
                }
            }
        })
    })
</script>
    
</html>

20.8 生成验证码的后台接口

    @GetMapping("/account/getCheckCode")
    @ApiOperation("给指定手机号,生成验证码")
    public R getCheckCode(@ApiParam("手机号")@RequestParam("pphone") String phone) throws ClientException {
        // 生成一个四位随机数
        int code = (int) (Math.random() * (9999 - 1000 + 1) + 1000);
        //发送短短信
        smsService.sendCheckCode(phone, code+"");
        // 保存验证码
        checkCodeMap.put(phone, code + "");
        return new R(ResponseEnum.SUCCESS,null);
    }

20.9 登录的后台接口

	/**
     * 登录2(手机号验证码快速登录)
     * @param [accno, password]
     * @return com.jliul.sbmtest.common.result.R
     */
    @GetMapping("/account/login2/{phone}/{code}")
    @ApiOperation("手机验证码登录")
    public R login2(@ApiParam("手机号")@PathVariable("phone") String phone,@ApiParam("验证码")@PathVariable("code") String code) {
        String sc = checkCodeMap.get(phone);
        if (!sc.equals(code)) {
            throw new AppException(ResponseEnum.CHECKCODE_ERROR);
        }
        return new R(ResponseEnum.SUCCESS,null);
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值