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
//启动springbootg
java -jar boottest01-1.0-SNAPSHOT.jar
//改变端口启动springboot
java -jar boottest01-1.0-SNAPSHOT.jar --server.port=8088
2.5 Spring Initializr 创建
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.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 文件
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构建
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 仓库
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>
- 使用下载的组件,配置类自动注入
- 相关命令
mvn clean deploy //上传
9.2 上传下载优化版本v1.1
// 1. 在组件开发方写配置类
// 2. 将组件重新上传到私服
mvn clean deploy
// 3. 使用方导入配置类(在主程序)
@Import(MyConfig.class)
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 在需要验证登录的方法上 加上此注解
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 自定义注解
// 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,注解,反射
- 在切入点的方法上加上注解
- 在切面定义,拥有该注解的方法,才是 切除点。
- 进入增强方法后,通过反射获取 注解中的值
//获取切入点
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 配置文件
<?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>
- application-dev.yaml
logging:
level:
com.jliul.sbmtest.mapper: debug
- application.yaml
spring:
profiles:
active: dev
20 集成阿里云短信服务
20.1 准备工作
1. 注册阿里云的账号
2. 登录
3. 进入控制台
4. 选择短信服务
5. 国内消息
a.申请签名
b.申请短信模板
c.申请密匙
d.给新用户添加权限
6.阅读源码,发送第一条短信
20.2 配置文件
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);
}
}