atcrowdfunding01-admin-parent
atcrowdfunding02-admin-webui
atcrowdfunding03-admin-component
atcrowdfunding04-admin-entity
atcrowdfunding05-common-util
atcrowdfunding06-common-reverse
创建工程时参与继承、聚合的工程以“Maven module”的方式创建,继承和聚合 可以自动配置出来。具体做法是创建 parent 工程后,在 parent 工程上点右键,new→ Maven module
同时继承和聚合得到了自动化配置。
webui 依赖 component
component 依赖 entity
component 依赖 util
3.1.1 理论
CREATE DATABASE project_crowd CHARACTER SET utf8;
CREATE TABLE `t_admin` (
`id` int(11) 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.1 atcrowdfunding06-common-reverse工程的pom 配置
<!-- 依赖 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 在atcrowdfunding06-common-reverse的resources目录下的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 -->
<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?characterEncoding=utf8"
userId="root"
password="root">
</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 在atcrowdfunding06-common-reverse点击界面右边的Maven—>Plugins—>mybatis-generator下的mybatis-generator:generate命令,代码生成,并在Admin.java文件添加无参构造、有参构造、tostring方法
4.4 逆向工程生成的资源各归各位
WebUI 工程将来在 Tomcat 上运行时,现在 resources 目录下的资源会直接放在 WEB-INF/classes 目录(也就是类路径)下,所以放在 resources 目录下运行的时候更容 易找到
并在atcrowdfunding03-admin-component的pom下添加依赖包
<!-- 依赖 MyBatis 核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<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>
<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>
<!--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>
adminMapper 通过 IOC 容器装配到当前组件中后,就可以直接调用它的方法,享受 到框架给我们提供的方便
在子工程中加入搭建环境所需要的具体依赖
准备 jdbc.properties
创建 Spring 配置文件专门配置 Spring 和 MyBatis 整合相关
在 Spring 的配置文件中加载 jdbc.properties 属性文件
配置数据源
测试从数据源中获取数据库连接
配置 SqlSessionFactoryBean
配置 MapperScannerConfigurer
测试是否可以装配 XxxMapper 接口并通过这个接口操作数据库
6.4.1 在子工程中加入搭建环境所需的具体依赖
子工程:选择 component 工程。原因是具体依赖和 component 工程相关。
<!--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> <!--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> <!--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>
<!--JSTL 标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency> <!--https://mvnrepository.com/artifact/com.google.code.gson/gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/project_crowd?useUnicode=true&characterEncoding=UTF-8
jdbc.driver=com.mysql.jdbc.Driver
<?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.4.4 创建 spring-persist-mybatis.xml
6.4.5 Spring 具体配置:第一步 配置数据源
6.4.6 Spring 具体配置:第二步 配置 SqlSessionFactoryBean
6.4.7 Spring 具体配置:第三步 配置 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-4.3.xsd">
<!-- 加载外部属性文件 -->
<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}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
<!-- 配置SqlSessionFactoryBean整合MyBatis -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定MyBatis全局配置文件位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 指定Mapper.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>
创建 Spring 的 Junit 测试类
atcrowdfunding02-admin-webui的pom添加依赖
<!-- junit测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
// 指定 Spring 给 Junit 提供的运行器类
// 指定 Spring 给 Junit 提供的运行器类
@RunWith(SpringJUnit4ClassRunner.class)
// 加载 Spring 配置文件的注解
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml"})
public class CrowdTest {
@Autowired
private DataSource dataSource;
@Autowired
private AdminMapper adminMapper;
@Test
public void testDataSource() throws SQLException {
//1.通过数据源对象获取数据源连接
Connection connection = dataSource.getConnection();
//2.打印数据库连接
System.out.println(connection);
}
@Test
public void testInsertAdmin() {
Admin admin = new Admin(null, "tom", "123123", "汤姆", "[email protected]", null);
int count = adminMapper.insert(admin);
// 如果在实际开发中,所有想查看数值的地方都使用sysout方式打印,会给项目上线运行带来问题!
// sysout本质上是一个IO操作,通常IO的操作是比较消耗性能的。如果项目中sysout很多,那么对性能的影响就比较大了。
// 即使上线前专门花时间删除代码中的sysout,也很可能有遗漏,而且非常麻烦。
// 而如果使用日志系统,那么通过日志级别就可以批量的控制信息的打印。
System.out.println("受影响的行数="+count);
}
}
系统在运行过程中出了问题就需要通过日志来进行排查,所以我们在上手任何新技 术的时候,都要习惯性的关注一下它是如何打印日志的。
7.3.1 初始状态
Spring 使用 commons-logging 日志包。打印的日志是下面这样的。不用细看, 截图放在这是为了和后面日志打印的情况对比。
7.3.2 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.3.3 我们主动打印的日志
把查询到的 Admin 对象以日志的方式打印出来,代码如下:
@Test
public void testLog() {
Admin admin=adminMapper.selectByPrimaryKey(1511);
// 获取日志记录对象
Logger logger=LoggerFactory.getLogger(CrowdTest.class);
// 按照 Debug 级别打印日志
logger.debug(admin.toString());
// 2.根据不同日志级别打印日志
logger.debug("Hello I am Debug level!!!");
logger.debug("Hello I am Debug level!!!");
logger.debug("Hello I am Debug level!!!");
logger.info("Info level!!!");
logger.info("Info level!!!");
logger.info("Info level!!!");
logger.warn("Warn level!!!");
logger.warn("Warn level!!!");
logger.warn("Warn level!!!");
logger.error("Error level!!!");
logger.error("Error level!!!");
logger.error("Error level!!!");
}
使用日志打印信息和使用 sysout 打印信息的区别: sysout 如果不删除,那么 执行到这里必然会打印;如果使用日志方式打印,可以通过日志级别控制信息是否打印
效果
这里我们看到除了 Druid 数据源打印了两条日志,Spring 和 MyBatis 并没有通 过 slf4j 打印日志。所以下一步我们要考虑的就是如何将框架所使用的日志系统统 一到 slf4j。
7.3.4 更换框架的日志系统
<!-- junit测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency> -->
打印效果局部:
7.3.5 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="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
从事务角度:一个事务方法中包含的多个数据库操作,要么一起提交、要么一起回 滚。也就是说事务方法中的多个数据库操作,有任何一个失败,整个事务全部回滚。
从声明式角度:由 Spring 来全面接管数据库事务。用声明式代替编程式。
8.3.1 atcrowdfunding03-admin-component加入 AOP 依赖包
<!--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>
8.3.2 第一步:创建 Spring 配置文件
8.3.3 第二步:配置事务管理器
8.3.4 第三步:配置 AOP
8.3.5 第四步:配置事务属性
<?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:jdbc="http://www.springframework.org/schema/jdbc"
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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<!-- 配置自动扫描的包:主要是为了把Service扫描到IOC容器中 -->
<context:component-scan base-package="com.atguigu.crowd.service"/>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 装配数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务切面 -->
<aop:config>
<!-- 考虑到后面我们整合SpringSecurity,避免把UserDetailsService加入事务控制,让切入点表达式定位到ServiceImpl -->
<aop:pointcut expression="execution(* *..*ServiceImpl.*(..))" id="txPointcut"/>
<!-- 将切入点表达式和事务通知关联起来 -->
<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:默认值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务。如果已经有了,那么就使用这个已有的事务。
顾虑:用别人的事务有可能“被”回滚。
REQUIRES_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>
<jdbc:embedded-database id="dataSource"/>
</beans>
在CrowdTest中添加测试类方法testTx
public interface AdminService {
void saveAdmin(Admin admin);
}
@Service
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
public void saveAdmin(Admin admin) {
adminMapper.insert(admin);
// throw new RuntimeException();
}
}
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml","classpath:spring-persist-tx.xml"})
@Autowired
AdminService adminService;
@Test
public void testTx() {
Admin admin = new Admin(null, "jerry", "123456", "杰瑞", "[email protected]", null);
adminService.saveAdmin(admin);
}
使用 SpringMVC 环境引入 spring-webmvc 依赖即可,同时可以把 spring-context 依赖 去掉,因为根据依赖的传递性,spring-webmvc 会依赖 spring-context。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
10.2.1ContextLoaderListener
作用:加载 Spring 的配置文件,根据 Spring 的配置文件初始化 IOC 容器。
10.2.2CharacterEncodingFilter
解决 POST 请求的字符乱码问题。需要注意的是:在 web.xml 中存在多个 Filter 时,让这个 Filter 作为过滤器链中的第一个 Filter。
request.setCharacterEncoding(encoding) 要求必须在所有request.getParameter(xxx)操作前面
response.setCharacterEncoding(encoding)要求必须在所有 response.getWriter() 操作前面
不满足这个顺序要求字符集设定无法生效
10.3.1*.html 扩展名
举例
http://localhost:8080/atcrowdfunding02-admin-webui/save/emp.html
作用:伪静态
表面上看起来是一个访问静态资源的请求,但是实际上是由 SpringMVC 交给 handler 来处理的动态资源。
缺陷:不符合 RESTFUL 风格
10.3.2*.json 扩展名
描述问题
请求扩展名 http://localhost:8080/extra01-ajax/get/emp/by/ajax.html
服务器端打算返回的数据:JSON 格式 二者不匹配!!!
分析问题
请求扩展名和响应体的数据格式不匹配!!!
解决问题
让请求扩展名和预计的响应体数据格式一致。
http://localhost:8080/extra01-ajax/get/emp/by/ajax.json
同时让 SpringMVC 在映射*.html 扩展名之外再映射*.json 扩展名,不然会 返回 404
10.4.1导入名称空间
context
mvc
10.4.2创建 SpringMVC 扫描的包
在atcrowdfunding03-admin-component创建com.atguigu.crowd.mvc
10.4.3具体配置
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
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">
<!-- 配置自动扫描的包:扫描handler -->
<context:component-scan base-package="com.atguigu.crowd.mvc"/>
<!-- 配置SpringMVC的注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置基于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>
</beans>
10.5.1在atcrowdfunding02-admin-webui加入依赖
<!-- 引入Servlet容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- JSP页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>
10.5.2作用
将页面上路径中的${pageContext.request.contextPath}部分提取到页面开头
10.5.3写法
<base href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/"/>
10.5.4需要注意的点
base 标签必须写在 head 标签内部
base 标签必须在所有“带具体路径”的标签的前面
serverName 部分 EL 表达式和 serverPort 部分 EL 表达式之间必须写“:”
serverPort部分EL表达式和contextPath部分EL表达式之间绝对不能写“/”
serverPort 部分 EL 表达式后面必须写“/”
**10.6.1在atcrowdfunding03-admin-component创建包com.atguigu.crowd.mvc.handler类TestHandler **
@Controller
public class TestHandler {
@Autowired
private AdminService adminService;
@RequestMapping("/test/ssm.html")
public String testSsm(ModelMap modelMap) {
List<Admin> adminList = adminService.getAll();
modelMap.addAttribute("adminList", adminList);
return "target";
}
}
AdminService添加方法
List<Admin> getAll();
AdminServiceImpl实现
public List<Admin> getAll() {
return adminMapper.selectByExample(new AdminExample());
}
10.6.3在atcrowdfunding02-admin-webui创建目标 JSP 页面(视图)
创建index.jsp和targer.jsp页面
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- http://localhost:8080/atcrowdfunding02-admin-webui/test/ssm.html -->
<base href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/"/>
</head>
<body>
<a href="test/ssm.html">测试SSM整合环境</a>
</body>
</html>
targer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Success</h1>
${requestScope.adminList }
</body>
</html>
启动测试
10.6.4友情提示
跟着视频学习项目开发,表面上的现象错综复杂, 一会儿在页面,一会儿写 jQuery,一会儿写 handler,一会儿写 Service,一会儿写 SQL,一会儿写配置文件。
背后有思路作为一根红线把所有的现象都穿起来,跟着思路走,就不会迷路。
思路的背后是目标。
思维一定要有层次!! !
前端发送过来,后端要处理的请求有两种:
index.jsp中引入jquery
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
11.3.1作用
让 handler 方法的返回值本身就是当前请求的响应数据。不再参考视图处理器 中配置的前后缀信息。
11.3.2注意
<mvc:annotation-driven/>
11.4.1场景设定
在index.jsp中添加script
<script type="text/javascript">
$(function(){
});
</script>
jQuery 通过 Ajax 请求给服务器端发送一个数组:[5,8,12]
11.4.2尝试方案一
在中添加按钮
<br/>
<br/>
<button id="btn1">Send [5,8,12] One</button>
在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);
}
});
在TestHandler添加方法,测试
@ResponseBody
@RequestMapping("/send/array/one.html")
public String testReceiveArrayOne(@RequestParam("array[]") List<Integer> array) {
for (Integer number : array) {
System.out.println("number="+number);
}
return "success";
}
11.4.3尝试方案二
前端发送数据
在中添加按钮
<br/>
<br/>
<button id="btn2">Send [5,8,12] Two</button>
在function内添加请求
$("#btn2").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);
}
});
});
public class ParamData {
private List<Integer> array;
public ParamData() {
}
public ParamData(List<Integer> array) {
super();
this.array = array;
}
@Override
public String toString() {
return "ParamData [array=" + array + "]";
}
public List<Integer> getArray() {
return array;
}
public void setArray(List<Integer> array) {
this.array = array;
}
}
在TestHandler添加方法,测试
@ResponseBody
@RequestMapping("/send/array/two.html")
public String testReceiveArrayTwo(ParamData paramData) {
List<Integer> array = paramData.getArray();
for (Integer number : array) {
System.out.println("number="+number);
}
return "success";
}
11.4.3尝试方案三
前端发送数据
在中添加按钮
<br/>
<br/>
<button id="btn3">Send [5,8,12] Three</button>
在function内添加请求
$("#btn3").click(function(){
// 准备好要发送到服务器端的数组
var array = [5, 8, 12];
console.log(array.length);
// 将JSON数组转换为JSON字符串
var requestBody = JSON.stringify(array);
// "['5','8','12']"
console.log(requestBody.length);
$.ajax({
"url": "send/array/three.html", // 请求目标资源的地址
"type": "post", // 请求方式
"data": requestBody, // 请求体
"contentType": "application/json;charset=UTF-8", // 设置请求体的内容类型,告诉服务器端本次请求的请求体是JSON数据
"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) {
for (Integer number : array) {
logger.info("number="+number);
}
return "success";
}
11.4.6发送复杂对象举例
在中添加按钮
<br/>
<br/>
<button id="btn4">Send Compose Object</button>
在function内添加请求
$("#btn4").click(function(){
// 准备要发送的数据
var student = {
"stuId": 5,
"stuName":"tom",
"address": {
"province": "广东",
"city": "深圳",
"street":"后瑞"
},
"subjectList": [
{
"subjectName": "JavaSE",
"subjectScore": 100
},{
"subjectName": "SSM",
"subjectScore": 99
}
],
"map": {
"k1":"v1",
"k2":"v2"
}
};
// 将JSON对象转换为JSON字符串
var requestBody = JSON.stringify(student);
// 发送Ajax请求
$.ajax({
"url":"send/compose/object.json",
"type":"post",
"data":requestBody,
"contentType":"application/json;charset=UTF-8",
"dataType":"json",
"success":function(response){
console.log(response);
},
"error":function(response){
console.log(response);
}
});
});
<!-- 引入Servlet容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
public class CrowdUtil {
/**
* 判断当前请求是否为Ajax请求
* @param request 请求对象
* @return
* true:当前请求是Ajax请求
* false:当前请求不是Ajax请求
*/
public static boolean judgeRequestType(HttpServletRequest request) {
// 1.获取请求消息头
String acceptHeader = request.getHeader("Accept");
String xRequestHeader = request.getHeader("X-Requested-With");
// 2.判断
return (acceptHeader != null && acceptHeader.contains("application/json"))
||
(xRequestHeader != null && xRequestHeader.equals("XMLHttpRequest"));
}
}
ResultEntity
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;
/**
* 请求处理成功且不需要返回数据时使用的工具方法
* @return
*/
public static <Type> ResultEntity<Type> successWithoutData() {
return new ResultEntity<Type>(SUCCESS, null, null);
}
/**
* 请求处理成功且需要返回数据时使用的工具方法
* @param data 要返回的数据
* @return
*/
public static <Type> ResultEntity<Type> successWithData(Type data) {
return new ResultEntity<Type>(SUCCESS, null, data);
}
/**
* 请求处理失败后使用的工具方法
* @param message 失败的错误消息
* @return
*/
public static <Type> ResultEntity<Type> failed(String message) {
return new ResultEntity<Type>(FAILED, message, null);
}
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
super();
this.result = result;
this.message = message;
this.data = data;
}
@Override
public String toString() {
return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
}
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;
}
}
<!-- 引入Servlet容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
@ResponseBody
@RequestMapping("/send/compose/object.json")
public ResultEntity<Student> testReceiveComposeObject(@RequestBody Student student, HttpServletRequest request) {
boolean judgeResult = CrowdUtil.judgeRequestType(request);
logger.info("judgeResult="+judgeResult);
logger.info(student.toString());
// 将“查询”到的Student对象封装到ResultEntity中返回
ResultEntity<Student> resultEntity = ResultEntity.successWithData(student);
String a = null;
System.out.println(a.length());
return resultEntity;
}
11.4.7小结
@RequestBody 使用的场景:传统发送请求参数方式不方便发送的数据,使用 JSON 请求体的方式发送。特别是要发送复杂对象的时候。
前面的工具类