一、项目功能概述

电商秒杀需要完成的3个功能:

1.展示一个商品列表页面,我们可以从中看到可秒杀的商品列表

2.点击进入商品详情页,获取该商品的详细信息

3.秒杀时间开始后,点击进入下单确认页面,并支付成功

  二、基于SpringBoot进行项目环境搭建

步骤1:创建一个maven工程,使用quickStart骨架。

步骤2:在pom.xml导入SpringBoot相关依赖。

1 <?xml version="1.0" encoding="UTF-8"?>
 2 
 3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5   <modelVersion>4.0.0</modelVersion>
 6 
 7   <groupId>org.example</groupId>
 8   <artifactId>Spike</artifactId>
 9   <version>1.0-SNAPSHOT</version>
10 
11   <name>Spike</name>
12   <!-- FIXME change it to the project's website -->
13   <url>http://www.example.com</url>
14 
15  <parent>
16    <groupId>org.springframework.boot</groupId>
17    <artifactId>spring-boot-starter-parent</artifactId>
18    <version>2.0.5.RELEASE</version>
19  </parent>
20 
21   <properties>
22     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23     <maven.compiler.source>1.8</maven.compiler.source>
24     <maven.compiler.target>1.8</maven.compiler.target>
25   </properties>
26 
27   <dependencies>
28     <dependency>
29       <groupId>org.springframework.boot</groupId>
30       <artifactId>spring-boot-starter-web</artifactId>
31     </dependency>
32     <dependency>
33       <groupId>junit</groupId>
34       <artifactId>junit</artifactId>
35       <version>4.11</version>
36       <scope>test</scope>
37     </dependency>
38   </dependencies>
39 
40   <build>
41     <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
42       <plugins>
43         <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
44         <plugin>
45           <artifactId>maven-clean-plugin</artifactId>
46           <version>3.1.0</version>
47         </plugin>
48         <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
49         <plugin>
50           <artifactId>maven-resources-plugin</artifactId>
51           <version>3.0.2</version>
52         </plugin>
53         <plugin>
54           <artifactId>maven-compiler-plugin</artifactId>
55           <version>3.8.0</version>
56         </plugin>
57         <plugin>
58           <artifactId>maven-surefire-plugin</artifactId>
59           <version>2.22.1</version>
60         </plugin>
61         <plugin>
62           <artifactId>maven-jar-plugin</artifactId>
63           <version>3.0.2</version>
64         </plugin>
65         <plugin>
66           <artifactId>maven-install-plugin</artifactId>
67           <version>2.5.2</version>
68         </plugin>
69         <plugin>
70           <artifactId>maven-deploy-plugin</artifactId>
71           <version>2.8.2</version>
72         </plugin>
73         <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
74         <plugin>
75           <artifactId>maven-site-plugin</artifactId>
76           <version>3.7.1</version>
77         </plugin>
78         <plugin>
79           <artifactId>maven-project-info-reports-plugin</artifactId>
80           <version>3.0.0</version>
81         </plugin>
82       </plugins>
83     </pluginManagement>
84   </build>
85 </project>

pom.xml

步骤3:在main/java/app中,我们对SpringBoot和SpringMVC进行简单的配置工作。掌握这几个注解的作用。

1 package org.example;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.RestController;
 7 
 8 //SpringBoot会帮我们启动tomcat,并加载默认配置
 9 @EnableAutoConfiguration
10 //SpringMVC相关配置
11 @RestController
12 public class App {
13     @RequestMapping("/")
14     public String home(){
15         //网页中输出
16         return "Hello World!";
17     }
18     public static void main( String[] args ){
19         //控制台输出
20         System.out.println( "Hello World!" );
21         SpringApplication.run(App.class,args);
22     }
23 }

运行结果:

用浏览器打开http://localhost:8080/,我们可以看到页面上输出:Hello World!

同时,控制台也输出了Hello World!,以及一些Spring相关的信息。

SpringBoot小技巧:可以在resource目录下创建一个application.propeties配置文件,在其中写:server.port = 端口号来设置端口号。

步骤4:接入mybatis,首先在pom.xml添加需要的依赖(mysql,druid连接池,mybatis)

写一个plugin标签,引入对应的mybatis自动生成文件的插件 {

    添加对应的依赖:mybatis generator的core(第一次使用要单独在前面导入依赖,不可直接放在plugin中),mysql数据库的解析

    写一个excution标签:设置允许移动生成的文件,允许自动覆盖文件(实际工作中不可以)

    写一个configuration标签:指定mybatis generator 配置文件的路径 }

1 <?xml version="1.0" encoding="UTF-8"?>
  2 
  3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5   <modelVersion>4.0.0</modelVersion>
  6 
  7   <groupId>org.example</groupId>
  8   <artifactId>Spike</artifactId>
  9   <version>1.0-SNAPSHOT</version>
 10 
 11   <name>Spike</name>
 12   <!-- FIXME change it to the project's website -->
 13   <url>http://www.example.com</url>
 14 
 15  <parent>
 16    <groupId>org.springframework.boot</groupId>
 17    <artifactId>spring-boot-starter-parent</artifactId>
 18    <version>2.0.5.RELEASE</version>
 19  </parent>
 20 
 21   <properties>
 22     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 23     <maven.compiler.source>1.8</maven.compiler.source>
 24     <maven.compiler.target>1.8</maven.compiler.target>
 25   </properties>
 26 
 27   <dependencies>
 28     <dependency>
 29       <groupId>org.springframework.boot</groupId>
 30       <artifactId>spring-boot-starter-web</artifactId>
 31     </dependency>
 32     <dependency>
 33       <groupId>mysql</groupId>
 34       <artifactId>mysql-connector-java</artifactId>
 35       <version>5.1.6</version>
 36     </dependency>
 37     <dependency>
 38       <groupId>com.alibaba</groupId>
 39       <artifactId>druid</artifactId>
 40       <version>1.1.3</version>
 41     </dependency>
 42     <dependency>
 43       <groupId>org.mybatis.spring.boot</groupId>
 44       <artifactId>mybatis-spring-boot-starter</artifactId>
 45       <version>1.3.1</version>
 46     </dependency>
 47     <dependency>
 48       <groupId>junit</groupId>
 49       <artifactId>junit</artifactId>
 50       <version>4.11</version>
 51       <scope>test</scope>
 52     </dependency>
 53     <dependency>
 54       <groupId>org.mybatis.generator</groupId>
 55       <artifactId>mybatis-generator-core</artifactId>
 56       <version>1.3.5</version>
 57     </dependency>
 58   </dependencies>
 59 
 60   <build>
 61     <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
 62       <plugins>
 63         <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
 64         <plugin>
 65           <artifactId>maven-clean-plugin</artifactId>
 66           <version>3.1.0</version>
 67         </plugin>
 68         <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
 69         <plugin>
 70           <artifactId>maven-resources-plugin</artifactId>
 71           <version>3.0.2</version>
 72         </plugin>
 73         <plugin>
 74           <artifactId>maven-compiler-plugin</artifactId>
 75           <version>3.8.0</version>
 76         </plugin>
 77         <plugin>
 78           <artifactId>maven-surefire-plugin</artifactId>
 79           <version>2.22.1</version>
 80         </plugin>
 81         <plugin>
 82           <artifactId>maven-jar-plugin</artifactId>
 83           <version>3.0.2</version>
 84         </plugin>
 85         <plugin>
 86           <artifactId>maven-install-plugin</artifactId>
 87           <version>2.5.2</version>
 88         </plugin>
 89         <plugin>
 90           <artifactId>maven-deploy-plugin</artifactId>
 91           <version>2.8.2</version>
 92         </plugin>
 93 
 94         <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
 95         <plugin>
 96           <artifactId>maven-site-plugin</artifactId>
 97           <version>3.7.1</version>
 98         </plugin>
 99         <plugin>
100           <artifactId>maven-project-info-reports-plugin</artifactId>
101           <version>3.0.0</version>
102         </plugin>
103 
104         <plugin>
105           <groupId>org.mybatis.generator</groupId>
106           <artifactId>mybatis-generator-maven-plugin</artifactId>
107           <version>1.3.5</version>
108           <dependencies>
109             <dependency>
110               <groupId>org.mybatis.generator</groupId>
111               <artifactId>mybatis-generator-core</artifactId>
112               <version>1.3.5</version>
113             </dependency>
114             <dependency>
115               <groupId>mysql</groupId>
116               <artifactId>mysql-connector-java</artifactId>
117               <version>5.1.6</version>
118             </dependency>
119           </dependencies>
120           <executions>
121             <execution>
122               <id>mybatis generator</id>
123               <phase>package</phase>
124               <goals>
125                 <goal>generate</goal>
126               </goals>
127             </execution>
128           </executions>
129           <configuration>
130             <!--允许移动生成的文件-->
131             <verbose>true</verbose>
132             <!--允许自动覆盖文件-->
133             <overwrite>true</overwrite>
134             <!--mybatis generator 配置文件的路径-->
135             <configurationFile>
136               src/main/resource/mybatis-generator.xml
137             </configurationFile>
138           </configuration>
139         </plugin>
140 
141       </plugins>
142     </pluginManagement>
143   </build>
144 </project>

pom.xml

 步骤5:创建mysql底层的数据库与相关表格

1.创建数据库spike

2.创建一个user_info表格

 

电商 spring 技术架构 springboot电商项目_maven

  3.创建一个user_password表格,并设置user_id为外键关联user_info的id

电商 spring 技术架构 springboot电商项目_spring_02

步骤6:在步骤4中,我们最后指定了mybatis generator 配置文件的路径,于是我们在指定路径(resource目录下)创建一个mybatis generator.xml,并进行如下配置:

1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE generatorConfiguration
 3         PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
 4         "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 5 <generatorConfiguration>
 6     <context id="mysql"  targetRuntime="MyBatis3" >
 7         <!--数据库连接地址账号密码-->
 8         <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/spike" userId="root" password="0322">
 9         </jdbcConnection>
10         <!--生成Data Object类存放位置-->
11         <javaModelGenerator targetPackage="org.example.dataobject" targetProject="src/main/java">
12             <property name="enableSubPackages" value="true"/>
13             <property name="trimStrings" value="true"/>
14         </javaModelGenerator>
15         <!--生成映射文件存放位置-->
16         <sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources">
17             <property name="enableSubPackages" value="true"/>
18         </sqlMapGenerator>
19         <!--生成dao类存放位置-->
20         <javaClientGenerator targetPackage="org.example.dao" type="XMLMAPPER" targetProject="src/main/java">
21             <property name="enableSubPackages" value="true"/>
22         </javaClientGenerator>
23         <!--生成对应表及类名-->
24         <table tableName="user_info" domainObjectName="UserDo" enableCountByExample="false"
25                enableUpdateByExample="false" enableDeleteByExample="false"
26                enableSelectByExample="false" selectByExampleQueryId="false"
27         ></table>
28         <table tableName="user_password" domainObjectName="UserPasswordDO" enableCountByExample="false"
29                enableUpdateByExample="false" enableDeleteByExample="false"
30                enableSelectByExample="false" selectByExampleQueryId="false"
31         ></table>
32     </context>
33 </generatorConfiguration>

步骤7:根据步骤6中指定的位置,我们在org.example目录下新建一个dataobject的包,一个dao包。并测试是否能够成功生成相应的文件:

run——edit configurations——+maven——command line:mybatis-generator:generate——apply

然后我们运行这个新建的命令,可以看到resources/mapping下多了两个文件:

电商 spring 技术架构 springboot电商项目_电商 spring 技术架构_03

 dataobject包与dao包下生成了如下文件:

电商 spring 技术架构 springboot电商项目_电商 spring 技术架构_04

 手动删除两个Example文件。

步骤8:为了接入mybatis对应mysql的数据源,我们继续编写application.properties文件

1 server.port = 8090
 2 mybatis.mapperLocations = classpath:mapping/*.xml
 3 
 4 spring.datasource.name = Spike
 5 spring.datasource.url = jdbc:mysql://127.0.0.1:3306/Spike
 6 spring.datasource.username = root
 7 spring.datasource.password = 0322
 8 
 9 #使用druid数据源
10 spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
11 spring.datasource.driverClassName = com.mysql.jdbc.Driver

application

步骤9:回到app.java

将@EnableAutoConfiguration注解改为@SpringBootApplication(scanBasePackages = "org.example"),作用是将app交给spring托管,并且指定为主启动类。

添加注解@MapperScan("org.example.dao"),把dao存放的地方设置在对应注解下面。

最后,写一个方法来测试我们的搭建工作是否完成,(事先在表格中添加一条数据)

1 package org.example;
 2 
 3 import org.example.dao.UserDoMapper;
 4 import org.example.dataobject.UserDo;
 5 import org.mybatis.spring.annotation.MapperScan;
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.boot.SpringApplication;
 8 import org.springframework.boot.autoconfigure.SpringBootApplication;
 9 import org.springframework.web.bind.annotation.RequestMapping;
10 import org.springframework.web.bind.annotation.RestController;
11 
12 //SpringBoot会帮我们启动tomcat,并加载默认配置
13 @SpringBootApplication(scanBasePackages = {"org.example"})
14 
15 //SpringMVC相关配置
16 @RestController
17 @MapperScan("org.example.dao")
18 public class App {
19     @Autowired
20     private UserDoMapper userDoMapper;
21 
22     @RequestMapping("/")
23     public String home(){
24         UserDo userDo = userDoMapper.selectByPrimaryKey(1);
25         if(userDo == null){
26             return "用户对象不存在";
27         }else{
28             return userDo.getName();
29         }
30 
31     }
32     public static void main( String[] args ){
33         //控制台输出
34         System.out.println( "Hello World!" );
35         SpringApplication.run(App.class,args);
36     }
37 }

app.java

打开http://localhost:8090/,我们可以看到页面上显示了我们添加的数据中name字段的内容。

  三、用户模块开发

1.使用SpingMVC模式开发用户信息

步骤1:补全框架结构:

电商 spring 技术架构 springboot电商项目_apache_05

步骤2:service层的编写:

UserService接口:

1 package org.example.service;
2 
3 import org.example.service.model.UserModel;
4 
5 public interface UserService {
6     UserModel getUserById(Integer id);
7 }

UserService实现类:

1 @Service
 2 public class UserServiceImpl implements UserService {
 3     @Autowired
 4     private UserDoMapper userDoMapper;
 5 
 6     @Autowired
 7     private UserPasswordDOMapper userPasswordDOMapper;
 8 
 9     @Override
10     public UserModel getUserById(Integer id) {
11         UserDo userDo = userDoMapper.selectByPrimaryKey(id);
12         if(userDo == null){
13             return null;
14         }
15         //通过用户id获取对应的用户加密密码信息
16         UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDo.getId());
17         return convertFromDataObject(userDo,userPasswordDO);
18     }
19 
20     public UserModel convertFromDataObject(UserDo userDo, UserPasswordDO userPasswordDO) {
21         if(userDo == null){
22             return null;
23         }
24         UserModel userModel = new UserModel();
25         BeanUtils.copyProperties(userDo,userModel);
26         if(userPasswordDO != null){
27             userModel.setEncriptPassword(userPasswordDO.getEncriptPassword());
28         }
29         return userModel;
30     }
31 }

UserModel类:存放数据库的所有对应字段与getters&setters,用于service层与数据库数据的解耦,使service层无法直接接触数据库

1 package org.example.service.model;
 2 
 3 public class UserModel {
 4     private Integer id;
 5     private String name;
 6     private Byte gender;
 7     private Integer age;
 8     private String telephone;
 9     private String registerMode;
10     private String thirdPartyId;
11     private String encriptPassword;
12 
13     public Integer getId() {
14         return id;
15     }
16 
17     public void setId(Integer id) {
18         this.id = id;
19     }
20 
21     public String getName() {
22         return name;
23     }
24 
25     public void setName(String name) {
26         this.name = name;
27     }
28 
29     public Byte getGender() {
30         return gender;
31     }
32 
33     public void setGender(Byte gender) {
34         this.gender = gender;
35     }
36 
37     public Integer getAge() {
38         return age;
39     }
40 
41     public void setAge(Integer age) {
42         this.age = age;
43     }
44 
45     public String getTelephone() {
46         return telephone;
47     }
48 
49     public void setTelephone(String telephone) {
50         this.telephone = telephone;
51     }
52 
53     public String getRegisterMode() {
54         return registerMode;
55     }
56 
57     public void setRegisterMode(String registerMode) {
58         this.registerMode = registerMode;
59     }
60 
61     public String getThirdPartyId() {
62         return thirdPartyId;
63     }
64 
65     public void setThirdPartyId(String thirdPartyId) {
66         this.thirdPartyId = thirdPartyId;
67     }
68 
69     public String getEncriptPassword() {
70         return encriptPassword;
71     }
72 
73     public void setEncriptPassword(String encriptPassword) {
74         this.encriptPassword = encriptPassword;
75     }
76 }

UserModel

步骤3:修改UserPasswordDOMapper.xml,添加一个selectByUserId操作的配置

1 <select id="selectByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
2     select
3     <include refid="Base_Column_List" />
4     from user_password
5     where user_id = #{userId,jdbcType=INTEGER}
6   </select>

同步修改UserPasswordDOMapper.java,添加一行代码:

UserPasswordDO selectByUserId(Integer userId);

步骤4:编写Controller包中的UserController.java

1 @Controller("user")
 2 @RequestMapping("/user")
 3 public class UserController {
 4     @Autowired
 5     private UserService userService;
 6     @RequestMapping("/get")
 7     @ResponseBody
 8     public UserModel getUser(@RequestParam(name="id") Integer id) {
 9         //调用service服务获取对应id的用户对象并返回给前端
10         UserModel userModel = userService.getUserById(id);
11         return userModel;
12     }
13 }

运行后,访问http://localhost:8090/user/get?id=1(需要事先添加好一条完整的数据),可以看到页面上输出了这条数据的完整信息。

步骤5:发现问题:在UserController中,我们把userModel模型直接返回给前端,导致密码直接输出在页面中,这是非常不专业的。

因此,我们在controller层(包)中需要新建一个模型对象。在controller层中新建一个viewobject包,并在其中写一个viewobject类,里面只写需要展示在前端的字段与getters&setters。

1 package org.example.controller.viewobject;
 2 
 3 public class UserVO {
 4     //只写前端用户所需要的信息
 5     private Integer id;
 6     private String name;
 7     private Byte gender;
 8     private Integer age;
 9     private String telephone;
10 
11     public Integer getId() {
12         return id;
13     }
14 
15     public void setId(Integer id) {
16         this.id = id;
17     }
18 
19     public String getName() {
20         return name;
21     }
22 
23     public void setName(String name) {
24         this.name = name;
25     }
26 
27     public Byte getGender() {
28         return gender;
29     }
30 
31     public void setGender(Byte gender) {
32         this.gender = gender;
33     }
34 
35     public Integer getAge() {
36         return age;
37     }
38 
39     public void setAge(Integer age) {
40         this.age = age;
41     }
42 
43     public String getTelephone() {
44         return telephone;
45     }
46 
47     public void setTelephone(String telephone) {
48         this.telephone = telephone;
49     }
50 }

viewobject

同时,我们修改UserController类,将UserModel转化为viewobject后,再返回给前端。

1 @Controller("user")
 2 @RequestMapping("/user")
 3 public class UserController {
 4     @Autowired
 5     private UserService userService;
 6     @RequestMapping("/get")
 7     @ResponseBody
 8     public UserVO getUser(@RequestParam(name="id") Integer id) {
 9         //调用service服务获取对应id的用户对象并返回给前端
10         UserModel userModel = userService.getUserById(id);
11         //将核心领域模型对象转化为可供UI使用的viewobject
12         return convertFromModel(userModel);
13     }
14     private UserVO convertFromModel(UserModel userModel){
15         if(userModel == null){
16             return null;
17         }
18         UserVO userVO = new UserVO();
19         BeanUtils.copyProperties(userModel,userVO);
20         return userVO;
21     }
22 }

这一步中,我们做了一个完整的从数据库中读取数据,展示在前端页面上的操作。

controller层——>service层——>dao层

dataobject层负责数据存储到service的传输,并且在用户的service的服务中组装了对应的核心领域模型。

controller层做了到用户viewobject之间的传递,保证密码等信息不会输出到前端。

2.定义通用的返回对象

步骤1:自主管理前端页面的返回——返回正确信息

org.example包下创建一个response包,在其中创建一个CommonReturnType.java文件。

在该文件中,设置两个属性:status,data,并生成对应的getters&setters。然后写两个构造方法,包含了两个属性的设置。

电商 spring 技术架构 springboot电商项目_apache_06

1 package org.example.response;
 2 
 3 public class CommonReturnType {
 4     //表名对应请求的返回处理结果,success/fail
 5     private String status;
 6     //若status返回success,则data内返回前端需要的json数据
 7     //若status返回success,则data内使用通用的错误码格式
 8     private Object data;
 9 
10     //定义一个通用的创建方法
11     public static CommonReturnType create(Object result){
12         return CommonReturnType.create(result,"success");
13     }
14 
15     public static CommonReturnType create(Object result,String status){
16         CommonReturnType type = new CommonReturnType();
17         type.setStatus(status);
18         type.setData(result);
19         return type;
20     }
21 
22     public String getStatus() {
23         return status;
24     }
25 
26     public void setStatus(String status) {
27         this.status = status;
28     }
29 
30     public Object getData() {
31         return data;
32     }
33 
34     public void setData(Object data) {
35         this.data = data;
36     }
37 }

CommonReturnType

修改我们的UserController.java,将返回值改为CommonReturnType,由CommonReturnType调用create方法来引用UserVO中的信息。以下代码为需要修改的部分:

1 public CommonReturnType getUser(@RequestParam(name="id") Integer id) {
2         //调用service服务获取对应id的用户对象并返回给前端
3         UserModel userModel = userService.getUserById(id);
4         //将核心领域模型对象转化为可供UI使用的viewobject
5         UserVO userVO =  convertFromModel(userModel);
6         //返回通用对象
7         return CommonReturnType.create(userVO);
8     }

运行后,我们仍然访问http://localhost:8090/user/get?id=1,可以看到页面上输出了:

电商 spring 技术架构 springboot电商项目_apache_07

 步骤2:自主管理前端页面的返回——返回错误信息

org.example包下创建一个error包,在其中创建一个CommonError接口,写3个方法:获取错误码,获取错误信息,设置错误信息

1 public interface CommonError {
2     public int getErrCode();
3     public String getErrMsg();
4     public CommonError setErrMsg(String errMsg);
5 }

error包下写一个枚举类型的EmBusinessError,实现CommonError接口。

1 package org.example.error;
 2 
 3 public enum EmBusinessError implements CommonError{
 4     //通用错误类型10001
 5     PARAMETER_VALIDATION_ERROR(10001,"参数不合法"),
 6     //未知错误10002
 7     UNKNOWN_ERROR(10002,"未知错误"),
 8 
 9     //20000开头相关为用户信息相关错误定义
10     USER_NOT_EXIST(20001,"用户不存在"),
11     ;
12 
13     private EmBusinessError(int errCode,String errMsg){
14         this.errCode = errCode;
15         this.errMsg = errMsg;
16     }
17 
18     private int errCode;
19     private String errMsg;
20 
21     @Override
22     public int getErrCode() {
23         return this.errCode;
24     }
25 
26     @Override
27     public String getErrMsg() {
28         return this.errMsg;
29     }
30 
31     @Override
32     public CommonError setErrMsg(String errMsg) {
33         this.errMsg = errMsg;
34         return this;
35     }
36 }

error包下写一个BusinessException,实现CommonError接口,并继承Exception类。

1 public class BusinessException extends Exception implements CommonError{
 2     private CommonError commonError;
 3 
 4     //直接接收EmBusinessError的传参用于构造业务异常
 5     public BusinessException(CommonError commonError) {
 6         super();
 7         this.commonError = commonError;
 8     }
 9 
10     public BusinessException(CommonError commonError,String errMsg) {
11         super();
12         this.commonError = commonError;
13         this.commonError.setErrMsg(errMsg);
14     }
15 
16     @Override
17     public int getErrCode() {
18         return this.commonError.getErrCode();
19     }
20 
21     @Override
22     public String getErrMsg() {
23         return this.commonError.getErrMsg();
24     }
25 
26     @Override
27     public CommonError setErrMsg(String errMsg) {
28         this.commonError.setErrMsg(errMsg);
29         return this;
30     }
31 }

UserController中添加如下代码:

1      //若获取的对应用户信息不存在
2         if(userModel==null){
3             throw new BusinessException(EmBusinessError.USER_NOT_EXIST);
4         }

步骤3:异常处理

在controller目录下单独写一个BaseController类,定义exceptionhandler解决未被controller层吸收的exception。

1 import java.util.Map;
 2 
 3 public class BaseController {
 4     //定义exceptionhandler解决未被controller层吸收的exception
 5     @ExceptionHandler(Exception.class)
 6     @ResponseStatus(HttpStatus.OK)
 7     @ResponseBody
 8     public Object handlerException(HttpServletRequest request, Exception ex){
 9         Map<String,Object> responseData = new HashMap<>();
10         if(ex instanceof BusinessException){
11             BusinessException businessException = (BusinessException)ex;
12             responseData.put("errCode",businessException.getErrCode());
13             responseData.put("errMsg",businessException.getErrMsg());
14         }else{
15             responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
16             responseData.put("errMsg",EmBusinessError.UNKNOWN_ERROR.getErrMsg());
17 
18         }
19         return CommonReturnType.create(responseData,"fail");
20     }
21 }

然后,UserController类需要继承BaseController类。

运行后,我们访问http://localhost:8090/user/get?id=2,(id=2的数据是不存在的),可以看到页面为:

电商 spring 技术架构 springboot电商项目_spring_08

 为了程序的健壮性,我们在BaseController中添加了一个unknown error。我们可以手动地来测试一下这段代码是否起了作用:

修改UserController部分代码如下:

1         if(userModel==null){
2             userModel.setEncriptPassword("123");
3             //throw new BusinessException(EmBusinessError.USER_NOT_EXIST);
4         }

运行后,我们再次访问http://localhost:8090/user/get?id=2,可以看到页面为:

 

电商 spring 技术架构 springboot电商项目_maven_09

总结:

在这一环节中,首先我们定义了一个CommonReturnType,能够用对应的status+objectdata的方式返回所有json序列化方式的固定对象,供前端解析使用。

然后,我们定义了EmBusinessError,统一管理我们所需要的错误码。

接着,我们在BaseController中定义了一个通用的ExceptionHandler的类来解决未被controller层吸收的exception(采用errorCode+errorMsg的统一定义方式),处理了所有不可预知的异常。

3.用户模型管理

步骤1:otp验证码获取

我们在userController类中编写如下代码,用于随机生成一个验证码,并与手机号关联。发送短信的过程我们暂时用控制台打印来替代。

1     @Autowired
 2     private HttpServletRequest httpServletRequest;
 3 
 4     //用户获取otp短信接口
 5     @RequestMapping("/getotp")
 6     @ResponseBody
 7     public CommonReturnType getOtp(@RequestParam(name="telephone")String telephone){
 8         //按照一定的规则生成otp验证码
 9         Random random = new Random();
10         int randomInt = random.nextInt(99999);
11         randomInt += 10000;
12         String otpCode = String.valueOf(randomInt);
13         //将otp验证码桶对应用户的手机号关联,使用httpsession的方式绑定他的手机号与OTPCODE
14         httpServletRequest.getSession().setAttribute(telephone,otpCode);
15         //将otp验证码通过短信通道发送给用户,这里简化打印至控制台查看
16         System.out.println("telephone = " + telephone + " & otpCode = "+otpCode);
17         return CommonReturnType.create(null);
18     }

登录http://localhost:8090/user/getotp?telephone=13565656565(手机号任意),页面显示:

电商 spring 技术架构 springboot电商项目_电商 spring 技术架构_10

 控制台中看到输出:

电商 spring 技术架构 springboot电商项目_spring_11

 步骤2:用前后端分离的设计模式制作用户登录的界面

用Metronic模板写一个用于用户注册的html,与static文件夹放在一起,代码如下:

1 <html>
 2 <head>
 3     <meta charset="UTF-8">
 4     <script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <div>
 9         <h3>获取otp信息</h3>
10         <div>
11             <label>手机号</label>
12             <div>
13                 <input type="text" placeholder="手机号" name="telephone" id="telephone"/>
14             </div>
15         </div>
16         <div>
17             <button id="getotp" type="submit">
18                 获取otp短信
19             </button>
20         </div>
21     </div>
22 
23 </body>
24 
25 <script>
26     jQuery(document).ready(function () {
27 
28         //绑定otp的click事件用于向后端发送获取手机验证码的请求
29         $("#getotp").on("click",function () {
30 
31             var telephone=$("#telephone").val();
32             if (telephone==null || telephone=="") {
33                 alert("手机号不能为空");
34                 return false;
35             }
36 
37 
38             //映射到后端@RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
39             $.ajax({
40                 type:"POST",
41                 contentType:"application/x-www-form-urlencoded",
42                 url:"http://localhost:8090/user/getotp",
43                 data:{
44                     "telephone":$("#telephone").val(),
45                 },
46                 success:function (data) {
47                     if (data.status=="success") {
48                         alert("otp已经发送到了您的手机,请注意查收");
49                     }else {
50                         alert("otp发送失败,原因为" + data.data.errMsg);
51                     }
52                 },
53                 error:function (data) {
54                     alert("otp发送失败,原因为"+data.responseText);
55                 }
56             });
57         });
58     });
59 </script>
60 </html>

getotp.html

在BaseController中定义一个常量如下:

public static final String CONTENT_TYPE_FORMED ="application/x-www-form-urlencoded";

在UserController中,我们修改RequestMapping注解为:

@RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})

最后为了实现跨域请求,我们在UserController类上面添加一个@CrossOrigin注解。

运行project后,我们用google浏览器(千万不要用IE,会提示undefined错误!)打开getotp.html,界面如下:

电商 spring 技术架构 springboot电商项目_maven_12

 输入任意手机号,点击按钮,浏览器提示:

电商 spring 技术架构 springboot电商项目_maven_13

 控制台输出了手机号与otpcode。

html页面可以通过引入样式来美化,美化后代码如下:

1 <html>
 2 <head>
 3     <meta charset="UTF-8">
 4     <script src = "static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
 5     <link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
 6     <link href="static/assets/global/plugins/css/component.css" rel="stylesheet" type="text/css"/>
 7     <link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
 8 </head>
 9 <body class="login">
10 <div class="content">
11     <h3 class="form-title">获取otp信息</h3>
12     <div class="form-group">
13         <label class="control-label">手机号</label>
14         <div>
15             <input class="form-control" type="text" placeholder="手机号" name="telephone" id="telephone"/>
16         </div>
17     </div>
18     <div class="form-actions">
19         <button class="btn blue" id="getotp" type="submit">
20             获取otp短信
21         </button>
22     </div>
23 </div>
24 
25 </body>
26 
27 
28 <script>
29     jQuery(document).ready(function () {
30 
31         //绑定otp的click事件用于向后端发送获取手机验证码的请求
32         $("#getotp").on("click",function () {
33 
34             var telephone=$("#telephone").val();
35             if (telephone==null || telephone=="") {
36                 alert("手机号不能为空");
37                 return false;
38             }
39 
40 
41             //映射到后端@RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
42             $.ajax({
43                 type:"POST",
44                 contentType:"application/x-www-form-urlencoded",
45                 url:"http://localhost:8090/user/getotp",
46                 data:{
47                     "telephone":$("#telephone").val(),
48                 },
49                 success:function (data) {
50                     if (data.status=="success") {
51                         alert("otp已经发送到了您的手机,请注意查收");
52                     }else {
53                         alert("otp发送失败,原因为" + data.data.errMsg);
54                     }
55                 },
56                 error:function (data) {
57                     alert("otp发送失败,原因为"+data.responseText);
58                 }
59             });
60         });
61     });
62 </script>
63 </html>

getotp.html

步骤3:用户注册功能的实现

UserController写一个用户注册的接口,用来接收前端写入的数据。

1 //用户注册接口
 2     @RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
 3     @ResponseBody
 4     public CommonReturnType register(@RequestParam(name = "telephone") String telephone,
 5                                      @RequestParam(name = "otpCode") String otpCode,
 6                                      @RequestParam(name = "name") String name,
 7                                      @RequestParam(name = "gender") String gender,
 8                                      @RequestParam(name = "age") String age,
 9                                      @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
10 
11         //验证手机号和对应的otpCode相符合
12         String inSessionOtpCode = (String) this.httpServletRequest.getSession().getAttribute(telephone);
13         if (!com.alibaba.druid.util.StringUtils.equals(otpCode, inSessionOtpCode)) {
14             throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信验证码不符合");
15         }
16         //用户的注册流程
17         UserModel userModel = new UserModel();
18         userModel.setName(name);
19         userModel.setAge(Integer.valueOf(age));
20         userModel.setGender(Byte.valueOf(gender));
21         userModel.setTelephone(telephone);
22         userModel.setRegisterMode("byphone");
23 
24         //密码加密
25         userModel.setEncriptPassword(this.EncodeByMd5(password));
26         userService.register(userModel);
27         return CommonReturnType.create(null);
28     }
29 
30     //密码加密
31     public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
32         //确定计算方法
33         MessageDigest md5 = MessageDigest.getInstance("MD5");
34         Base64Encoder base64en = new Base64Encoder();
35 
36         //加密字符串
37         String newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
38         return newstr;
39     }

在pom中引入做校验的依赖:

1     <dependency>
2       <groupId>org.apache.commons</groupId>
3       <artifactId>commons-lang3</artifactId>
4       <version>3.7</version>
5     </dependency>

在UserServiceImpl中写register方法,将controller层传过来的数据与数据库做交互,并修改跨域注解为:

@CrossOrigin(allowCredentials = "true",allowedHeaders = "*")

1     @Override
 2     @Transactional//声明事务
 3     public void register(UserModel userModel) throws BusinessException {
 4         //校验
 5         if (userModel == null) {
 6             throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
 7         }
 8         if (StringUtils.isEmpty(userModel.getName())
 9                 || userModel.getGender() == null
10                 || userModel.getAge() == null
11                 || StringUtils.isEmpty(userModel.getTelephone())) {
12             throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
13         }
14 
15         //实现model->dataobject方法
16 
17         UserDo userDO = convertFromModel(userModel);
18         //insertSelective相对于insert方法,不会覆盖掉数据库的默认值
19         userDoMapper.insertSelective(userDO);
20 
21         userModel.setId(userDO.getId());
22 
23         UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
24         userPasswordDOMapper.insertSelective(userPasswordDO);
25 
26         return;
27     }
28 
29     private UserPasswordDO convertPasswordFromModel(UserModel userModel) {
30         if (userModel == null) {
31             return null;
32         }
33         UserPasswordDO userPasswordDO = new UserPasswordDO();
34         userPasswordDO.setEncriptPassword(userModel.getEncriptPassword());
35         userPasswordDO.setUserId(userModel.getId());
36 
37         return userPasswordDO;
38     }
39 
40     private UserDo convertFromModel(UserModel userModel) {
41         if (userModel == null) {
42             return null;
43         }
44         UserDo userDo = new UserDo();
45         BeanUtils.copyProperties(userModel,userDo);
46         return userDo;
47     }

制作前端界面:

修改getotp.html,验证码发送成功后写一个跳转,转到register.html(window是小写!),最后再添加一句允许跨域:

1                xhrFields:{withCredentials:true},
 2                 success:function (data) {
 3                     if (data.status=="success") {
 4                         alert("otp已经发送到了您的手机,请注意查收");
 5                         window.location.href="register.html";
 6                     }else {
 7                         alert("otp发送失败,原因为" + data.data.errMsg);
 8                     }
 9                 },
10                 error:function (data) {
11                     alert("otp发送失败,原因为"+data.responseText);
12                 }

getotp.html改动部分

写一个register.html页面,用于输入用户信息:

1 <html>
  2 <head>
  3     <meta charset="UTF-8">
  4     <script src = "static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
  5     <link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
  6     <link href="static/assets/global/plugins/css/component.css" rel="stylesheet" type="text/css"/>
  7     <link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
  8 </head>
  9 <body class="login">
 10     <div class="content">
 11         <h3 class="form-title">用户注册</h3>
 12         <div class="form-group">
 13             <label class="control-label">手机号</label>
 14             <div>
 15                 <input class="form-control" type="text" placeholder="手机号" name="telephone" id="telephone"/>
 16             </div>
 17         </div>
 18         <div class="form-group">
 19             <label class="control-label">验证码</label>
 20             <div>
 21                 <input class="form-control" type="text" placeholder="验证码" name="otpCode" id="otpCode"/>
 22             </div>
 23         </div>
 24         <div class="form-group">
 25             <label class="control-label">用户昵称</label>
 26             <div>
 27                 <input class="form-control" type="text" placeholder="用户昵称" name="name" id="name"/>
 28             </div>
 29         </div>
 30         <div class="form-group">
 31             <label class="control-label">性别</label>
 32             <div>
 33                 <input class="form-control" type="text" placeholder="性别" name="gender" id="gender"/>
 34             </div>
 35         </div>
 36         <div class="form-group">
 37             <label class="control-label">年龄</label>
 38             <div>
 39                 <input class="form-control" type="text" placeholder="年龄" name="age" id="age"/>
 40             </div>
 41         </div>
 42         <div class="form-group">
 43             <label class="control-label">密码</label>
 44             <div>
 45                 <input class="form-control" type="password" placeholder="密码" name="password" id="password"/>
 46             </div>
 47         </div>
 48         <div class="form-actions">
 49             <button class="btn blue" id="register" type="submit">
 50                 提交注册
 51             </button>
 52         </div>
 53     </div>
 54 
 55 </body>
 56 
 57 <script>
 58     jQuery(document).ready(function () {
 59 
 60         //绑定otp的click事件用于向后端发送获取手机验证码的请求
 61         $("#register").on("click",function () {
 62 
 63             var telephone=$("#telephone").val();
 64             var otpCode=$("#otpCode").val();
 65             var password=$("#password").val();
 66             var age=$("#age").val();
 67             var gender=$("#gender").val();
 68             var name=$("#name").val();
 69             if (telephone==null || telephone=="") {
 70                 alert("手机号不能为空");
 71                 return false;
 72             }
 73             if (otpCode==null || otpCode=="") {
 74                 alert("验证码不能为空");
 75                 return false;
 76             }
 77             if (name==null || name=="") {
 78                 alert("用户名不能为空");
 79                 return false;
 80             }
 81             if (gender==null || gender=="") {
 82                 alert("性别不能为空");
 83                 return false;
 84             }
 85             if (age==null || age=="") {
 86                 alert("年龄不能为空");
 87                 return false;
 88             }
 89             if (password==null || password=="") {
 90                 alert("密码不能为空");
 91                 return false;
 92             }
 93 
 94             //映射到后端@RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
 95             $.ajax({
 96                 type:"POST",
 97                 contentType:"application/x-www-form-urlencoded",
 98                 url:"http://localhost:8090/user/register",
 99                 data:{
100                     "telephone":telephone,
101                     "otpCode":otpCode,
102                     "password":password,
103                     "age":age,
104                     "gender":gender,
105                     "name":name
106                 },
107                 //允许跨域请求
108                 xhrFields:{withCredentials:true},
109                 success:function (data) {
110                     if (data.status=="success") {
111                         alert("注册成功");
112                     }else {
113                         alert("注册失败,原因为" + data.data.errMsg);
114                     }
115                 },
116                 error:function (data) {
117                     alert("注册失败,原因为"+data.responseText);
118                 }
119             });
120             return false;
121         });
122     });
123 </script>
124 
125 </html>

register.html

 修改UserDoMapper.xml与UserPasswordMapper.xml的insertSelective操作,修改insert标签头,使id自增

keyProperty="id" useGeneratedKeys="true"

踩雷:一定要保证数据库中的两张表格的约束正确,否则会出错(血泪史TAT)

电商 spring 技术架构 springboot电商项目_maven_14

电商 spring 技术架构 springboot电商项目_电商 spring 技术架构_15

要会debug:添加断点,然后点击右上角的小甲虫运行,在前端操作之后观察调试台的参数是否正确!

运行结果:输入手机号,页面跳转至输入信息页面,输入个人信息以及对应的手机号,otp验证码,页面提示注册成功,数据库中会增加一条刚刚输入的用户的数据。

步骤4:登录功能实现

UserController中写一个登录接口:

1 //用户登录接口
 2     @RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
 3     @ResponseBody
 4     public CommonReturnType login(@RequestParam(name = "telephone") String telephone,
 5                                   @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
 6         //入参校验
 7         if (StringUtils.isEmpty(telephone) || StringUtils.isEmpty(password)) {
 8             throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
 9         }
10 
11         //用户登录服务,用来校验用户登录是否合法
12         //用户加密后的密码
13         UserModel userModel = userService.validateLogin(telephone, this.EncodeByMd5(password));
14 
15         //将登陆凭证加入到用户登录成功的session内
16         this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);
17         this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);
18         return CommonReturnType.create(null);
19     }

 UserDoMapper接口中增加一个方法:

UserDo selectByTelephone(String telephone);

UserDoMapper.xml中进行配置:

1   <select id="selectByTelephone" resultMap="BaseResultMap">
2     select
3     <include refid="Base_Column_List"/>
4     from user_info
5     where telephone = #{telephone,jdbcType=VARCHAR}
6   </select>

 UserSeivice接口中添加一个方法:

UserModel validateLogin(String telephone, String encriptPassword) throws BusinessException;

然后我们就可以在UserServiceImpl中实现这个方法:

1 @Override
 2     public UserModel validateLogin(String telephone, String encriptPassword) throws BusinessException {
 3             //通过用户手机获取用户信息
 4             UserDo userDo = userDoMapper.selectByTelephone(telephone);
 5             if (userDo == null) {
 6                 throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
 7             }
 8             UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDo.getId());
 9             UserModel userModel = convertFromDataObject(userDo, userPasswordDO);
10 
11             //比对用户信息内加密的密码是否和传输进来的密码相匹配
12             if (StringUtils.equals(encriptPassword, userModel.getEncriptPassword())) {
13                 throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
14             }
15             return userModel;
16         }

建立前端页面:login.html

1 <html>
 2 <head>
 3     <meta charset="UTF-8">
 4     <script src = "static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
 5     <link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
 6     <link href="static/assets/global/plugins/css/component.css" rel="stylesheet" type="text/css"/>
 7     <link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
 8 </head>
 9 <body class="login">
10     <div class="content">
11         <h3 class="form-title">用户登录</h3>
12         <div class="form-group">
13             <label class="control-label">手机号</label>
14             <div>
15                 <input class="form-control" type="text" placeholder="手机号" name="telephone" id="telephone"/>
16             </div>
17         </div>
18         <div class="form-group">
19             <label class="control-label">密码</label>
20             <div>
21                 <input class="form-control" type="password" placeholder="密码" name="password" id="password"/>
22             </div>
23         </div>
24         <div class="form-actions">
25             <button class="btn blue" id="login" type="submit">
26                 登录
27             </button>
28             <button class="btn green" id="register" type="submit">
29                 注册
30             </button>
31         </div>
32     </div>
33 
34 </body>
35 
36 <script>
37     jQuery(document).ready(function () {
38 
39         //绑定注册按钮的click事件用于跳转到注册页面
40         $("#register").on("click",function () {
41             window.location.href = "getotp.html";
42         });
43 
44         //绑定登录按钮的click事件用于登录
45         $("#login").on("click",function () {
46 
47             var telephone=$("#telephone").val();
48             var password=$("#password").val();
49             if (telephone==null || telephone=="") {
50                 alert("手机号不能为空");
51                 return false;
52             }
53             if (password==null || password=="") {
54                 alert("密码不能为空");
55                 return false;
56             }
57 
58             //映射到后端@RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
59             $.ajax({
60                 type:"POST",
61                 contentType:"application/x-www-form-urlencoded",
62                 url:"http://localhost:8090/user/login",
63                 data:{
64                     "telephone":telephone,
65                     "password":password
66                 },
67                 //允许跨域请求
68                 xhrFields:{withCredentials:true},
69                 success:function (data) {
70                     if (data.status=="success") {
71                         alert("登录成功");
72                     }else {
73                         alert("登录失败,原因为" + data.data.errMsg);
74                     }
75                 },
76                 error:function (data) {
77                     alert("登录失败,原因为"+data.responseText);
78                 }
79             });
80             return false;
81         });
82     });
83     </script>
84 </html>

login.html

运行project,我们进入login页面,输入已注册的手机号与密码,就会收到登录成功的提示。

电商 spring 技术架构 springboot电商项目_apache_16

 步骤5:优化校验逻辑

引入validator依赖

1     <dependency>
2       <groupId>org.hibernate</groupId>
3       <artifactId>hibernate-validator</artifactId>
4       <version>5.2.4.Final</version>
5     </dependency>

 org.example下新建一个validator包:

写一个validationResult类:

1 package org.example.validator;
 2 
 3 import org.apache.commons.lang3.StringUtils;
 4 
 5 import java.util.HashMap;
 6 import java.util.Map;
 7 
 8 public class ValidationResult {
 9     //校验结果是否有错
10     private boolean hasErrors = false;
11 
12     //存放错误信息的map
13     private Map<String, String> errorMsgMap = new HashMap<>();
14 
15     public boolean isHasErrors() {
16         return hasErrors;
17     }
18 
19     public void setHasErrors(boolean hasErrors) {
20         this.hasErrors = hasErrors;
21     }
22 
23     public Map<String, String> getErrorMsgMap() {
24         return errorMsgMap;
25     }
26 
27     public void setErrorMsgMap(Map<String, String> errorMsgMap) {
28         this.errorMsgMap = errorMsgMap;
29     }
30 
31     //实现通用的通过格式化字符串信息获取错误结果的msg方法
32     public String getErrMsg() {
33         return StringUtils.join(errorMsgMap.values().toArray(), ",");
34     }
35 }

validationResult

写一个validatorImpl类:

1 @Component
 2 public class ValidatorImpl implements InitializingBean {
 3 
 4     private Validator validator;
 5 
 6     //实现校验方法并返回校验结果
 7     public ValidationResult validate(Object bean) {
 8         final ValidationResult result = new ValidationResult();
 9         Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean);
10         if (constraintViolationSet.size() > 0) {
11             //有错误
12             result.setHasErrors(true);
13             constraintViolationSet.forEach(constraintViolation ->{
14                 String errMsg = constraintViolation.getMessage();
15                 String propertyName = constraintViolation.getPropertyPath().toString();
16                 result.getErrorMsgMap().put(propertyName, errMsg);
17             });
18         }
19         return result;
20     }
21 
22     @Override
23     public void afterPropertiesSet() throws Exception {
24         //将hibernate validator通过工厂的初始化方式使其实例化
25         this.validator = Validation.buildDefaultValidatorFactory().getValidator();
26     }
27 }

validatorImpl

修改UserModel,加入校验相关的注解:

1     private Integer id;
 2     @NotBlank(message = "用户名不能为空")
 3     private String name;
 4     @NotNull(message = "性别不能不填")
 5     private Byte gender;
 6     @NotNull(message = "年龄不能不填")
 7     @Min(value = 0,message="年龄必须大于0岁")
 8     @Max(value = 150,message = "年龄必须小于150岁")
 9     private Integer age;
10     @NotNull(message = "手机号不能为空")
11     private String telephone;
12     private String registerMode;
13     private String thirdPartyId;
14     @NotNull(message = "密码不能为空")
15     private String encriptPassword;

在UserServiceImpl中引入并使用使用validator(先把原来的非空校验注释掉):

@Autowired
private ValidatorImpl validator;

1         /*
 2         if (StringUtils.isEmpty(userModel.getName())
 3                 || userModel.getGender() == null
 4                 || userModel.getAge() == null
 5                 || StringUtils.isEmpty(userModel.getTelephone())) {
 6             throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
 7         }
 8         */
 9         ValidationResult result = validator.validate(userModel);
10         if (result.isHasErrors()) {
11             throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
12         }

 运行project,我们在注册时,可尝试输入一个大于150的年龄,提交后系统会报错:

电商 spring 技术架构 springboot电商项目_电商 spring 技术架构_17

 项目上篇完结~