kiddkid的SSM笔记

SSM

MyBatis

MyBatis的基本用法

  1. pom.xml导入依赖

  1. log4j.properties

  2. mapper.xml写入映射

    1. 命名空间:类似java包的名字,用于区分不同的映射文件
    2. 输入映射:parameterType 输出映射:resultType
    <mapper namespace="workerMapper">
        <!-- 此映射配置文件 用来设计worker表对应的各种操作 -->
    
        <!-- 配置各种操作 -->
        <select id="selectByName" parameterType="java.lang.String" resultType="com.ls.bean.Worker">
            select * from worker where wname = #{value}
        </select>
    
        <!-- 增 -->
        <insert id="add" parameterType="com.ls.bean.Worker">
            insert into worker values(#{wid},#{wname},#{age},#{sex})
        </insert>
        <!-- 删 -->
        <delete id="del" parameterType="java.lang.String">
            delete from worker where wid = #{value}
        </delete>
    
        <!-- 改 -->
        <update id="upd" parameterType="com.ls.bean.Worker">
            update worker set age = #{age} where wname = #{wname}
        </update>
    </mapper>
    

    注: 除select标签外,其余标签没有resultType

  3. sqlMapConfig.xml 中关联表的映射文件

    在这里插入图片描述

  • 补充:
    • ​ 单元测试里 @Before 标注的方法 会在每次@Test标注的方法 运行之前运行

​ @After 之后运行

Mybatis的mapper代理

步骤
  1. 建包,对照

  1. sqlMapConfiger.xml 配置

在这里插入图片描述

  1. 要实现Mapper接口 / dao接口 自动进行动态代理产生实现类
    1.映射文件 需要 和 接口 放在相同的类路径下
    2.映射文件 命名空间的值 跟 接口的路径名一样
    3.映射文件 标签id 跟 接口的方法名一样
    4.映射文件 标签 输入映射 跟 接口的方法入参类型一样
    5.映射文件 标签 输出映射 跟 接口的方法返回值类型一样
uuid作主键
  • <mapper namespace="com.ls.mapper.WorkerDao">
    
        <insert id="addWorker" parameterType="com.ls.bean.Worker">
            /*
             对于这样字符串做主键的
             先执行selectkey里的操作,将结果设置到worker对象的wid里,再插入数据
            */
            <selectKey keyProperty="wid" keyColumn="wid" resultType="java.lang.String" order="BEFORE">
                select uuid()
            </selectKey>
            insert into worker values (#{wid},#{wname},#{age},#{sex})
        </insert>
    </mapper>
    

在这里插入图片描述

主键自增

当主键为自增型,想要查看插入后的主键

<insert id="addGoods" parameterType="com.ls.bean.Goods">
    /* 在插入到数据库之后,将最后一次主键值 设置到 入参的goods对象的 goodsId 成员变量里 */
    <selectKey keyProperty="goodsId" keyColumn="goodsId" resultType="java.lang.Integer" order="AFTER">
        select last_insert_id()
    </selectKey>
    insert into goods(goodsName,shopPrice) values (#{goodsName},#{shopPrice})
</insert>
入参为多条件值

1.将多个条件值封装为一个对象
2.将多个条件值封装到一个Map
3.使用User类型传入角色和地址

<!-- vo -->
    <select id="queryByRoleAndAddress" parameterType="com.ls.bean.QueryVo" resultMap="myUser">
        select * from user where role_code = #{roleCode} and address = #{address}
    </select>

<!-- 输入映射是map -->
<!-- #{roleCode} #{address} roleCode,address 就是Map里的一个键的名字 -->
    <select id="queryByRoleAndAddress2"
            parameterType="map" resultMap="myUser">
        select * from user where role_code = #{roleCode} and address = #{address}
    </select>

<!-- user -->
    <select id="queryByRoleAndAddress3"
            parameterType="com.ls.bean.User" resultMap="myUser">
          select * from user where role_code = #{roleCode} and address = #{address}
    </select>
下划线和驼峰的自动映射/手动映射

当数据库表里列名java对象成员变量名 不一致 且不属于下划线和驼峰的自动映射
resultType 输出映射 采用已经存在的java类型
自定义 输出映射 手动将列名和成员变量名 映射起来 采用resultMap

resultMap除了自定义输出映射之外,还能进行高级查询映射,1对1,1对多.

UserMapper.xml

<resultMap id="myUser" type="com.ls.bean.User">
    <!-- 第一个主键列的映射 -->
    <id property="userId" column="user_id" javaType="java.lang.Integer"/>
    <!-- 其他列 -->
    <result property="telephone" column="telphone" javaType="java.lang.String"/>
    <!-- 其他的符合驼峰到下划线映射 或者 一致 -->
</resultMap>
<update id="updateTel" parameterType="com.ls.bean.User">
    update user set telphone = #{telephone} where user_id = #{userId}
</update>

<select id="queryByID" parameterType="integer" resultMap="myUser">
    select * from user where user_id = #{value}
</select>
${} 与 #{}

#{} 占位符
${} 字符串拼接

@Test
public void test050905() throws Exception{
    GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);
    List<Goods> goods = goodsMapper.queryByName2("'%X%'");//${} 识别 or 1=1
    for (Goods good : goods) {
        System.out.println(good);
    }
}

@Test
public void test050904() throws Exception{
    GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);
    List<Goods> goods = goodsMapper.queryByName("%X%");//#{} 
    for (Goods good : goods) {
        System.out.println(good);
    }
}
入参为list
  • foreach遍历入参list,每个值放在id变量里,遍历到拼接的sql语句里 自动去除最后一个id值后的" , "
  • 输出映射,这里是泛型
<select id="queryByIds" parameterType="list" resultType="com.ls.bean.Goods">
    select * from goods where goodsId
    <foreach collection="list" item="id" open="in(" close=")" separator=",">
        #{id}
    </foreach>
</select>
根据入参里非空的属性值来修改某条记录
int update(User user);
<!--根据入参里非空的属性值来修改某条记录-->
<!-- set 标签自动去除最后一个逗号 -->
<update id="update" parameterType="com.ls.bean.User">
    update user
    <set>
        <if test="userPass!=null and userPass!=''">
            user_pass = #{userPass} ,
        </if>
        <if test="cname!=null and cname!=''">
            cname = #{cname} ,
        </if>
        <if test="email!=null and email!=''">
            email = #{email} ,
        </if>
    </set>
    where user_id = #{userId}
</update>
@Test
public void test051002() throws Exception{
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setUserPass("q1w2e3");
    user.setCname("小球");
    //user.setEmail("qqqqqq@qq.com");
    user.setUserId(711);

    userMapper.update(user);
}

查询

多条件查询
//多条件查询商品 条件有几个用户方不一定
List<Goods> queryByCondition(Goods condition);
<!-- 多条件查询 -->
<!-- where标签 自动去除第一个and关键字 -->
<select id="queryByCondition" parameterType="com.ls.bean.Goods" resultType="com.ls.bean.Goods">
    select * from goods
    <where>
        <if test="goodsName!=null and goodsName!=''">
            and goodsName like #{goodsName}
        </if>
        <if test="goodsStock!=null">
            and goodsStock &lt; #{goodsStock}
        </if>
        <if test="goodsStatus!=null">
            and goodsStatus = #{goodsStatus}
        </if>
    </where>
</select>
1对多查询

collection

1对多 1对1 都是属于高级映射查询,输出映射必须是resultMap

User.java内

private List<Orders> ordersList;//一个用户下过的多个订单
//根据用户id查询用户数据 并 一同查询 他所下过的所有订单数据 -- 1对多查询
User queryUserAndOrders(Integer userId);
<!-- 高级映射 无论 成员变量名 和 列名 是否满足驼峰和下划线的映射,都必须手动写出来 -->
<resultMap id="user2" type="com.ls.bean.User">
    <id property="userId" column="user_id" javaType="java.lang.Integer"/>
    <result property="userName" column="user_name" javaType="java.lang.String"/>
    <result property="roleCode" column="role_code" javaType="java.lang.String"/>
    <result property="userPass" column="user_pass" javaType="java.lang.String"/>
    <result property="cname" column="cname" javaType="java.lang.String"/>
    <result property="isLogin" column="is_login" javaType="java.lang.String"/>
    <result property="telephone" column="telphone" javaType="java.lang.String"/>
    <result property="address" column="address" javaType="java.lang.String"/>
    <result property="nation" column="nation" javaType="java.lang.String"/>
    <result property="email" column="email" javaType="java.lang.String"/>
    <result property="userImg" column="user_img" javaType="java.lang.String"/>
    <result property="gender" column="gender" javaType="java.lang.String"/>
    
                                        <!-- 此处为外键列:userId 和 user_id均可 -->
    									<!-- ofType写泛型 -->
    <collection property="ordersList" column="userId" ofType="com.ls.bean.Orders">
        <id property="orderId" column="orderId" javaType="java.lang.Integer"/>
        <result property="userId" column="userId" javaType="java.lang.Integer"/>
        <result property="price" column="price" javaType="java.lang.Double"/>
        <result property="orderDate" column="orderDate" javaType="java.util.Date"/>
        <result property="payment" column="payment" javaType="java.lang.Byte"/>
    </collection>
</resultMap>
<select id="queryUserAndOrders" parameterType="java.lang.Integer" resultMap="user2">
    select u.*,o.*
    from user u left join orders o on u.user_id = o.userId
    where u.user_id = #{value}
</select>
1对1查询

association

1对多 1对1 都是属于高级映射查询,输出映射必须是resultMap

//查询某个订单 一同查询 订单的下单用户
Orders queryOrdersAndUser(Integer orderId);
<resultMap id="orders2" type="com.ls.bean.Orders">
    <id property="orderId" column="orderId" javaType="java.lang.Integer"/>
    <result property="userId" column="userId" javaType="java.lang.Integer"/>
    <result property="price" column="price" javaType="java.lang.Double"/>
    <result property="orderDate" column="orderDate" javaType="java.util.Date"/>
    <result property="payment" column="payment" javaType="java.lang.Byte"/>
    
    
    <association property="user" column="userId" javaType="com.ls.bean.User">
        <id property="userId" column="user_id" javaType="java.lang.Integer"/>
        <result property="userName" column="user_name" javaType="java.lang.String"/>
        <result property="roleCode" column="role_code" javaType="java.lang.String"/>
        <result property="userPass" column="user_pass" javaType="java.lang.String"/>
        <result property="cname" column="cname" javaType="java.lang.String"/>
        <result property="isLogin" column="is_login" javaType="java.lang.String"/>
        <result property="telephone" column="telphone" javaType="java.lang.String"/>
        <result property="address" column="address" javaType="java.lang.String"/>
        <result property="nation" column="nation" javaType="java.lang.String"/>
        <result property="email" column="email" javaType="java.lang.String"/>
        <result property="userImg" column="user_img" javaType="java.lang.String"/>
        <result property="gender" column="gender" javaType="java.lang.String"/>
    </association>
</resultMap>
<select id="queryOrdersAndUser" parameterType="java.lang.Integer" resultMap="orders2">
    select o.*,u.* from orders o left join user u on o.userId = u.user_id
    where o.orderId = #{value}
</select>
多对多查询
  • 查询用户某个订单的商品

    • 1个用户 -> 多个订单 1个订单 -> 多个订单信息 1个订单信息 -> 1件商品

    • 字段

在这里插入图片描述

在这里插入图片描述

  • userMapper.java

    • //根据用户id查询用户数据 并 一同查询 他所购买过的所有商品数据
      User queryUserAndGoods(Integer userId);
      
  • userMapper.xml

    • <resultMap id="user3" type="com.ls.bean.User">
          <id property="userId" column="user_id" javaType="java.lang.Integer"/>
          <result property="userName" column="user_name" javaType="java.lang.String"/>
          <result property="roleCode" column="role_code" javaType="java.lang.String"/>
          <result property="userPass" column="user_pass" javaType="java.lang.String"/>
          <result property="cname" column="cname" javaType="java.lang.String"/>
          <result property="isLogin" column="is_login" javaType="java.lang.String"/>
          <result property="telephone" column="telphone" javaType="java.lang.String"/>
          <result property="address" column="address" javaType="java.lang.String"/>
          <result property="nation" column="nation" javaType="java.lang.String"/>
          <result property="email" column="email" javaType="java.lang.String"/>
          <result property="userImg" column="user_img" javaType="java.lang.String"/>
          <result property="gender" column="gender" javaType="java.lang.String"/>
          <!-- 此处userId 和 user_id均可 -->
          <!-- orderList -->
          <collection property="ordersList" column="userId" ofType="com.ls.bean.Orders">
              <id property="orderId" column="orderId" javaType="java.lang.Integer"/>
              <result property="userId" column="userId" javaType="java.lang.Integer"/>
              <result property="price" column="price" javaType="java.lang.Double"/>
              <result property="orderDate" column="orderDate" javaType="java.util.Date"/>
              <result property="payment" column="payment" javaType="java.lang.Byte"/>
      
              <!-- orderDetailList -->
              <collection property="orderDetailList" column="orderId" ofType="com.ls.bean.OrderDetail">
                  <id property="detailId" column="detailId" javaType="java.lang.Integer"/>
                  <result property="orderId" column="orderId" javaType="java.lang.Integer"/>
                  <result property="goodsId" column="goodsId" javaType="java.lang.Integer"/>
                  <result property="buyNum" column="buyNum" javaType="java.lang.Integer"/>
      
                  <!-- goods -->
                  <association property="goods" column="goodsId" javaType="com.ls.bean.Goods">
                      <id property="goodsId" column="goodsId" javaType="java.lang.Integer"/>
                      <result property="goodsName" column="goodsName" javaType="java.lang.String"/>
                      <!-- 为了节省篇幅 其他商品属性暂时不写,真正要查询时必须完整的写全 -->
                  </association>
              </collection>
          </collection>
      </resultMap>
      
      
      <select id="queryUserAndGoods" parameterType="java.lang.Integer" resultMap="user3">
          select * from user u left join orders o on u.user_id = o.userId
          left join order_detail od on o.orderId = od.orderId
          left join goods g on od.goodsId = g.goodsId
          where u.user_id = #{value}
      </select>
      
  • test

    • @Test
      public void test051005() throws Exception{
          UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
          User user = userMapper.queryUserAndGoods(704);
      
          System.out.println(user);
          List<Orders> ordersList = user.getOrdersList();
          for (Orders orders : ordersList) {
              List<OrderDetail> detailList = orders.getOrderDetailList();
              for (OrderDetail detail : detailList) {
                  System.out.println(detail.getGoods());
              }
          }
      }
      
  • 结果
    在这里插入图片描述

延迟加载(懒查询)
  • ordersMapper.java

    • 询某个订单 一同查询 订单的下单
      所以 将 两表连接查询 变为 2
      那么查询订单 只需要单表查询

    • Orders queryOrdersAndUserLazy(Integer orderId);
      
  • ordersMapper.xml

    • <!-- 先查订单数据,并按照如下封装,关联了user属性时,再按照
       com.ls.mapper.UserMapper.queryByID进行查询 -->
      <resultMap id="orders3" type="com.ls.bean.Orders">
          <id property="orderId" column="orderId" javaType="java.lang.Integer"/>
          <result property="userId" column="userId" javaType="java.lang.Integer"/>
          <result property="price" column="price" javaType="java.lang.Double"/>
          <result property="orderDate" column="orderDate" javaType="java.util.Date"/>
          <result property="payment" column="payment" javaType="java.lang.Byte"/>
          <association property="user" column="userId" javaType="com.ls.bean.User"
              fetchType="lazy" select="com.ls.mapper.UserMapper.queryByID">
          </association>
      </resultMap>
      <select id="queryOrdersAndUserLazy" parameterType="java.lang.Integer" resultMap="orders3">
          select * from orders where orderId = #{value}
      </select>
      
  • test

    • @Test
      public void test051006() throws Exception{
          OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
          Orders orders = ordersMapper.queryOrdersAndUserLazy(1003);
          System.out.println(orders.getOrderId());
          System.out.println(orders.getOrderDate());
      
          Thread.sleep(5000);
          System.out.println("5s以后...");
          System.out.println(orders.getUser().getCname());
      }
      
  • 结果

    -

缓存

一级缓存

​ mybatis一级缓存的作用域是同一个sqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。mybatis默认开启一级缓存。

@Test
public void test1() throws Exception{
    InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
    SqlSession sqlSession = factory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    User user = userMapper.queryByID(702);
    System.out.println(user);

    Thread.sleep(3000);
    User user2 = userMapper.queryByID(702);
    System.out.println(user2);

    sqlSession.commit();
    sqlSession.close();
}
二级缓存

二级缓存是sqlSessionFactory级别的

在mybatis中允许多个同一个工厂的SqlSession对象共享一个缓存区域,只不过这个缓存区域并不一定在内存中,也可能是存储硬盘空间内,这个共享区域就是mybatis的二级缓存。mybatis同样适用hashMap这种数据结构来存储二级缓存中保存的数据。 如下图所示:

在这里插入图片描述

​ 从上图中可以看出,mybatis的二级缓存是根据mapper配置文件的namespace命名空间进行划分的,相同namespace的查询数据操作放在同一个缓存区中。即用户的查询操作的数据是放在UserMapper的缓存区中,订单查询操作的数据是放在OrderMapper的缓存区中。

​ 如果两个用户的SqlSession会话都是执行同一个UserMapper接口中的方法,并且都去查询用户数据,每次查询都会先从缓存区中查找,如果找不到从数据库中查询,查询到的数据写入到二级缓存。

任何一个用户的SqlSession 执行insert、update、delete等操作commit提交后都会清空该mapper下的二级缓存区域。

​ 在mybatis中二级缓存是默认关闭的,如果要开启mybatis的二级缓存,配置如下:

<setting name="cacheEnabled" value="true"/>
  1. 把要加入缓存的数据所在的类型实现序列化接口

    ​ 二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。因为mybatis实现的二级缓存,数据并不一定存储在内存中,也有可能是其他位置,比如硬盘、网络空间等

    public class User implements Serializable { 
        //省略...
    }
    
  2. 在UserMapper.xml中开启二级缓存, 加入cache标签

    <mapper namespace="com.langsin.mapper.UserMapper">
        <cache/>
    ...
    
  3. 测试:

        @Test
        public void test2() throws Exception{
            //一级缓存是sqlSession级别的,同一个sqlSession里 多次查询同样的数据
            InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
            SqlSession sqlSession = factory.openSession();
            //查第一次
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.queryByID(702);
            System.out.println(user);
            sqlSession.commit();
            sqlSession.close();
    
    
            //一级缓存是sqlSession级别的,同一个sqlSession里 多次查询同样的数据
            SqlSession sqlSession2 = factory.openSession();//------这里的factory要注意
            //查第一次
            UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
            //查第二次 日志里没有输出执行sql语句,查询了缓存
            User user2 = mapper2.queryByID(702);
            System.out.println(user2);
            sqlSession2.commit();
            sqlSession2.close();
    
        }
    

    ​ 在SqlSession会话中,如果会话没有结束,数据只会存储于一级缓存中,如果此SqlSession的会话结束并且此命名空间的mapper开启了二级缓存,这时数据才会写入到二级缓存中。

    结果:

在这里插入图片描述

整合ehcache
  1. pom.xml导入依赖
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.11</version>
</dependency>

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>
  1. 添加ehcache的配置文件ehcache.xml到resources目录中

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
        <!-- 磁盘保存路径 -->
        <diskStore path="D:\ehcache" />
        <defaultCache
                maxElementsInMemory="10000"
                maxElementsOnDisk="10000000"
                eternal="false"
                overflowToDisk="true"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    
  2. UserMapper.xml修改cache标签

  • 开启mapper级别的缓存,即二级缓存,不同sqlSession使用此mapper,都共享二级缓存
    • 不提供实现类 默认采用mybatis的内存形式二级缓存
    • 提供mybatis整合ehcache工具时的实现类 二级缓存就采用 实现类设定的形式 在磁盘
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  1. 测试运行: 使用二级缓存时的测试

​ D盘下发现缓存文件:

在这里插入图片描述

properties属性文件加载

  • 加入的resources/jdbc.properties:

    mysql.driver=com.mysql.cj.jdbc.Driver
    mysql.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai
    mysql.user=root
    mysql.password=123456
    oracle.driver=com.oracle.driver
    oracle.url=http://sdsdsdsd
    oracle.user=root
    oracle.password=123456
    
  • sqlMapConfig.xml:

按此顺序:

在这里插入图片描述

在这里插入图片描述

<!-- 以下配置不需掌握,和Spring整合后,以下配置将被废除 -->
<environments default="mysqlenv">
    <environment id="mysqlenv">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${mysql.driver}"/>
            <property name="url" value="${mysql.url}"/>
            <property name="username" value="${mysql.user}"/>
            <property name="password" value="${mysql.password}"/>
        </dataSource>
    </environment>
</environments>

逆向工程

​ 逆序工程的更多介绍:http://mybatis.org/generator/index.html

根据表结构, 产生javabean ,mapper接口 , mapper.xml. (简单的单表增删改查)

  1. 创建普通的java工程
  2. 加入generatorConfig.xml配置文件到项目路径
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
    <!-- context元素用于指定生成一组对象的环境。targetRuntime:此属性用于指定生成的代码的运行时环境。MyBatis3:*这是默认值* -->
    <context id="testTables" targetRuntime="MyBatis3Simple">

        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=Asia/Shanghai"
                        userId="root" password="123456">
            <!-- connectionURL属性,防止生成不同数据库同名表的代码 -->
            <property name="nullCatalogMeansCurrent" value="true"/>
        </jdbcConnection>

        <!-- 如使用oracle请参考如下 -->
        <!-- <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver"
        connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl"
            userId="senior1802" password="123">
        </jdbcConnection> -->

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL
            和 NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO类的位置  需要写绝对路径-->
        <javaModelGenerator targetPackage="com.ls.bean"
                            targetProject=".\src">
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.ls.mapper"
                         targetProject=".\src">
        </sqlMapGenerator>

        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.ls.mapper" targetProject=".\src">
        </javaClientGenerator>

        <!-- 指定数据库表 -->
        <table tableName="user" domainObjectName="User">
            <columnOverride column="telphone" property="telephone" javaType="java.lang.String"></columnOverride>
        </table>

        <table tableName="orders" domainObjectName="Orders">
            <columnOverride column="orderId" property="orderId" javaType="java.lang.Integer"></columnOverride>
            <columnOverride column="userId" property="userId" javaType="java.lang.Integer"></columnOverride>
            <!-- 指定某列到成员变量的生成规则 -->
            <columnOverride column="price" property="price" javaType="java.lang.Double"></columnOverride>
            <columnOverride column="orderDate" property="orderDate" javaType="java.util.Date"></columnOverride>
            <columnOverride column="payment" property="payment" javaType="java.lang.Byte"></columnOverride>

        </table>
        <table tableName="goods" domainObjectName="Goods">
            <columnOverride column="shopPrice" javaType="java.lang.Double"></columnOverride>
        </table>
        <table tableName="order_detail" domainObjectName="OrderDetail"></table>
        <table tableName="worker" domainObjectName="Worker"></table>
    </context>
</generatorConfiguration>
  1. 加入jar包到lib文件夹中, lib在项目下新建 并 指定为 lib

在这里插入图片描述

  1. 运行类main方法运行

    public static void main(String[] args) throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
    

注解

  • 简单的操作可以使用注解 就可以不写xml
  • 复杂的操作 适合使用xml映射文件配置

WorkerDao:

@Select("select * from worker where age = #{value}")
    List<Worker> queryByAge(int age);

@Update("update worker set age = #{age} where wname = #{wname}")
    int updWorker(Worker worker);

测试:

@Test
public void test051202() throws Exception{
    WorkerDao workerDao = sqlSession.getMapper(WorkerDao.class);
    List<Worker> workers = workerDao.queryByAge(500);
    workers.forEach(System.out::println);
    Worker worker = new Worker();
    worker.setAge(50);
    worker.setWname("金毛狮王");
    workerDao.updWorker(worker);
}

Spring

  • 创建maven普通项目

在这里插入图片描述

  • 导入配置文件

    -

    • <?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/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      

Spring创建bean

  • 使用无参构造创建bean对象

    • spring_config.xml

      • <!-- 演示spring容器功能 -->
        <!-- 1.使用无参构造创建对象 -->
        <bean class="com.ls.bean.Cat" id="cat"></bean>
        <bean class="com.ls.bean.Beauty" id="beauty"></bean>
        
    • MyTest.java

      • // 获取spring容器对象
        ApplicationContext context =
                //基于类路径xml配置文件的 容器对象 实现类
                new ClassPathXmlApplicationContext("spring_config.xml");
        //通过bean的id 来获取 容器管理的bean对象
        Cat cat = context.getBean("cat", Cat.class);
        Beauty beauty = context.getBean("beauty", Beauty.class);
        
        System.out.println(cat);
        System.out.println(beauty);
        

      -

      默认的 spring 容器管理的bean对象是单例
      容器默认用无参构造来创建对象

  • 使用工厂方法创建bean对象

    • 静态工厂方法 产生对象

      • public class StaticFlyFactory {
            public static Flyable creat(String choose){
                if ("can".equals(choose)){
                    return new CanFly();
                }else if ("cannot".equals(choose)){
                    return new CannotFly();
                }else{
                    return null;
                }
            }
        }
        
      • <!-- 2.使用静态工厂方法创建对象 -->
        <bean class="com.ls.factory.StaticFlyFactory" id="scan" factory-method="creat">
            <constructor-arg name="choose" value="can"/>
        </bean>
        <bean class="com.ls.factory.StaticFlyFactory" id="snot" factory-method="creat">
            <constructor-arg name="choose" value="cannot"/>
        </bean>
        
    • 非静态工厂方法 产生对象

      • public class FlyFactory {
            //非静态工厂方法 产生对象
            public Flyable creat(String choose){
                if ("1".equals(choose)){
                    return new CanFly();
                }else if ("2".equals(choose)){
                    return new CannotFly();
                }else{
                    return null;
                }
            }
        }
        
      • <!-- 3.使用非静态工厂方法创建对象,首先创建 工厂对象 -->
        <bean class="com.ls.factory.FlyFactory" id="flyFactory"></bean>
        <bean class="com.ls.bean.Flyable" id="can1" factory-bean="flyFactory"
            factory-method="creat">
            <constructor-arg name="choose" value="1"/>
        </bean>
        <bean class="com.ls.bean.Flyable" id="cannot2" factory-bean="flyFactory"
              factory-method="creat">
            <constructor-arg name="choose" value="2"/>
        </bean>
        
    • test

      • Flyable can = context.getBean("scan", Flyable.class);
        Flyable cannot = context.getBean("snot", Flyable.class);
        Flyable can1 = context.getBean("can1", Flyable.class);
        Flyable cannot2 = context.getBean("cannot2", Flyable.class);
        
        can.fly();
        cannot.fly();
        can1.fly();
        cannot2.fly();
        

spring配置依赖

property注入/setter注入
  • <!-- 1.spring通过setter方法 完成依赖注入,也称property注入 -->
    <bean class="com.ls.bean.Dog" id="dog">
        <property name="name" value="旺财"/>
        <property name="color" value="黄色"/>
    </bean>
    <bean class="com.ls.bean.Person" id="person">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <property name="dog" ref="dog"/>
        <property name="hobbyArray">
            <array value-type="java.lang.String">
                <value>游泳</value>
                <value>登山</value>
                <value>钓鱼</value>
            </array>
        </property>
        <property name="scoreList">
            <list value-type="java.lang.Integer">
                <value>99</value>
                <value>100</value>
                <value>96</value>
            </list>
        </property>
        <property name="schoolSet">
            <set value-type="java.lang.String">
                <value>长清一小</value>
                <value>长清一中</value>
            </set>
        </property>
        <property name="sizeMap">
            <map key-type="java.lang.String" value-type="java.lang.String">
                <entry key="height" value="168cm"/>
                <entry key="weight" value="88kg"/>
            </map>
        </property>
        <property name="prop">
            <props>
                <prop key="x1">v1</prop>
                <prop key="x2">v2</prop>
            </props>
        </property>
     </bean>
    
构造注入
  • <!-- 2.spring通过有参构造进行 属性值的注入 -->
    <bean class="com.ls.bean.Dog" id="dd">
        <constructor-arg name="name" value="大黄"/>
        <constructor-arg name="color" value="yellow"/>
    </bean>
    
    <bean class="com.ls.bean.Person" id="pp">
        <constructor-arg name="name" value="lucy"/>
        <constructor-arg name="age" value="22"/>
        <constructor-arg name="dog" ref="dd"/>
    </bean>
    
简化配置
  • spring通过p c util 命名空间 来简化 property注入,构造注入和集合的写法
  • util命名空间简化集合 集合复用 用的极少
  • 配置文件
<?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:util="http://www.springframework.org/schema/util"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 3.spring通过p c util 命名空间 来简化 property注入,构造注入和集合的写法-->
<bean class="com.ls.bean.Dog" id="dog2" p:name="小狗2" p:color="灰色"></bean>
<bean class="com.ls.bean.Dog" id="dog3" c:name="小狗3" c:color="黑色"></bean>

<bean class="com.ls.bean.Person" id="person2" p:name="张小帅"></bean>
<bean class="com.ls.bean.Person" id="person3" c:name="李希" c:age="21" c:dog-ref="dog2"></bean>

<!-- util命名空间简化集合 集合复用 用的极少 -->
<util:list id="myscore" value-type="java.lang.Integer">
    <value>100</value>
    <value>99</value>
    <value>98</value>
</util:list>
<bean class="com.ls.bean.Person" id="person4" c:name="jacky" c:scoreList-ref="myscore"></bean>

单例

  • ​ Spring默认的容器ApplicationContext管理的bean是单例.可以使用scope属性来指定proptotype等模式.
<bean class="com.ls.bean.Dog" id="dog2" p:name="小狗2" p:color="灰色"></bean>
<bean class="com.ls.bean.Dog" id="dog3" c:name="小狗3" c:color="黑色" scope="prototype"></bean>
  • 测试单例
Dog dog2 = ac.getBean("dog2", Dog.class);
Dog dog22 = ac.getBean("dog2", Dog.class);
Dog dog3 = ac.getBean("dog3", Dog.class);
Dog dog33 = ac.getBean("dog3", Dog.class);
System.out.println(dog2 == dog22);
System.out.println(dog3 == dog33);

Spring容器

  • Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。

  • ApplicationContext默认会预初始化所有的singletion Bean,也可通过为bean元素指定**lazy-init=”true”**取消预初始化。

    • 测试单例的预初始化

      • @Test
        public void test003() throws Exception{
            //测试单例的预初始化
            ApplicationContext ac = new ClassPathXmlApplicationContext("spring2.xml");
        
        }//此时dog3非单例,dd dog2 dog3
        

      -

    • 取消单例的初始化

      -

      -

  • BeanFactory不会进行单例的初始化

    -

    • 没有任何输出
  • 命名空间

自动装配

<?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/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
        default-autowire="byName"><!-- 可以开启全局自动注入 -->
  1. 按类型自动装配(该类型只有一个)

    <bean class="com.ls.bean.Cat" id="cc">
        <property name="cname" value="tom"/>
        <property name="cage" value="2"/>
    </bean>
    
    <bean class="com.ls.bean.Beauty" id="beauty" autowire="byType">
        <property name="name" value="lucy"/>
        <property name="age" value="20"/>
    </bean>
    
  2. 按成员变量名和bean的id对应来自动装配

<bean class="com.ls.bean.Cat" id="cc">
    <property name="cname" value="tom"/>
    <property name="cage" value="2"/>
</bean>

<bean class="com.ls.bean.Cat" id="cat">
    <property name="cname" value="kate"/>
    <property name="cage" value="3"/>
</bean>
<bean class="com.ls.bean.Beauty" id="beauty" autowire="byName">
    <property name="name" value="lucy"/>
    <property name="age" value="20"/>
</bean>

在这里插入图片描述

  1. spring配置文件里使用context命名空间下的标签来加载properties配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd"><!-- context命名空间 -->
        
    <!-- 使用context命名空间里的标签 加载 properties配置文件 -->
    <context:property-placeholder location="jdbc.properties"/>
    
    <!-- 管理连接池 -->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="driverClassName" value="${druid.driver}"/>
        <property name="username" value="${druid.user}"/>
        <property name="password" value="${druid.password}"/>
        <property name="url" value="${druid.url}"/>
        <property name="initialSize" value="2"/>
        <property name="maxActive" value="10"/>
        <property name="minIdle" value="3"/>
        <property name="maxWait" value="60000"/>
    </bean>
    
    DruidDataSource dataSource = ac.getBean("dataSource", DruidDataSource.class);
    DruidPooledConnection connection = dataSource.getConnection();
    System.out.println(connection);
    
  2. 管理JDBCTemplate对象

    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>
    
    JdbcTemplate template = ac.getBean("jdbcTemplate", JdbcTemplate.class);
    String sql = "select count(*) cs from goods";
    Integer value = template.queryForObject(sql, Integer.class);
    System.out.println(value);
    
  3. 配置类

    /**
     * 配置类 替代 配置文件
     * 注解和方法 替代 配置文件里的标签
     */
    @Configuration //标注此注解的类 是个 配置类
    public class MyConfig {
        @Value("lucy")
        private String name;
        @Value("22")
        private Integer age;
    
        @Bean
        public Beauty beauty(Cat cat){//默认的 方法名即容器管理的对象的id,该cat为id为cat的
            //先按照类型匹配,若该类型有多个实现类,则按照名字匹配
            Beauty beauty = new Beauty();
            beauty.setName("萨达");
            beauty.setAge(20);
            beauty.setCat(cat);
            return beauty;
        }
    
        @Bean(name = "girl")//起别名
        public Beauty b2(Cat cc){//ByName
            Beauty beauty = new Beauty(name, age);
            beauty.setCat(cc);
            return beauty;//了解
        }
    
        @Bean
        public Cat cat(){
            Cat cat = new Cat();
            cat.setCname("tomcat");
            cat.setCage(1);
            return cat;
        }
    
        @Bean
        public Cat cc(){
            Cat cat = new Cat();
            cat.setCname("cc");
            cat.setCage(2);
            return cat;
        }
    }
    
  4. 扫描五个注解的类称为容器里的组件_bean

@Repository //仓库 指持久层的组件 被容器管理的对象
public class UserDaoImpl implements UserDao {
    @Override
    public String getName(int id) {
        //假装查数据库
        if (id==1){
            return "张三";
        }else if (id==2){
            return "李四";
        }else return "王五";
    }
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired //自动装配默认 先按照类型装配,然后尝试从多个里再按名字进行装配
    //最后如果名字也没有符合的 则 报错 希望1个但找到2个

    //@Qualifier("userDaoImpl2") 两个以上可以使用限定符

    @Resource(name = "userDaoImpl")//两个以上可以使用resource
    private UserDao userDao;//IOC DI

    @Override
    public String getName(int id) {
        return userDao.getName(id);
    }
}
//@Controller("uc") //默认的将类名首字母小写 作为bean的id值
@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void show(){
        String name = userService.getName(1);
        System.out.println(name);
        String name1 = userService.getName(2);
        System.out.println(name1);
    }
}
@Configuration //标注此注解的类 是个 配置类

//扫描配置 扫描哪些个包里带有五大注解的类 成为 容器管理的对象 即组件
@ComponentScan(basePackages = {"com.ls"})
public class MyConfig {
    ....
}
@Test
public void test003() throws Exception{
    ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
    //获取容器中定义的所有组件/bean 的名字 id/name
    String[] names = ac.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

在这里插入图片描述

配置类和配置文件一起使用

配置类 中引用 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean class="com.ls.bean.Dog" id="bigDog">
        <property name="name" value="大狗"/>
        <property name="color" value="灰色"/>
    </bean>
@Configuration
@ComponentScan(basePackages = {"com.ls"})
@ImportResource(locations = {"spring_b.xml"})
public class MyConfig {
@Test
public void test006() throws Exception{
    ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
    String[] names = ac.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

在这里插入图片描述

配置文件为主 引用 配置类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean class="com.ls.bean.Dog" id="bigDog">
        <property name="name" value="大狗"/>
        <property name="color" value="灰色"/>
    </bean>
    
	<!-- 以配置文件为主 扫描5个注解组件,其中就有配置类 -->
    <context:component-scan base-package="com.ls"/>

</beans>
@Configuration
//@ComponentScan(basePackages = {"com.ls"})
public class MyConfig {

结果:

@Test
public void test006() throws Exception{
    ApplicationContext ac = new  ClassPathXmlApplicationContext("spring_b.xml");
    String[] names = ac.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

在这里插入图片描述

Spring-test

spring-test包 里面增强了单元测试功能

  • 导入依赖

    • <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.3.18</version>
      </dependency>
      
    • 属性locations配置多个xml文件地址

      • //单元测试时 使用spring增强过的单元测试工具
        @RunWith(value = SpringRunner.class)
        //容器配置  
        @ContextConfiguration(locations = "classpath:spring_b.xml")
        public class SpTest {
            @Autowired
            ApplicationContext ac;
        
            @Test
            public void test001() throws Exception{
                Dog bigDog = ac.getBean("bigDog", Dog.class);
                System.out.println(bigDog);
            }
        }
        
    • 属性classes配置多个配置类的类型

      • @RunWith(value = SpringRunner.class)
        @ContextConfiguration(classes = MyConfig.class)
        public class SpTest {
            @Autowired
            ApplicationContext ac;
        
            @Test
            public void test001() throws Exception{
                Dog bigDog = ac.getBean("bigDog", Dog.class);
                System.out.println(bigDog);
            }
        }
        

AOP

相关概念
  1. 通知/增强: 公共业务逻辑 , 对主体业务逻辑进行的增强. 通知通常是方法形式.
    • 前置通知: 主体业务逻辑执行前
    • 后置通知: 主体业务逻辑执行完,无论有无异常
    • 后置通知之返回值之后:主体业务逻辑返回了返回值之后
    • 异常通知: 主体业务逻辑执行发生了异常
    • 环绕通知: 主体业务逻辑执行前,执行后
  2. 连接点: 指主体业务逻辑所在的**方法**,可以在这些方法内添加 增强/通知.
    • 通常指业务层的方法
  3. 切入点: 连接点满足条件时,加入了增强/通知,就变为了切入点.
  4. 目标对象: 通常指业务层的对象. 被代理对象
  5. 代理对象: 框架通过JDK动态代理 或 cglib代理产生的目标对象的代理对象.(业务层的替身)
  6. 织入: 把通知添加到切入点里,形成代理对象的过程. 这是一个动作.
  7. 切面: 通知/增强 与 切入点的关系! 通知和切入点的统称. 这里强调 通知和切入点的关系. 比如: 前置还是后置等. 这里强调的是 我们要 配置!!

在这里插入图片描述

通知和切入点的关系:前置,后置,环绕,异常,返回值之后

前置/后置/异常通知
  • 导入依赖

    • <dependencies>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.18</version>
      </dependency>
      
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.3.18</version>
      </dependency>
      
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.3.18</version>
      </dependency><!--!!!!!!!!!!!!!!!!-->
      
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.13.2</version>
      </dependency>
      
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.24</version>
          <scope>provided</scope>
      </dependency>
      </dependencies>
      
  • 通知

  • 增强/通知 以方法形式存在的

    • public class MyAdvice {
          //业务层方法执行之前执行
          public void logTime(){
              String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
              System.out.println("Time: "+format);
          }
      
          //正常执行业务层方法之后
          public void after(){
              System.out.println("----执行之后才调用----");
          }
      
          //业务层方法异常之后执行
          public void th(){
              System.out.println("===出现异常之后执行===");
          }
      }
      
  • xml

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">
      
          <!-- 扫描添加了5个注解的组件 -->
          <context:component-scan base-package="com.ls"/>
          <!-- 通知类 -->
          <bean class="com.ls.util.MyAdvice" id="myAdvice"/>
      
          <!-- aop配置 -->
          <aop:config>
              <!-- 切面配置,切面就是通知和切入点 -->
              <aop:aspect ref="myAdvice"><!-- ref:关联已有的通知类对象 -->
                  <!-- 切入点表达式,在业务层的所有连接点方法里 找 符合的 进行增强,之后成为切入点方法 -->
                  <!-- 第一个* 表示 任何返回值类型 -->
                  <!-- com.ls.service..* 表示:com.ls.service包及其所有子包里的任何类 -->
                  <!-- add() 表示add方法 -->
                  <!-- (..) 表示 任何参数 -->
                  <aop:pointcut id="pt1" expression="execution(* com.ls.service..*.add(..))"/>
                  <aop:pointcut id="pt2" expression="execution(* com.ls.service..*.update*(..))"/>
                  <aop:pointcut id="pt3" expression="execution(* com.ls.service..*.del*(..))"/>
                  <!-- 通知和切入点的关系:前置,后置,环绕,异常,返回值之后 -->
                  <!-- 按照pt1表达式来找到 切入点方法,织入logTime方法,前置织入 -->
                  <aop:before method="logTime" pointcut-ref="pt1"/>
                  <aop:before method="logTime" pointcut-ref="pt2"/>
                  <aop:after method="after" pointcut-ref="pt2"/>
                  <!-- 异常通知 -->
                  <aop:after-throwing method="th" pointcut-ref="pt3"/>
              </aop:aspect>
          </aop:config>
      </beans>
      
  • 测试类

    • @RunWith(SpringRunner.class)
      @ContextConfiguration(locations = {"classpath:spring.xml"})
      public class MyTest {
          @Autowired
          private ApplicationContext ac;
      
          @Test
          public void test() throws Exception{
              UserController userController = ac.getBean("userController", UserController.class);
      //        userController.add();
              userController.del();
      //        userController.update();
      //        userController.query();
          }
      }
      
  • 结果

    -

    -

环绕通知

环绕通知 可以自己控制 被代理对象:目标对象 的切入点方法在何时执行,可以偷换切入点方法的入参和返回值

环绕通知必须提供一个参数 表示切入点对象:ProceedingJoinPointer

MyAdvice.java

@Around("ptt3()")
public Object myAround(ProceedingJoinPoint pt){
    //环绕通知里的目标方法执行前:
    System.out.println("环绕通知 之前置通知:");

    //执行目标方法
    try {
        Object result = pt.proceed();

        //环绕通知里的目标方法执行后
        System.out.println("环绕通知 之后置通知:");
    } catch (Throwable throwable) {
        //环绕通知里的目标方法发生异常时
        System.out.println("环绕通知 执行目标方法时有异常了!");
    }

    return null;
}
<!-- aop配置 -->
<aop:config>
    <!-- 切面配置,切面就是通知和切入点 -->
    <aop:aspect ref="myAdvice"><!-- ref:关联已有的通知类对象 -->

        <aop:pointcut id="pt4" expression="execution(* com.ls.service..*.query*(..))"/>

        <!-- 环绕通知 -->
        <aop:around method="myAround" pointcut-ref="pt4"/>
        
    </aop:aspect>
</aop:config>
    @Test
    public void test() throws Exception{
        UserController userController = ac.getBean("userController", UserController.class);
        userController.query();
    }

在这里插入图片描述

注解配置

xml里的 切点 标签 改为方法+注解

通知的方法前加注解

  • spring2.xml

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">
      
          <!-- 扫描添加了5个注解的组件 -->
          <context:component-scan base-package="com.ls"/>
      
          <!-- 开启aop的注解 -->
          <aop:aspectj-autoproxy/>
      </beans>
      
  • 通知类

    • @Component //成为spring容器里的一个组件
      @Aspect //切面类,在此配置切面
      public class MyAdvice {
      
          //xml里的 切点 标签 改为方法+注解
          @Pointcut("execution(* com.ls.service..*.*(..))")
          public void ptt(){
      
          }
      
          @Pointcut("execution(* com.ls.service..*.update(..))")
          public void ptt2(){
      
          }
      
          @Pointcut("execution(* com.ls.service..*.del(..))")
          public void ptt3(){
      
          }
      
      
          //业务层方法执行之前执行
          @Before("ptt()")
          public void logTime(){//通知
              String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
              System.out.println("Time: "+format);
          }
      
          //正常执行业务层方法之后
          @After("ptt2()")
          public void after(){
              System.out.println("----执行之后才调用----");
          }
      
          //业务层方法异常之后执行
          public void th(){
              System.out.println("===出现异常之后执行===");
      
              
          @Around("ptt3()")
          public Object myAround(ProceedingJoinPoint pt){
              //环绕通知里的目标方法执行前:
              System.out.println("环绕通知 之前置通知:");
      
              //执行目标方法
              try {
                  Object result = pt.proceed();
      
                  //环绕通知里的目标方法执行后
                  System.out.println("环绕通知 之后置通知:");
              } catch (Throwable throwable) {
                  //环绕通知里的目标方法发生异常时
                  System.out.println("环绕通知 执行目标方法时有异常了!");
              }
      
              return null;
          }
      
      }
      
  • test

    • @RunWith(SpringRunner.class)
      @ContextConfiguration(locations = {"classpath:spring2.xml"})
      public class MyTest2 {
          @Autowired
          private ApplicationContext ac;
      
          @Test
          public void test001() throws Exception{
              UserController userController = ac.getBean("userController", UserController.class);
              userController.add();
      //        userController.del();
              userController.update();
              userController.query();
          }
      }
      

在这里插入图片描述

XML配置的AOP事务

给业务层方法添加事务操作,事务操作就是通知/增强

事务操作: 异常回滚 手动提交等, 这些增强在spring中默认提供了一个类,事务通知类

事务属性: 隔离级别 传播级别

JDBC

  • 导入依赖

    • <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.13</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.20</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.3.18</version>
      </dependency>
      
    • jdbc.properties

    • spring3.xml

      • <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
        
               xmlns:tx="http://www.springframework.org/schema/tx"
        
               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/tx
               http://www.springframework.org/schema/tx/spring-tx.xsd
        
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd">
        
            <!-- 扫描添加了5个注解的组件 -->
            <context:component-scan base-package="com.ls"/>
        
            <!-- 关联properties属性配置文件 -->
            <context:property-placeholder location="jdbc.properties"/>
        
            <!-- 管理连接池对象 -->
            <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
                <property name="driverClassName" value="${druid.driver}"/>
                <property name="username" value="${druid.user}"/>
                <property name="password" value="${druid.password}"/>
                <property name="url" value="${druid.url}"/>
            </bean>
        
            <!-- 管理JDBCTemplate对象 -->
            <bean class="org.springframework.jdbc.core.JdbcTemplate" id="template">
                <property name="dataSource" ref="dataSource"/>
            </bean>
        
            <!-- 基于连接池的事务管理对象 -->
            <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
                <property name="dataSource" ref="dataSource"/>
            </bean>
            <!-- 事务属性配置 -->
            <tx:advice id="txAdvice" transaction-manager="transactionManager">
                <tx:attributes>
                    <!-- 配置insert开头的方法的事务属性,隔离,传播 -->
                    <tx:method name="insert*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                    <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                    <tx:method name="complex*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                </tx:attributes>
            </tx:advice>
        
            <!-- AOP面向切面编程 将 事务通知 切入到 业务层进行增强 -->
            <aop:config>
                <aop:pointcut id="pt1" expression="execution(* com.ls.service..*.*(..))"/>
                <!-- 事务助手(切面)          通知                   切点   -->
                <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
            </aop:config>
        
        </beans>
        
  • 测试


    • 在这里插入图片描述

    • 操作
      -

  • 结果

    • 数据库表中插入失败也失败,说明事务开启
注解版springAOP事务配置
  • spring4.xml

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
      
             xmlns:tx="http://www.springframework.org/schema/tx"
      
             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/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd
      
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">
      
          <!-- 扫描添加了5个注解的组件 -->
          <context:component-scan base-package="com.ls"/>
      
          <!-- 关联properties属性配置文件 -->
          <context:property-placeholder location="jdbc.properties"/>
      
          <!-- 管理连接池对象 -->
          <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
              <property name="driverClassName" value="${druid.driver}"/>
              <property name="username" value="${druid.user}"/>
              <property name="password" value="${druid.password}"/>
              <property name="url" value="${druid.url}"/>
          </bean>
      
          <!-- 管理JDBCTemplate对象 -->
          <bean class="org.springframework.jdbc.core.JdbcTemplate" id="template">
              <property name="dataSource" ref="dataSource"/>
          </bean>
      
          <!-- 基于连接池的事务管理对象 -->
          <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
              <property name="dataSource" ref="dataSource"/>
          </bean>
      
      
      	<!-- 开启注解版事务配置 -->
      	<tx:annotation-driven/>
      </beans>
      
  • service

    • @Service
      @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
      //当前业务层类 里的所有方法都可添加事务  默认事务配置
      public class WorkerServiceImpl implements WorkerService {
          @Autowired
          private WorkerDao workerDao;
      
          //单独给某方法配置事务属性
          @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRES_NEW)
          @Override
          public int insertWorker(Worker worker) {
              return workerDao.insertWorker(worker);
          }
      
          @Override
          public int updateWid(String wid, String oldId) {
              return workerDao.updateWid(wid,oldId);
          }
      
          @Override
          public void complexA() {
              // 第一操作 新增一个
              Worker w1 = new Worker("B12","阿萨德",23,"男");
              int i = insertWorker(w1);
              System.out.println("第一操作结果: "+i);
              //第二操作 将刚才新增的那个id值修改为其他的
              int i1 = updateWid("Rt001", "B12");
              System.out.println("第二操作结果: "+i1);
          }
      }
      
    • 测试成功(记得更换读的配置文件)

SM整合

  • 导入依赖

    • <!-- 依赖 -->
      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>5.3.18</version>
          </dependency>
      
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-aspects</artifactId>
              <version>5.3.18</version>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-test</artifactId>
              <version>5.3.18</version>
          </dependency>
      
      
      
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-jdbc</artifactId>
              <version>5.3.18</version>
          </dependency>
      
      
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.13.2</version>
          </dependency>
      
          <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <version>1.18.24</version>
              <scope>provided</scope>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>8.0.13</version>
          </dependency>
          <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>1.1.20</version>
          </dependency>
      
          <!-- mybatis log4j mybatis-spring -->
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.4.6</version>
          </dependency>
      
          <dependency>
              <groupId>commons-logging</groupId>
              <artifactId>commons-logging</artifactId>
              <version>1.2</version>
          </dependency>
      
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis-spring</artifactId>
              <version>2.0.6</version>
          </dependency>
      
      </dependencies>
      
  • 添加配置文件,建包

    -

    • mybatis

      • <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
        <configuration>
            <!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
            <settings>
                <setting name="logImpl" value="LOG4J"/>
                <setting name="lazyLoadingEnabled" value="true"/>
                <setting name="aggressiveLazyLoading" value="false"/>
                <setting name="cacheEnabled" value="true"/>
                <!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
                <setting name="mapUnderscoreToCamelCase" value="true"/>
            </settings>
            <!-- 类型别名 -->
            <typeAliases>
                <package name="com.ls.bean"/>
            </typeAliases>
            <!-- 连接池,事务管理等 都在spring里配置 -->
        
        </configuration>
        
    • spring.xml

      • <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
        
               xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xontext="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/tx
               http://www.springframework.org/schema/tx/spring-tx.xsd
        
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd">
        
            <!-- 扫描标注了5个注解组件 -->
            <context:component-scan base-package="com.ls"/>
            <!-- properties文件 -->
            <context:property-placeholder location="jdbc.properties"/>
            <!-- 连接池对象 -->
            <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
                <property name="url" value="${mysql.url}"/>
                <property name="username" value="${mysql.user}"/>
                <property name="password" value="${mysql.password}"/>
                <property name="driverClassName" value="${mysql.driver}"/>
            </bean>
        
            <!-- 事务管理器 -->
            <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
            id="transactionManager">
                <property name="dataSource" ref="dataSource"/>
            </bean>
            <!-- 开启注解版的AOP -->
            <aop:aspectj-autoproxy/>
        
            <!-- 事务注解驱动 -->
            <tx:annotation-driven transaction-manager="transactionManager"/>
        
            <!-- mybatis的sqlSessionFactoryBean -->
            <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
                <property name="dataSource" ref="dataSource"/>
                <!-- mybatis的配置文件 -->
                <property name="configLocation" value="mybatis.xml"/>
            </bean>
        
            <!-- 扫描@Mapper注解的一个类 -->
            <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="configurer">
                <property name="basePackage" value="com.ls.mapper"/>
                <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
            </bean>
        </beans>
        
    • UserMapper.java

      • //@Repository spring中 5个组件注解之一,语义:持久层
        @Mapper //spring 和 mybatis整合之后 有一个组件MapperScanConfig 专门扫描@Mapper注解
        public interface UserMapper {
            List<User> queryByCondition(User user);
        
            int updateUser(User user);
        
            int changePass(User u1);
        }
        
      • UserMapper.xml 完善查询

    • UserServiceImpl.java

      • @Service
        @Transactional(isolation = Isolation.REPEATABLE_READ)
        public class UserServiceImpl implements UserService {
            @Autowired
            private UserMapper userMapper;
        
            @Transactional(propagation = Propagation.SUPPORTS)
            @Override
            public List<User> queryByCondition(User user) {
                return userMapper.queryByCondition(user);
            }
        
            @Transactional(propagation = Propagation.REQUIRED)
            @Override
            public int updateUser(User user) {
                return userMapper.updateUser(user);
            }
        
            @Transactional(propagation = Propagation.REQUIRED)
            @Override
            public void showTrans(User u1, User u2) {
                int rows = userMapper.changePass(u1);
                System.out.println(rows);
        
                rows = userMapper.changePass(u2);
                System.out.println(rows);
            }
        }
        
    • test

      • public void showChangePass(){
            User u1 = new User();
            u1.setUserId(708);
            u1.setUserName("milla");
            u1.setUserPass("1234");
        
            User u2 = new User();
            u2.setUserId(711);
            u2.setUserName("milla");
            u2.setUserPass("1234");
        
            userService.showTrans(u1,u2);
        }
        
      • 在这里插入图片描述

      • 数据库未更改成功, 事务演示成功

SpringMVC

handler方法 / controller方法 的返回值类型

1.ModelAndView类型 数据和视图信息 是根本操作
2.String类型
A: 返回逻辑视图名,“abc” 经过视图解析器的解析封装
B: 转发
转发到其它handler方法
直接转发到jsp页面, 此用法不经过视图解析器,页面路径写全
C: 重定向
① 重定向到其它handler方法
② 重定向到jsp页面 不能是WEB-INF里面, 此用法不经过视图解析器,页面路径写全
③ 重定向到其它服务器
3.void
可以在handler方法入参位置,自由的写req,resp,session这些WEB AIP,传统的web用法
可以处理ajax请求 使用resp返回数据
4.@ResponseBody + 任意类型返回值 都能返回 json数据

1. 返回ModelAndView
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
<h3><a href="${pageContext.request.contextPath}/beauty/testModelAndView">testModelAndView</a></h3>
@GetMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    ModelAndView mv = new ModelAndView();
    mv.setViewName("show");
    String[] names = {"张三","李四","王五"};
    mv.addObject("names",names);
    return mv;
}

2. 返回String
1.返回逻辑视图名
  • pom.xml
<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.18</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.18</version>
  </dependency>

  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
  </dependency>

  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.3</version>
    <scope>provided</scope>
  </dependency>

  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
  </dependency>
</dependencies>
  • springconfig

    • Converter<S,T> 是 springMVC里的转换器接口,框架内已有多个 应用常用个类型转型的实现类
      但没有 将 yyyy-MM-dd 格式 字符串 转为 Date 类型的转换器
      需要自定义 字符串到日期类型转换器实现类 完成yyyy-MM-dd 格式 字符串 转为 Date 类型
      
<?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/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 注解组件扫描 -->
    <context:component-scan base-package="com.ls"/>

    <!-- 处理器映射器: 找,处理器适配器: 执行 -->
    <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>

    <!-- 视图解析器:根据路径信息找到jsp页面 -->
    <!-- 比如:/WEB-INF/templates/abc.jsp         abc叫做逻辑视图名 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/templates/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 转换器工厂bean 用于添加自定义的转换器 --> 要在注解里配置
    <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.ls.config.MyStringToDateConverter"/>
            </set>
        </property>
    </bean>

</beans>
  • web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
       version="3.1">
    
       <welcome-file-list>
         <welcome-file>/index.jsp</welcome-file>
       </welcome-file-list>
    
       <!-- 配置springMVC的 前端控制器:一个中心 -->
      <servlet>
          <servlet-name>dispatcherServlet</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <!-- 当tomcat初始化前端控制器时 需要将spring容器对象也初始化 -->
          <init-param>
              <!-- spring容器配置,配置文件路径 -->
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:spring_*.xml</param-value>
          </init-param>
      </servlet>
    
       <servlet-mapping>
           <servlet-name>dispatcherServlet</servlet-name>
           <url-pattern>/</url-pattern>
           <!-- springMVC的框架里, 前端控制器访问路径 / 指: 除了jsp之外的所有请求 -->
           <!-- tomcat里默认servlet 访问路径也是 / , 用于处理静态资源,此时就失效了 -->
       </servlet-mapping>
    
        <!-- 配置字符编码过滤器 springMVC里提供的 -->
        <filter>
            <filter-name>characterEncodingFilter</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>characterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    </web-app>
    
  • 准备前端页面index,点击链接访问handler

@Controller
@RequestMapping("/beauty")
public class BeautyController {
    //请求中的参数绑定:  请求参数自动封装到handler方法里的入参位置

    //@RequestMapping(path = "/testBasic",method = RequestMethod.GET)
    @GetMapping("/testBasic") //等于上面一句的效果
    public String testBasicGet(String bname,Integer bage){
        System.out.println(bname+"~"+bage);
        return "abc";
    }

    @PostMapping("/testBasic")
    public String testBasicPost(Beauty beauty){
        //请求参数名字 和 方法入参对象的成员变量名一致,自动封装到对象的成员变量里
        System.out.println(beauty);
        return "abc";
    }

    @PostMapping("/testBasicCat")
    public String testBasicCat(Beauty beauty){
        System.out.println(beauty);
        return "abc";
    }
}
  • handler返回abc页面
2.转发
  • 转发到其它handler方法

​ 转发如何携带数据 ModelAndView ModelMap Model 自由的定义到入参部分

<h3><a href="${pageContext.request.contextPath}/beauty/testDispatcher">testDispatcher</a></h3>
@GetMapping("testDispatcher")
public String testDispatcher(ModelMap map){
    map.addAttribute("msg","演示携带数据");
    return "forward:/hello";
}
@Controller
public class HelloHandler {
    @RequestMapping("/hello")
    public String helloShow(){
        System.out.println("helloShow执行了!");
        return "abc";
    }
}

  • 直接转发到jsp页面, 此用法不经过视图解析器,页面路径写全
<h3><a href="${pageContext.request.contextPath}/beauty/testDispatcherJsp">testDispatcherJsp</a></h3>
@RequestMapping("testDispatcherJsp")
public String testDispatcherJsp(Model model){
    model.addAttribute("msg","转到jsp");
    //转发不经过视图解析器,需要自己写全页面路径
    return "forward:/WEB-INF/templates/abc.jsp";
}
3.重定向
  • 重定向到其它handler方法
  1. 重定向无法使用 ModelAndView ModelMap Model 共享数据,因为是两次请求
  2. mvc框架自动的将 上述数据 拼接到 要重定向的路径 之后 ? 开头 键值对形式
  3. 重定向路径开头的**/也代表当前应用**,跟web阶段的重定向(localhost:8080)不同的
<h3><a href="${pageContext.request.contextPath}/beauty/testRedirect">testRedirect</a></h3>
@RequestMapping("/testRedirect")
public String testRedirect(ModelMap map){
    map.addAttribute("msg","重定向到handler方法演示");
    return "redirect:/hello";
}

在这里插入图片描述

  • 重定向到jsp页面
<h3><a href="${pageContext.request.contextPath}/beauty/testRedirectJsp">testRedirectJsp</a></h3>
@GetMapping("/testRedirectJsp")
public String testRedirectJsp(ModelMap map){
    map.addAttribute("msg","重定向到jsp页面演示");
    return "redirect:/plain.jsp";
}

页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>plain</title>
</head>
<body>
<h2>这是一个普通的jsp页面! msg = ${msg} / 请求参数msg = ${param.msg}</h2>
</body>
</html>

在这里插入图片描述

  • 重定向其他服务器,需要将url写全
return "redirect:http://www.jd.com"
3.返回void

可以在handler方法入参位置,自由的写req,resp,session这些WEB AIP,传统的web用法
可以处理ajax请求 使用resp返回数据

  • testServlet
<form action="${pageContext.request.contextPath}/beauty/testServlet" method="post">
    <h3>testBasic - POST</h3>
    美女名字:<input type="text" name="bname" value="大岩子"/><br/>
    美女年龄:<input type="text" name="bage" value="20"/><br/>
    <input type="submit"/>
</form>
<hr style="border: 1px dashed purple;" />
@PostMapping("/testServlet")
public void testServlet(HttpServletRequest req, HttpServletResponse resp, HttpSession session,Beauty beauty)throws Exception{
    System.out.println(beauty);
    req.setAttribute("beauty",beauty);

    session.setAttribute("bea",beauty);

    req.getRequestDispatcher("/WEB-INF/templates/abc.jsp").forward(req,resp);
}
  • testAjax

导入jq

<!-- 开启静态资源处理器  作用如同: tomcat里的默认servlet -->
<!-- 前端控制器 DispatcherServlet 访问路径为 / ,除了jsp之外的资源,包括静态资源 -->
<!-- 我们希望 前端控制器 只处理handler动态资源,所以要配置默认servlet来处理 js,css等静态资源 -->
<mvc:default-servlet-handler/>
<h2><button id="btn">点击使用ajax</button></h2>
<script>
    $("#btn").click(function () {
        $.ajax({
            url:'${pageContext.request.contextPath}/beauty/testAjax',
            type:'post',
            async:'true',
            data:{"bname":'李小花',"bage":22},
            dataType:'text',
            success: function (backData) {
                console.log(backData);
            }
        })
    })
</script>
@PostMapping("/testAjax")
public void testAjax(HttpServletRequest req, HttpServletResponse resp,Beauty beauty)throws Exception{
    System.out.println(beauty);
    resp.getWriter().write("ajax ok!");
}

4.返回 json数据

@ResponseBody + 任意类型返回值 都能返回 json数据

<h3><button id="testResponseBody">testResponseBody</button></h3>
<table id="tab" style="width: 50%;" border="1px">
    <tr>
        <td>美女名字</td>
        <td>美女年龄</td>
        <td>美女猫名字</td>
        <td>美女猫年龄</td>
    </tr>
</table>

<script>
    $("#testResponseBody").click(function () {
        $.ajax({
            url:'${pageContext.request.contextPath}/beauty/testResponseBody',
            type: 'post',
            dataType: 'json',
            success:function (backData) {
                console.log(backData);
                for (let i = 0; i < backData.length; i++) {
                    let newTr = $("<tr></tr>");
                    let td1 = $("<td></td>");td1.text(backData[i].bname);
                    let td2 = $("<td></td>");td2.text(backData[i].bage)
                    let td3 = $("<td></td>");td3.text(backData[i].cat.cname);
                    let td4 = $("<td></td>");td4.text(backData[i].cat.cage);
                    newTr.append(td1);
                    newTr.append(td2);
                    newTr.append(td3);
                    newTr.append(td4);
                    $("#tab").append(newTr);
                }
            }
        })
    })
</script>
@PostMapping("/testResponseBody")
@ResponseBody
public List<Beauty> testResponseBody(){
    Cat c1 = new Cat();c1.setCname("tom");c1.setCage(2);
    Beauty b1 = new Beauty();b1.setCat(c1);b1.setBname("福克斯");b1.setBage(28);

    Cat c2 = new Cat();c2.setCname("cat");c2.setCage(3);
    Beauty b2 = new Beauty();b2.setCat(c2);b2.setBname("露西");b2.setBage(28);

    List<Beauty> beautyList = new ArrayList<>();
    beautyList.add(b1);
    beautyList.add(b2);
    return beautyList;
}

参数绑定

1.请求参数名 和 handler方法入参名 不一致
<a href="${pageContext.request.contextPath}/beauty/testRequestParam?name=王五&age=20">testRequestParam</a>

在这里插入图片描述

@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(name = "name") String bname,
                               @RequestParam(name = "age") Integer bage,
                               @RequestParam(name = "sex",required = false,defaultValue = "女") String sex){
    System.out.println(bname + "/" + bage+ "/" + sex);
    return "abc";
}

2.@CookieValue 获取请求中的cookie
<h3><a href="${pageContext.request.contextPath}/beauty/testCookieValue">testCookieValue</a></h3>

@CookieValue 和 @RequestParam 一样 有四个属性

@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue(name = "JSESSIONID") String sessionID){
    System.out.println(sessionID);
    return "abc";
}

3.@ModelAttribute

@ModelAttribute 添加到方法上,在handler其它方法执行前,必须先执行

目的:

​ 这样的方法 设置一些数据 连同 用户端的请求参数 一并封装到 其他方法的入参位置
提前准备一些客户端没有的数据

<form action="${pageContext.request.contextPath}/beauty/testModelAttribute" method="post">
    <h3>testModelAttribute</h3>
    美女名字:<input type="text" name="bname" value="李冰冰"/><br/>
    美女年龄:<input type="text" name="bage" value="38"/><br/>
    <input type="submit"/>
</form>
<hr style="border: 1px dashed greenyellow;"/>
@ModelAttribute
public Beauty init(){
    Beauty beauty = new Beauty();
    Cat cat = new Cat();
    cat.setCage(3);
    cat.setCname("tomcat");
    beauty.setCat(cat);
    String[] hobby = {"看书","写作","游泳","cosplay"};
    beauty.setHobby(hobby);
    return beauty;
}

@PostMapping("/testModelAttribute")
public String testModelAttribute(Beauty beauty){
    System.out.println(beauty);
    return "abc";
}

Beauty(bname=李冰冰, bage=38, cat=Cat(cname=tomcat, cage=3), hobby=[看书, 写作, 游泳, cosplay], dogs=null, edu=null, birthday=null)

上述带有 @ModelAttribute的方法具有返回值, 也可以不写返回值,但需要将数据添加到Model或ModelMap中,在需要参数绑定的方法的入参部分 使用@ModelAttribute来指定.

@ModelAttribute
public void init(ModelMap map) {
    Beauty beauty = new Beauty();
    Cat cat = new Cat();
    cat.setCage(2);
    cat.setCname("mimi");
    beauty.setCat(cat);
    String[] hobby = {"蓝球","足球","羽毛球","cosplay"};
    beauty.setHobby(hobby);
    map.addAttribute("bb",beauty);
}
@PostMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute(name = "bb") Beauty beauty){
    System.out.println(beauty);
    return "abc";
}

Beauty(bname=李冰冰, bage=38, cat=Cat(cname=mimi, cage=2), hobby=[蓝球, 足球, 羽毛球, cosplay], dogs=null, edu=null, birthday=null)

4.@SessionAttribute

​ 将session域中的属性 绑定到Handler方法的入参中

<%-- 向session域中 设置属性 --%>
<c:set scope="session" var="ns" value="老杨"/>

<h3>
    <a href="${pageContext.request.contextPath}/beauty/testSessionAttribute">testSessionAttribute</a>
</h3>
@RequestMapping("/testSessionAttribute")
public String testSessionAttribute(@SessionAttribute(name = "ns") String name,
                                   HttpSession session){
    //springMVC建议 去除 web API
    System.out.println(name);
    System.out.println(session.getAttribute("ns"));
    return "abc";
}

5.@SessionAttributes

默认的Model , ModelMap 中存放的属性都在请求域里, 如果想把属性设置到session域中而不使用ServletAPI,那么需要使用和这个注解. 此注解用在上.

  1. index.jsp

    <h3><a href="${pageContext.request.contextPath}/beauty/testSessionAttributes">testSessionAttributes</a></h3>
    
  2. Controller

@Controller
@RequestMapping("/beauty")
@SessionAttributes(names = {"beauty"})
//向请求域中设置名为beauty的域属性时,一并设置到 会话域里, 为了不让使用webAPI
public class BeautyController {
    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(ModelMap map){
        Beauty beauty = new Beauty();
        beauty.setBname("lucy");
        beauty.setBage(20);
        //默认的 ModelMap Model ModelAndView 管理的属性 设置到请求域里
        map.addAttribute("beauty",beauty);

        Beauty b2 = new Beauty();
        b2.setBname("TomSen");
        b2.setBage(22);
        map.addAttribute("b2",b2);
        return "dataShow";
    }
}    
  1. dataShow.jsp
<html>
<head>
    <title>Title</title>
</head>
<body>
 <p>请求域里的beauty = ${requestScope.beauty.bname} - ${requestScope.beauty.bage}</p>
 <p>会话域里的beauty = ${sessionScope.beauty.bname} - ${sessionScope.beauty.bage}</p>

 <p>请求域里的b2 = ${requestScope.b2.bname} - ${requestScope.b2.bage}</p>
 <p>会话域里的b2 = ${sessionScope.b2.bname} - ${sessionScope.b2.bage}</p>
</body>
</html>
6.@RequestHeader

获取请求中的头信息 用法跟@RequestParam , @CookieValue 类似 .

  1. index.jsp

    <h3><a href="${pageContext.request.contextPath}/beauty/testRequestHEAD">testRequestHEAD</a></h3>
    
  2. handler

@GetMapping("/testRequestHEAD")
public String testRequestHEAD(@RequestHeader(name = "host") String host,
                              @RequestHeader Map<String,Object> map){
    System.out.println(host);
    Set<String> keys = map.keySet();
    for (String key : keys) {
        System.out.println(key + "=" + map.get(key));
    }
    return "abc";
}
  1. 输出:

在这里插入图片描述

7.@RequestAttribute

是给 Handler方法的 入参 添加的注解.

从request域中 拿出指定名字的属性值 给 方法入参 封装进来.

@GetMapping("/testRequestHEAD")
public String testRequestHEAD(@RequestHeader(name = "host") String host,
                              @RequestHeader Map<String,Object> map,
                              Model model){
    System.out.println(host);
    Set<String> keys = map.keySet();
    for (String key : keys) {
        System.out.println(key + "=" + map.get(key));
    }
    model.addAttribute("car","小汽车");
    //转发到/testAttr 里
    return "forward:/beauty/testAttr"; //"forward:testAttr"
}

@RequestMapping("/testAttr")
public String testAttr(@RequestAttribute(name = "car") String attrData,
                       Model model){
    System.out.println(attrData);
    model.addAttribute("msg","过年了要!");
    return "abc";
}

restful风格

RESTful(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释。Representational State Transfer的含义就是“表现层的状态转化”。

RestFul 表现层状态转换. 是一种处理web层请求的风格

  1. URL简介 原本?后的key,value形式的请求参数 不在使用键值对,直接把值变为URL的一部分 称为:路径参数
  2. 请求方式 使用四种
    get: 执行查询请求
    post:执行新增请求
    put: 执行修改请求
    delete: 执行删除请求
    为了可以把post请求 变为 put 和 delete ,配置过滤器
    此过滤器 会根据表单隐藏域_method 的值 来转换post请求

​ 3、除post get外要设置成json格式的返回值

在这里插入图片描述

  1. web.xml
<filter>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  <init-param>
    <param-name>methodParam</param-name>
    <param-value>mym</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2.index.jsp

在这里插入图片描述

3.handler

@RequestParam 是 处理传统的键值对形式的 请求参数 到 Handler方法入参的变量 相关
Restful风格里 请求参数 变为了路径参数
@Controller
public class RestfulHandler {

    @GetMapping("/beautyOpreation/{age}.html") //{age} 是路径参数 {}为占位符
    public String testRestfulGet(@PathVariable(name = "age") Integer bage){
        System.out.println("进行查询: 目标年龄 : " + bage);
        return "abc";
    }

    @PostMapping("/beautyOpreation")
    public String testRestfulPost(Beauty beauty){
        System.out.println(beauty);
        return "abc";
    }

    @PutMapping("/beautyOpreation/{id}/{color}")
    @ResponseBody
    public String testRestfulPut(Beauty b1,
                                 @PathVariable String id,
                                 @PathVariable String color){
        System.out.println("PUT请求");
        System.out.println(b1);
        System.out.println(id);
        System.out.println("color = " + color);
        return "put okk!";
    }

    @DeleteMapping("/beautyOpreation")
    @ResponseBody
    public String testRestfulDelete(Beauty b1){
        System.out.println(b1);
        System.out.println("删除操作");
        return "del okk!";
    }
}

​ 矩阵变量: @MatrixVariable 可以将restful风格请求路径里的矩阵参数 绑定到 handler方法入参中. 矩阵变量需要开启配置

  1. SpringMVC默认未开始 对 矩阵变量 解析和封装为handler方法入参, 需要在配置文件中开启 矩阵变量的使用 .
<mvc:annotation-driven enable-matrix-variables="true"/>
  1. web.xml
<h3><a href="${pageContext.request.contextPath}/item/97;name=lucy/98;name=lee/908;name=ss">多矩阵变量同名</a></h3>

<form action="${pageContext.request.contextPath}/user/97;name=zhangsasn;age=1,2,3" method="post">
    <input type="text" name="myparam" value="修改"/>
    <input type="hidden" name="mym" value="PUT">
    <input type="submit" value="提交">
</form>

解决 字符编码过滤器 和 hiddenMethod过滤器 restful乱码:

<mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>
                            text/plain;charset=utf-8
                        </value>
                        <value>
                            text/html;charset=utf-8
                        </value>
                        <value>
                            application/json;charset=utf-8
                        </value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
</mvc:annotation-driven>

上传文件

  1. 静态资源服务器准备

在这里插入图片描述

  1. 导入commons-fileupload jersy

  2. spring.xml

<!-- 配置文件上传解析器,来解析文件上传数据 自动封装到handler方法入参部分的 MultipartFile -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
    <!-- 每个上传的文件限制大小为100Mb -->
    <property name="maxUploadSizePerFile" value="104857600"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
  1. index.jsp
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    美女的名字:<input type="text" name="bname" value="小梅"/><br/>
    美女的年龄:<input type="text" name="bage" value="25"/><br/>
    上传的文件:<input type="file" name="myfile" id="myfile"/><br/>
    <img id="myimg" width="50px" height="50px" style="display: none" />
    <input type="submit"/>
</form>

<script type="text/javascript">
    $("#myfile").change(function (){
        var file = $("#myfile").get(0).files[0];// js对象的files属性
        var srcStr = URL.createObjectURL(file);
        $("#myimg").attr("style", "display:inline;");
        $("#myimg").attr("src", srcStr);
    });
</script>
  1. handler
@PostMapping("/upload")
public String upload(Beauty beauty, MultipartFile myfile, ModelMap map) throws IOException {
    System.out.println(beauty);
    System.out.println(myfile.getName());
    System.out.println(myfile.getContentType());
    System.out.println(myfile.getContentType());
    System.out.println(myfile.getOriginalFilename());
    System.out.println(myfile.getSize() + "byte");

    String uuid = UUID.randomUUID().toString().replace("-","");
    String saveFileName = uuid + myfile.getOriginalFilename();
    String saveURL = "http://localhost:7070/mvc/" + saveFileName;
    Client client = new Client();
    WebResource webResource = client.resource(saveURL);
    webResource.put(myfile.getInputStream());
    map.addAttribute("msg","上传成功! 路径: " + saveURL);
    return "abc";
}

自定义异常及处理器

  • 自定义异常
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class CustomException extends Exception{
    private String code;
    private String message;
}
  • 自定义异常处理器
@Component//自动扫描为 spring组件/bean
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Object handler, Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        //ex 是发生的异常对象
        if (ex instanceof CustomerException){
            CustomerException ee = (CustomerException) ex;
            mv.addObject("errMsg",ee.getCode() + "~" + ee.getMessage());
        }else {
            mv.addObject("errMsg",ex.getMessage());
        }
        return mv;
    }
}
  • index.jsp
<form action="${pageContext.request.contextPath}/testException" method="post">
    <h4>值: 0出现未知异常,1出现Customer异常,其他正常</h4>
    测试的参数number:<input type="text" name="number"/><br/>
    <input type="submit"/>
</form>
  • handler
@PostMapping("/testException")
public String testException(String number, Model model)throws Exception{
    //测试: 0抛出非custom异常 , 1抛出custom异常 , 其他正常
    if ("0".equals(number)){
        throw new ArrayIndexOutOfBoundsException("数组下标越界异常!");
    }else if ("1".equals(number)){
        throw new CustomerException("999","xxx异常!");
    }else {
        model.addAttribute("msg","正常情况无异常!");
    }
    return "abc";
}

在这里插入图片描述

拦截器

​ SpringMVC的拦截器类似于JSP/Servlet中的Filter过滤器,用于对处理器handler进行预处理和后处理

​ 在SpringMVC中提供了一个HandlerInterceptor接口,实现了该接口的实现类即为SpringMVC的拦截器.

public class MyInterceptorA implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptorA 预处理,放行!");
        //response.sendRedirect(request.getContextPath()+"/index.jsp");
        return true;//false 拦截  true 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptorA 后处理!");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptorA 在页面渲染之后!");
    }
}
public class MyInterceptorB implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptorB 预处理,放行!");
        return true;//false 拦截  true 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptorB 后处理!");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptorB 在页面渲染之后!");
    }
}

​ preHandle方法是在Handler方法执行前执行, 根据返回值来决定是否向后传递请求和响应.返回false表示拦截.

​ postHandle方法是在Handler方法执行后并返回ModelAndView之前执行.

​ afterCompletion方法是在ModelAndView渲染完毕后执行.

演示:

  1. index.jsp
<h3><a href="${pageContext.request.contextPath}/testInterceptor">testInterceptor</a></h3>
  1. 拦截器A和B
public class MyInterceptorA implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptorA 预处理,放行!");
        //response.sendRedirect(request.getContextPath()+"/index.jsp");
        return true;//false 拦截  true 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptorA 后处理!");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptorA 在页面渲染之后!");
    }
}
public class MyInterceptorB implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptorB 预处理,放行!");
        return true;//false 拦截  true 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptorB 后处理!");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptorB 在页面渲染之后!");
    }
}

​ preHandle方法是在Handler方法执行前执行, 根据返回值来决定是否向后传递请求和响应.返回false表示拦截.

​ postHandle方法是在Handler方法执行后并返回ModelAndView之前执行.

​ afterCompletion方法是在ModelAndView渲染完毕后执行.

  1. Spring配置文件中配置拦截器
<!-- 配置拦截器的 拦截路径 -->
<!-- 配置拦截器bean -->
<bean class="com.ls.interceptor.MyInterceptorA" id="a"></bean>
<bean class="com.ls.interceptor.MyInterceptorB" id="b"></bean>
<!-- 配置拦截器的 拦截路径等 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截路径 -->
        <mvc:mapping path="/testInterceptor"/>
        <mvc:mapping path="/a/b/c/**"/>
        <mvc:mapping path="/plain.jsp"/>
        <ref bean="a"/>
    </mvc:interceptor>

    <mvc:interceptor>
        <!-- 拦截路径 -->
        <mvc:mapping path="/**"/>
        <!-- 排除路径 -->
        <mvc:exclude-mapping path="/index.jsp"/>
        <mvc:exclude-mapping path="/plain.jsp"/>
        <mvc:exclude-mapping path="/js/**"/>
        <mvc:exclude-mapping path="/css/**"/>
        <mvc:exclude-mapping path="/img/**"/>
        <ref bean="b"/>
    </mvc:interceptor>
</mvc:interceptors>

注: /test/** 是指访问路径为 /test之下任意路径.

  1. 执行结果

总结:

preHandle方法按拦截器的配置顺序调用

postHandler方法按拦截器的配置顺序逆向调用

afterCompletion方法按拦截器的配置顺序逆向调用

postHandler方法在拦截器链内的所有拦截器返回为true才调用

afterCompletion方法在拦截器的preHandle方法返回为true才调用

在这里插入图片描述

我们可以使用拦截器完成web阶段过滤器能完成的功能:比如 登录验证等.

SpringMVC的校验

SpringMVC在 Controller / Handler层的校验 仅仅是 校验 handler方法入参封装的合法性。

  • 添加Hibernate的Validator的校验框架的jar包

这里必须使用 6版本

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.1.5.Final</version>
</dependency>
  • 配置messageSource错误消息Bean
<!-- 可以读取 验证信息文件的 bean -->
    <bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
    id="messageSource">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="basenames">
            <array value-type="java.lang.String">
                <value>classpath:beautyValidator</value>
            </array>
        </property>
    </bean>
  • 将hibernate校验器类型 配置给 校验器工厂bean
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
      id="localValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
    <property name="validationMessageSource" ref="messageSource"/>
</bean>
  • Bean中的validatorMessage.properties
# 验证handler方法入参为Beauty类型时,各个成员变量的格式或数据的报错信息
beauty.bname.blank=美女名字不能为空,长度大于0
beauty.bage.min=美女必须是个成年人
beauty.birthday.past=美女生日必须是个过去时间
  • 将配好的校验器注入到处理器适配器的驱动Bean中
<!-- 处理器映射器 , 处理器适配器  -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean" validator="localValidatorFactoryBean"/>
  • 在bean的成员变量上添加校验规则
@Data
@ToString
@NoArgsConstructor
public class Beauty {
    @NotBlank(message = "{beauty.bname.blank}")
    private String bname;
    @Min(message = "{beauty.bage.min}",value = 18)
    private Integer bage;
    private Cat cat;
    private String[] hobby;//爱好
    private List<Dog> dogs;
    private LinkedHashMap<String,String> edu;//学历
    @Past(message = "{beauty.birthday.past}")
    private Date birthday;
    private String birthStr;
}
  • handler
@PostMapping("/testValidator")
public String testValidator(@Validated Beauty beauty, BindingResult result, Model model){
    // @Validated 开启bean数据的校验
    //BindingResult 校验的结果,必须紧跟在@Validated的后面
    if (result.hasErrors()){//如果校验结果里 有错误信息
        List<ObjectError> allErrors = result.getAllErrors();//获取所有错误信息
        for (ObjectError allError : allErrors) {
            System.out.println("objectName"+allError.getObjectName());
            System.out.println("code" + allError.getCode());
            System.out.println("defaultMessage"+allError.getDefaultMessage());
            System.out.println("------------------------------------");
        }
        model.addAttribute("msg","有错误!");
        model.addAttribute("allErrors",allErrors);
        beauty.setBirthStr(new SimpleDateFormat("yyyy-MM-dd").format(beauty.getBirthday()));
        model.addAttribute("beauty",beauty);
        return "start";
    }
    System.out.println(beauty);
    return "abc";
}

SSM整合

pom.xml

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.3.18</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.3.18</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.3.18</version>
</dependency>

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.14</version>
</dependency>

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.6</version>
</dependency>

<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>javax.servlet.jsp-api</artifactId>
  <version>2.3.3</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.16.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.33</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.20</version>
</dependency>
<!-- 文件上传,单元测试等 -->

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.30</version>
</dependency>

配置文件

<?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>
    <!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="cacheEnabled" value="true"/>
        <!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!-- 输入映射和输出映射时 类型别名配置 -->
    <typeAliases>
        <package name="com.ls.bean"/>
    </typeAliases>
</configuration>

Mybatis

  • log4j
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
  • sqlMapConfigure(Mybatis配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
    <!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="cacheEnabled" value="true"/>
        <!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!-- 输入映射和输出映射时 类型别名配置 -->
    <typeAliases>
        <package name="com.ls.bean"/>
    </typeAliases>
</configuration>

Spring

<?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>
    <!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="cacheEnabled" value="true"/>
        <!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!-- 输入映射和输出映射时 类型别名配置 -->
    <typeAliases>
        <package name="com.ls.bean"/>
    </typeAliases>
</configuration>

SpringMVC

<?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/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 只扫描控制层 @Controller -->
    <context:component-scan base-package="com.ls.controller"/>

    <!-- 处理器映射器 和  处理器适配器 的注解驱动 -->
    <!-- 自定义转换器服务bean 开启矩阵变量 bean数据校验 -->
    <mvc:annotation-driven/>

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/templates/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 静态资源 -->
    <mvc:default-servlet-handler/>

    <!-- 其他配置如 转换器,拦截器,自定义异常解析器,文件上传... -->
</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <welcome-file-list>
    <welcome-file>/index.jsp</welcome-file>
  </welcome-file-list>

  <!-- 前端控制器 -->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring_*.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!-- 除了jsp之外的所有请求 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>


  <!-- 字符编码过滤器 -->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>


  <!-- Restful风格请求方法过滤器 -->
  <filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    <init-param>
      <param-name>methodParam</param-name>
      <param-value>mym</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- 配置了 字符编码过滤器 和 restful风格过滤器之后,restful请求里还可能乱码 -->

</web-app>

测试

  • index.jsp
<a href="${pageContext.request.contextPath}/show/36">GET</a>

<form action="${pageContext.request.contextPath}/show" method="post">
    最大年龄: <input type="number" name="age"><br/>
    <button type="submit">提交</button>
</form>
  • WorkerController
@Controller
public class WorkerController {
    @Autowired
    private WorkerService workerService;

    @RequestMapping("/show")
    public String show(Integer age, Model model){
        List<Worker> workers = workerService.queryByMaxAge(age);
        model.addAttribute("workers",workers);
        return "show";
    }

    @GetMapping("/show/{age}")
    public String show2(@PathVariable Integer age, Model model){
        List<Worker> workers = workerService.queryByMaxAge(age);
        model.addAttribute("workers",workers);
        return "show";
    }
}
  • show.jsp
<c:forEach items="${workers}" var="w">
    <p>${w.wname} - ${w.age} - ${w.sex}</p>
</c:forEach>
  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值