ssm 介绍
- ssm 框架由两个开源的框架 spring 和 mybatis组成 springMVC是spring框架的一部分
- spring
- Spring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。也可以称之为项目中的粘合剂。
- Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地
new
一个对象,而是让Spring框架帮你来完成这一切- springMVC
- SpringMVC在项目中拦截用户请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责,将用户请求通过HandlerMapping去匹配Controller,Controller就是具体对应请求所执行的操作。SpringMVC相当于SSH框架中struts。
- mybatis
- mybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行sql命令。
一、mybatis
1.1-mybatis的介绍
- mybatis是什么
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- mybatis让程序员只关注sql本身,而不需要去关注如连接的创建、statement的创建等操作。
- 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
- mybatis的特点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
1.2-mybatis的基本使用步骤
首先创建一个maven项目
【可以删除这个项目的src 文件夹作为父工程】
然后 在pom.xml中导入包 dependencies mysql mydatis
【创建子项目】
然后编写mybits的核心配置文件 创建一个叫mybatis-config的xml文件 编写里面的配置 ,
主要就是为了 起别名 连接数据库 声明映射的xml文件 连接数据库的时候,
在路径后面加上 ?userSSL=true(安全连接) &(相当于&,因为这里不支持& ,因此转义) userUnicode=true
characterEncoding=UTF-8
编写mapper的实体类
- 主要有三行代码 这三个可以封装成工具类
- Resources.getResourceAsStream(Mybatis的配置文件) 得到 一个stream 用来读配置文件得到stream 然后就没什么作用了 因此可以放在静态代码块之中
- new SqlSessionFactoryBuilder().build(上面的stream) 得到SQLSessionDFactory
- SqlSessionFactory.openSession() 得到一个SqlSession SqlSession 用来执行sql语句 这个线程不安全,不能放在静态代码块
然后就可以进行编写代码
- 写实体类
- 操作数据库的dao 接口
- 实现接口 在这里写实现的是时候,用的是xml注解的形式
- 即写一个xml文件作为dao接口的mapper映射 mapper标签 的namespace命名空间,必须与dao进行绑定,即namespace必须写对应的dao的全限定名
1.3-mybatis的步骤代码实现
安装:
在maven项目之中 ,在pom.xml配置文件之中,导入依赖的jar包
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>
配置mybatis的配置文件
在maven项目的resources包下添加一个mybatis的xml文件,作为mybatis’的主配置文件
- 这个mybatis的作用有
- 起别名
- 数据库的连接环境 和spring联合起来之后,数据库的连接会交给spring
- 引入映射文件
<!-- 这个是mybatis的声明,不用改变 --> <?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> <!-- 给项目之中的实体类 起别名 --> <typeAliases> <typeAlias type="com.itqf.demo04.entity.User" alias="user" ></typeAlias> </typeAliases> <!-- 数据库的连接环境 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="wxhdszz..."/> </dataSource> </environment> </environments> <!-- 引入映射文件 ,注意,这里的映射文件全限定名,中间的连接不能用 . 需要用 / --> <mappers> <mapper resource="com/itqf/demo04/mapper/UserDaoMapper.xml" ></mapper> </mappers> </configuration>
有了主配置文件,就可以进行dao层的配置文件编写
即dao层的mapper映射文件
<!-- 这个是映射配置文件的声明,不需要修改 --> <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- mapper映射 namespace 命名空间,需要和映射的dao接口保持一致 --> <mapper namespace="com.itqf.dao.UserDao"> <!-- dao接口之中的声明的方法 id为方法名,并且这个方法必须在dao接口之中有声明出来 paramterType 参数的类型 resultType 返回的结果类型 --> <select id="findUser" parameterType="int" resultMap="com.itqf.entity.User" > SELECT * FROM WHERE u_id = #{uid}; </select> </mapper>
- 有了映射的xml文件,不能忘了在主配置文件之中去声明出来
<mappers> <mapper resource="com/itqf/mapper/UserDaoMapper.xml" ></mapper> </mappers>
声明结束,就可以通过SqlSessionFactory 返回一个SqlSession来执行了
新建一个测试的类 ,读取xml配置,并执行、】
// 这个是mybatis的主配置文件,在编译后,默认的路径就在classes下,因此,直接写文件名 String resource = "mybatis.xml"; //通过 Resources.getResourceAsStream 读取配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //new SqlSessionFactoryBuilder().build(inputStream) 通过它得到SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); //得到SqlSession SqlSession sqlSession = build.openSession(); //得到UserDao的mapper映射 UserDao mapper = sqlSession.getMapper(UserDao.class) ; //执行方法 USer user = mapper.findUser(2); //提交 注意,不能忘了提交 , //否则在执行DML语句的时候,虽然会显示执行成功,但是数据库的数据不会改变 //在执行insert的时候,自增的主键会改变结构,但是不会显示添加的数据 sqlSession.commit(); sqlSession.close();
1.4mybatis的常用依赖
日志的打印:
导入依赖
<!--日志的包--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
配置文件
- log4j.properties 文件 需要把它黏贴到resources这个文件夹中
log4j.rootLogger=debug,stdout ### \u628A\u65E5\u5FD7\u4FE1\u606F\u8F93\u51FA\u5230\u63A7\u5236\u53F0 ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender #log4j.appender.stdout.Target=System.err log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout ###\u663E\u793ASQL\u8BED\u53E5\u90E8\u5206 log4j.logger.com.ibatis=DEBUG log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
参数的使用
- 参数就是在sql语句中 需要的条件
- 一定要注意如果sql语句需要传参数的话,可以传一个pojo对象
<!-- 一定要注意你的参数必须和实体类中的属性一致 parameterType="pojo的全限定名" --> <insert id="add2" parameterType="com.qfedu.first.Employee"> <!-- #{name}, #{sex}, #{age}, #{phone} 这个相当于占位符 ,{ } 中的名字必须与属性名保持一致 --> insert into employee (name, sex, age, phone) values (#{name}, #{sex}, #{age}, #{phone}) </insert>
别名的使用
- 别名的设置在主配置文件之中,即mybatis之中,在mybatis.xml之中声明之后
- 就可以在映射文件之中使用,别名代替全限定名
<typeAliases> <!-- type代表着类的全限定名 alias就是别名 --> <typeAlias type="com.itqf.demo04.entity.User" alias="user" ></typeAlias> </typeAliases>
参数的其他数据类型
参数可以使用String map list 等
<!-- 其他参数 void add3(String name)--> <insert id="add3" parameterType="string"> insert into employee (name) values(#{name}) </insert> <!-- 其他参数 void add4( Map map )--> <insert id="add4" parameterType="map"> insert into employee (name, sex) values(#{name}, #{sex}) </insert>
删除和修改
<!--删除数据--> <delete id="deleteById" parameterType="int"> delete from employee where id=#{id} </delete> <!--修改数据--> <update id="update" parameterType="employee"> update employee set name=#{name}, age=#{age}, sex=#{sex}, phone=#{phone} where id=#{id} </update>
查询
- 查询出来的结果,有时和实体类的属性名不一致,这个时候就需要resultMap 来接收,不能直接使用resultType
<resultMap id="empResultMap" type="employee" > <!-- 主键 --> <id column="uid" property="uid" ></id> <!-- 别的列 对应的属性 column数据库的列名,因此查询的时候可以不用起别名,因此,可以直接用* property 类的属性名 --> <result column="ename" property="name" ></result> </resultMap> <!--进行查询的时候必须设置resultType或者resultMap--> <select id="findAll" resultType="employee"> select * from employee </select> <!-- 这里需要注意 resultType 变成了 resultMap 调用的是上面声明的resultMap --> <select id="findById2" parameterType="int" resultMap="empResultMap"> select id, name ename, sex, age, phone from employee where id=#{id} </select>
映射代理
写一个接口并写接口所对应的映射的配置文件 该配置文件的命名空间必须是接口名
接口的方法必须和mapper中的标签的id 一致
映射代理有四个必须的条件
1.mapper的接口的全限定名一定要和mapper映射的配置文件的namespace的名字保持一致 <mapper namespace="com.itqf.dao.UserDao"> 2.mapper接口中的方法名称要和mapper映射的配置文件中id一致 <select id="findAll" parameterType="int" resultType="com.itqf.entity.User" > 3.mapper接口中的方法的参数类型要和mapper映射的配置文件中的parameterType保持一致 <select id="findAll" parameterType="int" resultType="com.itqf.entity.User" > select u_id uid , u_name uname,u_sex usex from user where u_id = #{id} </select> 4.mapper接口中的方法的返回值和mapper映射的配置文件中resultType要保持一致 resultType = "com.itqf.entity.User"
满足条件之后,就可以使用映射代理
在类中,sqlSession.getMapper(接口的class对象) 得到代理,然后执行
MoneyDao mapper = sqlSession.getMapper(MoneyDao.class); int i = mapper.updateMoney(userMoney);
特殊字符的处理
- 主要针对的是 <
- 两种解决方法:
<!-- 1.字符实体 < 注意 ; 也是他的一部分 2.原样输出 <![CDATA[<]]> cdata:字符串 --> <select id="findByAge" parameterType="int" resultType="employee"> select * from employee where age < #{age} </select>
#{} 和 ${}
- #{} 在预编译的时候,相当于一个占位符"?",用来不全预编译语句的,防止sql注入
- ${} 表示内容的原样输出,相当单纯的内容替换,拼接完成以后这个sql语句才会执行
获取自增的值
也是两种方法:
<!-- 第一种解决方案:只争对于 mysql 这个方案是针对于mysql数据库的生效,如果是oracle不生效 useGeneratedKeys:开启使用产生的key keyProperty:将自增的值赋值给那个对象中的属性 --> <insert id="add" parameterType="employee" useGeneratedKeys="true" keyProperty="id"> insert into employee (name, age, sex, phone) values (#{name}, #{age}, #{sex}, #{phone}) </insert> <!-- 第二种,mysql和oracle都可以使用的 order 执行语句的先后顺序 resultType 结果的返回值类型 执行的sql语句 select last_insert_id() --> <insert id="add2" parameterType="employee"> insert into employee (name, age, sex, phone) values (#{name}, #{age}, #{sex}, #{phone}) <selectKey order="AFTER" keyProperty="id" resultType="int"> select last_insert_id() </selectKey> </insert>
动态的sql语句
如果不知道sql查询的条件有哪些,这个时候怎么办? 要使用动态的sql语句 相当于一个if 重要
muybatis中提供了一些关键字来进行动态sql的操作
- if(简单的条件判断)
- trim(对包含的内容加上前缀和后缀)
- where(主要是简化sql语句where条件判断的)
- set(主要是用于更新)
- foreach(where id in (1,2,3)))
<!--动态的sql语句--> <insert id="add3" parameterType="employee"> insert into employee ( <!--if 单条件判断 test是给出的条件 trim 可以设置前缀和后缀的, suffixOverrides 相当于去除了指定的后缀 相当于sql语句 insert into employee (sex, age, phone) values(#{sex}, #{age}, #{phone}) --> <trim suffixOverrides=","> <!-- test条件 --> <if test="name != null"> name, </if> <if test="sex != null"> sex, </if> </trim> ) values ( <trim suffixOverrides=","> <if test="name != null"> #{name}, </if> <if test="sex != null"> #{sex}, </if> </trim> ) </insert> <!--where--> <select id="findByCondition" parameterType="employee" resultType="employee"> <!--如果name为空 select * from employee where name="战三" and age = 12 where标签的作用:在sql语句中会使用where 关键字,还会删除多余的and or where 1=1 and name="absasj" --> select * from employee <where> <if test="name != null"> and name=#{name} </if> </where> </select> <!--set--> <update id="update" parameterType="employee"> update employee <!-- set 会自动删除多余的逗号--> <set> <if test="name != null"> name=#{name}, </if> <if test="age != null"> age=#{age}, </if> </set> where id = #{id} </update> <!--foreach 一般用做删除的时候 多选删除 delete from employee where id in(1,2,3) collection 遍历的元素 item 临时的变量名字 open ,close 遍历出来的数据使用哪些符号进行包裹 separator 遍历出来的元素使用的分割符 --> <delete id="deleteByIds" parameterType="list"> delete from employee where id in <foreach collection="list" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete> <!-- 如果dao层的参数是一个数组, 配置中需要的参数是map, collection 是一个array--> <delete id="deleteByIds2" parameterType="map"> delete from employee where id in <foreach collection="array" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>
1.5-mybatis中的多表处理
- 关系映射的介绍
- 嵌套查询
- 嵌套结果
- 重要的标签
- 嵌套查询
- 一对一
- 一对多
- 多对多
- 嵌套结果查询
- 一对一
- 一对多
- 多对多
- 懒加载
1.6-mybatis的注解(了解为主)
1.7-代理模式
1.7.1-代理模式的介绍
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。通过代理,可以实现对目标对象的间接访问,即通过代理对象访问目标对象
代理模式中涉及到两方:
委托方、代理方 或者叫做 目标对象、代理对象
1.7.2-静态代理
- 代理类和目标类实现相同的接口,在代理类中维护目标类的对象,以此实现对目标对象方法的调用。
- 优点:可以实现不修改目标对象代码的情况下,对目标对象的功能进行扩展。
- 缺点:代理对象与目标对象一样的接口,不易维护,一旦接口增加方法,则目标对象和代理类都需要维护
1.7.3-动态代理
动态代理:通过jdk代理,可以动态的在内容中构建代理对象(在程序运行时运用反射机制动态创建)
使用动态代理,要求目标对象必须实现了接口
动态代理设计到的类及接口
- proxy 调度器 用来创建动态代理的class 和 实例 ,也是所有动态代理的父类
只管生成一个代理的对象 获取proxy动态代理的对象 newProxyInstance- invocationHandler 接口 只有一个invoke 方法
只管生成的代理对象的执行方法 指定规则- 分析
- 因为需要被代理对象的实例,因此先写一个静态的成员变量,最好是Object的,这样,传入的对被代理,就能是任何的对象了;还有,就是,这个是一个工厂,因此,最好不要写有参的构造,而是通过set将被代理的对象传进去,这样,将会更加灵活,不用每换一个被代理的对像,就需要新建一个工厂
1.8-AOP
aop介绍
- 底层是通过动态代理 他就是动态代理的一种规范化 可以使用jdk,cglib两种的代理方式
他把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理- AOP aspect 切面,给目标类增加功能,就是切面。就像上面的日志,事务都是切面。
切面的特点,一般都是非业务方法,可以独立使用的。
orient 面向,对着 programming 编程- cglib动态代理 :第三方的工具库 ,创建代理对象 ,原理是继承 ,通过继承目标类,创建子类
子类就是代理对象。要求目标类不能是final的,方法也不能是finalaop的几个专业术语
pointcut 切点 切点就是需要添加修饰的方法 多个连接点方法的集合,即 多个方法
语法格式: execution(【修饰符】 返回值类型(可以是* 代表全部) 方法名(参数) 异常的类型 ) 方法名由 包名?方法名(参数) 三部分组成 * 0至多个任意的字符 .. 用在方法参数中,表示任意多个参数,用在包名之后,表示当前的包以及包的子路径 + 用在类名之后 表示当前类及其子类的借口之后,表示当前接口及其实现类 例如:*(..) 任意的公共方法 set*(..) set开头的方法 com.xyz.service.*.*(..) service包名中的 所有类中 的 所有任意方法 com.xyz.service..*.*(..) service包名及子包中的 所有类中 的 所有任意方法 *..service.*.*(..) 所有包下的service包下的 所有类中 的 所有任意方法
advisor 关注点 通知,通知表示切面的执行时间
aspect 切面类 切面 表示增强的功能,完成某一个非业务功能,常见的有 日志,事务,统计信息,权限验证
- 一个切面的三个关键的要素
- 切面的功能代码,切面干啥的
- 切面的执行位置,使用pointcut表示切面执行的位置
- 切面的执行时间,使用advice表示时间,在之前还是在之后
JoinPoint:连接点 ,连接业务方法和切面的位置。就某一类中的业务方法 一个方法
目标对象:给哪个类的方法增加功能,这个类就是目标对象
1.8.1-手动实现AOP
- 手动实现
- 就和静态代理的模式类似
1.8.2-xml配置实现AOP spring 提供的AOP
aop的几个通知类型
- before 前置通知
- 切面类的格式 public 没有返回值 可以有参数 也可以没有参数
如果有参数 参数不是自定义 有几个类型可以使用
JoInPoint 可以获取方法执行时的信息,例如方法名称 方法实参 如果由需要方法的信息,就加入JoinPoint- afterReturning 后置通知
- 通过returning="" 可以得到目标方法的返回值 自定义的变量名必须和通知方法形参名一样 可以根据这个返回值做不同的处理功能 可以修改这个返回值
- 切面类的格式 public void 有参数 参数类型推荐使用Object 参数名自定义
- around 环绕通知
- 切面类的格式 方法必须有返回值 有固定的参数ProceedingJoinPoint 等同于jdk动态代理的 InvocationHandler接口
参数ProceedingJoinPoint 就相当于Method方法 作用就是执行目标的方法 proceedingJoinPoint.proced() 就相当于 method.invoke()
返回值就是目标方法的执行结果 可以被修改- 能控制目标方法是否被调用 能修改原来的目标方法的执行结果 ,影响最后的调用结果 这个功能最强大
常用来做事务- afterThrowing 异常通知 抛出异常时用
- 切面类的格式 public void 有一个参数Exception JoinPoint
- 属性 throwing 自定义的变量 表示目标方法抛出的异常对象 变量名必须和方法的参数名一致
- 在目标的方法抛出异常时抛出
可以作异常的监控 监控目标方法执行时的是不是有异常<aop:config > <!-- 切入点,对哪些方法织入切面方法 --> <!-- execution([修饰符] 返回值类型 方法名(参数) [异常模式]) []表示可选部分 第一个* 任意返回类型 com.rr.aop 包 第二个* 包下面的任意类 *(..) 任意名称和参数的方法 --> <aop:pointcut id="point" expression="execution(* com.itqf.demo04.dao..*.*(..))"/> <!-- 切面类 ref指向切面类的bean 这个时候,可以给他实例化一个bean在xml中 当然也可以用注解 --> <aop:aspect ref="userAop"> <!-- 前置通知 method="before" 通知的名称 pointcut-ref="point" 切点--> <aop:before method="before" pointcut-ref="point" ></aop:before> <aop:after-returning method="afterReturn" pointcut-ref="point" returning="result" ></aop:after-returning> </aop:aspect> </aop:config>
package com.itqf.demo04.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.stereotype.Component; /** * projectName:ssm * * @author: 三毛 * time:2020/9/1415:37 * description: */ @Component public class UserAop { /** * 前置通知 */ public void before(){ System.out.println("之前的方法==="); } /** * 后置通知 * @param result */ public void afterReturn(Object result){ System.out.println("result = " +((int) result + 12)); result = (int)result +1000 ; } /** * 环绕通知 最强大 * @param proceedingJoinPoint * @return */ public Object around(ProceedingJoinPoint proceedingJoinPoint){ //可以在此处添加 可以在此处做方法前的功能增强 System.out.println("around 前"); Object proceed = 0 ; //相当于 proxy.invoke() try { proceed = proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } //可以在此处添加 可以在此处做方法之后的功能增强 System.out.println("around 后"); //还能够修改返回值 proceed = (int)proceed + 8989 ; return proceed ; } /** * 异常通知 * @param ex */ public void throwth(Exception ex){ System.out.println("ex = " + ex.getMessage()); } }
1.8.3-注解AOP
注解aop
<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" 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"> <!--扫描注解 扫描的是spring的注解 在项目中通常是service --> <context:component-scan base-package="com.qfedu.anno"></context:component-scan> <!-- aop的扫描 这个是aop的注解扫描 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
package com.itqf.demo04.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; /** * projectName:springMVC * description: 这个是aop的 切面类 还需要一个切点 */ //切面类 @Component// LogAop logAop = new LogAop() @Aspect//表示的是一个切面类的注解 public class AnnotationAop { //设置切点 公共的切点 @Pointcut("execution(* com.itqf.demo04.dao..*.*(..))") //是用来承载上卖弄注解的,没有实际的意义 public void pointcut() {} //开始时间 //关注点 //获取当前执行的方法的名字 只需要写JoinPoint接口即可 //前置通知:指定切入的表达式 @Before("pointcut()") public void begin (JoinPoint joinPoint) { Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String name = joinPoint.getSignature().getName(); System.out.println(name + "调用开始的时间:"+ simpleDateFormat.format(date)); } //关注点 @After("pointcut()") public void end () { Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("调用结束的时间:"+ simpleDateFormat.format(date)); } @AfterReturning("pointcut()") public void afterReturn () { System.out.println("after return "); } //异常返回的 public void afterException (Exception e) { System.out.println("after exception"); System.out.println(e.getMessage()); } //环绕通知 我个人在开发的时候使用较多, 必须给参数 public void around (ProceedingJoinPoint proceedingJoinPoint) { System.out.println("环绕前"); //日志 try { //执行目标方法 add() delete() proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("环绕后"); } }
测试
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); UserDao userDaoImpl = (UserDao)context.getBean("userDaoImpl"); int adu = userDaoImpl.add(12); System.out.println("adu = " + adu);
二 、$$$$$ spring
spring的介绍
spring的使用
spring 的注解
spring 与mybatis的整合
三、spring MVC
3.1 springmvc简介
- spring 为展现层提供的基于mvc设计理念的优秀的web框架,是目前最主流的mvc框架之一
- springmvc 通过一套mvc注解,让pojo成为处理请求的控制器,而无需实现任何的接口
- pojo : plain old java Object 普通而古老的实例
- 支持rest风格的url请求
- 采用了松散耦合可插拔组件结构,比其他的mvc框架更加具有扩展性和灵活性
3.2 spring MVC的工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3F6uHzwl-1600871241159)(F:\D\java\笔记\ssm\img\springMVC.png)]
DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。
HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。
流程:
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图。
11、 DispatcherServlet响应用户,DispatcherServlet也是整个Spring MVC的核心,它负责接收HTTP请求组织协调Spring MVC的各个组成部分
详解:
请求如何给前端控制器?
这个应该在web.xml中进行部署描述
前端控制器如何根据请求信息选择页面控制器进行功能处理?
需要配置HandlerMapping进行映射
如何支持多种页面控制器呢?
配置HandlerAdapter从而支持多种类型的页面控制器
如何页面控制器如何使用业务对象?
利用Spring IoC容器的依赖注入功能
页面控制器如何返回模型数据?
使用ModelAndView返回
前端控制器如何根据页面控制器返回的逻辑视图名选择具体的视图进行渲染?
使用ViewResolver进行解析
不同的视图技术如何使用相应的模型数据?
因为Model是一个Map数据结构,很容易支持其他视图技术
3.3 springmvc的用法
导入依赖:
<?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.itqf</groupId> <artifactId>demo_springMVC</artifactId> <version>1.0-SNAPSHOT</version> <!-- 这个是打包的方式 在自己运行项目的时候 , 若不将打包方式设为war,则tomcat启动的之后,会自己退出 指定打包类型使用<packing>标签,它默认是jar类型。 pom:父类型都为pom类型 jar:内部调用或者是作服务使用 war:打包项目,用于在容器(Tomcat、Jetty等)上部署 --> <packaging>war</packaging> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!--springmvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.5.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!-- tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <uriEncoding>UTF-8</uriEncoding> <path>/springmvc</path> </configuration> </plugin> <!-- 编译插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerVersion>1.8</compilerVersion> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
springMVC的核心配置
在web.xml 中配置 首先声明DispathcherServlet 然后加载springmvc的配置文件
DispathcherServlet 就是一个servlet
<!-- 引入spring mvc的核心控制器 配置 这两个不变 --> <servlet> <servlet-name>DispatchServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载哪个配置文件 这个需要注意 classpath 相当于编译后的classes 文件夹 spring-mvc.xml 就是spring-mvc的配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!-- tomcat服务器启动时,创建servlet对象 饿汉模式 设置优先级 启动之初就进行加载 创建实例 --> <load-on-startup>1</load-on-startup> </servlet> <!-- servlet 对应的mapping映射, 也是DispathcherServlet拦截的文件设置 --> <servlet-mapping> <servlet-name>DispatchServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
实现controller
两种方法 xml 配置 和 注解的形式
xml配置
首先创建一个实现 Controller 接口的类
然后在 springMVC.xml中配置 引入命名空间
<!-- springmvc id 是对应的访问的路径, class访问对应的的实体类(controller)--> <bean id="/test.do" class="com.itqf.demo06.controller.UserController"></bean> <!--默认的映射器 可以不用写--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> <!--默认的适配器 把modelandview 对象 扔给 Disipatcherservlet--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean> <!-- 前端控制器 告诉Disipatcherservlet哪些静态资源不用拦截 mapping="/js/** 映射 location="/js/ 地址--> <mvc:resources mapping="/js/**" location="/js/" ></mvc:resources> <!-- 视图解析器 通过dispatcherservlet将适配器给我modelandview 解析成前端所需要的对象--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean>
通过注解的方式 这种方法 需要在springMVC中添加扫描 扫描注解,才能找到controller
<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: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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- base-package 这个就是需要扫面的controller包 这个需要注意 它的默认映射器 和 适配器 都和上面的方式不一样 --> <context:component-scan base-package="com.itqf.demo06.controller" ></context:component-scan> <!-- 处理@RequestMapping 的映射器 --> <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> --> <!-- 处理 @RequestMapping 的适配器--> <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean> --> <!-- 使用下面的配置,可以不用写RequestMappingHandlerMapping和 RequestMappingHandlerAdapter--> <mvc:annotation-driven /> <!-- 视图解析器 通过dispatcherservlet将适配器给我modelandview 解析成前端所需要的对象--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
注意:当视图解释器解析ModelAndVIew是,其中model本生就是一个Map的实现类的子类。视图解析器将model中的每个元素都通过request.setAttribute(name, value);添加request请求域中。这样就可以在JSP页面中通过EL表达式来获取对应的值
关于 mvc:annotation-driven:
该配置可以注册RequestMappingHandlerMapping和RequestMappingHandlerAdapter。如果不写,也不会影响资源的访问,这是因为spring4中,springmvc容器启动时会默认的加载DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter,但是这两个类已经不建议使用了。Spring5中默认就是使用RequestMappingHandlerMapping。
3.4 springMVC相关类
视图解析器 InternalResourceViewResolver
<!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀 会自己添加 因此在跳转的时候 return "index" 会找到 /index.jsp --> <property name="prefix" value="/"></property> <!-- 后缀 --> <property name="suffix" value=".jsp"></property> </bean>
映射器 BeanNameUrlHandlerMapping
将请求的资源(路径)直接映射到对应的Bean进行处理,即某个Controller
<!-- 默认的映射器,可以不写 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
适配器 SimpleControllerHandlerAdapter
通过该适配器,实现处理器(Controller)某个功能方法的调用,SimpleControllerHandlerAdapter将会调用处理器的handleRequest方法进行功能处理,该处理方法返回一个ModelAndView给DispatcherServlet
<!-- 默认的适配器,可以不写 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
3.5 springMVC注解
- 常见的注解
- @RequestMapping
- 用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径**;简单来说就是设置访问路径**
- 类似的注解还有 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
- @RequestParam
- 用于将指定请求参数映射到方法的参数上 ,即将前端的参数,赋给方法的参数
- 该注解用在 方法形参之前 方法形参和前端请求的参数名一样
- @RequestBody
- 用于获取请求体之中的内容 ,get请求不适用,直接使用得到的是 key=value&key=value…的结构的数据
- 该注解用在 方法形参之前 方法形参是 String body
- @SessionAttributes
- 在默认情况下,ModelMap中的属性作用域是request级别,如果希望在多个请求中共享ModelMap中的属性,可以将属性转存到session 中。通过该注解,允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中。
- @ModelAttribute
- 可以应用在方法参数上或方法上。
- 当修饰在方法参数上时,会将注解的参数对象添加到Model中;
- 当用在请求处理方法上时会将该方法变成一个非请求处理的方法,但其它请求处理方法被调用时会首先调用该方法。
- 他能让form表单提交的不完整数据,变得更加完整 , 即能够自动补全参数
- @PathVariable
- 用于绑定url中的占位符, 可以 拿到url中的占位符 ,例如:请求中的url中 /delete/{id} , 这个{id} 就是urlzhanweifu
value :用于指定 url中的占位符名称 required :用于提供占位符- @ResponseBody
- 用于将控制层中的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的数据区。一般转为json格式
- @CookieValue
- 从Http请求头中获取指定的Cookie的值
- value:cookie的名称;required:是否必须
3.6 springMVC 常用的使用方法
向界面返回数据
- 借助Model/ModelMap等对象 即相当用了三个共享域
- model -> requert session
- HttpSession -> session
- requert 获取 servletContext -> servletContext
//http://localhost:8080/F15_SpringMvc/user/world.action @Controller //表示控制层 spring的注解 @RequestMapping("/user") //springMVC注解 @SessionAttributes(value={"tid", "tname"}) //可以向session中存数据 存两个数据 public class WorldController { //第一种:通过model 对象 //Model是一个接口, 其实现类为ExtendedModelMap,该类继承了ModelMap类 //Model是每次请求中都存在的默认参数 , 它将参数放在了requestScorp中 //利用其addAttribute()方法即可将服务器的值传递到jsp页面中; //ModelAndView包含model和view两部分,使用时需要自己实例化, //利用ModelMap用来传值,也可以设置view的名称 @RequestMapping("/world.action") public String annotest(Model mm){ //向Model对象中写数据 mm.addAttribute("name", "lisi"); //返回跳转的url return "/index.jsp"; } //第二种:通过Model将参数写入session 中 //这种方法,必须在类上声明 @SessionAttributes //若不声明,则在重定向的时候找不到参数,因为重定向,不是同一次请求 @RequestMapping("/session.action") public String test(Model m){ //tid是@SessionAttributes中出现的key值,配合@SessionAttributes, //然后通过向Model中写入数据,会将该值也写到session中 m.addAttribute("tid", "xxxxxxx"); return "redirect:/session.jsp"; } //第三种:通过HttpSession 将参数写入session中 @RequestMapping("/session2.action") public String testSession(HttpSession session){ //通过HttpSession对象向session域中写入数据 session.setAttribute("tage", 20); return "redirect:/session.jsp"; } //第一种:通过request获取servletcontext @RequestMapping("/context.action") public String testContext(HttpServletRequest request){ //通过request获取servletcontext request.getSession().getServletContext().setAttribute("context", "hahah"); return "redirect:/context.jsp"; } }
转发/重定向
- spring mvc 默认跳转方式为转发 , 使用forward和redirect前缀后,视图解析器不起作用
- 支持写法:
- “forward:/index.jsp”;
- “redirect:/index.jsp”;
- 其中,"/"相对于应用来说,另外,通过这两个前缀,还可以转发或重定向到其他的controller资源
传递表单数据 spring mvc 内部封装了类型转换 因此不是String类型的也可以进行封装
- 直接接收
- 三种接收的方法
- 直接通过参数接收
- 注解@RequertParam(“页面的参数名”) 常用在前端和后端的名称不匹配的时候
- 通过requert 获取
//直接传参,String username参数名与<input type="text" name="username"/>的name值必须一致 @RequestMapping("/register") public String register(String username, String pwd, Model m){ System.out.println(username); System.out.println(pwd); m.addAttribute("name", username); return "index"; } //@RequestParam 表示请求参数的注解 表示的是页面中的name值 将其映射给注解的参数 //本例中,jsp页面的请求参数为user,将user的值赋值给username @RequestMapping("/register1") public String register1(@RequestParam("user") String username, String pwd, Model m){ System.out.println(username); System.out.println(pwd); m.addAttribute("name", username); return "index"; } //通过HttpServletRequest @RequestMapping("/register2") public String register2(HttpServletRequest request){ System.out.println(request.getParameter("user")); System.out.println(request.getParameter("pwd")); request.setAttribute("name", request.getParameter("user")); return "index"; }
封装对象
直接传入一个对象参数 ,在属性名与name值对应的情况下,可以直接获得前端的值
若不匹配,则不会拿到值,但是也不会抛出异常
引用类型的封装,最简单的方法就是 在前端页面中将name值写成 表达式例如
user 中有一个school对象 则前端输入框的属性name 写 name = “school.sname”
若封装对象中有集合的时候,前端
name = “list[0].sname” 这种是 list集合的 [0] 代表下标
name = “map[‘first’].sname” 这种是 map集合的 [‘first’] 代表key值
//通过对象传参 @RequestMapping("/register3") public String register3(User user, ModelMap mm){ System.out.println(user.getUsername()); System.out.println(user.getPwd()); mm.addAttribute("name", user.getUsername()); return "index"; }
请求参数中的日期格式转化
有三种方法 @DataTimeFormat @InitBrid 全局配置
全局配置的步骤
新建一个类,实现Convert接口
package com.itqf.demo06.convert; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * projectName:ssm * @author: 三毛 * time:2020/9/1010:26 * description: 自定义的日期转化工具类 */ public class DataTimmeConvert implements Converter<String , Date> { SimpleDateFormat[] sim = new SimpleDateFormat[]{ new SimpleDateFormat("yyyy-MM-dd"), new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy年MM月dd日"), } ; /** * 将字符串转换成日期 * @param s 待转换的日期字符串 * @return Date 转换后的日期 */ @Override public Date convert(String s) { //判断日期是不是空 if(s == null || s.isEmpty()){ return null ; } //遍历解析 转换日期 for (SimpleDateFormat simpleDateFormat : sim){ try { return simpleDateFormat.parse(s); } catch (ParseException e) { e.printStackTrace(); continue; } } return null; } }
配置spring-MVC配置文件
<!-- 加载转换器服务 convertorService 这个就是转换器实例的id--> <mvc:annotation-driven conversion-service="convertorService" /> <!-- 自定义转化器配置 可以有多个转换器 不单单是日期转换器 --> <bean id="convertorService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters" > <list> <bean class="com.itqf.demo06.convert.DataTimmeConvert" ></bean> </list> </property> </bean>
/** * 通过注解 将字符串转换成日期类型 java.util.Date * @param date * @param model model对象 相当于request级别的共享域 * @return */ @RequestMapping("/annotation.do") public String first(@DateTimeFormat(pattern = "yyyy-MM-dd")Date date , Model model){ model.addAttribute("date" , date) ; return "time" ; } /** * 通过全局配置 xml配置 * 但是需要注意的就是 必须要添加服务器 <mvc:annotation-driven conversion-service="convertorService" /> * @param date 前端的日期字符串 * @param model * @return */ @RequestMapping("/auto.do") public String two(Date date , Model model){ model.addAttribute("date" , date) ; return "time" ; }
- post提交中文乱码
<!-- post 方式的中文乱码解决 --> <filter> <filter-name>characterEncoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <!-- encoding是CharacterEncodingFilter中的一个属性 --> <param-name>encoding</param-name> <param-value>utf8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
servlet 中的 url-pattern 配置
¥¥3.6 springmvc 与spring 的整合
四、springmvc的高级应用
4.1 Restful API
4.2 json数据的处理
默认的使用jackson
步骤
导入 jar 依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.5</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.5</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.5</version> </dependency>
可以通过 response 设置响应的格式
使用注解 responseBody 可以将对象转换成json格式的数据 返回的是实体,不是String
对于json中的日期格式的处理
有两种方法
第一种 使用注解的方式 在实体类的get方法上用注解声明即可
@JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
第二种使用配置
<mvc:annotation-driven> <!-- 处理json里的日期数据,默认日期被fastjson转为时间戳数据 --> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.fasterxml.jackson.databind.ObjectMapper"> <property name="dateFormat"> <bean class="java.text.SimpleDateFormat"> <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" /> </bean> </property> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
- 注意:使用配置的时候 mvc:annotation-driven 标签的conversion-service=“convertorService” 属性,这个是加载处理请求参数中的日期格式的 service ,只需要一个
4.3 验证器
采用Hibernate-validator来进行验证,Hibernate-validator实现了JSR-303验证框架支持注解风格的验证
第一步:导入依赖
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.0.1.Final</version> </dependency>
第二步:配置相关的配置 在resources中写入 配置文件validationMessages.peoperties,在xml中声明配置
<mvc:annotation-driven validator="validator" /> <!-- 配置资源文件 --> <bean id="hibernateMessages" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="defaultEncoding" value="UTF-8"></property> <property name="basenames"> <list> <value>ValidationMessages</value> </list> </property> </bean> <!-- 配置验证器 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property> <property name="validationMessageSource" ref="hibernateMessages"></property> </bean>
第三步:在实体类中使用注解
4.4 springmvc异常的处理
异常处理
controller抛出异常 若是不进行处理 会直接在页面显示异常
为了对用户更加友好 添加一个异常的处理器组件 通过异常处理异常 将异常解析 给客户错误提示页面 步骤
编写一个自定义 的异常类 (做提示信息)
public class ParamException extends RuntimeException { //存储异常信息 private String message ; public ParamException(String message) { this.message = message; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
- 编写一个异常处理器 异常处理器 需要实现 HandlerExceptionResolver
public class ParamExceptionResolver implements HandlerExceptionResolver { /** *处理异常的业务逻辑 * @param httpServletRequest * @param httpServletResponse * @param o * @param e 当前抛出的异常 * @return */ @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { //捕获异常对象 ParamException paramException = null ; if (e instanceof ParamException) { paramException = (ParamException) e ; }else { paramException = new ParamException("系统正在维护") ; } //创建一个ModelAndView 对象 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exceptionMessage" , paramException.getMessage()) ; modelAndView.setViewName("error"); return modelAndView; } }
- 配置异常处理器 (跳转到提示页面) 只需要在springmvc之中实例化一次就行了
<bean id="paramException" class="com.itqf.demo06.exception.ParamExceptionResolver" ></bean>
配置完成之后,就能自己进行异常的捕获,并处理,即controller会将异常交给异常处理组件处理
异常处理器 可以不用自定义声明 而用springmvc框架的注解 不用继承HandlerExceptionResolver
用注解的形式 有两种
局部
- 直接在controller中处理异常 只需要 @ExceptionHandler(ParamException.class) 一个注解
全局
重新建立一个专门用来处理异常的类
使用@ControllerAdvice 注解 在类上声明 @ExceptionHandler(ParamException.class) 在方法上声明
/** * @ControllerAdvice controller 控制器增强 给controller增加功能 */ @ControllerAdvice public class AnnotationResolver { /** * @param e 这个是捕获的异常 也就是controller抛出来的异常 * @return * @ExceptionHandler(ParamException.class) 说明方法是处理异常的 也可以一次处理多个异常 * 属性value :异常的class对象 表示处理的异常类型 * 当发生这个异常的时候,交由这个方法处理 * 位置,方法上声明 */ @ExceptionHandler(value = ParamException.class) public ModelAndView getException(Exception e ){ //创建一个ModelAndView 对象 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exceptionMessage" , "annotation") ; modelAndView.setViewName("error"); return modelAndView; } /** * @param e * @return * @ExceptionHandler value属性 不赋值 代表 捕获其他 的所有异常 */ @ExceptionHandler public ModelAndView getException1(Exception e ){ //创建一个ModelAndView 对象 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exceptionMessage" ,e.getMessage()) ; modelAndView.setViewName("error"); return modelAndView; } }
然后让框架知道这个注解的位置,也就是需要在springmvc.xml之中指定位置
<!-- 扫描注解 base-package @ControllerAdvice注解所在的包名 --> <context:component-scan base-package="com.itqf.demo06.exception" > <!-- include-filter 设置扫描哪些 注解 exclude-filter 设置不扫描哪些注解 --> <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>--> <!-- <contebxt:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>--> </context:component-scan>
4.5 文件的上传
前端上传一个文件 将文件通过request 传递给前端控制器 DispatcherServlet
给前端控制器 配置一个文件解析器 解析request 解析 返回一个上传文件的对象 找到对应的controller 处理
controller 有一个固定的 文件上传对象接收 MultipartyFile 文件解析器传递过来的 对象文件上传的 需要借助第三方组件实现文件上传
使用Commons-fileupload 组件实现文件的上传 需要导入该组件相应的支撑
jar包:Commons-fileupload 和 commons-io
commons-io包不属于文件上传组件的开发jar包,但是Commons-fileupload 组件从1.1开始,工作时需要Commons-io包的支持<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
步骤
首先需要有一个前端的文件上传
注意这个文件上传的 name 必须和 MultipartFile 名一样 或者使用注解@RequestParam
form表单的enctype的取值必须是 multipart/form-data
method 属性值必须是post 注意post请求存在请求体之中,需要处理post请求的编码格式 提供一个文件选择域
<form action="/file/fileUpload.do" method="post" enctype="multipart/form-data" > <input type="file" name="upload" placeholder="传统上传的文件"><br> <input type="submit" value="提交"> </form>
需要配置文件解析器
<!-- 这个id必须是multipartResolver 不能改变 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10485760"></property> </bean>
有了文件解析器,那就可以拦截来自form表单来的请求 ,可以写controller了
/** * springMVC 文件上传 * @param request * @param upload * @return * @throws Exception * MultipartFile upload 这个upload必须和前端的input的name属性值一致 */ @RequestMapping("/upload.do") public String upload(HttpServletRequest request , MultipartFile upload) throws Exception { //使用文件上传组件完成文件的上传 //上传的位置 获取项目的根路径 String path = request.getSession().getServletContext().getRealPath("/uploads/") ; File file = new File(path) ; //判断该路径是否存在 if (!file.exists()){ file.mkdirs() ; } //获得上传的文件的名称 String filename = upload.getOriginalFilename(); //完成上传文件 upload.transferTo(new File(path , filename)); return "success" ; }
文件的异步上传
需要引入 jquery-form 表单提交函数
文件的异步上传就不能用ajax了 需要用到ajaxSubmit
controller 和之前的一样,不过需要返回ajax时 ,需要注意 图片的路径
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>文件的异步上传</title> <script src="./js/jquery-1.12.2.min.js"></script> <script src="./js/jquery.form.min.js"></script> </head> <body> <center> <h1>文件的异步上传</h1> </center> <form method="post" enctype="multipart/form-data" > <input type="file" name="upload"><br> <input id="butsub" type="submit" value="提交"> </form> <img src="${path}${filename}" height="100px" width="100px" > </body> <script type="text/javascript"> $("form").submit(function () { //注意文件的异步上传 不能再用ajax 需要ajaxSubmit $("form").ajaxSubmit({ url:"file/upload.do", type:"post", data:$("form").serialize() , success:function (data) { if (data.code == 1){ $("img").attr("src" , data.info) }else { location.href = "error.jsp" ; } } }) return false ; }) </script> </html>
@RequestMapping("/upload.do") @ResponseBody public Json upload(HttpServletRequest request , MultipartFile upload) throws IOException { //使用文件上传组件完成文件的上传 //上传的位置 获取 "/uploads/" 文件夹所在的绝对路径 String path = request.getSession().getServletContext().getRealPath("/uploads/") ; File file = new File(path) ; //判断 该路径是否存在 if (!file.exists()){ file.mkdirs() ; } //获得上传的文件的名称 这个文件名就是完整的文件名 包含了文件的后缀 String filename = upload.getOriginalFilename(); System.out.println(path+filename); //完成上传文件 upload.transferTo(new File(path , filename)); Json json = new Json(1,"/uploads/"+filename); //这个path 在添加之后 添加在了编译之后的文件之中,和WEB-INF同级 //这里只需要文件夹的名称就能找到该文件 ,和 js css 位置类似 request.getSession().setAttribute("path" , "/uploads/"); request.getSession().setAttribute("filename" ,filename); return json ; }
4.6 拦截器
拦截器的介绍
- 拦截器 也是aop思想的具体应用 我们要自定义拦截器,就必须实现:HandleInterceptor接口
- 在请求到达controller之前 会先经过拦截器 然后到达controller controller执行之后 在返回的时候,还会经过拦截器 拦截器和过滤器很相似 都需要放行
在放行前可以进行预处理 在放行之后 可以进行后处理- 类似于servlet中的filter ,用来对处理器进行预处理和后处理,用户可以自定义一些拦截器来实现特定的功能。
拦截器与过滤器的区别
- filter 是servlet规范中的一部分,任何的Java web工程都可以使用
- 拦截器 是springmvc框架的,只有使用了springmvc框架的工程才能使用
- 过滤器在url-patten中配置了 /* 之后 可以对所有要访问的资源进行拦截
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp html css image 或者 js 是不会进行拦截的
拦截器的使用步骤
有一个拦截器的类 这个类实现了 HandleInterceptor 接口
HandleInterceptor 接口 在实现接口之后 我们发现 不需要必须实现接口中的方法 这是因为HandleInterceptor 接口之中没有方法吗,很明显不是的
是因为在jdk1.8之后,对接口进行了增强,使得接口中可以有方法体的存在 而在HandleInterceptor 接口之中,对方法进行了实现 我们可以根据需要重写方法HandleInterceptor 接口有三个方法
preHandle 预处理 可以判断拦截 看有没有登录
- return true 代表放行 执行下一个拦截器 或者执行controller中的方法
- return false 代表不放行 可以通过 request 或者 response 执行转发或者重定向
postHandle 后处理
afterCompletion 最后处理 可以释放资源
public class MyInterceptor implements HandlerInterceptor { /** * 预处理 * return true 代表放行 执行下一个拦截器 或者执行controller中的方法 * return false 代表不放行 可以通过 request 或者 response 执行转发或者重定向 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle执行了"); return true; } /** * 后处理的方法 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle执行了"); } /** * *最后执行的方法 在 postHandle 之后执行 最后执行 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion执行了"); } }
配置拦截器 springmvc 之中
<!-- 拦截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 你要拦截的具体的方法 /user/* 代表了user下的所有方法都拦截 --> <mvc:mapping path="/user/*"/> <!-- 你不要要拦截的具体的方法 --> <mvc:exclude-mapping path="/user/login.do"/> <!-- 配置拦截器的对象 --> <bean class="com.itqf.demo02.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
4.6 ssm中的事务处理
需要在sprigbean.xml中配置就行了
底层是aop的思想
<!-- 配置事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务通知 首先导入事务的约束 tx id="txAdvice" 设置一个唯一标识 transaction-manager="dataSourceTransactionManager" 给事务通知提供一个事务管理器的引用 --> <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager" > <!-- 配置事务的属性 isolation="" 事务的隔离级别 默认值式default 数据库的默认 propagation=" "用于指定事务的传播行为 默认值 required 表示一定会有事务 增删改的选择 查询可以选择supports read-only=" " 用于指定事务是否只读 只有查询设为 true 默认false读写 timeout="" 用于指定事务的超时时间 默认-1 表示永不超时 如果指定 则以秒为单位 rollback-for="" 用于指定一个异常,当该异常产生的时候 事务回滚 产生别的异常的时候 则不会回滚 没有默认 表示任何异常都回滚 no-rollback-for=" " 用于指定一个异常 ,当该异常产生的时候,不回滚 没有默认 表示任何异常都回滚 --> <tx:attributes> <tx:method name="*" propagation="REQUIRED" read-only="false" /> </tx:attributes> </tx:advice> <!-- 配置aop --> <aop:config> <!-- 切点 --> <aop:pointcut id="poin" expression="execution(* com.itqf.demo05.service.impl.*.*(..))"/> <!-- 建立切点和 表达式的关系 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="poin"></aop:advisor> </aop:config>
4.7 ssm解决跨域访问
导入依赖
<dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>java-property-utils</artifactId> <version>1.9</version> </dependency>
web.xml中配置
<filter> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <init-param> <param-name>cors.allowOrigin</param-name> <!--配置授信的白名单的域名! --> <param-value>*</param-value> </init-param> <init-param> <param-name>cors.supportedMethods</param-name> <param-value> GET, POST, HEAD, PUT, DELETE </param-value> </init-param> <init-param> <param-name>cors.supportedHeaders</param-name> <param-value> Accept, Origin, X-Requested-With, Content-Type, Last-Modified </param-value> </init-param> <init-param> <param-name>cors.exposedHeaders</param-name> <param-value>Set-Cookie</param-value> </init-param> <init-param> <param-name>cors.supportsCredentials</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CORS</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4.8 springMVC的分页查询
- 导入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.4</version> </dependency>
- 配置文件 mybatis.xml中配置
<plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 --> <property name="helperDialect" value="mysql"/> </plugin> </plugins>
- 然后在service中用
//页码以及页容量 PageHelper.startPage(pagenum,pagcapacity); //查所有 List<VoOrder> list = orderDao.selectAll(user.getUid()); //总的行数 int count = (Page)list).getTotal() ;