框架

框架:软件的半成品,为解决问题而指定的一套约束,在提供功能基础上进行扩充。
框架中一些不能被封装的代码(变量),需要新建xml文件,在文件中添加变量内容。

类库:没有封装逻辑

MyBatis

环境搭建

    1. 导入jar

    Cglib依赖的包
    动态代理包
    日志包
    MyBatis核心包
    驱动

    1. 全局配置文件

    在 src 下新建全局配置文件(编写 JDBC 四个变量)
    引入 DTD 或 schema

  <?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> <!-- default 引用 environment 的 id,当前所使用的环境 --> <environments default="default"> <!-- 声明可以使用的环境 --> <environment id="default"> <!-- 使用原生 JDBC 事务 --> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm"/> <property name="username" value="root"/> <property name="password" value="smallming"/> </dataSource> </environment> </environments><mappers> <mapper resource="com/bjsxt/mapper/FlowerMapper.xml"/> </mappers> </configuration> 
    1. 实体类名+Mapper.xml
    • 编写需要执行的SQL命令,可把xml文件理解为xxxdaoimpl实现类

      <mapper namespace=“a.b” 理解成实现类的全路径(包名+类名)

  <!-- id:方法名 parameterType:定义参数类型 resultType:返回值类型. 如果方法返回值是 list,在 resultType 中写 List 的泛型, 因为 mybatis 对 jdbc 封装,一行一行读取数据 --> <select id="selAll"resultType="com.bjsxt.pojo.Flower"> select * from flower </select>

#{}

2.2.2.1 使用索引,从 0 开始 #{0}表示第一个参数
2.2.2.2 也可以使用#{param1}第一个参数
2.2.2.3 如果只有一个参数(基本数据类型或 String),mybatis
对#{}里面内容没有要求只要写内容即可.
2.2.2.4 如果参数是对象#{属性名}
2.2.2.5 如果参数是 map 写成#{key}

  <select id="selById" resultType="com.bjsxt.pojo.People" parameterType="int"> select * from people where id=#{0} </select>

${}
- <![CDATA[ 内容 ]]>

在 xml 文件中出现 “<” , “>” ,双引号 等特殊字符时,可以使用 XML 文件转义标签
分页:?不允许在关键字,前后进行数学运算
- typeAliases 别名

  <typeAliases> <typeAlias type="com.bjsxt.pojo.People" alias="peo"/> </typeAliases>
    1. 测试结果

    只有在单独使用 mybatis 时使用,最后 ssm 整合时下面代
    码不需要编写

 InputStream is = Resources.getResourceAsStream("myabtis.xml"); //使用工厂设计模式 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); //生产 SqlSession SqlSession session=factory.openSession(); List<Flower> list = session.selectList("a.b.selAll"); for (Flower flower : list) { System.out.println(flower.toString()); }session.close();

三种查询方式

  • selectList()

    • 返回值为 List<resultType 属性控制> 适用于查询结果都需要遍历的需求
  List<Flower> list = session.selectList("a.b.selAll"); for (Flower flower : list) { System.out.println(flower.toString()); }
  • selectOne()

    • 返回值 Object 适用于返回结果只是变量或一行数据时
  int count = session.selectOne("a.b.selById"); System.out.println(count);
  • selectMap()

    • 返回值 Map 适用于需要在查询结果中通过某列的值取到这行数据的需求. Map<key,resultType 控制>
Map<Object, Object> map = session.selectMap("a.b.c", "name123"); System.out.println(map);

注解

简化 xml 文件的开发
如果涉及动态 SQL 依然使用 mapper.xml
注解可以有属性,因为注解其实就是一个接口(类)
注解语法: @XXXX(属性名= 值)

6.1 如果值是基本数据类型或字符串: 属性名=值6.2 如果值是数组类型: 属性名={值,值}
6.2.1 如果只有一个值可以省略大括号
6.3 如果值是类类型,属性名=@名称

  • 路径

    1. 编写路径为了告诉编译器如何找到其他资源.

    2. 路径分类
      2.1 相对路径: 从当前资源出发找到其他资源的过程
      2.2 绝对路径: 从根目录(服务器根目录或项目根目录)出发找到其他资源的过程
      标志: 只要以/开头的都是绝对路径

    3. 绝对路径:
      3.1 如果是请求转发 / 表示项目根目录(WebContent)
      3.2 其他重定向,<img/> <script/>,<style/>,location.href 等 / 都表示服务器根目录(tomcat/webapps 文件夹)

  • Log4j

    在项目中编写 System.out.println();输出到控制台,当项目发布
    到 tomcat 后,没有控制台

    log4j 作用,不仅能把内容输出到控制台,还能把内容输出到文件
    中.便于观察结果

      1. 导入 log4j-xxx.jar
      1. 在 src 下新建 log4j.properties
log4j.rootCategory=DEBUG, CONSOLE ,LOGFILE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppend erlog4j.appender.CONSOLE.layout=org.apache.log4j.Patter nLayout log4j.appender.CONSOLE.layout.ConversionPattern=%C %d{YYYY-MM-dd hh:mm:ss} %m %n log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=E:/my.log log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.Patter nLayout log4j.appender.LOGFILE.layout.ConversionPattern=%C %m %L %n

在 mybatis.xml 中开启 log4j

  <settings><setting name="logImpl" value="LOG4J"/> </settings>

通过标签控制 mybatis 全局开关

  • 实现

    1. 实现查询
  @Select("select * from teacher") List<Teacher> selAll();
  1. 实现新增
@Insert("insert into teacher values(default,#{name})") int insTeacher(Teacher teacher);
  1. 实现修改
@Update("update teacher set name=#{name} where id=#{id}") int updTeacher(Teacher teacher);
  1. 实现删除
 @Delete("delete from teacher where id=#{0}") int delById(int id);

使用注解实现功能

在 TeacherMapper 接口添加
9.3.1 @Results() 相当于
9.3.2 @Result() 相当于或
9.3.2.1 @Result(id=true) 相当与
9.3.3 @Many() 相当于
9.3.4 @One() 相当于

@Results(value={ @Result(id=true,property="id",column="id"), @Result(property="name",column="name"), @Result(property="list",column="id",many=@Many(select="com.bjsxt.mapper.StudentMapper.selByTid")) }) @Select("select * from teacher") List<Teacher> selTeacher();

功能实现

  • 新增

    • mybatis 中默认是关闭了 JDBC 的自动提交功能

      功能:从应用程序角度出发,软件具有哪些功能.
      业务:完成功能时的逻辑.对应 Service 中一个方法

      事务:从数据库角度出发,完成业务时需要执行的 SQL 集合,统称一个事务.

      事务回滚.如果在一个事务中某个 SQL 执行事务,希望回归到事务的原点,保证数据库数据的完整性.

      2.1 每一个 SqlSession 默认都是不自动提交事务.
      2.2 session.commit()提交事务.
      2.3 openSession(true);自动提交.setAutoCommit(true)

      mybatis 底层是对 JDBC 的封装.
      3.1 JDBC 中 executeUpdate()执行新增,删除,修改的 SQL.返回值 int, 表示受影响的行数.
      3.2 mybatis 中 标签没有 resultType 属性, 认为返回值都是 int

      在 openSession()时 Mybatis 会创建 SqlSession 时同时创建一个
      Transaction(事务对象),同时 autoCommit 都为 false
      如果出现异常,应该 session.rollback()回滚事务.

      1. 在 mapper.xml 中提供标签,标签没有返回值类型
<insert id="ins" parameterType="People"> insert into people values(default,#{name},#{age}) </insert>
    1. 通过 session.insert()调用新增方法
  int index1 = session.insert("a.b.ins", p); if(index1>0){ System.out.println("成功"); }else{ System.out.println("失败"); }
  • 修改

      1. 在 mapper.xml 中提供标签
  <update id="upd" parameterType="People"> update people set name = #{name} where id = #{id} </update>
    1. session.update
People peo = new People(); peo.setId(3); peo.setName("王五"); int index = session.update("a.b.upd", peo); if(index>0){ System.out.println("成功"); }else{ System.out.println("失败"); }session.commit();
  • 删除

      1. 在 mapper.xml 提供标签
 <delete id="del" parameterType="int"> delete from people where id = #{0} </delete>
    1. session.delete
  int del = session.delete("a.b.del",3); if(del>0){ System.out.println("成功");}else{ System.out.println("失败"); }session.commit();
  • 多表查询

    Mybatis 实现多表查询方式
    1.1 业务装配.对两个表编写单表查询语句,在业务(Service)把查询
    的两个结果进行关联.
    1.2 使用 Auto Mapping 特性,在实现两表联合查询时通过别名完成映射.
    1.3 使用 MyBatis 的标签进行实现.

    • resultMap 标签

      • 单表映射
 <resultMap type="teacher" id="mymap"> <!-- 主键使用 id 标签配置映射关系 --> <id column="id" property="id1" /> <!-- 其他列使用 result 标签配置映射关系 --> <result column="name" property="name1"/> </resultMap><select id="selAll" resultMap="mymap"> select * from teacher </select>
  • 关联单个对象(N+1 方式)

    • N+1 查询方式,先查询出某个表的全部信息,根据这个表的信息 查询另一个表的信息
      - 实现步骤:1. 在 Student 实现类中包含了一个 Teacher 对象
 public class Student { private int id; private String name; private int age; private int tid; private Teacher teacher;
    1. 在 TeacherMapper 中提供一个查询
 <select id="selById" resultType="teacher" parameterType="int"> select * from teacher where id=#{0} </select>
    1. StudentMapper

4.3.3.1 装配一个对象时使用
4.3.3.2 property: 对象在类中的属性名
4.3.3.3 select:通过哪个查询查询出这个对象的信息
4.3.3.4 column: 把当前表的哪个列的值做为参数传递给另一个查询
4.3.3.5 大前提使用 N+1 方式.时如果列名和属性名相同可以不配置,使用 Auto mapping 特性.但是 mybatis 默认只会给列专配一次

  <resultMap type="student" id="stuMap"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <result property="tid" column="tid"/> <!-- 如果关联一个对象 --> <association property="teacher" select="com.bjsxt.mapper.TeacherMapper.selById" column="tid"></association> </resultMap> <select id="selAll" resultMap="stuMap"> select * from student </select>
  • 关联集合对象(N+1)
  • 加载集合数据(联 合查询方式)

接口绑定方案

  • 创建一个接口后把mapper.xml由mybatis 生成接口的实现 类,通过调用接口对象就可以获取 mapper.xml 中编写的 sql.

  • 接口绑定实现步骤

      1. 创建一个接口

      3.1.1 接口包名和接口名与 mapper.xml 中namespace
      相同
      3.1.2 接口中方法名和 mapper.xml 标签的 id 属性相同

 public interface LogMapper { List<Log> selAll(); }
    1. 使用进行扫描接口和 mapper.xml

      mybatis.xml 中

  <mappers><package name="com.bjsxt.mapper"/> </mappers>
    1. 新建LogMapper.xml

      4.3.1 namespace 必须和接口全限定路径(包名+类名)一致
      4.3.2 id 值必须和接口中方法名相同
      4.3.3 如果接口中方法为多个参数,可以省略 parameterType

  <mapper namespace="com.bjsxt.mapper.LogMapper"> <select id="selAll" resultType="log"> select * from log </select> </mapper>
  • 多参数实现办法

    1. 接口中声明方法 List selByAccInAccout(String accin,String accout);
    1. 在 mapper.xml 中添加

    #{}中使用 0,1,2 或 param1,param2

  <!-- 当多参数时,不需要写 parameterType --><select id="selByAccInAccout" resultType="log" > select * from log where accin=#{0} and accout=#{1} </select>
  • 注解方式实现

    1. 接口中声明方法
/*** mybatis 把参数转换为 map 了,其中@Param("key") 参数内 容就是 map 的 value * @param accin123 * @param accout3454235 * @return */ List<Log> selByAccInAccout(@Param("accin") String accin123,@Param("accout") String accout3454235);
    1. 在 mapper.xml 中添加

    #{} 里面写@Param(“内容”)参数中内容

 <!-- 当多参数时,不需要写 parameterType --> <select id="selByAccInAccout" resultType="log" > select * from log where accin=#{accin} and accout=#{accout} </select>

动态 SQL

  • 根据不同的条件需要执行不同的 SQL 命令。MyBatis 中动态 SQL 在 mapper.xml 中添加逻辑判断等.
  • <if
  <select id="selByAccinAccout" resultType="log"> select * from log where 1=1 <!-- OGNL 表达式,直接写 key 或对象的属性.不需要添加任 何特字符号 --> <if test="accin!=null and accin!=''"> and accin=#{accin} </if> <if test="accout!=null and accout!=''"> and accout=#{accout} </if> </select>
  • <where

    4.1 当编写 where 标签时,如果内容中第一个是 and 去掉第一个 and
    4.2 如果中有内容会生成 where 关键字,如果没有内容不 生成 where 关键

  • <choose <when <otherwise

  • <set用在修改 SQL 中 set 从句

    6.1 作用:去掉最后一个逗号
    6.2 作用:如果<set里面有内容生成 set 关键字,没有就不生成
    6.3 示例
    6.3.1 id=#{id} 目的防止<set中没有内容,mybatis 不生成 set 关
    键字,如果修改中没有 set 从句 SQL 语法错误

  • <trim

    7.1 prefix 在前面添加内容
    7.2 prefixOverrides 去掉前面内容
    7.3 suffix 在后面添加内容
    7.4 suffixOverrieds 去掉后面内容
    7.5 执行顺序去掉内容后添加内容

  • <bind 给参数重新赋值

    场景:8.2.1 模糊查询
    8.2.2 在原内容前或后添加内容

  <select id="selByLog" parameterType="log" resultType="log"> <bind name="accin" value="'%'+accin+'%'"/> #{money} </select>
  • <foreach

    循环参数内容,还具备在内容的前后添加内容,还具备添加分隔符功能.

    适用场景:in 查询中.批量新增中(mybatis 中 foreach 效率比较 低)

  • 和<include

    10.1 某些 SQL 片段如果希望复用,可以使用定义这个片段

  <sql id="mysql"> id,accin,accout,money </sql>

10.2 在<select或<delete或<update或<insert中使用<include引用

 <select id=""> select <include refid="mysql"></include> from log</select> 

其他

  • 数据库连接池

    1.在内存中开辟一块空间,存放多个数据库连接对象.
    2.JDBC Tomcat Pool,直接由 tomcat 产生数据库连接池

    3.1 active 状态:当前连接对象被应用程序使用中
    3.2 Idle 空闲状态:等待应用程序使用

    在高频率访问数据库时,使用数据库连接池可以降低服务器系
    统压力,提升程序运行效率.

    • 实现JDBC tomcat Pool 步骤
      1. META-INF 中存放 context.xml
  <?xml version="1.0" encoding="UTF-8"?> <Context> <Resource driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/ssm" username="root"password="smallming" maxActive="50" maxIdle="20" name="test" auth="Container" maxWait="10000" type="javax.sql.DataSource" /> </Context>
    1. 把项目发布到tomcat 中,数据库连接池产生了
    • 在 java 中使用 jndi 获取数据库连接池中对象
  Context cxt = new InitialContext(); DataSource ds = (DataSource) cxt.lookup("java:comp/env/test"); Connection conn = ds.getConnection();

当关闭连接对象时,把连接对象归还给数据库连接池,把状态 改变成 Idle

  • ThreadLocal

    线程容器,给线程绑定一个 Object 内容,后只要线程不变,可以随时 取出。改变线程,无法取出内容。

https://www.bilibili.com/video/BV137411V7Y1?p=678&spm_id_from=pageDriver
  final ThreadLocal<String> threadLocal = new ThreadLocal<>(); threadLocal.set("测试"); new Thread(){ public void run() { String result = threadLocal.get(); System.out.println("结果:"+result); }; }.start();
  • 缓存

    缓存存在的意义:让应用程序减少对数据库的访问,提升程序运行效率

    • MyBatis 中默认 SqlSession 缓存开启

      3.1 同一个 SqlSession 对象调用同一个<select时,只有第一次访问数据库,第一次之后把查询结果缓存到 SqlSession 缓存区(内存)中
      3.2 缓存的是 statement 对象.(简单记忆必须是用一个<select
      3.2.1 在 myabtis 时一个<select对应一个 statement 对象
      3.3 有效范围必须是同一个 SqlSession 对象

    • 缓存流程

      4.1 步骤一: 先去缓存区中找是否存在 statement
      4.2 步骤二:返回结果
      4.3 步骤三:如果没有缓存 statement 对象,去数据库获取数据
      4.4 步骤四:数据库返回查询结果
      4.5 步骤五:把查询结果放到对应的缓存区中

    • SqlSessionFactory 缓存

      5.1 又叫:二级缓存
      5.2 有效范围:同一个 factory 内哪个 SqlSession 都可以获取
      5.3 什么时候使用二级缓存?
      当数据频繁被使用,很少被修改

      5.4 使用二级缓存步骤
      5.4.1 在 mapper.xml 中添加
      5.4.2 如果不写 readOnly=”true”需要把实体类序列化

  <cache readOnly="true"></cache>

5.5 当 SqlSession 对象 close()时或 commit()时会把 SqlSession 缓存的数据刷(flush)到 SqlSessionFactory 缓存区中

运行原理

  • 运行过程中涉及到的类

    1.1 Resources MyBatis 中 IO 流的工具类
    1.1.1 加载配置文件

    1.2 SqlSessionFactoryBuilder() 构建器
    1.2.1 作用:创建 SqlSessionFactory 接口的实现类

    1.3 XMLConfigBuilder MyBatis 全局配置文件内容构建器类
    1.3.1 作用负责读取流内容并转换为 JAVA 代码.

    1.4 Configuration 封装了全局配置文件所有配置信息.
    1.4.1 全局配置文件内容存放在 Configuration 中

    1.5 DefaultSqlSessionFactory 是SqlSessionFactory接口的实现类

    1.6 Transaction 事务类
    16.1 每一个 SqlSession 会带有一个 Transaction 对象.

    1.7 TransactionFactory 事务工厂
    1.7.1 负责生产 Transaction

    1.8 Executor MyBatis 执行器1.8.1 作用:负责执行 SQL 命令
    1.8.2 相当于 JDBC 中 statement 对象(或 PreparedStatement
    或 CallableStatement)
    1.8.3 默认的执行器 SimpleExcutor
    1.8.4 批量操作 BatchExcutor
    1.8.5 通过 openSession(参数控制)

    1.9 DefaultSqlSession 是 SqlSession 接口的实现类
    1.10 ExceptionFactory MyBatis 中异常工厂

  • 流程图

  • 文字解释

    在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.下面
    需要实例化 SqlSessionFactoryBuilder 构建器.帮助 SqlSessionFactory 接
    口实现类 DefaultSqlSessionFactory.
    在实例化 DefaultSqlSessionFactory 之前需要先创建 XmlConfigBuilder
    解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把
    Configuratin 传递给 DefaultSqlSessionFactory.到此 SqlSessionFactory 工
    厂创建成功.
    由 SqlSessionFactory 工厂创建 SqlSession.
    每次创建 SqlSession 时,都需要由 TransactionFactory 创建 Transaction
    对象,同时还需要创建 SqlSession 的执行器 Excutor,最后实例化
    DefaultSqlSession,传递给 SqlSession 接口.
    根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作.
    如果事务执行失败,需要进行 rollback 回滚事务.
    如果事务执行成功提交给数据库.关闭 SqlSession
    到此就是 MyBatis 的运行原理.(面试官说的.)

Spring

Spring 几大核心功能
4.1 IoC/DI 控制反转/依赖注入
4.2 AOP 面向切面编程
4.3 声明式事务.

Spring Framework Runtime

  • test: spring 提供测试功能

  • Core Container:核心容器.Spring 启动最基本的条件

    • Beans : Spring 负责创建类对象并管理对象
    • Core: 核心类
    • Context: 上下文参数.获取外部资源或管理注解等
    • SpEl: expression.jar
  • AOP: 实现 aop 功能需要依赖

  • Aspects: 切面 AOP 依赖的包

  • Data Access/Integration : spring 封装数据访问层相关内容

    • JDBC : Spring 对 JDBC 封装后的代码.
    • ORM: 封装了持久层框架的代码.例如 Hibernate
    • transactions:对应 spring-tx.jar,声明式事务使用
  • WEB:需要 spring 完成 web 相关功能时需要

    • 例如:由 tomcat 加载 spring 配置文件时需要有 spring-web 包

环境搭建

    1. 导入 四个核心包一个日志包(commons-logging)
    1. 在 src 下新建 applicationContext.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" xsi:schemaLocation="http://www.springframework.org/sc hema/beans http://www.springframework.org/schema/beans/spring-be ans.xsd"> <!-- id 表示获取到对象标识 class 创建哪个类的对象 --> <bean id="peo" class="com.bjsxt.pojo.People"/> </beans>
  • 配置的信息最终存储到了 AppliationContext 容器中

    • spring 配置文件是基于 schema

      2.3.1 schema 文件扩展名.xsd

      2.3.2 把 schema 理解成 DTD 的升级版.
      2.3.2.1 比 DTD 具备更好的扩展性.

      2.3.3 每次引入一个 xsd 文件是一个 namespace(xmlns)

    1. 编写测试方法

    3.1 getBean(“标签 id 值”,返回值类型);如果没有第二个参数, 默认是 Object
    3.2 getBeanDefinitionNames(),Spring 容器中目前所有管理的所有对象

  ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xm l"); People people = ac.getBean("peo",People.class); System.out.println(people); // String[] names = ac.getBeanDefinitionNames(); // for (String string : names) { // System.out.println(string); // } 

Spring 创建对象的三种方式

  • 构造方法

    • 无参构造创建:默认情况

    • 有参构造创建:需要明确配置

      1.2.2.2 index : 参数的索引,从 0 开始
      1.2.2.3 name: 参数名
      1.2.2.4 type:类型(区分开关键字和封装类 int 和 Integer)

  <bean id="peo" class="com.bjsxt.pojo.People"> <!-- ref 引用另一个 bean value 基本数据类型或 String 等 --> <constructor-arg index="0" name="id" type="int" value="123"></constructor-arg> <constructor-arg index="1" name="name" type="java.lang.String" value="张三 "></constructor-arg> </bean>
  • 实例工厂

    • 实现步骤: 1. 必须要有一个实例工厂
 public class PeopleFactory { public People newInstance(){ return new People(1,"测试");} }
    1. 在 applicationContext.xml 中配置工厂对象和需要创建的对象
 <bean id="factory" class="com.bjsxt.pojo.PeopleFactory"></bean> <bean id="peo1" factory-bean="factory" factory-method="newInstance"></bean>
  • 静态工厂

    不需要创建工厂,快速创建对象

    • 实现步骤 1. 编写一个静态工厂(在方法上添加 static)
  public class PeopleFactory { public static People newInstance(){ return new People(1,"测试"); } } 
    1. applicationContext.xml
  <bean id="peo2" class="com.bjsxt.pojo.PeopleFactory" factory-method="newInstance"></bean>

Bean 属性赋值(注入)

    1. 通过构造方法设置值

<constructor-arg

    1. 设置注入(通过 set 方法)

DI

当一个类(A)中需要依赖另一个类()对象时,把 B 赋值给 A 的过
程就叫做依赖注入

<bean id="peo" class="com.bjsxt.pojo.People">
<property name="desk" ref="desk"></property>
</bean> <bean id="desk" class="com.bjsxt.pojo.Desk">
<property name="id" value="1"></property>
<property name="price" value="12"></property>
</bean>
  • ioc的特殊情况:给对象赋值,就是DI

AOP

在程序原有纵向执行流程中,针对某一个或某一些方法添加通
知,形成横切面过程就叫做面向切面编程

5.1 原有功能: 切点, pointcut
5.2 前置通知: 在切点之前执行的功能. before advice
5.3 后置通知: 在切点之后执行的功能,after advice
5.4 如果切点执行过程中出现异常,会触发异常通知.throws dvice
5.5 所有功能总称叫做切面.
5.6 织入: 把切面嵌入到原有功能的过程叫做织入

  • AOP 实现方式 1. Schema-based

    6.1.1 每个通知都需要实现接口或类
    6.1.2 配置 spring 配置文件时在<aop:config>配置

      1. 导入 jar
      1. 新建通知类

      2.1 新建前置通知类
      2.1.1 arg0: 切点方法对象 Method 对象
      2.1.2 arg1: 切点方法参数
      2.1.3 arg2:切点在哪个对象中

public class MyBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Objectarg2) throws Throwable { System.out.println("执行前置通知"); } }
    1. 配置 spring 配置文件

      3.1 引入 aop 命名空间
      3.2 配置通知类的<bean>
      3.3 配置切面
      3.4 * 通配符,匹配任意方法名,任意类名,任意一级包名
      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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/sc hema/beans http://www.springframework.org/schema/beans/spring-be ans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop. xsd"><!-- 配置通知类对象,在切面中引入 --> <bean id="mybefore" class="com.bjsxt.advice.MyBeforeAdvice"></bean><bean id="myafter" class="com.bjsxt.advice.MyAfterAdvice"></bean> <!-- 配置切面 --> <aop:config> <!-- 配置切点 --> <aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo2())" id="mypoint"/> <!-- 通知 --> <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/> <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/> </aop:config> <!-- 配置 Demo 类,测试使用 --> <bean id="demo" class="com.bjsxt.test.Demo"></bean> </beans> 
    1. 编写测试代码
public class Test { public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xm l"); Demo demo = ac.getBean("demo",Demo.class); demo.demo1(); demo.demo2(); demo.demo3();
    1. AspectJ

    6.2.1 每个通知不需要实现接口或类
    6.2.2 配置 spring 配置文件是在<aop:config>的子标签
    <aop:aspect>中配置

    • 配置异常通知的步骤

      1. 只有当切点报异常才能触发异常通知
      2. 在 spring 中有 AspectJ 方式提供了异常通知的办法.
    • 1 新建类,在类写任意名称的方法

 public class MyThrowAdvice{ public void myexception(Exception e1){ System.out.println("执行异常通知 "+e1.getMessage()); } }
  • 2 在 spring 配置文件中配置

    3.2.1 <aop:aspect>的 ref 属性表示:方法在哪个类中.
    3.2.2 <aop: xxxx/> 表示什么通知
    3.2.3 method: 当触发这个通知时,调用哪个方法
    3.2.4 throwing: 异常对象名,必须和通知中方法参数名相同(可
    以不在通知中声明异常对象)

<bean id="mythrow" class="com.bjsxt.advice.MyThrowAdvice"></bean> <aop:config> <aop:aspect ref="mythrow"> <aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo1())" id="mypoint"/> <aop:after-throwing method="myexception" pointcut-ref="mypoint" throwing="e1"/></aop:aspect> </aop:config> <bean id="demo" class="com.bjsxt.test.Demo"></bean>
  • 使用注解

spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解

引入 xmlns:context

 <context:component-scan base-package="com.bjsxt.advice"></context:component-scan>
  • @Component 相当于<bean/> 即类被 spring 管理

  • @Aspect 在方法上添加@Pointcut(“”) 定义切点

代理

设计模式:前人总结的一套解决特定问题的代码.

代理设计模式优点:
2.1 保护真实对象
2.2 让真实对象职责更明确.
2.3 扩展

  • 静态代理设计模式

    • 由代理对象代理所有真实对象的功能.

      1.1 自己编写代理类
      1.2 每个代理的功能需要单独编

  • 动态代理

    • JDK 提供的

      优点:jdk 自带,不需要额外导入 jar
      缺点:
      1.2.1 真实对象必须实现接口
      1.2.2 利用反射机制.效率不高

      代码流程:

      1. 创建Gongneng接口,Laozong类实现Gongneng里的方法chifan()
 2. Mishu类实现InvocationHandler(代理实例的调用处理程序实现的接口)该接口下只有invoke方法 private Laozong laozong=new Laozong(): public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{ System.out. println("预约时间");Object result=method.invoke(laozong,args); System.out.println("记录访客信息");return result;} 3.Women类 newProxyInstance参数一是反射时使用的类加载器,参数二是Proxy需要实现什么接口,参数三是通过接口对 象调用方法时,需要调用哪个类的invoke方法) Mishu mishu=new Mishu(); Gonneng gongneng=(Gongneng)Proxy.newProxyInstance(Women.class.getClassLoader(),new Class[] {Gongneng.class},mishu);gongneng.chifan();}
  • cglib 动态代理

    1.1 基于字节码,生成真实对象的子类.
    运行效率高于 JDK 动态代理.
    1.2 不需要实现接口
    使用 spring aop 时,只要出现 Proxy 和真实对象转换异常
    3.1 设置为 true 使用 cglib
    3.2 设置为 false 使用 jdk(默认值)

  <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
 Mishu类实现MethodInterceptor public Object intercept(Object arg0,Method arg1,Object[] arg2,MethodProxy arg 3) throws Throwable{ System.out.println("预约时间"); Object result=arg1.invoke(arg0,arg2);或者是arg3.invokeSuper(arg0,arg2) System.out.println("备注"); return result;} Women类 Enhancer enhancer=new Enhancer(): enhancer.setSuperclass(Laozong.class); enhancer.setCallback(new Mishu()); Laozong laozong=(Laozong) enhancer.create(); laozong.chifan();

自动注入

1.在 Spring 配置文件中对象名和 ref=”id”id 名相同使用自动注入,可以 不配置<property/

2.两种配置办法
2.1 在<bean中通过 autowire=”” 配置,只对这个<bean生效
2.2 在<beans中通过 default-autowire=””配置,表当当前文件中所 有<bean都是全局配置内容

3.autowire=”” 可取值
3.1 default: 默认值,根据全局 default-autowire=””值.默认全局和局部都没有配置情况下,相当于 no
3.2 no: 不自动注入3.3 byName: 通过名称自动注入.在 Spring 容器中找类的 Id
3.4 byType: 根据类型注入.
3.4.1 spring 容器中不可以出现两个相同类型的<bean
3.5 constructor: 根据构造方法注入.
3.5.1 提供对应参数的构造方法(构造方法参数中包含注入对 戏那个)
3.5.2 底层使用 byName, 构造方法参数名和其他<bean的 id 相同.

加载 properties 文件

在 spring 配置文件中先引入 xmlns:context,在下面添加

如果需要记载多个配置文件逗号分割

<context:property-placeholder
location="classpath:db.properties"/>
  • 在被Spring管理的类中通过@Value(“${key}”)取出properties中内容

单例设计模式

作用: 在应用程序有保证最多只能有一个实例
实现数据共享. 案例:application 对象

  • <bean/标签对应的对象默认是单例的. 无论获取多少次,都是同一个对象

    scope 可取值
    4.1 singleton 默认值,单例
    4.2 prototype 多例,每次获取重新实例化
    4.3 request 每次请求重新实例化
    4.4 session 每个会话对象内,对象是单例的.
    4.5 application 在 application 对象内是单例
    4.6 global session spring 推 出 的 一 个 对 象 , 依 赖 于 spring-webmvc-portlet ,类似于 session

声明式事务

1.编程式事务:
1.1 由程序员编程事务控制代码.
1.2 OpenSessionInView 编程式事务

2.声明式事务:
2.1 事务控制代码已经由 spring 写好.程序员只需要声明出哪些方法需要进行事务控制和如何进行事务控制.
3.声明式事务都是针对于 ServiceImpl 类下方法的.
4.事务管理器基于通知(advice)的.
5.在 spring 配置文件中配置声明式事务

<context:property-placeholder
location="classpath:db.properties,classpath:second.pr
operties"/>
<bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="${jdbc.driver}"></property>
<property name="url"
value="${jdbc.url}"></property>
<property name="username"
value="${jdbc.username}"></property>
<property name="password"
value="${jdbc.password}"></property>
</bean>
<!-- spring-jdbc.jar 中 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSour
ceTransactionManager">
<property name="dataSource"
ref="dataSource"></property>
</bean>
<!-- 配置声明式事务 -->
<tx:advice id="txAdvice"transaction-manager="txManager">
<tx:attributes>
<!-- 哪些方法需要有事务控制 -->
<!-- 方法以 ins 开头事务管理 -->
<tx:method name="ins*" />
<tx:method name="del*" />
<tx:method name="upd*" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切点范围设置大一些 -->
<aop:pointcut expression="execution(*
com.bjsxt.service.impl.*.*(..))"
id="mypoint" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="mypoint" />
</aop:config>

SpringMVC

简介

  • 重要组件

    • DispatcherServlet : 前端控制器,接收所有请求(如果配置/不包 含 jsp)
    • HandlerMapping: 解析请求格式的.判断希望要执行哪个具体 的方法
    • HandlerAdapter: 负责调用具体的方法
    • ViewResovler:视图解析器.解析结果,准备跳转到具体的物理视 图
  • 运行原理

    如果在 web.xml 中设置 DispatcherServlet 的为/时,当用户发 起 请 求 , 请 求 一 个 控 制 器 ,
    首 先 会 执 行 DispatcherServlet由DispatcherServlet调用HandlerMapping的DefaultAnnotationHandlerMapping 解 析 URL,
    解 析 后 调 用 HandlerAdatper 组 件 的 AnnotationMethodHandlerAdapter 调 用 Controller 中的 HandlerMethod.
    当 HandlerMethod 执行完成后会返回 View,会被 ViewResovler 进行视图解析,
    解析后调用 jsp 对应的.class 文 件并运行,
    最终把运行.class 文件的结果响应给客户端. 以上就是 springmvc 运行原理(给面试官说的

  • Spring 容器和 SpringMVC 容器是父子容器

  • 字符编码过滤器

    • 在 web.xml 中配置 Filter

      tomcat一启动就被实例化,等待回调

  <!-- 字符编码过滤器 --> <filter> <filter-name>encoding</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> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
  • 视图解析器

    • SpringMVC 会提供默认视图解析器
    • 自定义视图解析器
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalR esourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean>

如果希望不执行自定义视图解析器,在方法返回值前面添加
forward:或 redirect:

  • @ResponseBody

    • 在方法上添加@ResponseBody(恒不跳转)

      2.1 如果返回值满足 key-value 形式(对象或 map)
      2.1.1 把响应头设置为 application/json;charset=utf-8
      2.1.2 把转换后的内容输出流的形式响应给客户端.

      2.2 如果返回值不满足 key-value,例如返回值为 String
      2.2.1 把相应头设置为 text/html
      2.2.2 把方法返回值以流的形式直接输出.
      2.2.3 如果返回值包含中文,出现中文乱码
      2.2.3.1 produces 表示响应头中 Content-Type 取值.

@RequestMapping(value="demo12",produces="text/html; charset=utf-8")@ResponseBody public String demo12() throws IOException{ People p = new People(); p.setAge(12); p.setName("张三"); return "中文"; }
  1. 底层使用 Jackson 进行 json 转换,在项目中一定要导入 jackson 的 jar
    3.1 spring4.1.6 对 jackson 不支持较高版本,jackson 2.7 无效
  • JSP

    • 九大内置对象

      • 原理:编译后,在前面定义的九个对象
    • 四大作用域

      • 数据共享

环境搭建

    1. 导入 jar
    • spring-webmvc-4.1.6.RELEASE.jar
    1. 在 web.xml 中配置前端控制器 DispatcherServlet

    如 果 不 配 置 <init-param> 会 在 /WEB-INF/<servlet-name>-servlet.xml

 <servlet> <servlet-name>jqk</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet< SpringMVC 容器/servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jqk</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
    1. 在 src 下新建 springmvc.xml

    引入 xmlns:mvc 命名空间

  <?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 扫描注解 --> <context:component-scan base-package="com.bjsxt.controller"></context:component-scan> <!-- 注解驱动 --> <!-- org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandler Mapping --> <!-- org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerA dapter --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 静态资源 --> <mvc:resources location="/js/" mapping="/js/**"></mvc:resources> <mvc:resources location="/css/" mapping="/css/**"></mvc:resources> <mvc:resources location="/images/" mapping="/images/**"></mvc:resources> </beans> 
    1. 编写控制器类
  @Controller public class DemoController { //请求映射,就是url有项目名/demo2@RequestMapping("demo") public String demo(){ System.out.println("执行 demo"); //返回值由内置的视图解析器进行解析return "main.jsp"; }@RequestMapping("demo2") public String demo2(){ System.out.println("demo2"); return "main1.jsp"; } }

传参

  • 基本数据类型参数

    默认保证参数名称和请求中传递的参数名相同

 @Controller public class DemoController { @RequestMapping("demo") //想要参数是什么类型,就直接在这里写。People类也可以public String demo(String name,int age){ System.out.println("执行 demo"+" "+name+" "+age);//如果出现400错误,就是参数不能赋给属性,类型不匹配return "main.jsp"; } }

还可以获取httpservletrequestreq参数
req.setAttribute(“demo123”,“测试”);
前端就可以${demo123}获取到值

  • @RequestParam()

如果请求参数名和方法参数名不对应使用@RequestParam()赋 值

 @RequestMapping("demo") public String demo(@RequestParam(value="name1") String name,@RequestParam(value="age1")int age){ System.out.println("执行 demo"+" "+name+" "+age);return "main.jsp"; }

如果方法参数是基本数据类型(不是封装类)可以通过 @RequestParam 设置默认值.
2.3.1 防止没有参数时 500

 @RequestMapping("page") public String page(@RequestParam(defaultValue="2") int pageSize,@RequestParam(defaultValue="1") int pageNumber){ System.out.println(pageSize+" "+pageNumber); return "main.jsp"; }

如果强制要求必须有某个参数

 @RequestMapping("demo2") public String demo2(@RequestParam(required=true) String name){ System.out.println("name 是 SQL 的查询条件,必须要传 递 name 参数"+name); return "main.jsp"; }
  • restful 传值方式

    7.1 简化 jsp 中参数编写格式
    7.2 在 jsp 中设定特定的格式

  <a href="demo8/123/abc">跳转</a>

7.3 在控制器中
7.3.1 在@RequestMapping 中一定要和请求格式对应
7.3.2 {名称} 中名称自定义名称
7.3.3 @PathVariable 获取@RequestMapping 中内容,默认按照
方法参数名称去寻找.

@RequestMapping("demo8/{id1}/{name}") public String demo8(@PathVariable String name,@PathVariable("id1") int age){ System.out.println(name +" "+age); return "/main.jsp"; }
  • 把内容写到方法(HandlerMethod)参数中,SpringMVC 只要有这个内 容,注入内容

  • HandlerMethod 中参数是对象类型

    请求参数名和对象中属性名对应(get/set 方法)

@RequestMapping("demo4") public String demo4(People peo){ return "main.jsp"; }
  • 请求参数中包含多个同名参数的获取方式

    复选框传递的参数就是多个同名参数

  @RequestMapping("demo5") public String demo5(String name,int age,@RequestParam("hover")List<String> abc){ System.out.println(name+" "+age+" "+abc); return "main.jsp"; }
  • 请求参数中对象.属性格式

    jsp 中代码

  <input type="text" name="peo.name"/> <input type="text" name="peo.age"/>

新建一个类 :对象名和参数中点前面名称对应

 public class Demo { private People peo; 控制器 @RequestMapping("demo6") public String demo6(Demo demo){ System.out.println(demo); return "main.jsp"; }
  • 在请求参数中传递集合对象类型参数

    jsp 中格式

  <input type="text" name="peo[0].name"/> <input type="text" name="peo[0].age"/> <input type="text" name="peo[1].name"/> <input type="text" name="peo[1].age"/>

新建类

  public class Demo {private List<People> peo; 控制器 @RequestMapping("demo6") public String demo6(Demo demo){ System.out.println(demo); return "main.jsp"; }
  • 默认跳转方式请求转发

作用域传值

  • 使用原生 Servlet
  @RequestMapping("demo1") public String demo1(HttpServletRequest abc,HttpSession sessionParam){ //request 作用域 abc.setAttribute("req", "req 的值"); //session 作用域 HttpSession session = abc.getSession(); session.setAttribute("session", "session 的值"); sessionParam.setAttribute("sessionParam", "sessionParam 的值"); //appliaction 作用域 ServletContext application = abc.getServletContext(); application.setAttribute("application","application 的值"); return "/index.jsp"; } 
  • 在 HanlderMethod 参数中添加作用域对象

  • 使用 Map 集合

    2.1 把 map 中内容放在 request 作用域中
    2.2 spring 会对 map 集合通过 BindingAwareModelMap 进行实例化

@RequestMapping("demo2") public String demo2(Map<String,Object> map){ System.out.println(map.getClass()); map.put("map","map 的值"); return "/index.jsp"; }
  • 使用 SpringMVC 中 Model 接口

    把内容最终放入到 request 作用域中.

@RequestMapping("demo3") public String demo3(Model model){ model.addAttribute("model", "model 的值"); return "/index.jsp"; }
  • 使用 SpringMVC 中 ModelAndView 类
  @RequestMapping("demo4")public ModelAndView demo4(){ //参数,跳转视图 ModelAndView mav = new ModelAndView("/index.jsp"); mav.addObject("mav", "mav 的值"); return mav; }

文件下载

访问资源时相应头如果没有设置 Content-Disposition,浏览器默认按照 inline 值进行处理。inline 能显示就显示,不能显示就下载.

只需要修改相应头中 Context-Disposition=”attachment;filename=文件名”
2.1 attachment 下载,以附件形式下载.
2.2 filename=值就是下载时显示的下载文件名

  • 实现步骤

    1. 导入 apatch 的两个 jar
    1. 在 jsp 中添加超链接,设置要下载文件

    在 springmvc 中放行静态资源 files 文件夹<a href="download?fileName=a.rar">下载</a>

    1. 编写控制器方法
@RequestMapping("download") public void download(String fileName,HttpServletResponse res,HttpServletRequest req) throws IOException{ //设置响应流中文件进行下载 //这里filename=什么,下载到本地的文件名就是什么res.setHeader("Content-Disposition", "attachment;filename="+fileName); //把二进制流放入到响应体中. ServletOutputStream os = res.getOutputStream(); //获取到磁盘根目录下的路径String path = req.getServletContext().getRealPath("files"); System.out.println(path); File file = new File(path, fileName); //导入的jar包所封装的类byte[] bytes = FileUtils.readFileToByteArray(file); os.write(bytes); os.flush(); os.close(); }

文件上传

基于 apache 的 commons-fileupload.jar 完成文件上传.

  1. MultipartResovler 作用: (springMVC的组件)
    2.1 把客户端上传的文件流转换成 MutipartFile 封装类.
    2.2 通过 MutipartFile 封装类获取到文件流

  2. 表单数据类型分类
    3.1 在的 enctype 属性控制表单类型
    3.2 默认值 application/x-www-form-urlencoded,普通表单数据.(少 量文字信息)
    3.3 text/plain 大文字量时使用的类型.邮件,论文
    3.4 multipart/form-data 表单中包含二进制文件内容(这样表单才能上传文件流)

  • 实现步骤:

    1. 导入 springmvc 包和 apache 文件上传 commons-fileupload 和 commons-io 两个 jar
    1. 编写 JSP 页面

    post最大2GB,字节流
    get最大2kb,字符流

 <form action="upload" enctype="multipart/form-data" method="post"> 姓名:<input type="text" name="name"/><br/> 文件:<input type="file" name="file"/><br/> <input type="submit" value="提交"/> </form>
    1. 配置 springmvc.xml
  <!-- MultipartResovler 解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.Comm onsMultipartResolver"> <property name="maxUploadSize" value="50"></property> </bean> <!-- 异常解析器 --> <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.Simple MappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.springframework.web.multipart.MaxUploadSizeE xceededException">/error.jsp</prop> </props> </property> </bean> 
    1. 编写控制器类

    MultipartFile 对象名必须和的 name 属性值相同

@RequestMapping("upload") public String upload(MultipartFile file,String name) throws IOException{ String fileName = file.getOriginalFilename(); String suffix = fileName.substring(fileName.lastIndexOf(".")); //判断上传文件类型 if(suffix.equalsIgnoreCase(".png")){ String uuid = UUID.randomUUID().toString(); FileUtils.copyInputStreamToFile(file.getInputStream (), new File("E:/"+uuid+suffix)); return "/index.jsp"; }else{ return "error.jsp"; } }

自定义拦截器

1.跟过滤器比较像的技术.

2.发送请求时被拦截器拦截,在控制器的前后添加额外功能.
2.1 跟 AOP 区分开.AOP 在特定方法前后扩充(对 ServiceImpl)
2.2 拦截器,请求的拦截.针对点是控制器方法.(对 Controller)

3.SpringMVC 拦截器和 Filter 的区别
3.1 拦截器只能拦截器 Controller
3.2 Filter 可以拦截任何请求.

  • 实现步骤:1. 新建类实现 HandlerInterceptor
public class DemoInterceptor implements HandlerInterceptor { //在进入控制器之前执行 //如果返回值为 false,阻止进入控制器 //控制代码 @Overridepublic boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("arg2:"+arg2); System.out.println("preHandle"); return true; }//控制器执行完成,进入到 jsp 之前执行. //日志记录. //敏感词语过滤 @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("往"+arg3.getViewName()+"跳转 "); System.out.println("model 的值 "+arg3.getModel().get("model")); String word = arg3.getModel().get("model").toString();String newWord = word.replace("祖国", "**"); arg3.getModel().put("model", newWord); // arg3.getModel().put("model", "修改后的内容"); System.out.println("postHandle"); }//jsp 执行完成后执行 //记录执行过程中出现的异常. //可以把异常记录到日志中 @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("afterCompletion"+arg3.getMessag e()); } }
    1. 在 springmvc.xml 配置拦截器需要拦截哪些控制器

    拦截所有控制器

  <mvc:interceptors> <bean class="com.bjsxt.interceptor.DemoInterceptor"></bean></mvc:interceptors>

拦截特定的的 url

  <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/demo"/> <mvc:mapping path="/demo1"/> <mvc:mapping path="/demo2"/> <bean class="com.bjsxt.interceptor.DemoInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
  • 拦截器栈

    1. 多个拦截器同时生效时,组成了拦截器栈

    2. 顺序:先进后出.

    3. 执行顺序和在 springmvc.xml 中配置顺序有关

    4. 设置先配置拦截器 A 在配置拦截器 B 执行顺序为
      preHandle(A) --> preHandle(B) --> 控制器方法 --> postHandle(B) --> postHanle(A) --> JSP --> afterCompletion(B) --> afterCompletion(A)

SpringMVC 对 Date 类型转换

  • 在 springmvc.xml 中配置,代码中不需要做任何修改

    1.1 必须额外导入 joda-time.jar
    1.2 时间类型 java.sql.Date

<mvc:annotation-driven conversion-service="conversionService"></mvc:annotati on-driven><bean id="conversionService" class="org.springframework.format.support.Formattin gConversionServiceFactoryBean"> <property name="registerDefaultFormatters" value="false" /> <property name="formatters"> <set> <bean class="org.springframework.format.number.NumberForm atAnnotationFormatterFactory" /> </set> </property> <property name="formatterRegistrars"> <set> <bean class="org.springframework.format.datetime.joda.Jod aTimeFormatterRegistrar"><property name="dateFormatter"> <bean class="org.springframework.format.datetime.joda.Dat eTimeFormatterFactoryBean"> <property name="pattern" value="yyyy-MM-dd" /> </bean> </property> </bean> </set> </property> </bean>
  • 使 用 注 解 . 在 需 要 转 换 的 参 数 或 实 体 类 属 性 上 添 加 @DateTimeFormatter(pattern=”表达式”)

    2.1 使用 Date 参数接收

@RequestMapping("demo") public String demo(@DateTimeFormat(pattern="yyyy-MM-dd") Date time){ System.out.println(time); return "abc.jsp"; }

2.2 在实体类中

 @RequestMapping("demo") public String demo( Demo1 demo){ System.out.println(demo); return "abc.jsp"; } public class Demo1 { @DateTimeFormat(pattern="yyyy/MM/dd") private Date time;

2.3 注意地方:
2.3.1 不需要导入额外 jar
2.3.2 Date 是 java.util.Date

jFinal

其他

RBAC

  • 基于角色的访问控制

    一种思想.根据 RBAC 思想进行数据库设计,根据数据库设计更
    好的完成权限控制

    权限控制常用分类:
    4.1 菜单功能
    4.2 url 控制(控制访问不同的控制器.)
    4.3 资源可见性控制(页面某些元素对不同用户可见性是不同的 )

JUnit

JUnit来源
测试-------
黑盒
白盒,单元测试需要编码实现《单元测试之道》JUnit
JUnit使用--------
导入jar
@Test
断言Assert.assertEquals(期望值,实际值)

shiro

难点在于有很多名词

Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过
相应的接口注入给 Shiro 即可。

认证、 授权、加密、会话管理、与 Web 集成、缓存等

shiro的结构体系

  • Primary Concerns

    • Authentication

      用户是否合法

      身份认证/登录,验证用户是不是拥有相应的身份;

    • Authorization

      授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用 户是否能做事情,
      常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用 户对某个资源是否具有某个权限;

    • Session Manager

      会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信 息都在会话中;
      会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

      • 登录信息持久化

        用户登录后的用户信息通过session management来进行管理,不管在什么应用中。

    • Cryptography

      加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

      盐,散列

  • Supporting Features

    Web Support:Web 支持,可以非常容易的集成到 Web 环境;
    Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率; 支持多种缓存架构,如ehcache。还支持缓存数据库,如redis.
    Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能 把权限自动传播过去;
    Testing:提供测试支持;
    Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
    Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录
    了。

Shiro 架构

  • Subject

    需要进行认证和身份授权的用户或第三方程序,用于获取主体信息

    Subject 即主体,外部应用与 subject 进行交互,subject 记录了当前操作用户,将用户的 概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序
    Subject 在 shiro 中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过 subject 进行认证授,而 subject 是通过 SecurityManager 安全管理器进行认证授权.

    • 包含身份和凭证
  • SecurityManager

    SecurityManager 即安全管理器,对全部的 subject 进行安全管理,它是 shiro 的核心
    负责对所有的 subject 进行安全管理。通过 SecurityManager 可以完成 subject 的认证、授权 等,实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通
    过 SessionManager 进行会话管理等。
    SecurityManager 是一个接口,继承了 Authenticator, Authorizer, SessionManager 这三个接 口

    • 协调管理shiro各个组件
  • Authenticator

    Authenticator 即认证器,对用户身份进行认证,Authenticator 是一个接口,shiro 提供
    ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器

  • Authorizer

    Authorizer 即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用
    户是否有此功能的操作权限

  • realm

    Realm 即领,相当于 datasource 数据源,
    securityManager 进行安全认证需要通过 Realm
    获取用户权限数据,比如:如果用户身份数据在数据库那么 realm 就需要从数据库获取用户 身份信息。

    注意:不要把 realm 理解成只是从数据源取数据,在 realm 中还有认证授权校验的相关 的代码。

    • shiro不维护数据信息
  • 其他

    1.4.6 sessionManager
    sessionManager 即会话管理,shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,
    所以 shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性 可使它实现 单点登录
    1.4.7 SessionDAO
    SessionDAO 即会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库。
    1.4.8 CacheManager
    CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
    1.4.9 Cryptography
    Cryptography 即密码管理,shiro 提供了一套加密/解密的组件,方便开发。比如提供常 用的散列、加 / 解密等功能。

认证

在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来 表明他就是他本人,如提供身份证,用户名/密码来证明。 在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能 验证用户身份

  • principals

    • 主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可
  • credentials

    • 证明/凭证,只有主体知道的安全值,如密码/数字证书等

环境搭建

    1. 加入相关 jar 包
  • log4j.properties 日志配置文件
  log4j.rootLogger=debug, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    1. 配置 shiro 环境文件 shiro.ini

    ini文件可以进行数据分组

    通过 Shiro.ini 配置文件初始化 SecurityManager 环境。

  [users] zhangsan=1111 lisi=1111

写数据库里也行

    1. 代码实现
  //用户登录和退出 @Test public void testAuthenticator(){ // 构建 SecurityManager 工厂,IniSecurityManagerFactory 可以从 ini 文件中初始化 SecurityManager 环境 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //通过工厂获得 SecurityManager 实例SecurityManager securityManager = factory.getInstance(); //将 securityManager 设置到运行环境中 SecurityUtils.setSecurityManager(securityManager); //获取 subject 实例 Subject subject = SecurityUtils.getSubject(); //创建用户名,密码身份验证 Token 假装zhangsan在登陆UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "1111"); try {//登录,即身份验证 //←将token委托给securityManager接口--通过 //DefaultsecurityManager中的login---login调用了该 //类的authenticate()---authenticate()调用了 //authenticator(认证器)--authenticator的默认 //modularRealmAuthticator中的doAuthenticate来获取 //realms信息,来完成认证工作。subject.login(token); } catch (AuthenticationException e) { e.printStackTrace(); //身份认证失败 }//断言用户已经登录 Assert.assertEquals(true, subject.isAuthenticated()); //退出 subject.logout(); } 
  • 常见异常信息及处理:

    认证过程中有个父异常AuthenticationException子类AccountException,DisableAccountException等

自定义 Realm

Shiro 默认使用自带的 IniRealm,IniRealm 从 ini 配置文件中读取用户的信息,大部分情 况下需要从系统的数据库中读取用户信息,所以需要自定义 realm。

jdbcRealm实现从数据库获取用户的验证信息,但不够灵活。
最基础的是 Realm 接口(定义了根据token获取认证信息的方法),CachingRealm 负责缓存处理,AuthenticationRealm 负责认证, AuthorizingRealm 负责授权,通常自定义的 realm 继承 AuthorizingRealm。(既身份认证又授权)

  • 实现
  /*** 自定义 Realm 实现 */ public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; }//用于认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从 token 中获取身份信息 String username = (String)token.getPrincipal(); //根据用户名到数据库中取出用户信息 如果查询不到 返回 null String password = "1111";//假如从数据库中获取密码为 1111 //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, this.getName()); return simpleAuthenticationInfo; }//用于授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { return null; } }
  • 配置 Realm

    需要在 shiro.ini 配置 realm 注入到 securityManager 中。

  [main] #自定义 realmuserRealm=cn.siggy.realm.UserRealm #将 realm 设置到 securityManager securityManager.realms=$userRealm

散列算法

散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类 的数据,常见的散列算法如 MD5、SHA 等。一般进行散列时最好提供一个 salt(盐),比如 加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一 些 md5 解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和 ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解

  • MD5 算法
  public class ShiroTest { //shiro 提供了现成的加密类 Md5Hash @Test public void testMd5(){ //MD5 加密 String password = new Md5Hash("1111").toString(); System.out.println("加密后:"+password); //加盐 salt 默认一次散列 String password_salt=new Md5Hash("1111", "siggy").toString(); System.out.println("加盐后:"+password_salt); //散列 2 次 String password_salt_2 = new Md5Hash("1111", "siggy", 2).toString(); System.out.println("散列 2 次:"+password_salt_2); //使用 SimpleHashSimpleHash hash = new SimpleHash("MD5", "1111", "siggy", 2); System.out.println("simpleHash:"+hash.toString()); } }
  • 在自定义 Realm 中使用散列
 public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; }//用于认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从 token 中获取身份信息 String username = (String)token.getPrincipal(); //根据用户名到数据库中取出用户信息 如果查询不到 返回 null //按照固定规则加密码结果 ,此密码 要在数据库存储,原始密码 是 1111,盐是 siggy 2 次散列 String password = "1620d20433da92e2523928e351e90f97";//假 如从数据库中获取密码为 1111 //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes("siggy"),this.getName()); return simpleAuthenticationInfo; }//用于授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { return null; } }
  • Realm 配置

    Shiro.ini 在配置文件中,需指定凭证匹配器

[main] 配置应用程序的securitymanager实例及任何它的以来组件(如realms)

 [main]#定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #散列算法 credentialsMatcher.hashAlgorithmName=md5 #散列次数 credentialsMatcher.hashIterations=2 #将凭证匹配器设置到 realm userRealm=cn.siggy.realm.UserRealm userRealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$userRealm

[users]定义一组静态用户,角色
[roles]定义在[users]中的角色与权限关联起来

授权

也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面 操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

  • 关键对象

    主体:即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访 问相应的资源。

    资源:在应用中用户可以访问的任何东西,比如访问 JSP 页面、查看/编辑某些数据、访问某个业 务方法、打印文本等等都是资源。用户只要授权后才能访问。
    权限安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修
    改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等等。。。

    角色:代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权 限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

  • 权限粒度

    粗粒度:对表的操纵,如对user的crud
    细粒度:对记录的操作,如查询id=1的user的工资。
    shiro一般管理的是粗粒度的权限。如菜单,按钮,url
    细粒度权限一般是通过业务来控制的

    权限表示规则:
    资源:操作:实例,可以用通配符
    user:add
    user:delete:100 对user标识为100的记录有删除权限

  • 授权流程

    流程如下:
    1、首先调用 Subject.isPermitted*/hasRole接口,其会委托给 SecurityManager,而
    SecurityManager 接着会委托给 Authorizer;
    2、Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过
    PermissionResolver 把字符串转换成相应的 Permission 实例;
    3、在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的
    角色/权限;
    4、Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给
    ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted
    /hasRole*会返回 true,否
    则返回 false 表示授权失败。

  • 授权方式

    Shiro 支持三种方式的授权:

    编程式:通过写 if/else 授权代码块完成:

  Subject subject = SecurityUtils.getSubject();if(subject.hasRole(“admin”)) { //有权限 } else { //无权限 }

注解式:通过在执行的 Java 方法上放置相应的注解完成:

  @RequiresRoles("admin") public void hello() { //有权限 }

没有权限将抛出相应的异常;

JSP/GSP 标签:在 JSP/GSP 页面通过相应的标签完成:

  <shiro:hasRole name="admin"> <!— 有权限—> </shiro:hasRole>
  • 授权实现

    • 在 ini 配置文件配置用户拥有的角色及角色-权限关系 (shiro-permission.ini)
 [users] zhangsan=1111,role1,role2 lisi=1111,role1 [roles] role1=user:create,user:update role2=user:create,user:delete
  • 实现代码
  public class ShiroTest { //用户登录和退出 @Test public void testPermission(){ // 构建 SecurityManager 工厂,IniSecurityManagerFactory 可以 从 ini 文件中初始化 SecurityManager 环境 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini"); //通过工厂获得 SecurityManager 实例 SecurityManager securityManager = factory.getInstance(); //将 securityManager 设置到运行环境中 SecurityUtils.setSecurityManager(securityManager); //获取 subject 实例 Subject subject = SecurityUtils.getSubject(); //创建用户名,密码身份验证 Token UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "1111"); try {//登录,即身份验证 subject.login(token); } catch (AuthenticationException e) { e.printStackTrace(); //身份认证失败 }// 用户认证状态 boolean isAuthenticated = subject.isAuthenticated(); System.out.println("用户认证状态:" + isAuthenticated); //判断拥有角色:role1 Assert.assertTrue(subject.hasRole("role1")); //判断拥有角色:role1 and role2 Assert.assertTrue(subject.hasAllRoles(Arrays.asList("role1" , "role2"))); //判断拥有角色:role1 and role2 and !role3boolean[] result = subject.hasRoles(Arrays.asList("role1", "role2", "role3")); Assert.assertEquals(true, result[0]); Assert.assertEquals(true, result[1]); Assert.assertEquals(false, result[2]); //判断拥有权限:user:create Assert.assertTrue(subject.isPermitted("user:create")); //判断拥有权限:user:update and user:delete Assert.assertTrue(subject.isPermittedAll("user:update", "user:delete")); //判断没有权限:user:view Assert.assertFalse(subject.isPermitted("user:view")); } }
  • 自定义 Realm 实现授权

与上边认证自定义 realm 一样,大部分情况是要从数据库获取权限数据,这里直接实现 基于资源的授权。

  • UserRealm 实现代码
  public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; }//用于认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从 token 中获取身份信息String username = (String)token.getPrincipal(); //根据用户名到数据库中取出用户信息 如果查询不到 返回 null String password = "1111";//假如从数据库中获取密码为 1111 //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, this.getName()); return simpleAuthenticationInfo; }//用于授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { //获取身份信息 String username = (String)principals.getPrimaryPrincipal(); //根据身份信息获取权限数据 //模拟 List<String> permissions = new ArrayList<String>(); permissions.add("user:save"); permissions.add("user:delete"); //将权限信息保存到 AuthorizationInfo 中 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for(String permission:permissions){ simpleAuthorizationInfo.addStringPermission(permission); }return simpleAuthorizationInfo; } }
  • 配置文件

        [
    
main] #自定义 realm userRealm=cn.siggy.realm.UserRealm #将 realm 设置到 securityManager securityManager.realms=$userRealm
  • 测试代码
public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; }//用于认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从 token 中获取身份信息 String username = (String)token.getPrincipal(); //根据用户名到数据库中取出用户信息 如果查询不到 返回 null String password = "1111";//假如从数据库中获取密码为 1111 //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, this.getName()); return simpleAuthenticationInfo; }//用于授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { //获取身份信息 String username = (String)principals.getPrimaryPrincipal(); //根据身份信息获取权限数据 //模拟 List<String> permissions = new ArrayList<String>(); permissions.add("user:save"); permissions.add("user:update"); permissions.add("user:delete"); //将权限信息保存到 AuthorizationInfo 中 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for(String permission:permissions){ simpleAuthorizationInfo.addStringPermission(permission); }return simpleAuthorizationInfo; } }

shiro 与项目集成开发

完成 springmvc+spring+mybatis 整合
整合 shiro

  • web.xml 中配置 shiro 的 filter

    !-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和
    filter关联起来 -->

  <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</ filter-class> <!-- 设置true由servlet容器控制filter的生命周期 --> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> <!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致 的bean--><init-param> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
  • 在 spring 中配置 shiro
  <!-- web.xml 中 shiro 的 filter 对应的 bean --> <!-- Shiro 的 Web 过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- loginUrl 认证提交地址,如果没有认证将会请求此地址进行认证, 请求此地址将由 formAuthenticationFilter 进行表单认证 --> <property name="loginUrl" value="/login.do" /> <!-- 认证成功统一跳转到 index.do,建议不配置,shiro 认证成功自 动到上一个请求路径 --> <property name="successUrl" value="/index.do"/> <!-- 通过 unauthorizedUrl 指定没有权限操作时跳转页面--> <property name="unauthorizedUrl" value="/refuse.jsp" /> <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 --> <property name="filterChainDefinitions"> <value> <!-- /** = authc 所有 url 都必须认证通过才可以访问--> /login.jsp=anon /** = authc <!-- /** = anon 所有 url 都可以匿名访问 --> </value> </property> </bean> <!-- securityManager 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm" /> </bean> <!-- realm --> <bean id="userRealm" class="cn.siggy.realm.UserRealm"> <!-- 将凭证匹配器设置到 realm 中,realm 按照凭证匹配器的要求进 行散列 --> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!-- 凭证匹配器 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="md5" /> <property name="hashIterations" value="1" /> </bean>

登录

  • 原理

    Shiro 内置了很多默认的过滤器,比如身份验证、授权等相关的。默认过滤器可以参考
    org.apache.shiro.web.filter.mgt.DefaultFilter 中的过滤器:

    anon:例子/admins/=anon 没有参数,表示可以匿名使用。
    authc:例如/admins/user/
    =authc 表示需要认证(登录)才能使用,FormAuthenticationFilter 是 表单认证,没有参数 使用 FormAuthenticationFilter 过虑器实现 ,原理如下:将用户没有认证时,请求 loginurl 进行认证,用户身份和用户密码提交数据到 loginurl

    FormAuthenticationFilter 拦截住取出 request 中的 username 和 password(两个参数 名称是可以配置的)

    FormAuthenticationFilter 调用 realm 传入一个 token( username 和 password)

    realm 认证时根据 username 查询用户信息(在 Activeuser 中存储,包括 userid、 usercode、username、menus)。
    如果查询不到,realm 返回 null,FormAuthenticationFilter 向 request 域中填充一个 参数(记录了异常信息)

  • 登陆页面

    由于 FormAuthenticationFilter 的用户身份和密码的 input 的默认值(
    username 和
    过滤器简称
    对应的 java 类
    anon
    org.apache.shiro.web.filter.authc.AnonymousFilter
    authc
    org.apache.shiro.web.filter.authc.FormAuthenticationFilter
    authcBasic
    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
    perms
    org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
    port
    org.apache.shiro.web.filter.authz.PortFilter
    rest
    org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
    roles
    org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
    ssl
    org.apache.shiro.web.filter.authz.SslFilter
    user
    org.apache.shiro.web.filter.authc.UserFilter
    logout
    org.apache.shiro.web.filter.authc.LogoutFilterpassword),修改页面的账号和密码 的 input 的名称为 username 和 password

  • 代码实现

@Controller public class LoginController { @RequestMapping("/login.do") public String login(HttpServletRequest req,Model model){ String exceptionClassName = (String)req.getAttribute("shiroLoginFailure"); String error = null; if(UnknownAccountException.class.getName().equals(exception ClassName)) { error = "用户名/密码错误"; } else if(IncorrectCredentialsException.class.getName().equals(except ionClassName)) {error = "用户名/密码错误"; } else if(exceptionClassName != null) { error = "其他错误:" + exceptionClassName; }model.addAttribute("error", error); return "redirect:login.jsp"; } }

Hibernate

持久化:数据从瞬时状态转化为持久状态

Hibernate:是一个轻量级的持久化框架。没有侵入性。是一个 orm 映射框架。简化了 jdbc操作。极大了提高了开发效率。提供了缓存机制。强大的查询机制。支持多种数据库(数据库移植)

映射规则:
将类名映射数据库的表名
将类的属性名映射为表的字段名
将类的属性类型映射为表的字段的数据类型
将对象的属性映射为表的记录

环境搭建

    1. 导入相关 jar 包
    1. 编写 hibernate.cfg.xml 文件

    (拷贝 project/etc/hibernate.cfg.xml)

  <hibernate-configuration> <session-factory> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/hibernate4</ property> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> </session-factory> </hibernate-configuration>
    1. 数据库表准备
    1. 编写实体类

    pojo对象必须提供无参构造方法
    推荐实现序列化
    重写hashCode和equals

  public class User { private int id; private String name;private int age; public User() { } } 
    1. 编写实体类的映射文件(*.,hbm.xml)

    将映射文件加入到 hibernate.cfg.xml

  <hibernate-mapping package="cn.sxt.pojo"> <class name="User" table="t_user"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="age"/> </class> </hibernate-mapping>
    1. 编写测试类
 public class Test { public static void main(String[] args) { //读取 src 下 hibernate.cfg.xml Configuration cfg = new Configuration().configure(); //获取注册对象 4.3 的创建办法 //创建服务注册ServiceRegistry registry = new StandardServiceRegistryBuilder() .applySettings(cfg.getProperties()) .build(); SessionFactory factory = cfg.buildSessionFactory(registry); //不是http的sessionSession session = factory.openSession(); //保存数据 Transaction tx = session.beginTransaction(); User u = new User("张三",22); session.save(u); tx.commit(); session.close(); } }

别的获取数据的方法

  User user=(User)session.get(User.class,1);

配置文件讲解

  • hibernate.cfg.xml 配置文件
 <hibernate-configuration> <!-- session 工厂的配置 --> <session-factory> <!-- jdbc 配置 --> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="connection.url"> <!-- url 的两种配置方式 --> <!-- jdbc:mysql://localhost:3306/hibernate4--> jdbc:mysql:///hibernate4 </property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 数据库方言 hibernate 支持多种数据库,通过设置方言 hibernate 才知道应该 生成对应数据库的 sql 语句:hibernate 支持的数据库的方言 hibernate.properties 文件中都有 --> <property name="dialect"> org.hibernate.dialect.MySQL5Dialect </property> <!-- 打印 hibernate 生成的 sql 语句 --> <property name="show_sql">true</property> <!-- 格式化打印的 sql 语句 --> <property name="format_sql">true</property> <!-- 根据不同值,进行数据表表的操作 create 每次执行 都删除原有表,然后创建新表 create-drop 执行前创建表,执行后删除表 update 如果有则不改变表,如果没有则创建 --> <property name="hbm2ddl.auto">update</property> <!-- 将所有映射文件添加到这里 --> <mapping resource="cn/sxt/pojo/User.hbm.xml"/> </session-factory> </hibernate-configuration>
  • *.hbm.xml 映射文件
 <!-- 类的映射文件信息 --> <!-- package 指定类的包名 可以不配置 如果不配置 那么在配置 class 的 name 时需要指定该类所在包--> <hibernate-mapping><!-- class 配置类 name 指类名 table 指定表名 如果不写,默认类名为表名--> <class name="cn.sxt.pojo.User" table="t_user"> <!-- id 主键的配置 name 配置类的属性名 column 数据库字段名 不写和属性名一致 type 指定属性的类型 length 指定字段的长度 --> <id name="id" column="id"> <!-- 主键的生成策略increment 用于为 long, short 或者 int 类型生成 唯一标识。只有在 没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。 identity 对 DB2,MySQL, MS SQL Server, Sybase 和 HypersonicSQL的内置标识字段提供支持。 返回的标识符是 long, short 或 者 int 类型的。native -(如果是 mysql 自增,那么 native 和 identity 是一 样) 根据底层数据库的能力选择 identity, sequence 或者 hilo 中的一个。sequence 在 DB2,PostgreSQL, Oracle, SAP DB, McKoi 中使用序 列(sequence), 而在 Interbase 中使用生成器(generator)。返回的标识 符是 long, short 或者 int 类型的。 <generator class="sequence"> <param name="sequence">user_seq</param> </generator> assigned 让应用程序在 save()之前为对象分配一个标示符。这是<generator>元素没有指定时 的默认生成策略。 --> <generator class="identity"> </generator> </id> <!-- property 是配置类的属性 name 指属性名 --> <property name="name" length="40"/> <property name="age" /> </class> </hibernate-mapping>

hibernate 对象生命周期

对象的 3 种状态:
a) 临时状态/瞬时状态
该对象是新创建的;一个持久化状态的对象被删除;一个游离状态的数据被删除
b) 持久化状态
对象从数据库中查询出来时,临时状态的数据被保存时,游离状态的数据被更新/ 锁定
c) 游离状态
持久化状态的数据被(session)清理

总结:
临时状态:内存有,数据库没有
持久状态:内存有,session 有,数据库有
游离状态:内存有,数据有

Struts2

环境搭建

  • 导入所需 jar 包
  • 配置 struts2 的核心控制器 web.xml 文件
 <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.Stru tsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
  • 在 src 下添加 struts2 的配置文件,struts.xml

    struts.xml–文件名不能改

    Xml 文件的头文件—拷贝一个 struts.xml 文件,或者到 core 包下的 struts-default.xml 文件中拷贝。

    注:到这里 struts2 的框架搭建完成

  • 编写 Action 类

 public class HelloAction { public String execute(){ System.out.println("hello struts2"); return "success"; } }

注:在 servlet 中,默认执行 service 方法。在 struts2 中,默认执行 execute 方法。
在 servlet 中,service 方法参数时 HttpServletRequest 和 HttpServletResponse,无返回 值。在 struts2 中,方法都是 public 的,并且返回值都是 String 类型,而且方法都是没 有参数的。

  • 配置 action 类

    在 struts.xml 文件中,配置 HelloAction
    Struts2 是基于包管理的。

  <!--extends 必须写,直接或者间接继承 struts-default name 自定义 --><package name="hello" extends="struts-default"> <!-- name 是请求名称,不要写/;class 对应 action 完全限定名=包 名+类名 --> <action name="hello" class="cn.sxt.action.HelloAction"> <!-- result 是结果集 name 和对应 action 中的方法的返回值匹 配,默认是 success --> <result name="success">/index.jsp</result> </action> </package>
  • 访问:http://localhost:8080/02struts2_hello/hello

  • 执行流程

    当用户提交一个请求,服务器接收,并且交给 struts2 的核心过滤器来进行处理,struts2 的过滤器调用 struts2 的一系列处理器来进行处理(如:解析 struts.xml 配置文件,和用户提 交的请求对比,如果找不到返回 404,如果找到进行下一步处理。)直到调用对应的 Action 类中的 execute 方法执行,执行完后再进行一系列处理到核心过滤器。由核心过滤器返回给
    服务器,服务器对浏览器进行相应的响应。

发起请求—》服务器接受请求并交给struts2前端控制器–》根据请求的url查看struts.xml 中的 namespace + actionName —》执行action所对应的类的对应方法—》根据方法的执行结果到action的结果集进行匹配–》响应结果

简介

  • 回顾

    在 jsp/servlet 开发中,首先使用的是 Model1 开发模式,在 jsp 内嵌 javaBean 代码。好处 是执行效率比较高。在项目规模比较大的时候,代码非常乱,维护起来非常麻烦。不利于分 工,也不利于代码的重用。

    Model2 即MVC 将代码分为了 3 块,视图 View,模型 Model, 控制器 Controller。内容和显示进行分离,开发人员可以专注于某一块,从而提高效率。适 合的项目规模比较大的情况。而且重复利用率得到提高,便于维护。

    框架:框架替程序员完成一部分的代码。从而提高开发效率

    struts1出现很早,名气很大,局限性(支持前端的技术很窄,只jsp) 然后出现了webwork,支持新的技术,名气不大。
    struts2是轻量级的mvc框架
    轻量级:该框架没有侵入性
    侵入性:如果使用一个框架,必须实现框架提供的接口,或继承框架提供的类,就是 有侵入性。

    mvc 框架完成的事情:
    Servlet 做哪些事情:
    处理用户提交的数据
    调用业务方法
    处理业务结果
    控制视图显示
    用户请求映射到一个 java 类。

    Mvc 框架做的事情:
    将用户请求映射到一个 java 类。
    获取用户提交的数据
    渲染数据(将数据封装到前台显示(
    request))
    控制视图跳转

    • 重要的是学习如何去学习框架
  • struts2 线程安全

    1、线程安全:在一个进程中有多个线程并发执行,每个线程执行过程中,变量值是相同的, 执行结果也是相同的。

    2、Struts2 线程安全吗?
    每次请求都会重新创建新的 action 对象,所以线程安全。
    由于 action 对象是 struts2,反射生成的,所以要求 Action 类要有一个公共的无参构造 方法。

  • token令牌

    防止用户重复提交,造成冗余数据,使用token默 认拦截器解决。后台生成token令牌,一份存在 session中,一份存在jsp页面展示提交到action ,对比session中的token值,
    1.一样的话就是首 次提交。然后清除session中的token值。
    2.如果 session中没有这个token值,那就是重复提交。

  • struts2 的 ajax

    • 使用 ServletApi 实现 ajax

      • Jsp 页面
<script type="text/javascript" src="js/jquery-1.11.3.js"></script> <script type="text/javascript"> $(function(){ $('#btn').click(function(){ $.post("ajax.action",function(data){ $('#msg').html(data); }); }); }); </script> </head> <body> <input type="button" id="btn" value="获取 ajax 信息"/> <h3 id="msg"></h3> </body>
  • Action 代码
  public class AjaxAction { public String execute() throws IOException{ HttpServletResponse resp = ServletActionContext.getResponse(); resp.setCharacterEncoding("utf-8"); resp.getWriter().print("struts ajax"); return null; } }
  • struts.xml 配置文件
 <package name="default" extends="list-default" namespace="/"> <action name="ajax" class="cn.sxt.action.AjaxAction"> </action> </package>
  • 使用 struts2 插件来实现 ajax 处理(返回 json 格式数据)

  • 导入相关的 jar 包

        ezmorph-1.0.6.jar struts2-json-plugin-2.2.1.jar json-lib-2.1-jdk15.jar
    
  • Action 代码

 public class JsonAction { private JSONArray root;public String execute(){ List<User> list = new ArrayList<User>(); list.add(new User("siggy",23)); list.add(new User("zhangsan",22)); list.add(new User("老王",21)); root = JSONArray.fromObject(list); System.out.println("json="+root.toString()); return "success"; }public JSONArray getRoot() { System.out.println("获取 root 数据"); return root; }public void setRoot(JSONArray root) { this.root = root; } }
  • Jsp 代码
<script type="text/javascript" src="js/jquery-1.11.3.js"></script> <script type="text/javascript"> $(function(){ $('#btn').click(function(){ $.post("json.action",function(data){ var html=""; for(var i=0;i<data.length;i++){ html+="<tr><td>"+data[i].name+"</td><td>"+data[i].age+"</td ></tr>"; }$('#content').html(html); },'json'); }); }); </script> </head> <body> <input type="button" id="btn" value="获取 json 数据"/> <table width="80%" align="center"> <tr><td>姓名</td><td>年龄</td> </tr> <tbody id="content"> </tbody> </table> </body>
  • struts.xml 配置
<package name="default" extends="json-default" namespace="/"> <action name="json" class="cn.sxt.action.JsonAction"> <result type="json"> <param name="root">root</param> </result> </action> </package>
  • 异常处理

配置文件

  • 常量配置

    1)乱码解决

 <constant name="struts.i18n.encoding" value="UTF-8"/>

2)自定义扩展名

 <constant name="struts.action.extension" value="action,,siggy"/>

3)友好的提示信息

  <!-- 设置开发模式 --> <constant name="struts.devMode" value="true"/>

4)设置配置文件修改后自动加载–推荐在开发中使用

  <constant name="struts.configuration.xml.reload" value="true"/>
  • 常量配置方式二

    • 在 src 下添加 struts.properties 配置文件
  • 团队协作开发配置

    通过 include 添加不同人员的配置文件

  <include file="config/sxt/struts/user.xml"/>
  • 配置文件加载顺序

    • struts-default.xml---->struts-plugin.xml---->struts.xml
  • package 的配置

<!-- 包的名称自定义,可以配置多个包;
namespace 命名空间:不同模块可以 指定不同的空间 extends 值是直接或者间接继承 struts-default --> <package name="user" namespace="/user" extends="struts-default">
  • action 的配置

    class不写的话就是默认类

  <!-- name 是 url 请求名,不需要加后缀(.action) class 是处理 url 请求对应的 java 类,class 要求包名+类名 并且该类是由公共的无参构造方法的。 method 配置处理请求类的处理的方法,默认为 execute;方法要满 足是公共的,返回值类型是 String,无参 method 和 name 无关 --> <action name="login" class="cn.sxt.action.LoginAction">
  • 减少 action 的配置还可以使用 DMI(动态方法调用),不推荐存在安全隐患

在常量配置中开启DMI
配置不写method
调用处理方法actionName!methodNmae.action

  • 使用注解方法减少action的配置

  • result 配置

 <!-- result-指结果集 name:匹配请求处理方法的返回值;默认是 success type:结果处理类型;默认是 dispather 转发type 有哪些: <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResu lt"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" /> chain:指 action 链,链接下一个 action;执行 actionA 以 后直接执行 ActionB 后直接执行 ActionC,用得较少 地址栏是执行的第一 action ***dispatcher:转发;和 servlet 一致。如果 request 中 有数据要到视图显示,那么使用 dispatcher ***redirect:重定向;如果是重定向到 jsp 页面,可以直接 重定向,如果是重定向到另一个 action,需注意 是否配置了 action 的后缀名。如果要求有后缀名,那么重 定向的 action 一定要加上后缀名 ***redirectAction:重定向导另一个 action;不用加 action 的后缀名,会将前一个 action 的后缀名自动加上 ***stream:以流的形式显示---文件下载 --> <result type="redirectAction">logout</result>
- struts2默认提供了五种返回结果,success,none,error,input,login
  • 全局配置
 <!-- 配置全局结果集 --> <global-results> <result name="login">/login.jsp</result> </global-results>

在 action 的配置中,如果不去配置 class 属性,将会由默认的 action 来执行,默认的 action 是 ActionSuppot 类。

  <!-- 配置默认的 Action 当所请求的 action 不存在时,那么执行默认的 action--> <default-action-ref name="default"/> <!-- 配置默认执行的 class --> <default-class-ref class="cn.sxt.action.AddAction"/>
  • 通配符的配置
  <!-- 使用通配符来配置 action,可以减少 action 的配置 *表示匹配所有,占 位符用{1}表示 第一个*所代表的内容 --> <action name="user*" class="cn.sxt.action.AddAction" method="{1}"> <result>/index.jsp</result> </action>

Action 的实现方式

  • 1、定义一个 pojo 类

    无侵入性,将来好换框架

    好处:自定义一个普通的 java 类即可,不具有侵入型

public class PojoAction { public String execute(){ System.out.println("pojo action"); return "success"; } }
  • 2、实现 Action 接口

    大家使用的比较少
    可以直接使用action提供的常量

  public class InterfaceAction implements Action{ public String execute() throws Exception { System.out.println("interface action"); return SUCCESS; } }
  • 3、继承 ActionSupport 类

    好处:可以继承一些 ActionSuport 实现功能,如:验证;官方推荐使用

public class ExtendsAction extends ActionSupport{ @Override public String execute() throws Exception { System.out.println("extends action"); return SUCCESS; } }

获取表单数据

  • 属性驱动

    使用struts2获取表单数据,只需表单域名称和action处理类的属性名称一致,并提供属性的set方法,那 么在action处理类中即可获得表单数据。这种获取数据方式称为属性驱动。
    如果数据需要显示到页面上,那么该数据可以作为处理类的属性,处理方法后该属性有值,且有该属性的get方法,那么在页面上可以直接通过el表达式获取。
    由主动编程,变为被动编程。ioc 值由框架获得,赋值

    • 以对象的方式处理表单数据

      • Jsp 页面
  <form action="register.action" method="post"> 用户名:<input type="text" name="user.name" /><br> 密码:<input type="password" name="user.pwd" /><br> 年龄:<input type="text" name="user.age" /><br> 邮箱:<input type="text" name="user.email" /><br> <input type="submit" value="提交" /> </form>
  • Action 代码
  public class UserAction { //对象声明即可private User user; //注册 public String register(){ System.out.println(user); return Action.SUCCESS; }//框架通过反射 class的newinstance创建 对象。所以要有无//参构造public User getUser() { return user; }public void setUser(User user) { this.user = user; } }
  • User 类型
public class User { private String name; private String pwd; private int age; private String email; public String getName() { return name; }public void setName(String name) { this.name = name; }public String getPwd() { return pwd; }public void setPwd(String pwd) { this.pwd = pwd; }public int getAge() {return age; }public void setAge(int age) { this.age = age; }public String getEmail() { return email; }public void setEmail(String email) { this.email = email; }@Override public String toString() { return "User [name=" + name + ", pwd=" + pwd + ", age=" + age + ", email=" + email + "]"; } }
  • struts.xml 文件
 <package name="user" extends="struts-default"> <action name="register" class="cn.sxt.action.UserAction" method="register"> <result name="success">/show.jsp</result> </action> </package>
  • 对象驱动

    属性多的时候就封装为对象

  • 模型驱动

    对象驱动中,页面的表单域名称比较复杂

    建议在实体类属性比较多时,采用模型驱动进行开发。

    • Jsp 代码
<form action="regDriven.action" method="post"> 用户名:<input type="text" name="name" /><br> 密码:<input type="password" name="pwd" /><br> 年龄:<input type="text" name="age" /><br> 邮箱:<input type="text" name="email" /><br> <input type="submit" value="提交" /> </form>
  • Action 代码
 public class UserDrivenAction implements ModelDriven<User>{private User user = new User(); //注册 public String register(){ System.out.println(user); return Action.SUCCESS; }public User getModel() { return user; }public User getUser() { return user; }public void setUser(User user) { this.user = user; } }
  • 实体类代码
public class User { private String name; private String pwd; private int age; private String email; public String getName() { return name; }public void setName(String name) { this.name = name; }public String getPwd() { return pwd; }public void setPwd(String pwd) { this.pwd = pwd; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }public String getEmail() { return email; }public void setEmail(String email) { this.email = email; }@Override public String toString() { return "User [name=" + name + ", pwd=" + pwd + ", age=" + age + ", email=" + email + "]"; } }
  • Struts.xml 配置
<package name="user" extends="struts-default"> <action name="register" class="cn.sxt.action.UserAction" method="register"> <result name="success">/show.jsp</result> </action> <action name="regDriven" class="cn.sxt.action.UserDrivenAction" method="register"> <result name="success">/show.jsp</result> </action> </package>
  • 类型转换

    在 servlet 中 如果表单提交非字符串数据的时候,需要进行类型转换:如提交 age

 String strAge = req.getParameter("age"); int age=0; if(strAge!=null){ age=Integer.parseInt(strAge); }

在 struts2 中,常见数据类型 struts2 已经自动的进行了类型转换。在某些情况下,有自定义的类型时,struts2 不能完成类型转换,那么需要手动转换,如 果该自定义类型使用的频率较高时,手动转换重复代码将会增多-------使用 struts2 提供的类
型转换器来进行类型转换。

  • 案例:坐标点(x,y)

  • 不进行类型转换的处理方式

  • Jsp 页面

<body> <form action="point.action" method="post"> x:<input type="text" name="point.x"/><br> y:<input type="text" name="point.y"/><br> <input type="submit" value="提交"/> </form> </body>
  • Action 代码
  public class PointAction { private Point point; public String execute(){ System.out.println(point.getX()+"-----"+point.getY()); return Action.SUCCESS; }public Point getPoint() { return point; }public void setPoint(Point point) { this.point = point; } }
  <package name="default" namespace="/" extends="struts-default"> <action name="point" class="cn.sxt.action.PointAction"> <result>/index.jsp</result> </action> </package>
  • 这种方式不符合数学的表示形式;采用类型转换的处理:

  • 使用类型转换

  • 编写类型转换器–继承 StrutsTypeConverter 类

  public class PointConverter extends StrutsTypeConverter{ /*** 将表单提交的字符串数据 转换为 指定自定义类型 * context 是 ActionContext * values 要进行类型转换的字符串数组 * toClass 被转换的类型 * */ @Override public Object convertFromString(Map context, String[] values, Class toClass) { String value=values[0]; Point point = new Point(); String x = value.substring(1,value.indexOf(",")); String y=value.substring(value.indexOf(",")+1,value.length()-1); System.out.println("x="+x); System.out.println("y="+y); point.setX(Integer.parseInt(x)); point.setY(Integer.parseInt(y)); return point; }/*** 将定义类型 转换为字符串在前台页面显示----通过 ognl 表达式将会使 用该方法进行转换 * context ---actionContext * o 要转换的对象 * */ @Override public String convertToString(Map context, Object o) { Point point = (Point)o; return "("+point.getX()+","+point.getY()+")"; }}

xwork-conversion.properties 配置文件
cn.sxt.entity.Point=cn.sxt.converter.PointConverter
Action 代码不变,struts.xml 配置不变
Jsp 页面

  <form action="point.action" method="post"> 点:<input type="text" name="point"/><br> <input type="submit" value="提交"/> </form>
  • 编写 xwork-conversion.properties 的配置文件

放于 src 下;内容为 要转换的类型=类型转换器

获取 ServletAPI

struts2 有 2 种方式去获取 servletAPI:一种解耦,一种耦合;

解耦使得使用 struts2 来进行测试的时候 不需要启动服务器。在一定程度上提高开发效 率的。
action—>service—>dao

    1. 通过 ActionContext 对象获取

    使用解耦的方式来获取 servlet api;通过 ActionContext 对象获取。

 ActionContext.getContext().getSession().put("user", name);

用户登录后将信息写入session

  //处理方法 public String execute(){ System.out.println(name+"---"+pwd); if("siggy".equals(name)&&"1111".equals(pwd)){ //获取 session ActionContext.getContext().getSession().put("user", name); //获取 request---HttpServletRequest 对象的 attributes Map<String,Object> request = (Map)ActionContext.getContext().get("request"); //获取 application Map<String,Object> application=ActionContext.getContext().getApplication(); //获取 parameters Map<String,Object> parameters = ActionContext.getContext().getParameters(); //相当于 request.getParameter("name"); System.out.println("name===="+((String[])parameters.get("na me"))[0]);return "success"; }else{ return "login"; } }
    1. 通过 ActionContext 直接获取 HttpServletRequest
  //处理方法 public String execute(){ System.out.println(name+"---"+pwd); if("siggy".equals(name)&&"1111".equals(pwd)){ HttpServletRequest request=(HttpServletRequest)ActionContext.getContext().get(Str utsStatics.HTTP_REQUEST); request.getSession().setAttribute("user", name);System.out.println("name===="+request.getParameter("name")) ; return "success"; }else{ return "login"; } }
    1. 耦合

    通过耦合的方法获取 HttpServletRequest
    实现 ServletRequestAware 接口

  public class LoginAction2 implements ServletRequestAware{ private String name; private String pwd; HttpServletRequest request; //处理方法 public String execute(){ System.out.println(name+"---"+pwd); if("siggy".equals(name)&&"1111".equals(pwd)){ request.getSession().setAttribute("user", name); System.out.println("name===="+request.getParameter("name")) ; return "success"; }else{ return "login"; } }public void setServletRequest(HttpServletRequest request) { this.request=request; }public String logout(){ ActionContext.getContext().getSession().remove("user"); System.out.println("退出"); return "success"; }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getPwd() { return pwd; }public void setPwd(String pwd) {this.pwd = pwd; } }
    1. 通过获取 ServletActionContext 获取 HttpServletRequest 对象
 public String execute(){ System.out.println(name+"---"+pwd); if("siggy".equals(name)&&"1111".equals(pwd)){ HttpServletRequest request = ServletActionContext.getRequest(); request.getSession().setAttribute("user", name); System.out.println("name===="+request.getParameter("name")) ; return "success"; }else{ return "login"; } }
  • 建议大家用第一种和第四种

ActionContext

ActionContext 是 map 结构的容器。ActionContext 是 Action 的上下文(生命周期长),存放 Action 执行 过程中数据信息。ActionContext 存放 Action 的数据,ActionInvocation,request 的数据,session 的数据,application 的数据,locale 的数据,conversion errors 等。每次请求时会为当前线程 创 建 一 个 新 的 ActionContext 。 而 ActionContext 采 用 了 ThreadLocal 的 方 式 来 存 放 ActionContext 所以 ActionContext 是线程安全。

将servletAPI中的数据存入actioncontext 实现了struts2和servlet的解耦。使测试可以不依赖 于容器

  • ThreadLocal模式

    threadlocal也是个容器,存放在该容器的数据是线程安全的,谁放的谁取不会变
    servlet不是线程安全的(处理多个请求

 public static void main(String[] args) { //ThreadLocal 存放线程局部变量的容器 //存放在 threadlocal 中的局部变量 是线程安全的 final ThreadLocal<String> ac = new ThreadLocal<String>(); ac.set("siggy"); new Thread(new Runnable(){ public void run() {System.out.println("thread:"+ac.get()); } }).start(); System.out.println(ac.get()); } 
  • 获取 ActionContext

    ActionContext.getContext()获取。由于 ActionContext 是线程安全的,并且是通过静态方
    法获取的,所以在本线程中的非 Action 类中 也可以直接访问。

    注意:ActionContext 是基于请求创建的,所以在非请求的线程中是不能使用
    ActionContext 对象的。如:filter 的 init()方法。

  • 6 大对象

    application :servletcontext
    session
    request :存放的是httpservletrequest域中的数据
    parameters :请求参数
    attr(page–>request—>session—>application) 从数据域一个个取
    ValueStack(值栈):业务处理类的相关数据

    httpservletrequest中有两种数据:
    1.setAttribute即域
    2.请求

    • 值栈 ValueStack

      值栈是栈结构;特征:FILO(先进后出);
      Struts2 中值栈存放的数据是 Action 对象

  • ognl

    表达式—el,re,ognl----用简洁的表达式完成比较复杂的功能。
    OGNL 全称是 Object-Graph Navigation Language(对象图形导航语言),相对于 EL 语 言,除了保持 EL 语言的优点外,他的其他优点如下:
     能够访问对象的普通方法
     能够访问类的静态属性和静态方法
     强大的操作集合类对象的能力
     支持赋值操作和表达式串联
    访问 OGNL 上下文和 ActionContext

    • 使用
public static void main(String[] args) throws OgnlException { //ognl思想:原则:数据分为 2 类 ---常用的和不常用的---常用的一般是小数据, //不常用的一般是大数据 //表达式:常用的数据直接取,不常用的数据加#取 Map<String,Object> map=new HashMap<String,Object>(); map.put("name", "张三疯"); map.put("age", 125); User user = new User(); user.setName("lisi"); //表达式,上下文(不常用的数据,ognl表达式的根(常用数据Object obj = Ognl.getValue("#name", map, user); System.out.println(obj); }

actioncontext做ognl的上下文对象
valuestack做ognl的根对象

  • struts2 标签

    struts2 中使用 ognl 表达式是通过 struts2 的标签来取值的。
    在 jsp 中导入 struts2 标签库:

  <%@ taglib prefix="s" uri="/struts-tags"%>

注意:要使用 struts2 的标签,那么要通过 struts2 过滤器来启用。如果过滤器的配置为 *.action 结尾时,不能直接访问 jsp 页面的。需要通过 action 跳转。如果过滤器配置为/*时, 可以直接访问 jsp 页面。
Struts2 推荐不直接访问 jsp 页面,推荐使用 action 来控制。

  <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request. getServerPort()+path+"/"; %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 用户名:<s:property value="name"/>【action 中的属性】<br> 用户名:<s:property value="#session.user"/>-----------------<a href="logout.action">退出</a> </body> </html>
  • 结论:使用 ognl 表达式访问 action 的属性时,可以直接访问。访问 actionContext 中的数据时需要加#。

验证机制

数据校验分2类
1.js前端校验
2.后端数据校验

struts2提供两种后端校验方式
1.硬编码实现
2.校验框架实现
action需继承actionsupport类—提供了validate方法,可将验证规则卸载该方法,只由该方法执行通过后,才会执行业务方法。

  • 服务端验证

    如果 action 类继承 ActionSupport 类,那么该 action 类将会继承 ActionSupport 的相关功能:如:验证功能

    注意:如果执行的是 Action 中的 execute 方法,那么只会执行 validate 方法。如果执行的是自定义的 action ,register 方法,那么将会线执行 validateRegister–validate–register 方法。

  • 案例

    • Jsp 页面
一定要加上<s:actionerror/>或者是<s:fielderror/> <s:actionerror/> <form action="register.action" method="post"> 用户名:<input type="text" name="name"/><br> 密码:<input type="password" name="pwd"/><br> 年龄:<input type="text" name="age"/><br> 生日:<input type="text" name="birthday"/><br> <input type="submit" value="登录"/> </form>
  • Action 代码
 public class RegisterAction extends ActionSupport{ private String name; private String pwd; private int age; private Date birthday; @Override public String execute() throws Exception { System.out.println("execute"); return Action.SUCCESS; }public String register(){request validateXxx 方法 validate 方法 xxx 方法 TT Result F: input reqpsonseSystem.out.println("register"); return Action.SUCCESS; }public void validateRegister(){ System.out.println("validate age"); if(age>100||age<1){ //在validate方法中,添加了fielderror或actionerror那么该方//法将执行不能通 过,并且返回的结果集为INPUTthis.addActionError("年龄不合法"); } }public void validate() { System.out.println("validate"); }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getPwd() { return pwd; }public void setPwd(String pwd) { this.pwd = pwd; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }public Date getBirthday() { return birthday; }public void setBirthday(Date birthday) { this.birthday = birthday; } }
  • Struts.xml 配置文件
  <package name="default" extends="struts-default" namespace="/"> <action name="register" class="cn.sxt.action.RegisterAction" method="register"> <result>/index.jsp</result> <result name="input">/register.jsp</result></action> </package>
  • struts2 的验证框架

    使用验证框架的步骤:在对应的 action 的包下添加一个验证框架的配置文件 该文件名为:actionName-validation.xml

    • Jsp 页面
  <s:fielderror></s:fielderror> <form action="register.action" method="post"> 用户名:<input type="text" name="name"/><br> 密码:<input type="password" name="pwd"/><br> 年龄:<input type="text" name="age"/><br> 生日:<input type="text" name="birthday"/><br> <input type="submit" value="登录"/> </form>
  • Action 类
public class RegisterValidateAction extends ActionSupport{ private String name; private String pwd; private int age; private Date birthday; @Override public String execute() throws Exception { System.out.println("execute"); return Action.SUCCESS; }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getPwd() { return pwd; }public void setPwd(String pwd) { this.pwd = pwd; }public int getAge() { return age; }public void setAge(int age) { this.age = age;}public Date getBirthday() { return birthday; }public void setBirthday(Date birthday) { this.birthday = birthday; } }
  • struts.xml 配置
 <package name="default" extends="struts-default" namespace="/"> <!-- <action name="register" class="cn.sxt.action.RegisterAction" method="register"> <result>/index.jsp</result> <result name="input">/register.jsp</result> </action> --> <action name="register" class="cn.sxt.action.RegisterValidateAction"> <result>/index.jsp</result> <result name="input">/register.jsp</result> </action> </package>
  • RegisterValidateAction-validation.xml 验证文件

<field表示对哪一个表单域进行验证
<field-validator type="requiredstring"字段验证器,默认提供了很多

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <validators> <field name="name"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>用户名必填</message> </field-validator> <field-validator type="stringlength"> <param name="trim">true</param> <param name="maxLength">10</param> <param name="minLength">4</param> <message>用户名去掉 2 端空格后 长度为${minLength}到 ${maxLength}</message> </field-validator> </field> <field name="age"> <field-validator type="int"><param name="min">1</param> <param name="max">150</param> <message>年龄范围为 1~150</message> </field-validator> </field> </validators>

拦截器

拦截器实现是通过代理实现的aop
拦截器是单例的,所有action共享相同的 拦截器,所有拦截器定义常量时需要注意 线程安全问题。

应禁止使用jsp直接访问页面,而应该通过. action方法返回jsp页面,这样就可以进入拦截器 ,也更安全

拦截器和过滤器 很相似。在 action 执行的前后执行。Struts2 的核心功能都是通 过拦截器来实现。

作用:对于 action 的一些公共处理代码可以放到拦截器中来实现。如:权限控制,日志 等等。

多个拦截器之间的执行是采用责任链设计模式来实现。

  • struts2所有功能都是可插拔

  • 实现步骤

    • 编写拦截器

      自定义拦截器实现步骤:
      1.实现interceptor
      2.继承abstractinterceptor

  /*** *计算 action 执行时间 */ public class TimeInterceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation invocation) throws Exception { long start=System.currentTimeMillis(); //执行下一个拦截器,当拦截器执行完后执行 action String result= invocation.invoke(); long end=System.currentTimeMillis(); System.out.println("执行该 Action 所用时间为: "+(end-start)+"ms"); return result; }}
  • 在 struts.xml 中 配置拦截器
<struts> <package name="default" extends="struts-default" namespace="/"> <!-- 配置拦截器 --> <interceptors> <interceptor name="time" class="cn.sxt.interceptor.TimeInterceptor"/> </interceptors> <action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> <!-- 应用拦截器 --> <interceptor-ref name="time"/> </action> </package> </struts>
  • 在 action 中引用拦截器 ↑已引用

当请求 hello.action 时将会执行该拦截器。

  • 拦截器的配置详解

    • 当引用了自定义拦截器时,默认拦截器将不起作用

      默认拦截器:在 struts-default.xml 中,配置了默认拦截器。当配置默认拦截器以后,
      如果不引用拦截器,那么默认的拦截器将起作用。

 <default-interceptor-ref name="defaultStack"/>

当引用自定义拦截器后,又想使用 struts2 提供的拦截器功能,那么需要手动引用

  <action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> <!-- 引用拦截器 --> <interceptor-ref name="time"/> <!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方式一 样--> <interceptor-ref name="defaultStack"/> </action> 当 action 引用的拦截器个数比较多时,可以将多个拦截器放入一个拦截器栈中。 <!-- 一个拦截器栈中,可以包含多个拦截器的引用 拦截器栈的引用和拦截器一致 --> <interceptor-stack name="myStack"><!-- 引用拦截器 --> <interceptor-ref name="time"/> <!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方 式一样--> <interceptor-ref name="defaultStack"/> </interceptor-stack> <action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> <interceptor-ref name="myStack"/> </action>

当自定义拦截器栈在这个包下的所有 action 都使用的时,可以定义为默认的拦截器 栈,或默认的拦截器

  <!-- 定义默认的拦截器/栈 --> <default-interceptor-ref name="myStack"/> <action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> </action>
  • 登录案例

    • 拦截器
 public class LoginInterceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation invocation) throws Exception { //判断是否是 login.action 如果是则直接执行下一个拦截器 //如果不是 则判断是否登录,如果登录执行下一个拦截器 //如果没有则返回登录页面 //actionName 没有扩展名 String actionName=invocation.getProxy().getActionName(); if("login".equals(actionName)){ return invocation.invoke(); }Object obj = invocation.getInvocationContext().getSession().get("user"); if(obj==null){//没有登录 return Action.LOGIN; }return invocation.invoke(); } }
  • Struts.xml 的配置
  <struts> <package name="default" extends="struts-default" namespace="/"> <!-- 配置拦截器 --> <interceptors> <interceptor name="time" class="cn.sxt.interceptor.TimeInterceptor"/> <interceptor name="loginInterceptor" class="cn.sxt.interceptor.LoginInterceptor"/> <!-- 一个拦截器栈中,可以包含多个拦截器的引用 拦截器栈的引用和拦截器一致 --> <interceptor-stack name="myStack"> <interceptor-ref name="loginInterceptor"/> <!-- 引用拦截器 --> <interceptor-ref name="time"/> <!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方 式一样--> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <!-- 定义默认的拦截器/栈 --> <default-interceptor-ref name="myStack"/> <global-results> <result name="login">/login.jsp</result> </global-results> <action name="hello" class="cn.sxt.action.HelloAction"> <result>/WEB-INF/index.jsp</result> </action> <action name="login" class="cn.sxt.action.LoginAction"> <result>/success.jsp</result> </action> </package> </struts>
  • Jsp 页面
  <body> <form action="user/login.action" method="post"> 用户名:<input type="text" name="name"/><br> 密码:<input type="password" name="pwd"/><br> <input type="submit" value="登录"/> </form> </body>
  • Action 代码
public class LoginAction {private String name; private String pwd; //处理方法 public String execute(){ System.out.println(name+"---"+pwd); if("siggy".equals(name)&&"1111".equals(pwd)){ ActionContext.getContext().getSession().put("user", name); return "success"; }else{ return "login"; } }public String logout(){ System.out.println("退出"); return "success"; }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getPwd() { return pwd; }public void setPwd(String pwd) { this.pwd = pwd; } }
  • 方法拦截器

    方法拦截器时比 Action 拦截器更加细粒度的控制,主体实现和 Action 拦
    截器一致。但是方法拦截器时继承 MethodFilterInterceptor 类,重写 doIntercept()方法。
    引用方法拦截器配置会发生改变:

  <interceptor-ref name="methodInterceptor"> <!-- 配置被拦截的方法 --> <param name="includeMethods">list,add</param> <!-- 配置不被拦截的方法 --> <param name="excludeMethods">login</param> </interceptor-ref>
  • 拦截器栈:<interceptor-stack

文件操作

  • 文件上传

    struts2 的文件上传功能使用的是 apache 下的 commons-fileupload 来实现。

    • Jsp

      Jsp 的表单要求是 post 提交,并且 enctype=”multipart/form-data”

 <form action="upload.action" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="file"/> <input type="submit" value="上传"/> </form>
  • action

action 的代码:在 Action 中需要提供 3 个属性,一个 File 类型,名称是表单域名,其它
两个分别是表单域名+FileName,表单域名+ContentType;并且提供 get/set 方法

 public class UploadAction extends ActionSupport{ private File file; //文件名 private String fileFileName; //文件的类型 private String fileContentType; //上传 public String upload() throws IOException{ //写文件的过程 HttpServletRequest request = ServletActionContext.getRequest(); String path=request.getRealPath("/upload"); InputStream is = new FileInputStream(file); OutputStream os = new FileOutputStream(new File(path,fileFileName)); byte[] buffer = new byte[200]; int len=0; while((len=is.read(buffer))!=-1){ os.write(buffer, 0, len); }os.close(); is.close(); return Action.SUCCESS; }public File getFile() { return file; }public void setFile(File file) { this.file = file; }public String getFileFileName() { return fileFileName;}public void setFileFileName(String fileFileName) { this.fileFileName = fileFileName; }public String getFileContentType() { return fileContentType; }public void setFileContentType(String fileContentType) { this.fileContentType = fileContentType; } }
  • struts.xml

struts.xml 配 置 文 件 的 编 写 ; 主 要 是 上 传 文 件 大 小 的 控 制 。 需 要 配 置 拦 截 器 的 maximumSize 属性和 struts2 的静态属性:struts.multipart.maxSize;maxSize 要大于等于 maximumSize。

  <struts> <!-- 设置临时目录 --> <constant name="struts.multipart.saveDir" value="c:\"/> <!-- 设置上传文件的最大大小 必须大于等于 maximumSize --> <constant name="struts.multipart.maxSize" value="20971520"/> <package name="default" extends="struts-default" namespace="/"> <action name="upload" class="cn.sxt.action.UploadAction" method="upload"> <result>/index.jsp</result> <interceptor-ref name="fileUpload"> <param name="maximumSize">20971520</param> </interceptor-ref> <interceptor-ref name="defaultStack"/> </action> </package> </struts>
  • 批量文件上传

struts2 可以将批量文件处理为数组上传到 action 中

  • Jsp
 <style type="text/css"> p{margin:5px;} </style> <script type="text/javascript" src="js/jquery-1.11.3.js"></script> <script type="text/javascript"> $(function(){ $('#btn').click(function(){var field="<p><input type='file' name='file'/><input type='button' value='删除' onclick='removed(this);'/></p>"; $('#files').append(field); }); }); function removed(o){ $(o).parent().remove(); } </script> </head> <body><form action="batch.action" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="file"/><input type="button" id="btn" value="添加"/> <div id="files"></div> <input type="submit" value="上传"/> </form> </body>
  • Action
 public class BatchUploadAction { private File[] file; private String[] fileFileName; private String[] fileContentType; public String execute() throws IOException{ //写文件的过程 HttpServletRequest request = ServletActionContext.getRequest(); String path=request.getRealPath("/upload"); for(int i=0;i<file.length;i++){ InputStream is = new FileInputStream(file[i]); OutputStream os = new FileOutputStream(new File(path,fileFileName[i])); byte[] buffer = new byte[200]; int len=0; while((len=is.read(buffer))!=-1){ os.write(buffer, 0, len); }os.close(); is.close(); }return Action.SUCCESS; }public File[] getFile() { return file; }public void setFile(File[] file) { this.file = file; }public String[] getFileFileName() { return fileFileName; }public void setFileFileName(String[] fileFileName) { this.fileFileName = fileFileName; }public String[] getFileContentType() { return fileContentType; }public void setFileContentType(String[] fileContentType) { this.fileContentType = fileContentType; } }
  • Struts.xml
<action name="batch" class="cn.sxt.action.BatchUploadAction"> <result>/index.jsp</result> <interceptor-ref name="fileUpload"> <param name="maximumSize">20971520</param> </interceptor-ref> <interceptor-ref name="defaultStack"/> </action>
  • 文件下载

    在 servlet 中的文件下载是通过流来实现的。
    使用 HttpServletResponse 来实现,和以前 Servlet 中文件下载的方式一致。注意 在 action 执行方法中 返回 null;

    • 通过流来实现下载

      • Action
  public class DownloadAction { public String execute() throws IOException{ HttpServletRequest req=ServletActionContext.getRequest(); HttpServletResponse resp = ServletActionContext.getResponse(); //获取路径 String path=req.getRealPath("/download"); File file = new File(path,"Struts2.chm"); resp.setContentLength((int)file.length()); resp.setCharacterEncoding("utf-8"); resp.setContentType("application/octet-stream"); resp.setHeader("Content-Disposition", "attachment;filename=Struts2.chm"); byte[] buffer = new byte[400]; int len=0; InputStream is = new FileInputStream(file); OutputStream os = resp.getOutputStream(); while((len=is.read(buffer))!=-1){ os.write(buffer, 0, len); }os.close(); is.close(); return null; } }
  • Struts.xml
 <package name="default" namespace="/" extends="struts-default"> <!-- 当 action 的处理方法返回空是 不用配置结果集 --> <action name="download" class="cn.sxt.action.DownloadAction"> </action> </package>
  • Jsp
 <body> <a href="download.action">struts2 的文档</a> </body>
  • 使用 struts2 本来结果集来进行文件下载

  • Action

public class StreamDownloadAction { private String fileName; public String execute(){ return Action.SUCCESS; }public InputStream getInputStream() throws FileNotFoundException{ HttpServletRequest req=ServletActionContext.getRequest(); //获取路径 String path=req.getRealPath("/download"); return new FileInputStream(new File(path,fileName)); }public String getFileName() { return fileName; }public void setFileName(String fileName) { this.fileName = fileName; } }
  • Struts.xml
  <action name="streamDownload" class="cn.sxt.action.StreamDownloadAction"> <result type="stream"> <param name="inputName">inputStream</param> <param name="contentDisposition">attachment;filename=${fileName}</par am> </result> </action>
  • Jsp
  <body> <a href="streamDownload.action?fileName=Struts2.chm">struts2 的文档 </a><a href="streamDownload.action?fileName=Struts1.3.chm">struts1 的文档</a> </body>

java代码审计--之--常用框架了解相关推荐

  1. Java后台开发常用框架

    https://www.cnblogs.com/think90/p/7629826.html

  2. java开发的框架有哪些_java开发中常用框架有哪些

    1 java开发中常用框架有哪些 java从推出到现在技术不断发展,语言也优化的越来越好,对于java工程师来说技术的不断发展,他们需要不断学习java进阶,而对于新手来说就能从基础到核心.那么新手该 ...

  3. 【代码审计-JAVA】基于javaweb框架开发的

    前言: 介绍: 博主:网络安全领域狂热爱好者(承诺在CSDN永久无偿分享文章). 殊荣:CSDN网络安全领域优质创作者,2022年双十一业务安全保卫战-某厂第一名,某厂特邀数字业务安全研究员,edus ...

  4. java常用框架总结

    今天想看看现在常用的框架有哪些,发现网上文章不多决定根据自己的理解写一篇文章,如有错误希望大家包涵: 1.java的5大框架.springboot都不说了,网上资料很多: 2.缓存工具:Ehcache ...

  5. JAVA后端常用框架SSM,redis,dubbo等

    JAVA后端常用框架SSM,redis,dubbo等 一.SpringMVC http://blog.csdn.net/evankaka/article/details/45501811 spring ...

  6. Java EE开发四大常用框架

    2019独角兽企业重金招聘Python工程师标准>>> 我们对Java EE的框架有过很多介绍, 本文将对Java EE中常用的四个框架做一下系统的归纳,希望大家喜欢. Struts ...

  7. java代码审计_Java代码审计| Spring框架思路篇

    Java的WEB框架是Java进阶课程,当要进行Spring的漏洞分析,要有一定的Java代码知识储备. Java后端标准的学习路线:JavaSE->JavaEE->Java Web框架 ...

  8. java 中常用框架、intell idea简单使用、爬虫系统

    学习:http://www.ityouknow.com/spring-boot.html http://blog.didispace.com/spring-boot-learning-1/ ***in ...

  9. java三个框架该先学哪个_Java常用框架有哪些?先学哪一个比较有优势?

    对于学Java的人来说,学习和了解框架是必修的,但是Java的框架比较多,并不需要全部都学,只要学几个常用的框架,在工作的时候就差不多够用了.今天巨牛汇外包小助手就来给大家介绍一下Java常用的框架有 ...

最新文章

  1. 哈理工计算机学院孙广路,我校举行人工智能前沿领域-计算机视觉专题报告会...
  2. 团队开发个人总结05
  3. 1018.eclipse工具使用记录
  4. matlab求出拟合曲线的方程,已知数据点,拟合曲线并得到曲线方程。谢谢
  5. 服务器引导盘装系统怎么选择u盘,小编教你怎么设置u盘为启动盘
  6. 开源项目学习之(一)------zheng环境搭建
  7. Folx Pro5最新版适用Mac电脑网络BT下载器
  8. 安全检查未通过_QQ群文件未通过安全检查,禁止下载该文件解决办法(QQ收藏)
  9. 漫画:头条面试官谈自我介绍
  10. 工欲善其事,必先利其器——学会不将就,让自己事半功倍!
  11. 更改linux键盘布局,无法更改Centos 7上的键盘布局
  12. 算法训练Day25 | LeetCode216. 组合总和III(和77.组合很像!);LeetCode17. 电话号码的字母组合(不同集合中组合)
  13. 键盘鼠标是计算机标准输入输出设备,微型计算机输入输出设备之键盘和鼠标(ppt 32页).ppt...
  14. 到西藏工作,出差,旅行,出游,体验,好奇一切的一切,大全科!!绝对是经典“教科书”类,上面见不到的!全面了解,西藏的生活水平,和注意事项,是去西藏,拉萨前的必读性文章。
  15. 计算机找不到 bitlocker,win10中找不到bitlocker驱动器的解决方法介绍
  16. spring boot 项目增加flyway的使用遇到问题解决
  17. 实验任务(五)---综合渗透
  18. Web前端面试指导(二):编写简历,吃透简历内容
  19. 【无敌恢复U盘数据的方法】
  20. 10年代码经验程序员UP主复刻“阴间”超级马里奥,获赞27万,马里奥:我头呢?

热门文章

  1. Web开发者的福利 30段超实用CSS代码笔记
  2. Git——分支(详细解释)
  3. 阿里CEO张勇:网络安全需要全生态协作
  4. 微软Windows Azure云计算的几大竞争优势
  5. IT之家,这不是个案
  6. html中图片透明度渐变效果,三种用CSS滤镜实现的图片透明度及渐变效果
  7. 沃森变频器使用说明书_沃森(VicRuns)VD120A-GS系列变频器说明书用户手册.doc
  8. Android Studio 自带模拟器获取root权限
  9. kmp算法例题 登山
  10. vue路由的两种模式:hash与history的区别