项目架构
技术要求
Spring,MyBatis,SpringMVC,Maven:根据任务去分析实现任务功能的思路,然后根据思路去画出流程图步骤,在把流程图步骤翻译成注解,在根据注解去编写代码
总结:目标 > 思路 > 代码
创建工程
MyBatis逆向工程
MyBatis的逆向工程会去根据数据表自动生成pojo类、dao接口、sql映射文件。也就是说,逆向工程的目的是为了简化开发,加快我们的开发步骤。
配置逆向工程的依赖
<!--导入Mybatis核心依赖-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</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>
<?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"
userId="root"
password="123456">
</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>
执行逆向生成操作的Maven 命令
mybatis-generator:generate
把生成的文件放置对应的层
配置父工程的依赖管理
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding01-admin-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>atcrowdfunding02-admin-webui</module>
<module>atcrowdfunding03-admin-component</module>
<module>atcrowdfunding04-admin-entity</module>
<module>atcrowdfunding05-common-util</module>
<module>atcrowdfunding06-common-reverse</module>
</modules>
<!--统一管理版本号-->
<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>
<!--JSON解析器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.5</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>
</project>
依赖信息来源
到专门网站搜索:
https://mvnrepository.com
调试:根据实际运行情况,确认jar 包之间是否兼容
Spring和Mybatis整合
思路:
- 导入依赖
- 准备jdbc.properties配置文件
- 创建Spring和MyBatis的整合配置文件
- 在整合配置文件加载jdbc文件
- 配置数据源,如C3P0,Druid
- 配置sqlSessionFactory,装配数据源,配置一些MyBatis操作
- 配置MapperScannerConfigurer,去扫描Mapper接口
导入依赖
<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>
<!--JSON解析器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.5</version>
</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.properties配置文件
jdbc.driverClassName=com.mysql.jdbc.Driver
#useUnicode=true&characterencoding=UTF-8 指定字符的编码、解码格式
jdbc.url=jdbc:mysql://localhost:3306/project_crowd?useUnicode=true&characterEncoding=UTF-8
jdbc.user=root
jdbc.password=123456
配置整合
<?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"
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/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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--加载外部文件-->
<context:property-placeholder location="classpath*:*.properties"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--驱动程序-->
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<!--地址-->
<property name="url" value="${jdbc.url}"/>
<!--用户名-->
<property name="username" value="${jdbc.user}"/>
<!--密码-->
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--装配数据源-->
<property name="dataSource" ref="dataSource"/>
<!--加载MyBatis的全局文件,里面有一些MyBatis配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--加载Mapper的文件-->
<property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml"/>
</bean>
<!--配置MapperScannerConfigurer来扫描Mapper接口所在的包-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.crowd.mapper"/>
</bean>
</beans>
日志系统
能给我提供程序运行的信息和错误,方便我们根据信息去调试程序
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>
声明式事务
从事务角度: 一个事务方法中包含的多个数据库操作,要么一起提交、要么一起回滚。也就是说事务方法中的多个数据库操作,有任何一个失败,整个事务全部回滚。
从声明式角度: 交由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();
}
}
就是将以上操作交由Spring来操作
添加AOP依赖:
<!-- AOP 所需依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- AOP 所需依赖-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
思路:
<?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"
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/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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置自动扫描包-->
<context:component-scan base-package="com.atguigu.crowd.service"/>
<!--创建事务管理器,注入数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--虽然跟数据源不在一个文件里面,它是去Spring容器里加载数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<!--查询方法,使用read-only="true"属性配置查询方法为只读,让数据库知道这是个查询方法,然后可以对他进行优化-->
<!--查询的一些方法名字定义-->
<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>
<!--配置AOP-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="px" expression="execution(* *..*ServiceImpl.*(..))"/>
<!--将事务通知与切入点关联-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="px"/>
</aop:config>
</beans>
- 创建
DataSourceTransactionManager
事务管理器,注入数据源 - 配置事务通知,事务属性
- 使用AOP对方法进行增强
注意:tx:method事务属性是必须配置的,一个tx:method对应这你要进行的事务方法,否则事务对这个方法不起作用
配置SpringMvc【表示层】
页面 > handler 【@RequestMapping】> Service > Mapper > 数据库
添加MVC依赖
<!--SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- 引入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>
配置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">
<!--配置加载Spring文件的监听器,并初始化Spring容器-->
<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>
<!--配置编码过滤器-->
<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要在所有Filter之前
原因如下:
他是使用的是request.setCharacterEncoding(encoding)来设置请求和响应编码的,
他是要设置在获取请求参数request.getParameter()之前的和响应response.getWriter()之前的
-->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置Restful风格的URL 过滤器-->
<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>
<!--配置前端控制器-->
<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>
<!--
为什么要设置load-on-startup应为DispatcherServlet在创建后有大量的框架初始化的工作,不适合在第一次请求时来做
所以使用load-on-startup把他设置为web启动时去创建和初始化工作
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--<url-pattern>/*</url-pattern>-->
<!--
请求配置方式2:配置请求扩展名
缺点:不能使用RESTFul风格
优点:
1.可以使静态资源不经过SpringMVC,可以不要特殊处理
2.可以实现伪静态效果,表面上看起来是一个访问静态资源的请求,但是实际上是由SpringMVC交给handler 来处理的动态资源
有利于SEO优化
-->
<url-pattern>*.html</url-pattern>
<!--
为什么要扩展json
应为如果一个AJAX请求扩展是html,但是响应的是json格式,就会造成请求扩展名和响应体的数据格式不匹配,会出现406错误
-->
<url-pattern>*.json</url-pattern>
</servlet-mapping>
</web-app>
配置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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置自动扫描包,扫描handler-->
<context:component-scan base-package="com.atguigu.crowd.mvc"/>
<!--配置注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前置-->
<property name="prefix" value="/WEB-INF/"/>
<!--后置-->
<property name="suffix" value=".jsp"/>
</bean>
.......
</beans>
SSM整合测试
index.jsp页面
<%@ 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>
target.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${requestScope.admin}
</body>
</html>
创建Controller类
package com.atguigu.crowd.mvc.Controller;
import com.atguigu.crowd.entity.Admin;
import com.atguigu.crowd.service.api.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Controller
public class TestHandler {
@Autowired
private AdminService adminService;
@RequestMapping("/test/ssm.html")
public String testSsm(Model model){
List<Admin> admins=adminService.getAll();
model.addAttribute("admin",admins);
return "target";
}
}
getAll方法
/**
* 查询
* @return
*/
public List<Admin> getAll() {
//selectByExample()根据Example条件进行查询
return adminMapper.selectByExample(new AdminExample());
}
测试结果
使用Base配置通用url
将页面上路径中的${pageContext.request.contextPath}部分提取到页面开头
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--http://localhost:9090/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>
SpringMVC 环境下的Ajax 请求
前端发送过来,后端要处理的请求有两种:
- 普通请求:后端处理完成后返回页面,浏览器使用使用页面替换整个窗口中的内容
请求 > handler处理 > 页面
- Ajax 请求:后端处理完成后通常返回JSON 数据,jQuery 代码使用JSON 数据对页面局部更
ajax > handler处理 > json
常用注解
@ResponseBody和@RequestBody,使用它们要添加json依赖如:fastjson
<!--JSON解析器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.5</version>
</dependency>
流程:
@ResponseBody注解
@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
注意:在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据
package com.atguigu.crowd.mvc.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestHandler {
@ResponseBody
@RequestMapping("/test/json.html")
public String getJson(){
return "ResponseBody";
}
}
@RequestBody注解
用于获取请求体的内容(注意:get方法不可以,因为get没有请求体),也可以使用@RequestBody注解把json的字符串转换成JavaBean的对象
@RequestBody 使用的场景:传统发送请求参数方式不方便发送的数据,使用JSON 请求体的方式发送。特别是要发送复杂对象的时候。
场景设定
jQuery 通过Ajax 请求给服务器端发送一个数组:[5,8,12]
加入jQuery
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--http://localhost:9090/test/ssm.html--%>
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}"/>
<%--导入jquery--%>
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
//数组
var array=[5,8,12];
//传为json字符串
var jsonArray = JSON.stringify(array);
alert(jsonArray);
//使用ajax异步发送
$.ajax({
url: "send/array/three.html",//请求地址
type: "post",//请求方式
data: jsonArray,//请求体
contentType: "application/json;charset=UTF-8", // 告诉服务器端当前请求的请求体是JSON格式
dataType: "text", //如何对待服务器返回的数据
success: function (response) { //服务器成功处理请求后调用的回调函数,response是响应体数据
alert(response);
},
error: function (response) { //服务器处理请求失败后的回调函数,response是响应体数据
alert("请求失败")
}
});
});
})
</script>
</head>
<body>
<a href="test/ssm.html">测试SSM整合环境</a>
<button id="btn">Send [5,8,12]</button>
</body>
</html>
package com.atguigu.crowd.mvc.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class TestHandler {
@ResponseBody
@RequestMapping("/send/array/three.html")
public String getJson1(@RequestBody List<Integer> array){
for (Integer arrays:array){
System.out.println(arrays);
}
return "ok";
}
}
AJAX发送复杂对象
映射实体类
/**
* @author 强哥
*/
public class Student implements Serializable {
/**
* id
*/
private Integer stuId;
/**
* 姓名
*/
private String stuName;
/**
* 地址
*/
private Address address;
/**
* 科目
*/
private List<Subject> subject;
private Map<String,String> map;
.......
}
public class Address implements Serializable {
/**
* 省
*/
private String province;
/**
* 市
*/
private String city;
/**
* 街道
*/
private String street;
.....
}
public class Subject implements Serializable {
/**
* 科目
*/
private String subjectName;
/**
* 成绩
*/
private Integer subjectScore;
.....
}
统一返回数据格式
package com.atguigu.crowd.util;
/**
* 用于规范AJAX的返回,统一返回一个规范
* @author 强哥
* @param <T>
*/
public class ResultEntity<T> {
/**
* 返回成功的信息
*/
private static final String SUCCESS="success";
/**
* 返回错误的信息
*/
private 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>(SUCCESS,null,null);
}
/**
* 请求处理成功且需要返回数据的工具方法
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> successWithData(Type data){
return new ResultEntity<Type>(SUCCESS,null,data);
}
/**
* 请求处理失败的工具方法
* @param message
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> failed(String message){
return new ResultEntity<Type>(null,FAILED,null);
}
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
this.result = result;
this.message = message;
this.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;
}
@Override
public String toString() {
return "ResultEntity{" +
"result='" + result + '\'' +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
ajax代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--http://localhost:9090/test/ssm.html--%>
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}"/>
<%--导入jquery--%>
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(function () {
//单击事件
$("#btn1").click(function () {
var student={
//id
"stuId":5,
//姓名
"stuName":"tom",
//地址
"address":{
//省
"province":"广东",
//市
"city":"深圳",
//街道
"street":"后瑞"
},
//科目
"subject":[
//科目名 成绩
{"subjectName":"javaSE","subjectScore":100},
{"subjectName":"SSM","subjectScore":90}
],
"map":{
"k1":"v1",
"k2":"v2"
}
};
//就JSON对象转换为JSON字符串,方便对象的传输
var stringify = JSON.stringify(student);
//使用ajax异步发送
$.ajax({
//请求地址
url:"send/compose/object.json",
//请求方式
type:"post",
//请求体
data:stringify,
//告诉服务器端当前请求的请求体是JSON格式
contentType:"application/json;charset=UTF-8",
//如何对待服务器返回的数据
dataType:"json",
//服务器成功处理请求后调用的回调函数,response是响应体数据
success:function (response) {
console.log(response)
},
//服务器处理请求失败后的回调函数,response是响应体数据
error:function (response) {
alert("请求失败")
}
});
});
})
</script>
</head>
<body>
<button id="btn1">发送复杂对象</button>
</body>
</html>
Controller层
package com.atguigu.crowd.mvc.Controller;
import com.atguigu.crowd.entity.Admin;
import com.atguigu.crowd.entity.Student;
import com.atguigu.crowd.service.api.AdminService;
import com.atguigu.crowd.util.ResultEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Controller
public class TestHandler {
@Autowired
private AdminService adminService;
/**
* 使用指定类初始化日志对象,在日志输出的时候,可以打印出日志信息所在类
*/
private Logger logger= LoggerFactory.getLogger(TestHandler.class);
@ResponseBody
@RequestMapping("/send/compose/object.json")
public ResultEntity<Student> testReceiveComposeObject(@RequestBody Student student){
//打印日志
logger.info(student.toString());
//使用返回统一数据格式工具类,返回信息
return ResultEntity.successWithData(student);
}
}
结果
前台
后台
异常映射
使用异常映射机制对整个项目的异常和错误显示进行统一管理
注意:SpringMVC提供了基于XML和注解的两种异常映射机制
基于XML异常映射
异常页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
exception: 异常对象,当一个页面在运行中发生了异常,就会创建这个对象
message:返回错误信息
--%>
<H1>出错了,${pageContext.exception.message}</H1>
</body>
</html>
在MVC配置中配置XML异常映射
<!--配置基于XML的异常映射-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--配置异常类型和具体视图的对应关系-->
<property name="exceptionMappings">
<props>
<!--key:指定具体的异常全类名-->
<!--标签体中编写对应的视图-->
<prop key="java.lang.Exception">system-error</prop>
</props>
</property>
</bean>
Controller层
@Controller
public class TestHandler {
@RequestMapping("/test/error.html")
public String testError(){
//设置异常
int i=10/0;
return "target";
}
}
结果:
基于注解异常映射
编写判断当前请求是否为Ajax 请求工具类,判断依据
package com.atguigu.crowd.util;
import javax.servlet.http.HttpServletRequest;
/**
* 判断是普通请求还是ajax请求的工具类
* @author 强哥
*/
public class CrowdUtil {
/**
* 判断是否是ajax请求
* @param request
* @return
* true 是Ajax请求
* false 不是Ajax请求
*/
public static Boolean judgeRequestType(HttpServletRequest request){
String accept = request.getHeader("Accept");
String xRequested = request.getHeader("X-Requested-With");
//contains:判断是否包含某个字符串
return (accept!=null&&accept.contains(accept)) || (xRequested!=null&& "XMLHttpRequest".equals(xRequested));
}
}
测试工具类
@Controller
public class TestHandler {
@RequestMapping("/test/error.html")
public String testError(HttpServletRequest request){
boolean aBoolean = CrowdUtil.judgeRequestType(request);
logger.info("什么请求:"+(aBoolean?"Ajax请求":"普通请求"));
/*int i=10/0;*/
return "target";
}
}
在url中访/test/error.html
结果:
测试注解异常映射
package com.atguigu.crowd.mvc.config;
import com.alibaba.fastjson.JSON;
import com.atguigu.crowd.util.CrowdUtil;
import com.atguigu.crowd.util.ResultEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* ControllerAdvice:表示当前类是一个异常处理类
* @author 强哥
*/
@ControllerAdvice
public class CrowdExceptionResolver {
/**
* ExceptionHandler:将一个具体的异常跟一个方法关联起来
* @param request
* @return
*/
@ExceptionHandler(value = NullPointerException.class)
public ModelAndView resolveNullPointerException(Exception exception, HttpServletRequest request, HttpServletResponse response) throws IOException {
String viewName="system-error";
return commonResolveException(viewName,exception,request,response);
}
/**
* 公共方法
* @param viewName 异常对应的页面
* @param exception 异常类,通过它去获取异常信息
* @param request 请求
* @param response 响应
* @return
* @throws IOException
*/
private ModelAndView commonResolveException(String viewName,Exception exception,HttpServletRequest request,HttpServletResponse response) throws IOException {
ModelAndView modelAndView = new ModelAndView();
//如果是Ajax请求
if (CrowdUtil.judgeRequestType(request)){
//ResultEntity使用工具类返回错误信息,使用error.getMessage()去获取错误信息
ResultEntity<Object> failed = ResultEntity.failed(exception.getMessage());
//传为json字符串
String string = JSON.toJSONString(failed);
//把当前JSON 字符串作为当前请求的响应体数据返回给浏览器
response.getWriter().write(string);
//返回null,不给SpringMVC 提供ModelAndView 对象
//这样SpringMVC 就知道不需要框架解析视图来提供响应,而是程序员自己提供了响应
return null;
}
//如果不是Ajax请求,将Exception对象存入模型
modelAndView.addObject("error",exception);
//设置视图显示错误信息
modelAndView.setViewName(viewName);
//返回modelAndView对象
return modelAndView;
}
}
结果: 当出现空指针异常时会调用CrowdExceptionResolver异常处理类的对应异常方法进行处理