目录
6.2.4、创建 spring-persist-mybatis.xml
6.2.6、Spring 具体配置:第二步 配置 SqlSessionFactoryBean 及 MapperScannerConfigurer
10.4、对 Ajax 请求返回的结果进行规范(返回统一的数据格式)
学习尚硅谷尚筹网项目学习笔记
遇到 xxx.xxx 包找不到可以构建一下这个包的模块
1、环境搭建总目标
2、创建工程
2.1、项目架构图
2.2、工程创建计划
atcrowdfunding01-admin-parent
groupId:com.atguigu.crowd
artifactId:atcrowdfunding01-admin-parent
packaging:pom
atcrowdfunding02-admin-webui
groupId:com.atguigu.crowd
artifactId:atcrowdfunding02-admin-webui
packaging:war
atcrowdfunding03-admin-component
groupId:com.atguigu.crowd
artifactId:atcrowdfunding03-admin-component
packaging:jar
atcrowdfunding04-admin-entity
groupId:com.atguigu.crowd
artifactId:atcrowdfunding04-admin-entity
packaging:jar
atcrowdfunding05-common-util
groupId:com.atguigu.crowd
artifactId:atcrowdfunding05-common-util
packaging:jar
atcrowdfunding06-common-reverse
groupId:com.atguigu.crowd
artifactId:atcrowdfunding06-common-reverse
packaging:jar
2.3、创建空项目
2.3.1、创建对应 maven 模块
首先创建 空项目,然后在这个空项目下创建对应的模块
2.3.2、建立模块间的依赖
webui 依赖 component
component 依赖 entity
component 依赖 util
然后在 pom.xml 添加依赖
<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding03-admin-component</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding04-admin-entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding04-admin-entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
3、创建数据库和数据库表
3.1、物理建模
3.1.1、理论
第一范式:数据库表中的每一列都不可再分,也就是原子性
这个表中“部门”和“岗位”应该拆分成两个字段:“部门名称”、“岗位”。这样才能够专门针对“部门”或“岗位”进行查询。
第二范式:在满足第一范式基础上要求每个字段都和主键完整相关,而不是仅和主键部分相关(主要针对联合主键而言)
“订单详情表”使用“订单编号”和“产品编号”作为联合主键。此时“产品价格”、“产品数量”都和联合主键整体相关,但“订单金额”和“下单时间” 只和联合主键中的“订单编号”相关,和“产品编号”无关。所以只关联了主键中的部分字段,不满足第二范式。 把“订单金额”和“下单时间”移到订单表就符合第二范式了。
第三范式:表中的非主键字段和主键字段直接相关,不允许间接相关(传递依赖)
上面表中的“部门名称”和“员工编号”的关系是“员工编号”→“部门编号” →“部门名称”,不是直接相关。此时会带来下列问题:
-
数据冗余:“部门名称”多次重复出现。
-
插入异常:组建一个新部门时没有员工信息,也就无法单独插入部门 信息。就算强行插入部门信息,员工表中没有员工信息的记录同样是 非法记录。
-
删除异常:删除员工信息会连带删除部门信息导致部门信息意外丢失。
-
更新异常:哪怕只修改一个部门的名称也要更新多条员工记录。
正确的做法是:把上表拆分成两张表,以外键形式关联
“部门编号”和“员工编号”是直接相关的。 第二范式的另一种表述方式是:两张表要通过外键关联,不保存冗余字段。例如:不能在“员工表”中存储“部门名称”。
3.1.2、实践
规则的变通:三大范式是设计数据库表结构的规则约束,但是在实际开发中允许局部变通。比如为了快速查询到关联数据可能会允许冗余字段的存在。例如在员工表中存储部门名称虽然违背第三范式,但是免去了对部门表的关联查询。
根据业务功能设计数据库表:
-
看得见的字段:能够从需求文档或原型页面上直接看到的数据都需要设计对应的数据库表、字段来存储。
-
看不见的字段:除了能够直接从需求文档中看到的字段,实际开发中往往还会包含一些其他字段来保存其他相关数据。例如:管理员表需要再增加主键及创建时间以有利于数据维护。
-
冗余字段:为了避免建表时考虑不周有所遗漏,到后期再修改表结构非常麻烦,所以也有的团队会设置一些额外的冗余字段备用。
-
实际开发对接:实际开发中除了一些各个模块都需要使用的公共表在项目启动时创建好,其他专属于各个模块的表由该模块的负责人创建。但通常开发人员不能直接操作数据库服务器,所以需要把建表的 SQL 语句发送给运维工程师执行创建操作。
3.2、创建数据库
CREATE DATABASE `project_crowd` CHARACTER SET utf8;
3.3、创建数据表
USE project_crowd;
DROP TABLE IF EXISTS t_admin;
CREATE TABLE t_admin (
id INT NOT NULL AUTO_INCREMENT COMMENT '主键',
login_acct VARCHAR(255) NOT NULL COMMENT '登录账号 ',
user_pswd CHAR(32) NOT NULL COMMENT '登录密码 ',
user_name VARCHAR(255) NOT NULL COMMENT '昵称 ',
email VARCHAR(255) NOT NULL COMMENT '邮件地址 ',
create_time CHAR(19) COMMENT '创建时间 ',
PRIMARY KEY (id)
);
4、基于 Maven 的 MyBatis 的逆向工程
4.1、配置 pom
在 atcrowdfunding06-common-reverse 模块的 pom.xml 中配置
<!-- 依赖 MyBatis 核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
</dependencies>
<!-- 控制 Maven 在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version> <!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
4.2、创建 generatorConfig.xml
在 atcrowdfunding06-common-reverse 模块下的 src/main/resources 目录下创建 generatorConfig.xml,用来配置逆向工程
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- mybatis-generator:generate -->
<context id="atguiguTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释true:是;false:否-->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码-->
<jdbcConnection
driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/project_crowd"
userId="root"
password="zyj123">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和NUMERIC 类型解析为Integer,为true 时把
JDBC DECIMAL
和NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成Entity 类的路径-->
<javaModelGenerator targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.entity">
<!-- enableSubPackages:是否让schema 作为包的后缀-->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格-->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:XxxMapper.xml 映射文件生成的路径-->
<sqlMapGenerator targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.mapper">
<!-- enableSubPackages:是否让schema 作为包的后缀-->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:Mapper 接口生成的位置-->
<javaClientGenerator type="XMLMAPPER"
targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.mapper">
<!-- enableSubPackages:是否让schema 作为包的后缀-->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 数据库表名字和我们的entity 类对应的映射指定-->
<table tableName="t_admin" domainObjectName="Admin" />
</context>
</generatorConfiguration>
4.3、执行逆向工程操作的 maven 指令
双击即可
4.4、将逆向工程生成的资源归位
创建完后给 Admin 添加无参和全参构造器,然后给生成的资源归位
5、父工程依赖管理
5.1、版本声明
在 atcrowdfunding01-admin-parent 模块的 pom.xml 中进行版本声明
<!-- 版本声明 -->
<properties>
<!-- 声明属性,对Spring 的版本进行统一管理-->
<atguigu.spring.version>4.3.20.RELEASE</atguigu.spring.version>
<!-- 声明属性,对SpringSecurity 的版本进行统一管理-->
<atguigu.spring.security.version>4.2.10.RELEASE</atguigu.spring.security.version>
</properties>
5.2、依赖管理
在 atcrowdfunding01-admin-parent 中进行版本声明后,还需要引入依赖
遇到的问题:若依赖基本全部爆红(没下载 jar 包),可以将 dependencyManagement 删去后再重新加载,等下载完 jar 包后再加回去
<dependencyManagement>
<dependencies>
<!-- Spring 依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<!-- 数据库依赖 -->
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- MyBatis 与 Spring 整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- MyBatis 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<!-- Spring 进行 JSON 数据转换依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<!-- JSTL 标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- junit 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- JSP 页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
6、Spring 整合 MyBatis
6.1、思路
6.2、具体操作
6.2.1、在子工程中加入搭建环境所需的具体依赖
在 atcrowdfunding03-admin-component 模块添加依赖
<!-- Spring依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<!-- 数据库依赖 -->
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- MyBatis与Spring整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<!-- MyBatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!-- Spring进行JSON数据转换依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<!-- JSTL标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- 引入Servlet容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
6.2.2、创建 jdbc.properties
jdbc.user=root
jdbc.password=zyj123
jdbc.url=jdbc:mysql://localhost:3306/project_crowd?useUnicode=true&characterEncoding=UTF-8
jdbc.driver=com.mysql.jdbc.Driver
6.2.3、创建 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
6.2.4、创建 spring-persist-mybatis.xml
6.2.5、Spring 具体配置:第一步 配置数据源
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 加载外部属性文件jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 连接数据库的用户名-->
<property name="username" value="${jdbc.user}"/>
<!-- 连接数据库的密码-->
<property name="password" value="${jdbc.password}"/>
<!-- 目标数据库的URL 地址-->
<property name="url" value="${jdbc.url}"/>
<!-- 数据库驱动全类名-->
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
</beans>
遇到的问题:没有配置应用程序上下文
解决方法:application context not configured for this file于spring框架使用中的原因_YangJiaJun0506的博客-CSDN博客
6.2.6、测试能否连接数据库
若控制台输出 connection,则连接成功
@RunWith(SpringJUnit4ClassRunner.class) // 指定Spring 给Junit 提供的运行器类
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml"}) // 加载Spring 配置文件的注解
public class CrowTest {
@Autowired
private DataSource dataSource;
@Test
public void testConnection() throws SQLException {
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
}
遇到的问题:编译报错:webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode)
已经配置了 webapp/WEB-INF/web.xml,却还是报错,只好配置忽略检查 web.xml
<properties>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
6.2.6、Spring 具体配置:第二步 配置 SqlSessionFactoryBean 及 MapperScannerConfigurer
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 装配数据源-->
<property name="dataSource" ref="dataSource"/>
<!-- 指定MyBatis 全局配置文件位置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 指定Mapper 配置文件位置-->
<property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"/>
</bean>
<!-- 配置MapperScannerConfigurer来扫描Mapper接口所在的包 -->
<!-- 把MyBatis 创建的Mapper 接口类型的代理对象扫描到IOC 容器中-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 使用basePackage 属性指定Mapper 接口所在包-->
<property name="basePackage" value="com.atguigu.crowd.mapper"/>
</bean>
6.2.7、测试能否插入数据
@Autowired
private AdminMapper adminMapper;
@Test
public void testInsertAdmin(){
Admin admin = new Admin(null, "Tom", "123123", "汤姆", "tom@qq.com", null);
int count = adminMapper.insert(admin);
System.out.println("count = " + count);
}
遇到的问题:运行时报错:软件包、符号找不到
解决方法:构建找不到的包
7、日志系统
7.1、意义
系统在运行过程中出了问题就需要通过日志来进行排查,所以我们在上手任何新技 术的时候,都要习惯性的关注一下它是如何打印日志的。
7.2、具体操作
7.2.1、加入 slf4j+logback
将 atcrowdfunding03-admin-component 模块中之前日志相关的依赖全部删去,然后加入 slf4j+logback 依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
7.2.2、主动打印日志的方式
@Test
public void testLog(){
// 获取Logger对象,这里传入的class对象就是当前打印日志的类
Logger logger = LoggerFactory.getLogger(CrowTest.class);
// 根据不同日志级别打印日志
logger.debug("debug level");
logger.info("info level");
logger.warn("warn level");
logger.error("error level");
}
7.2.3、更换框架的日志系统
① 排除 commons-logging
② 加入转换包(atcrowdfunding03-admin-component 模块)
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
8、声明式事务
8.1、目标
从事务角度:一个事务方法中包含的多个数据库操作,要么一起提交、要么一起回 滚。也就是说事务方法中的多个数据库操作,有任何一个失败,整个事务全部回滚。
从声明式角度:由 Spring 来全面接管数据库事务。用声明式代替编程式。
try {
// 核心操作前:开启事务(关闭自动提交)
// 对应 AOP 的前置通知
connection.setAutoCommit(false);
// 核心操作
adminService.updateXxx(xxx, xxx);
// 核心操作成功:提交事务
// 对应 AOP 的返回通知
connection.commit();
}catch(Exception e){
// 核心操作失败:回滚事务
// 对应 AOP 的异常通知
connection.rollBack();
}finally{
// 不论成功还是失败,核心操作终归是结束了
// 核心操作不管是怎么结束的,都需要释放数据库连接
// 对应 AOP 的后置通知
if(connection != null){
connection.close();
}
}
8.2、思路
8.3、创建 spring-persist-tx.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置自动扫描的包:主要是为了把Service扫描到IOC容器中 -->
<context:component-scan base-package="com.atguigu.crowd.service"/>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 装配数据源。这里ref="dataSource"中的dataSource是spring-persist-mybatis.xml中配置的数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务切面 -->
<aop:config>
<!--
* *..*Service.*(..)
* 任意权限修饰符和任意返回类型
*.. 任意包名下的任意深度的包
*ServiceImpl 该包下以Service结尾的类
.*(..) 该类下的所有方法,且参数为任意
-->
<!-- 考虑到后面需要整合SpringSecurity,为了避免把UserDetailsService加入事务控制,让切入点表达式定位到ServiceImpl实现类 -->
<aop:pointcut id="txPointCut" expression="execution(* *..*ServiceImpl.*(..))"/>
<!-- 将切入点表达式和事务通知关联起来 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 配置事务属性 -->
<tx:attributes>
<!-- 查询方法:配置只读属性,让数据库知道这是一个查询操作,能够进行一定优化 -->
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
<!-- 增删改方法:配置事务传播行为、回滚异常 -->
<!--
propagation:
REQUIRED:默认值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务。如果已经有了,那么就使用这个已有的事务。
顾虑:用别人的事务有可能被回滚
REQUIRED_NEW:建议使用的值,表示当前方法必须工作在事务中,不管当前线程上有没有事务,都要自己开事务,在自己的事务中运行。
好处:不会受到其他事务回滚的影响
-->
<!--
rollback-for:配置事务方法针对什么样的异常回滚
默认:运行时异常回滚
建议:编译时异常和运行时异常都回滚
-->
<tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
<tx:method name="remove*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
<tx:method name="batch*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
</beans>
8.4、测试
8.4.1、创建 AdminService 接口及其实现类
AdminService
public interface AdminService {
void saveAdmin(Admin admin);
}
AdminServiceImpl
@Service
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Override
public void saveAdmin(Admin admin) {
adminMapper.insert(admin);
}
}
8.4.2、测试
① 在测试类的 @ContextConfiguration 注解加上事务的配置文件
@RunWith(SpringJUnit4ClassRunner.class) // 指定Spring 给Junit 提供的运行器类
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml", "classpath:spring-persist-tx.xml"}) // 加载Spring 配置文件的注解
public class CrowTest {
}
② 为了能够在控制台看到事务的创建,可以将 atcrowdfunding02-admin-webui 模块下的 spring-persist-mybatis.xml 的 root 标签下的 level 属性改为 DEBUG
③ 测试
@Autowired
private AdminService adminService;
@Test
public void testTx(){
Admin admin = new Admin(null, "jerry", "123456", "杰瑞", "jerry@qq.com", null);
adminService.saveAdmin(admin);
}
若抛出一个异常,则会回滚
9、表述层环境搭建
9.1、表述层工作机制
9.1.1、启动过程
9.1.2、访问过程
9.2、环境搭建
9.2.1、配置 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置ContextLoaderListener 加载 Spring 配置文件-->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-persist-*.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置 CharacterEncodingFilter 解决 POST 请求的字符乱码问题-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 指定字符集-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 强制请求进行编码-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!-- 强制响应进行编码-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- CharacterEncodingFilter执行的顺序要在所有其他Filter之前,否则会失效 -->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置SpringMVC 的前端控制器DispatcherServlet-->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 以初始化参数的形式指定SpringMVC 配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web-mvc.xml</param-value>
</init-param>
<!-- Servlet 默认在第一次请求的时候创建对象、初始化,而DispatcherServlet创建对象后有大量的框架初始化操作,不适合在第一次请求来做 -->
<!-- 让DispatcherServlet 在Web 应用启动时创建对象、初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- DispatcherServlet 映射的URL 地址-->
<!-- 大白话:什么样的访问地址会交给SpringMVC 来处理-->
<!-- 配置方式一:符合RESTFUL 风格使用“/”,表示拦截所有请求 -->
<!-- <url-pattern>/</url-pattern> -->
<!--
配置方式二:请求扩展名
优点1:xxx.css、xxx.js、xxx.png等静态资源完全不经过SpringMVC,不需要特殊处理
优点2:可以实现伪静态效果。表面上看起来是访问HTML文件这样的静态资源,但实际上是经过java代码运算的结果
伪静态作用1:给黑客入侵增加难道
伪静态作用2:有利于SEO优化(让百度、谷歌这样的搜索引擎更容易找到我们的项目)。
缺点:不符合RESTFil风格
-->
<url-pattern>*.html</url-pattern>
<!--
配置json扩展名:如果一个Ajax请求扩展名是html,但是实际服务器给浏览器返回的是json数据,二者就不匹配了,会出现406
为了让Ajax请求能够顺利拿到JSON格式的响应数据,另外配置json扩展名
-->
<url-pattern>*.json</url-pattern>
</servlet-mapping>
</web-app>
9.2.2、配置 spring-web-mvc.xml
① 创建 SpringMVC 扫描的包
② 配置 spring-web-mvc.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- 配置自动扫描的包-->
<context:component-scan base-package="com.atguigu.crowd.mvc"/>
<!-- 配置视图解析器-->
<!-- 拼接公式→前缀+逻辑视图+后缀=物理视图-->
<!--
@RequestMapping("/xxx/xxx")
public String xxx() {
// 这个返回值就是逻辑视图
return "target";
}
物理视图是一个可以直接转发过去的地址
物理视图:"/WEB-INF/"+"target"+".jsp"
转发路径:"/WEB-INF/target.jsp"
-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:附加到逻辑视图名称前-->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀:附加到逻辑视图名称后-->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置启用注解驱动-->
<mvc:annotation-driven/>
</beans>
9.2.3、测试 SSM 整合环境
① 在 atcrowdfunding02-admin-webui 模块中引入 jsp 相关依赖
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- JSP 页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
② 创建 index.jsp
<%--
Created by IntelliJ IDEA.
User: zhang
Date: 2022/5/1
Time: 14:38
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/test/ssm.html">测试整合ssm环境</a>
</body>
</html>
③ 在 AdminService 及其实现类生成获取所有用户的方法
public interface AdminService {
public void saveAdmin(Admin admin);
public List<Admin> getAll();
}
adminServiceImpl
@Override
public List<Admin> getAll() {
List<Admin> adminList = adminMapper.selectByExample(new AdminExample());
return adminList;
}
④ 创建 handle,用来处理请求
@Controller
public class TestHandle {
@Autowired
private AdminService adminService;
@RequestMapping("/test/ssm.html")
public String testSsm(ModelMap modelMap){
List<Admin> adminList = adminService.getAll();
modelMap.addAttribute("adminList", adminList);
return "target";
}
}
⑤ 创建跳转成功的页面 target.jsp
<%--
Created by IntelliJ IDEA.
User: zhang
Date: 2022/5/2
Time: 0:50
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>success</h1>
${requestScope.adminList}
</body>
</html>
⑥ 在 atcrowdfunding04-admin-entity 模块中的 Admin 类中生成 toString 方法,否则页面无法输出属性
⑦ 配置启动时的 运行/调试配置,然后启动,点击测试链接
遇到的问题:控制台中日志打印 Did not find handler method for[/test/ssm.html]
解决方法:检查 spring-web-mvc.xml 中配置的扫描包无误,handle 中的路径无误后,构建模块 atcrowdfunding03-admin-component,重新运行,成功
9.3、页面上的 base 标签
9.3.1、注意点
base 标签必须写在 head 标签内部
base 标签必须在所有“带具体路径”的标签的前面
serverName 部分 EL 表达式和 serverPort 部分 EL 表达式之间必须写“:”
serverPort 部分 EL 表达式和 contextPath 部分 EL 表达式之间不能写“/”
原因:contextPath 部分 EL 表达式本身就是“/”开头 , 如果多写一个“/”会干扰 Cookie 的工作机制
serverPort 部分 EL 表达式后面必须写“/
9.3.2、使用
index.jsp
<%--
Created by IntelliJ IDEA.
User: zhang
Date: 2022/5/1
Time: 14:38
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--
base 标签必须写在 head 标签内部
base 标签必须在所有“带具体路径”的标签的前面
serverName 部分 EL 表达式和 serverPort 部分 EL 表达式之间必须写“:”
serverPort 部分 EL 表达式和 contextPath 部分 EL 表达式之间不能写“/”
原因:contextPath 部分 EL 表达式本身就是“/”开头 , 如果多写一个“/”会干扰 Cookie 的工作机制
serverPort 部分 EL 表达式后面必须写“/
--%>
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
</head>
<body>
<a href="test/ssm.html">测试整合ssm环境</a>
</body>
</html>
10、SpringMVC 环境下的 Ajax 请求
10.1、建立意识及注意事项
前端发送过来,后端要处理的请求有两种:
普通请求:后端处理完成后返回页面,浏览器使用使用页面替换整个窗口中的内容
Ajax 请求:后端处理完成后通常返回 JSON 数据,jQuery 代码使用 JSON 数据对页面局部更新
@ResponseBody和@RequestBody要想正常工作必须有jackson的支持,请确认当前环境引入了如下依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
同时必须配置了mvc:annotation-driven
10.2、@RequestBody 注解的使用
10.2.1、引入 jquery
在 index.jsp 的 head 标签中引入 jquery
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
10.2.2、发送数组到服务器端:方案一
index.jsp
$(function(){
$("#btn1").click(function (){
$.ajax({
"url":"send/array/one.html", // 请求目标资源的地址
"type":"post", // 请求方式
"data":{ // 要发送的请求参数
"array":[5,8,12]
},
"dataType":"text", // 如何对待服务器端返回的数据
"success":function (response){ // 服务器端处理请求成功后调用的回调函数,response是响应体数据
alert(response);
},
"error":function (response){ // 服务器端处理请求失败后调用的回调函数,response是响应体数据
alert(response);
}
})
})
})
TestHandle:接收请求参数对应的数组参数需要多加 []
@ResponseBody
@RequestMapping("/send/array/one.html")
public String testReceiveArrayOne(@RequestParam("array[]") List<Integer> array){
for (Integer integer : array) {
System.out.println("number = " + integer);
}
return "success";
}
10.2.3、发送数组到服务器端:方案二
发送数组,但是要用实体类来接收
$(function(){
$("#btn1").click(function (){
$.ajax({
"url":"send/array/two.html", // 请求目标资源的地址
"type":"post", // 请求方式
"data":{ // 要发送的请求参数
"array[0]":5,
"array[1]":8,
"array[2]":12
},
"dataType":"text", // 如何对待服务器端返回的数据
"success":function (response){ // 服务器端处理请求成功后调用的回调函数,response是响应体数据
alert(response);
},
"error":function (response){ // 服务器端处理请求失败后调用的回调函数,response是响应体数据
alert(response);
}
})
})
})
@ResponseBody
@RequestMapping("/send/array/two.html")
public String testReceiveArrayTwo(ParamData paramData){
List<Integer> array = paramData.getParam();
for (Integer integer : array) {
System.out.println("number = " + integer);
}
return "success";
}
10.2.4、发送数组到服务器端:方案三
$(function(){
$("#btn3").click(function (){
// 准备要发送到服务器端的数组
var array = [5,8,12];
// 将JSON数组转换为JSON字符串
var requestBody = JSON.stringify(array);
$.ajax({
"url":"send/array/three.html", // 请求目标资源的地址
"type":"post", // 请求方式
"data":requestBody, // 请求体
"contentType":"application/json;charset=UTF-8", // 设置请求体类型
"dataType":"text", // 如何对待服务器端返回的数据
"success":function (response){ // 服务器端处理请求成功后调用的回调函数,response是响应体数据
alert(response);
},
"error":function (response){ // 服务器端处理请求失败后调用的回调函数,response是响应体数据
alert(response);
}
})
})
})
@ResponseBody
@RequestMapping("/send/array/three.html")
public String testReceiveArrayThree(@RequestBody List<Integer> array){
Logger logger = LoggerFactory.getLogger(TestHandle.class);
for (Integer integer : array) {
logger.info("number = " + integer);
}
return "success";
}
请求体
10.2.5、发送复杂对象
$(function () {
$("#btn3").click(function () {
// 准备要发送的 JSON 数据
var student = {
"stuId": 999,
"stuName": "tom",
"stuAge": 23,
"address": { // 对象
"province": "shandong",
"city": "heze",
"street": "hello"
},
"schoolList": [ // 对象数组
{
"schoolName": "schoolONe",
"schoolSize": 500
}, {
"schoolName": "schoolTwo",
"schoolSize": 1000
}, {
"schoolName": "schoolThree",
"schoolSize": 2000
}
],
"scoreMap": { // map
"yingyu": 100,
"shuxue": 80,
"wuli": 90
}
};
// 将 JSON 对象转换为 JSON 字符串
var requestBdoy = JSON.stringify(student);
// 发送 Ajax 请求
$.ajax({
"url": "save/student.html",
"type": "post",
"data": requestBdoy,
"contentType": "application/json;charset=UTF-8",
"dataType": "text",
"success": function (response) {
console.log(response);
}
})
})
})
10.3、@ResponseBody 注解
让 handler 方法的返回值本身就是当前请求的响应数据。不再参考视图处理器中配置的前后缀信息。
使用注意事项:
-
开启 SpringMVC 的注解驱动 <mvc:annotation-driven/>
-
必须有 jackson 依赖:jackson-core 和 jackson-databind
-
扩展名需要和实际返回的数据格式一致,响应体返回 JSON ,请求扩展名 *.json ,web.xml 中 DispatcherServlet 必须映射 *.json 扩展名
10.4、对 Ajax 请求返回的结果进行规范(返回统一的数据格式)
10.4.1、代码
package com.atguigu.crowd.util;
/**
* @Author zhang
* @Date 2022/5/2 - 19:02
* @Version 1.0
*/
/**
* 整个项目中Ajax请求返回的统一结果(未来也可以用于分布式架构各个模块调用时返回的统一类型)
* @param <T>
*/
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS"; // 成功的常量
public static final String FAILED = "FAILED"; // 失败的常量
// 用来封装当前请求处理的结果是成功还是失败
private String result;
// 请求处理失败时返回的错误信息
private String message;
// 要返回的数据
private T data;
/**
* 请求处理成功且不需要返回数据时使用的数据方法
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> successWithoutData(){
return new ResultEntity<Type>(ResultEntity.SUCCESS, null, null);
}
/**
* 请求处理成功且需要返回数据时使用的数据方法
* @param data 要返回的数据
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> successWithData(Type data){
return new ResultEntity<Type>(ResultEntity.SUCCESS, null, data);
}
/**
* 请求处理失败后使用的工具方法
* @param message 失败的错误消息
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> failed(String message){
return new ResultEntity<Type>(ResultEntity.FAILED, message, null);
}
public ResultEntity() {
}
public ResultEntity(String result, String message, T date) {
this.result = result;
this.message = message;
this.data = date;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "ResultEntity{" +
"result='" + result + '\'' +
", message='" + message + '\'' +
", date=" + data +
'}';
}
}
10.4.2、测试及注意事项
注意:
-
"dataType"要设置为"json",不能为"text"
-
"url"要以 ".json" 结尾
$(function () {
$("#btn3").click(function () {
// 准备要发送到服务器端的数组
var array = [5, 8, 12];
// 将JSON数组转换为JSON字符串
var requestBody = JSON.stringify(array);
$.ajax({
"url": "send/array/three.json", // 请求目标资源的地址
"type": "post", // 请求方式
"data": requestBody, // 请求体
"contentType": "application/json;charset=UTF-8", // 设置请求体类型
"dataType": "json", // 如何对待服务器端返回的数据
"success": function (response) { // 服务器端处理请求成功后调用的回调函数,response是响应体数据
console.log(response);
},
"error": function (response) { // 服务器端处理请求失败后调用的回调函数,response是响应体数据
console.log(response);
}
})
})
})
@ResponseBody
@RequestMapping("/send/array/three.json")
public ResultEntity<List<Integer>> testReceiveArrayThree(@RequestBody List<Integer> array){
Logger logger = LoggerFactory.getLogger(TestHandle.class);
for (Integer integer : array) {
logger.info("number = " + integer);
}
return ResultEntity.successWithData(array);
}
11、异常映射
11.1、目标
使用异常映射机制将整个项目的异常和错误提示进行统一管理。
11.2、思路
注:SpringMVC 提供了基于 XML 和 基于注解的异常映射方式,但是不能随意任选,若请求是经过 @RequesrMapping 抛出的,则需要基于注解的映射处来处理,若是经过 xml 中 mvc:view-controller 抛出的,则需要基于 XML 的异常映射来处理
11.3、判断请求类型的工具方法
11.3.1、判断依据
11.3.2、创建工具类
① 首先导入依赖
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
② 然后创建工具类
11.4、异常映射实现方式
11.4.1、基于 XML
① 创建错误页面
<%--
Created by IntelliJ IDEA.
User: zhang
Date: 2022/5/2
Time: 21:15
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>出错了!</h1>
<%-- 从请求域获取Exception对象,再进一步访问message属性就能显示错误信息 --%>
${requestScope.exception.message}
</body>
</html>
② 配置基于XML的异常映射
<!-- 配置基于XML的异常映射 -->
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 配置异常类型和具体视图页面的对应关系 -->
<property name="exceptionMappings">
<props>
<!-- key属性指定异常的全类名 标签体中写对应的视图(去掉前后缀) -->
<prop key="java.lang.Exception">system-error</prop>
</props>
</property>
</bean>
③ 测试
在下图方法中添加异常
访问对应的请求,成功跳转到错误页面
11.4.2、创建常量类
package com.atguigu.crowd.constant;
/**
* @Author zhang
* @Date 2022/5/2 - 23:03
* @Version 1.0
*/
/**
* 常量类
*/
public class CrowdConstant {
public static final String ATTR_NAME_EXCEPTION = "exception";
public static final String MESSAGE_LOGIN_FAILED = "抱歉,账号或密码错误,请重新输入!";
public static final String MESSAGE_LOGIN_ACCT_ALREADY_IN_USE = "抱歉,该账号已被注册!";
public static final String MESSAGE_ACCESS_FORBIDDEN = "请先登录后再访问";
}
11.4.3、基于注解的异常映射
package com.atguigu.crowd.mvc.config;
import com.atguigu.crowd.constant.CrowdConstant;
import com.atguigu.crowd.util.CrowdUtil;
import com.atguigu.crowd.util.ResultEntity;
import com.google.gson.Gson;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author zhang
* @Date 2022/5/2 - 22:00
* @Version 1.0
*/
// @ControllerAdvice表示当前类是一个基于注解的异常处理器类
@ControllerAdvice
public class CrowdExceptionResolver {
/**
*
* @param viewName 异常处理完成后要去的页面
* @param exception 实际捕获到的异常类型
* @param request 当前请求对象
* @param response 当前响应对象
* @return
* @throws IOException
*/
// @ExceptionHandler将一个具体的异常类型和一个方法关联起来
private ModelAndView commonResolve(
String viewName,
Exception exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 1.判断当前请求类型
boolean judgeResult = CrowdUtil.judgeRequestType(request);
// 2.如果是Ajax请求
if(judgeResult) {
// 3.创建ResultEntity对象
ResultEntity<Object> resultEntity = ResultEntity.failed(exception.getMessage());
// 4.创建Gson对象
Gson gson = new Gson();
// 5.将ResultEntity对象转换为JSON字符串
String json = gson.toJson(resultEntity);
// 6.将JSON字符串作为响应体返回给浏览器
response.getWriter().write(json);
// 7.由于上面已经通过原生的response对象返回了响应,所以不提供ModelAndView对象
return null;
}
// 8.如果不是Ajax请求则创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
// 9.将Exception对象存入模型
modelAndView.addObject(CrowdConstant.ATTR_NAME_EXCEPTION, exception);
// 10.设置对应的视图名称
modelAndView.setViewName(viewName);
// 11.返回modelAndView对象
return modelAndView;
}
/**
*
* @param exception 捕获到的异常类型
* @param request 当前请求对象
* @param response 当前响应对象
* @return
*/
// @ExceptionHandler将一个具体的异常类型和一个方法关联起来
@ExceptionHandler(value = NullPointerException.class)
public ModelAndView resolveNullPointerException(NullPointerException exception, HttpServletRequest request, HttpServletResponse response) throws IOException {
String viewName = "system-error";
return commonResolve(viewName, exception,request,response);
}
}
12、前端页面
12.1、静态资源引入
12.2、初步显示管理员登录页面
12.2.1、创建 admin-login.jsp 页面
从模型中复制内容进来,注意保留最上面的
然后做一些修改
字符集、base标签、引入jquery部分可以不用修改(放在后面可以先加载页面的内容,让客户优先看到页面,然后再加载jquery的方法)
对表单做一些修改
12.2.2、跳转到登录页面
配置 view-controller
<!-- 配置view-controller,直接把请求地址和视图名称关联起来 -->
<mvc:view-controller path="/admin/to/login/page.html" view-name="admin-login"></mvc:view-controller>
12.3、layer 弹层组件
12.3.1、加入 layer 环境
将下图 layer 文件夹复制到指定位置
12.3.2、在页面引入 layer 环境
注意顺序,需要在引入 jquery 后引入
<script type="text/javascript" src="layer/layer.js"></script>
12.3.3、使用 layer 弹层提示框
<button id="btn5">点我弹框</button>
$(function (){
$("#btn5").click(function (){
// alert("aaa");
layer.msg("Layer的弹框");
});
});
13、调整错误页面
<%--
Created by IntelliJ IDEA.
User: zhang
Date: 2022/5/2
Time: 23:48
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function (){
$("button").click(function (){
// 相当于浏览器的后退按钮
window.history.back();
});
});
</script>
<style>
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<div><a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a></div>
</div>
</div>
</nav>
<div class="container">
<h2 class="form-signin-heading" style="text-align: center">
<i class="glyphicon glyphicon-log-in"></i>尚筹网系统消息
</h2>
<%--
requestScope对应的是存放request域数据的map
requestScope.exception相当于request.getAttribute("exception")
requestScope.exception.message相当于exception.getMessage()
--%>
<h3 style="text-align: center">${requestScope.exception.message}</h3>
<button style="width: 120px;margin: 50px auto 0px auto" class="btn btn-lg btn-success btn-block">返回上一步</button>
</div>
</body>
</html>