从单一架构阶段到分布式架构阶段的过渡。后台管理员系统使用单一架构ssm开发。前台会员系统使用分布式架构SpringBoot+SpringCloud开发。
前置要求:Spring、SpringMVC、MyBatis、Maven
第三方接口:短信、蚂蚁金服、OSS对象存储服务
中间件:Redis、SpringSession
目的:从学习具体技术的语法转变为思考如何实现业务功能需求。
1.项目架构
2.环境搭建总体目标
3.创建工程,搭建环境
3.1项目架构图
3.2工程创建计划
#at代表尚硅谷,crowd众筹的众,funding众筹的筹,连起来就是尚硅谷的众筹的项目:尚筹网
#admin代表后台管理员系统,parent代表父工程
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
3.3工程结构图
3.4建立工程之间的依赖关系
#工程:atcrowdfunding02-admin-webui中的依赖
<dependencies>
<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding03-admin-component</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
#工程:atcrowdfunding03-admin-component中的依赖
<dependencies>
<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding04-admin-entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding05-common-util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
4.创建数据库和数据库表
4.1物理建模
①.数据库建表的原则:(三范式)
- 第一范式:数据库表中的每一列都不可再分,也就是原子性
- 第二范式:在满足第一范式基础上要求每个字段都和主键完整相关,而不是仅和主键部分相关(主要针对联合主键而言)
- 第三范式:表中的非主键字段和主键字段直接相关,不允许间接相关
4.2创建数据库project_crowd以及管理员数据库表 t_admin
CREATE DATABASE `project_crowd` CHARACTER SET utf8;
use project_crowd;
drop table if exists t_admin;
create table t_admin
(
id int not null auto_increment, # 主键
login_acct varchar(255) not null, # 登录账号
user_pswd char(32) not null, # 登录密码
user_name varchar(255) not null, # 昵称
email varchar(255) not null, # 邮件地址
create_time char(19), # 创建时间
primary key (id)
);
5.基于Maven的Mybatis逆向工程
①在reverse中配置pom
<dependencies>
<!--Mybatis依赖的核心包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</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>8.0.28</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
②在Resource下创建generatorConfig.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
mybatis-generator:generate
targetRuntime: 用于选择执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="atguiguTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是;false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/project_crowd?serverTimezone=UTC"
userId="root"
password="abc123">
</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>
<!-- SQL映射文件的生成策略,targetProject:XxxMapper.xml 映射文件生成的路径 -->
<sqlMapGenerator targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.mapper">
<!-- enableSubPackages:是否让 schema 作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略,targetPackage:Mapper 接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.mapper">
<!-- enableSubPackages:是否让 schema 作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_admin" domainObjectName="Admin" />
</context>
</generatorConfiguration>
③.执行逆向生成操作的 Maven 命令:mybatis-generator:generate
④.逆向工程生成的资源各归各位
6.父工程依赖管理
<dependencyManagement>
<dependencies>
<!-- Spring 依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.17</version>
</dependency>
<!--Spring-->
<!--Spring的AOP方面相关的依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- 数据库依赖 -->
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MyBatis 与 Spring 整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- MyBatis 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.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>
<!-- 其他日志框架的中间转换包 -->
<!--使用jcl-over-slf4j,把spring自带的日志包Commons-logging转换成slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<!--使用jcl-over-slf4j,把jdk自带的日志包java.util.logging转换成slf4j-->
<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>
<!-- 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>
<!-- junit 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.4.5</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.4.5</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.4.5</version>
</dependency>
</dependencies>
</dependencyManagement>
#SpringMVC 需要 jackson 的支持,来处理 JSON 数据。但是 SpringMVC 并没有依赖 jackson。所以需要我们自己保证 jar 包之间的兼容性。
7.Spring整合Mybatis
7.1思路
7.2操作清单
- 在子工程中加入搭建环境所需要的具体的依赖
- 准备jdbc.properties
- 创建Spring配置文件专门配置Spring和Mybatis整合相关
- 在Spring的配置文件中加载jdbc.properties属性文件
- 配置数据源
- 测试从数据源中获取数据库连接
- 配置SqlSessionFactoryBean(动态的得到Mapper接口对应的类)
- 装配数据源
- 指定XxxMapper.xml配置文件的位置
- 指定Mybatis全局配置文件的位置(可选)
- 配置MapperScannerConfigurer
- 测试是否可以装配XxxMapper接口并通过这个接口操作数据库
7.3操作步骤(配置文件统一放在webui下的Resource下)
①在子工程中加入搭建环境所需的具体的依赖,子工程选择compoment工程
<dependencies>
<!-- Spring 依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!--Spring-->
<!--Spring的AOP方面相关的依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</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>
<!-- 其他日志框架的中间转换包 -->
<!--使用jcl-over-slf4j,把spring自带的日志包Commons-logging转换成slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!--使用jcl-over-slf4j,把jdk自带的日志包java.util.logging转换成slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-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>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
②.准备jdbc.properties,在webui工程的Resource下创建
jdbc.user=root
jdbc.password=abc123
jdbc.url=jdbc:mysql://localhost:3306/project_crowd?serverTimezone=UTC&?useUnicode=true&characterEncoding=UTF-8
jdbc.driver=com.mysql.cj.jdbc.Driver
③.创建Spring配置文件spring-persist-mybatis.xml(专门配置Spring和Mybatis整合相关)
- 在Spring的配置文件中加载jdbc.properties属性文件
- 配置数据源
- 配置SqlSessionFactoryBean(可动态的得到Mapper接口对应的类)
- 装配数据源
- 指定XxxMapper.xml配置文件的位置
- 指定Mybatis全局配置文件的位置(可选)
- 配置MapperScannerConfigurer
<?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.xsd">
<!--引入外部属性文件,加载 jdbc.properties-->
<context:property-placeholder location ="classpath:jdbc.properties"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置SqlSessionFactoryBean整合Mybatis-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定mybatis全局配置文件的位置-->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<!--指定XxxMapper.xml配置文件的位置-->
<property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"/>
<!--装配数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置MapperScannerConfigurer来扫描Mapper接口所在的包-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.crowd.mapper"/>
</bean>
</beans>
④.测试从数据源中获取数据库连接,测试是否可以装配XxxMapper接口并通过该接口操作数据库
创建 Spring 的 Junit 测试类
// 指定 Spring 给 Junit 提供的运行器类
@RunWith(SpringJUnit4ClassRunner.class)
// 加载 Spring 配置文件的注解
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml"})
public class CrowdSpringTest {
@Autowired
private DataSource dataSource;
@Autowired
private AdminMapper adminMapper;
@Test
public void testInsertAdmin(){
Admin admin = new Admin(null, "tom", "123123", "汤姆", "tom@qq.com", null);
int insert = adminMapper.insert(admin);
System.out.println("受影响的行数:"+insert);
}
@Test
public void testDataSource() throws SQLException {
// 1.通过数据源对象获取数据源连接
Connection connection = dataSource.getConnection();
// 2.打印数据库连接
System.out.println(connection);
}
}
#注:为了能够在webui工程中执行Junit,需要把spring-test和junit依赖转移到webui这个工程,score是test
8.日志系统
如果在实际开发中,所有想查看数值的地方都使用sysout方式打印,会给项目上线运行带来问题! sysout本质上是一个IO操作,通常IO的操作是比较消耗性能的,如果项目中sysout很多,那么对性能的影响就比较大了。即使上线前专门花时间删除代码中的sysout,也可能有遗漏,而且很繁琐,而如果使用日志系统,那么通过日志级别就可以批量的控制信息的打印。
①引入slf4j+logback的日志依赖:
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<!--使用jcl-over-slf4j,把spring自带的日志包Commons-logging转换成slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!--使用jcl-over-slf4j,把jdk自带的日志包java.util.logging转换成slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
②logback 配置文件,logback工作时的具体细节可以通过 logback.xml 来配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="INFO">
<!-- 指定打印日志的 appender,这里通过“STDOUT”引用了前面配置的 appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
9.声明式事务
事务:声明式事务和编程式事务
9.1编程式事务:
try{
//开启事务(关闭自动提交)
connection.setAutoCommit(false);
//核心操作
adminService.saveAdmin(admin);
//提交事务
connection.commit();
}catch(Exception e){
//回滚事务
connection.rollBack();
}finally{
//释放数据库连接
connection.close();
}
9.2声明式事务:在框架环境下通过一系列的配置由spring来管理事务操作,然后让我们写的代码能够享受框架提供的服务。
9.3配置的思路
9.4代码步骤(可以配置到上面的spring配置文件中,也可以新建一个,为了防止过长,我们新建)
- 配置事务管理器
- 配置aop
- 配置事务属性
<?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="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--装配数据源,报错不影响,因为另一个配置文件中有数据源,且会加载到IOC容器中-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务的属性-->
<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:默认值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则开自己的事务。如果已经有事务,就使用这个已有的事务。
顾虑:用别人的事务有可能“被”回滚
REQUIRES_NEW:建议使用的值,表示当前方法必须工作在事务中,且必须启动新事务,并在他自己的事务中运行,如果有事务在运行,就挂起。
好处:不会受到其他事务回滚的影响
rollback-for属性:配置事务方法针对什么样的异常进行回滚
默认:运行时异常回滚
建议:编译时异常和运行时异常都回滚——java.lang.Exception
-->
<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>
<!--配置切入点和切面-->
<aop:config>
<!--配置切入点表达式,就是要声明事务的方法-->
<aop:pointcut id="txPointcut" expression="execution(* *..*ServiceImpl.*(..))"/>
<!--切面:将切入点表达式和事务通知关联起来-->
<aop:advisor advice-ref="" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
9.5测试
@Service
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Override
public void saveAdmin(Admin admin) {
adminMapper.insert(admin);
throw new RuntimeException();
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml","classpath:spring-persist-tx.xml"})
public class CrowdTest {
@Autowired
private AdminService adminService;
@Test
public void testTx(){
Admin admin = new Admin(null, "jerry", "123456", "杰瑞", "jerry@qq.com", null);
adminService.saveAdmin(admin);
}
}
注意:在基于xml的声明式事务中,事务属性的tx:method是必须配置的,如果某个方法没有配置对应的tx:method,那么事务对这个方法就不生效。
10.表述层
10.1表述层工作机制(web.xml和spring配置文件关系)
- spring的ioc容器中有:service(带事务)、mapper
- springmvc的ioc容器中有:controller、interceptor(拦截器)、异常映射机制
- 装配的关系:service装配mapper;controller装配service
10.2表述层环境搭建
- 目标一:controller中装配service
- 目标二:页面能够访问到controller
- 页面→controller(@RequestMapping)→service→mapper→数据库
①.web.xml配置文件
- 配置ContextLoaderListener加载Spring的配置文件:spring-persist-mybatis.xml和spring-persist-tx.xml,根据 Spring 的配置文件初始化 IOC 容器
- 配置CharacterEncodingFilter,解决 POST 请求的字符乱码问题。需要注意的是:在 web.xml 中存在多个 Filter时,让这个 Filter 作为过滤器链中的第一个 Filter。
- 配置HiddenHttpMethodFilter,遵循 RESTFUL 风格将 POST 请求转换为 PUT 请求、DELETE 请求时使用。
- 配置前端控制器DispatcherServlet
<?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的配置文件:spring-persist-mybatis.xml和spring-persist-tx.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-persist-*.xml</param-value>
</context-param>
<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>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置HiddenHttpMethodFilter编码过滤器,遵循RESTFUL风格将POST请求转换为PUT请求、DELETE请求时使用-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--注册前端控制器DispatcherServlet-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web-mvc.xml</param-value>
</init-param>
<!--
servlet默认生命周期中,创建对象是在第一次接收到请求时,而dispatcherServlet作为框架的核心组件,
在启动过程中有大量的初始化操作要做,而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
设置springMVC的核心控制器所能处理的请求的请求路径:
/所匹配的请求可以是/login或.html或.js或.css方式的请求路径
但是/不能匹配.jsp请求路径的请求
-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
<!-- <url-pattern>*.html</url-pattern>;使用此方式,在spring-web-mvc中就不需要配置default-servlet-handler了-->
<!-- <url-pattern>*.json</url-pattern>-->
</servlet-mapping>
</web-app>
②.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: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.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--组件扫描,自动扫描包-->
<context:component-scan base-package="com.atguigu.crowd.mvc"/>
<!--开启mvc注解驱动 -->
<mvc:annotation-driven/>
<!--配置Thymeleaf视图解析器-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<!--设置视图解析器的优先级-->
<property name="order" value="1"/>
<!--视图解析器所用的编码-->
<property name="characterEncoding" value="UTF-8"/>
<!--视图解析器的模板-->
<property name="templateEngine">
<!--内部bean,为templateEngine赋值-->
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<!--内部bean,为templateResolver赋值-->
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<!--模板的模型:HTML5-->
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--
处理静态资源,例如html、js、css、jpg
若只设置该标签,则只能访问静态资源,其他请求则无法访问
此时必须设置<mvc:annotation-driven/>解决问题
-->
<mvc:default-servlet-handler/>
</beans>
③.引入依赖
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
11.SpringMVC 环境下的 Ajax 请求
- 前端发送过来,后端要处理的请求有两种:
- 普通请求:后端处理完成后返回 页面,浏览器使用使用页面替换整个窗口中的内容
- Ajax 请求:后端处理完后通常返回 JSON 数据,jQuery 代码使用 JSON 数据对页面局部更新
常用注解:@RequestBody和@ResponseBody
12.异常映射
①.基于xml的异常机制
<!--配置基于XML的异常机制-->
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--配置异常的类型和具体视图页面的对应关系-->
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面,这个值要拼前后缀得到具体的路径
-->
<prop key="java.lang.Exception">system-error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息存储到请求域中,键为ex,异常信息为值
-->
<property name="exceptionAttribute" value="ex"/>
</bean>
②.判断请求类型的工具方法
//判断请求类型的工具类
public class CrowdUtil {
public static boolean judgeRequestType(HttpServletRequest request){
//请求类型(普通请求或者是Ajax请求)的判断依据是:
//1.获取请求消息头
String acceptHeader = request.getHeader("Accept");
String xRequestHeader = request.getHeader("X-Requested-With");
//2.判断
if(
(acceptHeader != null && acceptHeader.contains("application/json"))
||
(xRequestHeader != null && xRequestHeader.equals("XMLHttpRequest"))
){
//Ajax请求
return true;
}
return false; //普通请求
}
}
③.基于注解的异常机制
//@ControllerAdvice将当前类标识为一个基于注解的异常处理器类
@ControllerAdvice
public class CrowdExceptionResolver {
//把异常类型和方法关联起来
@ExceptionHandler(NullPointerException.class)
public ModelAndView resolverNullPointerException(
//1.实际捕获到的异常类型
Exception exception,
//当前请求的对象
HttpServletRequest request,
HttpServletResponse response) throws IOException {
//1.判断请求类型
boolean judgeRequestType = CrowdUtil.judgeRequestType(request);
//2.如果是ajax请求
if(judgeRequestType){
//3.获取异常信息
String message = exception.getMessage();
//4.创建Gson对象
Gson gson = new Gson();
//5.将异常信息转换为Json字符串
String json = gson.toJson(message);
//6.将JSON字符串作为响应体返回给浏览器
response.getWriter().write(json);
//7.由于上面已经通过原生的response对象返回了响应,所以不提供ModelAndView对象
return null;
}
//8.如果不是Ajax请求则创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//9.将Exception对象存入模型
modelAndView.addObject("ex",exception);
//10.设置对应的视图
modelAndView.setViewName("system-error");
return modelAndView;
}
}
13.layer弹层组件
①.加入layer库文件和样式文件
②在页面上引入layer环境
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="layer/layer.js"></script>
③.layer的使用:弹层提示框
layer.msg("这里是 layer 弹出的消息!");