介绍
SSM:Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术

mybatis

创建映射文件

编写测试类

@Test
public void findById()throws Exception {
//1.获取核心配置文件
InputStream is = Resources.getResourceAsStream ( “mybatis-config.xml”);
//2.创建sqlsessionFactory 工广对象
sqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.创建sqlSession对象
sqlSession sqlSession = sqlSessionFactory.openSession();
//4.执行sql语句, sqL语句的唯一标识:namespace.statementId
User user = sq1Session.selectone( s: “com.itheima.pojo.User.findById” , o:1);system.out.println(user);
//5.释放资源
sqlSession.close( );}

Mybatis配置文件的主要元素


由上至下

properties元素

setting元素

元素中常见配置参数的使用方式

typeAliases元素

多个全限定类设置别名的方式

mappers元素

  1. 使用类路径引入
    使用类路径引入映射文件的示例代码如下所示。

  2. 使用本地文件路径引入
    使用本地文件路径引入映射文件的示例代码如下所示。

  3. 使用接口类引入
    使用接口类引入映射文件的示例代码如下所示。

  4. 使用包名引入
    使用包名引入映射文件的示例代码如下所示。

Mybatis映射文件中的常用元素

select元素

select * from users where id = #{id}

insert元素

元素的插入使用


insert into users(uid,uname,uage) values (#{uid}, #{uname},#{uage})

a.使用支持主键自动增长的数据库获取主键值

insert into users(uid,uname,uage)values(#{uid},#{uname},#{uage})

b.使用不支持主键自动增长的数据库获取主键值
使用MyBatis提供的元素来自定义主键。

在上述元素的属性中,order属性可以被设置为BEFORE或AFTER。如果设置为BEFORE,那么它会首先执行元素中的配置来设置主键,然后执行插入语句;如果设置为AFTER,那么它先执行插入语句,然后执行元素中的配置内容。

update元素

元素的更新使用


update users set uname= #{uname},uage = #{uage} where uid = #{uid}

delete元素

元素的删除使用

delete from users where uid=#{uid}

sql元素

元素的作用
实现一个根据客户id查询客户信息的SQL片段

from

uid,uname,uage

resultMap元素

元素的作用




<result column=”sage”property=”age”>


多学一招:使用工具类创建SqlSession对象

案例:员工管理系统

  1. 项目搭建:建一个名称为mybatisdemo的项目,项目的具体搭建过程请参考1.3节。
  2. 数据准备:在mybatis数据库中创建employee表,并在employee表中插入几条数据。
    1
    2
    3
    4
    5
    6
    7
    use mybatis;
    create table employee(
    id int primary key auto_increment,name varchar(20) not null,
    age int not null,
    position varchar(20) );
    insert into employee(id,name,age,position) values(null,"张三',20,员工‘),
    (null,'李四',18, '员工'),.(null,'王五',35,'经理');
  3. POJO类准备:在项目src/main/java目录下创建com.itheima.pojo包,在该包下创建持久化类Employee,并在类中声明id(编号).name(姓名) 、 age (年龄)和position(职位)属性,以及属性对应的getter/setter方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Employee {
    private Integer id;
    private String name;
    private Integer age;
    private String position;
    //省略getter/setter方法
    @Override
    public String toString() {
    return "Employee{" + "id=" + id + ", name=" + name +
    " , age=" + age + ", position=" + position +'}' ;
    }
    }
  4. 编写映射文件:在项目src/main/java目录下创建com.itheima.mapper包,在com.itheima.mapper包下创建映射文件EmployeeMapper.xml,该文件主要用于实现SQL语句和Java对象之间的映射,部分文件内容如下。 insert into employee(id,name,age,position) values (#{id},#{name},#{age},#{position})
UPDATE employee set name=#{name} ,age=#{age},position=#{position} where id=#{id} DELETE FROM employee where id =#{id}
  1. 修改mybatis-config.xml核心配置文件:在mybatis-config.xml映射文件的元素下添加EmployeeMapper.xml映射文件路径的配置,用于将EmployeeMapper.xml映射文件加载到程序中。


  1. 编写MyBatisUtils工具类:在项目src/main/java目录下创建com.itheima.utils包,在该包下创建MyBatisUtils工具类,该类用于封装读取配置文件信息的代码。

  2. 编写测试类
    (1)在项目src/test/java目录下创建Test包,在Test包下创建MyBatisTest测试类,用于程序测试。在MyBatisTest测试类中添加findByldTest()方法,用于根据id查询员工信息。
    (2)在MyBatisTest测试类中添加insertEmployeeTest()方法,用于插入员工信息。
    (3)在MyBatisTest测试类中添加updateEmployeeTest()方法,用于更新员工信息。
    (4))在MyBatisTest测试类中添加deleteEmployeeTest()方法,用于删除员工信息。

动态SQL中的元素

if元素

元素的应用

  1. 创建映射文件:在项目com.itheima.mapper包下创建映射文件CustomerMapper.xml,在映射文件中,根据客户姓名和年龄组合条件查询客户信息,使用元素编写该组合条件的动态SQL。
and username like concat('%',#{username}, '%’) and jobs= #{jobs}

choose、when、otherwise元素

otherwise>使用场景

  1. 在映射文件CustomerMapper.xml中,添加使用元素
    执行上述情况的动态SQL。 and username like concat('%',#{username}, "%') and jobs= #{jobs} and phone is not null

where元素和trim元素

使用场景
在映射文件中,编写的SQL后面加入了“where 1=1”的条件的话,既保证了where后面的条件成立,又避免了where后面第一个词是and或者or之类的关键字。

元素

元素
元素用于删除多余的关键字,它可以直接实现元素的功能。元素包含4个属性。
属性 说明
prefix 指定给SQL语句增加的前缀
prefixOverrides 指定SQL语句中要去掉的前缀字符串
suffix 指定给SQL语句增加的后缀
suffixOverrides 指定SQL语句中要去掉的后缀字符串

元素

更新元素

元素使用场景

  1. 通过一个案例演示如何使用元素更新数据库的信息,案例具体步骤如下。
    在映射文件CustomerMapper.xml中,添加使用元素执行更新操作的动态SQL。


update t_customer

username=#{usernaime},

jobs=#{jobs},
phone=#{phone},
where id=#{id}

foreach元素中的属性

  1. 属性的取值
    在遍历参数时,属性的值是必须指定的。不同情况下,该属性的取值也是不一样的,主要有以下三种情况。
  2. List类型
    若入参为单参数且参数类型是一个List,collection属性值为list。
  3. 数组类型
    若入参为单参数且参数类型是一个数组,collection属性值为array。
  4. Map类型
    若传入参数为多参数,就需要把参数封装为一个Map进行处理,collection属性值为Map。

foreach元素迭代数组

实现入参为数组类型的遍历
例如,要从数据表t_customer中查询出id为1、2、3的客户信息,就可以利用数组作为参数,存储id的属性值1、2、3,并通过元素迭代数组完成客户信息的批量查询操作。

元素迭代数组的实现具体如下。

  1. 在映射文件CustomerMapper.xml中,添加使用元素迭代数组执行批量查询操作的动态SQL。

foreach元素迭代List

  1. 元素迭代List的实现步骤具体如下。
    在映射文件CustomerMapper.xml中,添加使用元素迭代List集合执行批量查询操作的动态SQL。

foreach元素迭代Map

在映射文件CustomerMapper.xml中,添加使用元素迭代Map集合执行批量查询操作的动态SQL。

案例:学生信息查询系统

多条件查询
当用户输入的学生姓名不为空,则只根据学生姓名进行学生信息的查询;
当用户输入的学生姓名为空,而学生专业不为空,则只根据学生专业进行学生的查询;
单条件查询
查询出所有id值小于5的学生的信息;

  1. 项目搭建:创建一个名称为mybatis-demo03的项目,项目的具体搭建过程请参考1.3节。
  2. 数据准备:在名称为mybatis的数据库中,创建一个dm_student表,并插入几条测试数据。

USE mybatis;
CREATE TABLE dm_student(
id int(32)PRIMARY KEY AUTO_INCREMENT,name varchar(50),
major varchar(50),sno varchar(16) );#插入7条数据,其他省略
INSERT INTO dm_student VALUES (‘1’, 张三’,’数学’,’10001’);

  1. POJO类准备:在项目src/main/java目录下创建com.itheima.pojo包,在该包下创建持久化类Student,在类中声明id、name、major和sno属性,以及属性对应的getter/setter方法。
    public class Student{//定义变量主键id,姓名name,专业major,学号sno
    private Integer id; private String name;
    private String major;
    private String sno;/省略getter/setter方法@Override
    public String toString0 {
    return”Student” +“id=” + id + “, name= “”+ name + “”,major=” + major + “, sno=” + sno + }’;》}

  2. 创建映射文件:在项目src/main/java目录下创建com.itheima.mapper包,在该包下创建映射文件StudentMapper.xml,编写根据学生姓名和专业组合成的条件查询学生信息的动态SQL。

  3. 修改mybatis-config.xml核心配置文件:在mybatis-config.xml映射文件的元素下添加StudentMapper.xml映射文件路径的配置,用于将
    StudentMapper.xml映射文件加载到程序中。具体配置代码如下。

  4. 编写MyBatisUtils工具类:在项目src/main/java目录下创建com.itheima.utils包,在com.itheima.utils包下创建MyBatisUtils工具类,该类用于封装读取配置文件信息的代码。

  5. 编写测试方法:在测试类MyBatisTest中,编写测试方法
    findStudentByNameOrMajorTest(),该方法用于根据学生姓名或专业查询学生信息。
    public void findStudentByNameOrMajorTest() {
    SqlSession session=MyBatisUtils.getSession();Student student=new Student();
    student.setName(“张三”);
    student.setMajor(“英语”);
    List students = session.selectList(“com.itheima.mapper”

  • “.StudentMapper.findStudentByNameAndMajor”,student);for (Student student2 : students) {System.out.printIn(student2);}session.close();}
  1. 查看运行结果:执行测试类MyBatisTest的findStudentByNameOrMajorTest()方法,控制台会输出结果。

关联映射概述

  • 一对一关系
    一个数据表中的一条记录最多可以和另一个数据表中的一条记录相关。例如,现实生活中学生与校园卡就属于一对一的关系,一个学生只能拥有一张校园卡,一张校园卡只能属于一个学生。
  • 一对多关系
    主键数据表中的一条记录可以和另外一个数据表的多条记录相关。但另外一个数据表中的记录只能与主键数据表中的某一条记录相关。例如,现实中班级与学生的关系就属于一对多的关系,一个班级可以有很多学生,但一个学生只能属于一个班级。
  • 多对多关系
    一个数据表中的一条记录可以与另外一个数据表任意数量的记录相关,另外一个数据表中的一条记录也可以与本数据表中任意数量的记录相关。例如,现实中学生与教师属于多对多的关系,一名学生可以由多名教师授课,一名教师可以为多名学生授课。

一对一查询


元素的配置方式

  1. 嵌套查询方式
    嵌套查询是指通过执行另外一条SQL映射语句来返回预期的复杂类型。

2.嵌套结果方式
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。



  • STEP 02
    持久化类IDCard类:在项目的com.itheima.pojo包下创建持久化类ldCard,用于封装身份证属性。
    public class ldCard {
    private lnteger id;
    //主键id
    private String code;
    //身份证号码
    /省略getter/setter方法}
    @Override
    public String toString0) {
    return “ldCard [id=” + id + “ , code=” + code + “]”;
    }

  • STEP 03
    持久化类Person类:在项目的com.itheima.pojo包下创建持久化类Person,用于封装个人属性。
    public class Person {
    private Integer id;
    主键id
    private String name;
    /姓名
    private Integer age;
    /年龄
    private String sex;
    /性别
    private ldCard card;
    /人员关联的证件
    /省略getter/setter方法,重写的toString()方法
    }

  • STEP 04
    编写ldCardMapper.xml文件:在com.itheima.mapper包中,创建身份证映射文件ldCardMapper.xml,并在映射文件中编写一对一关联映射查询的配置信息。

  • STEP 05
    编写PersonMapper.xml文件:在com.itheima.mapper包中,创建人员映射文件PersonMapper.xml,并在映射文件中编写一对一关联映射查询的配置信息。

  • 多学一招:MyBatis延迟加载的配置

一对多查询

元素

a.嵌套查询方式

b.嵌套结果方式



多对多查询

  • STEP 04
    在com.itheima.mapper包中,创建订单实体映射文件OrdersMapper.xml,用于编写订单信息的查询SQL语句,并在映射文件中编写多对多关联映射查询的配置信息。

  • STEP 05
    在com.itheima.mapper包中,创建商品实体映射文件ProductMapper.xml,用于编写订单与商品信息的关联查询SQL语句。

查询订单及关联商品的另一方式
除了使用嵌套查询的方式查询订单及其关联的商品信息外,还可以在OrdersMapper.xml中使用嵌套结果的方式进行查询。

一级缓存

  • MyBatis的一级缓存级别
    MyBatis的一级缓存是SqlSession级别的缓存扌如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。

  • 举例说明MyBatis的一级缓存级别
    例如,存在数据表tb_book,从表中多次查询id为1的图书信息,当程序第一次查询id为1的图书信息时,程序会将查询结果写入MyBatis一级缓存,当程序第二次查询id为1的图书信息时,MyBatis直接从一级缓存中读取,不再访问数据库进行查询。当程序对数据库执行了插入、更新、删除操作,MyBatis会清空一级缓存中的内容以防止程序误读。

  • STEP 06
    在工程的src/main/resources目录下创建log4jproperties文件,用于配置MyBatis和控制台的。

#全局日志配置
log4j.rootLogger=DEBUG, Console#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppenderlog4j.appender.Console.layout=org.apache.log4j.PatternLayoutlog4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c]- %m%n
#日志输出级别,只展示了一个
log4j.logger.java.sql.PreparedStatement=DEBUG

二级缓存

  • MyBatis二级缓存的执行过程
    在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。

  • 二级缓存与一级缓存的不同点
    与MyBatis的一级缓存不同的是,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。

  • a.开启二级缓存的全局配置
    与使用二级缓存前,需要在MyBatis的核心配置mybatis-config.xml文件中通过元素开启二级缓存的全局配置。

  • b.开启当前Mapper的namespace下的二级缓存
    开启当前Mapper的namespace下的二级缓存,可以通过MyBatis映射文件中的元素来完成。

  • 默认状态的二级缓存可实现的功能
    (1)映射文件中所有select语句将会被缓存。
    (2)映射文件中的所有jinsert、update和delete语句都会刷新缓存。
    (3)缓存会使用LRU算法回收!
    (4)投有刷新间隔,缓存不会以任何时间顺序来刷新。
    (5)缓存会存储列表集合或对象的1024个引用。
    (6)缓存是可读/回身的缓存,这意味着对象检索不是共享的,缓存可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

接下来通过一个案例演示MyBatis二级缓存的应用,该案例仍旧根据id查询图书信息,案例具体步骤如下。
修改映射文件BookMapper.xml,在映射文件的元素下追加编写

  • STEP 01
    素开启当前Mapper的namespace的二级缓存。
    <!—开启当前BookMapper的namespace下的二级缓仔–>

  • 多个SqlSession在同一个Mapper中执行
    在实际开发中,经常会遇到多个SqlSession在同一个Mapper中执行操作,例如,SqlSession1执行查询操作,SqlSession2执行插入、更新、删除操作,
    SqlSession3又执行和SqlSession1相同的查询操作。当SqlSession1执行查询操作时,程序会将查询结果写入MyBatis二级缓存,当SqlSession2对数据库执行了插入、更新、删除操作后,MyBatis会清空二级缓存中的内容,以防止程序误读

    SqlSession3执行和SqlSession1相同的查询操作时,MyBatis会重新访问数据库。

  • 多学一招: Cache Hit Ratio(缓存命中率)

案例:商品的类别

  • STEP 01 项目搭建:创建一个名称为mybatis-demo04的项目,项目的具体搭建过程请参考1.3节。
    数据库准备:在名为mybatis的数据库中,创建两个数据表,分别为product和category,同时在表中预先插入几条测试数据。
  • STEP 02

USE mybatis;
#创建一个名称为category的表CREATE TABLE category (
id int(32)PRIMARY KEY AUTO_INCREMENT,
typename varchar(40)
);
#插入2条数据
INSERT INTO category VALUES (1, ‘黑色家电’);
INSERT INTO category VALUES (2,’白色家电’);#创建一个名称为product的表,同理

  • STEP 03
    POJO类准备:(1)在com.itheima.pojo包中,创建持久化类Category,并在类中定义商品类别的相关属性和方法。
    public class Category {private Integer id;
    主键id
    private String typename;
    /类别名称
    private List productList;/商品集合省略getter/etter方法}
    @Override
    public String toString) {
    return “Category{“+”id=” + id + “ , typename=’” +
    typename +”, productList=” + productList + ‘};}

  • STEP 04
    编写映射文件:在com.itheima.mapper包中,创建商品类别实体映射文件CategoryMapper.xml,并在文件中编写一对多关联映射查询的配置

  • STEP 05
    修改mybatis-config.xml核心配置文件:在核心配置文件mybatis-config.xml中,引入CategoryMapper.xml,将CategoryMapper.xml映射文件加载到程序中。

    resource="com/itheima/mapper/CategoryMapper.xml"
  • STEP 06
    编写测试方法:在测试类MyBatisTest中,编写测试方法findCategoryTest()。
    public void findCategoryTest() {
    //1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();//⒉.查询id为2的商品类别信息
    Category category =
    session.selectOne(“com.itheima.mapper.”

  • “CategoryMapper.findCategoryWithProduct”,2);System.out.printIn(category);
    // 3.输出查询结果信息session.close();
    //4.关闭SqlSession}

@Select注解

下面通过一个案例演示@Select主解的使用,该案例要求根据员工的id查找员工信息,案例具体实现步骤如下。

编写查询方法:在项目的src/main/java目录下创建com.itheima.dao包,并在该包下创建WorkerMapper接口,用于编写@Select注解映射的select查询方法。

  • STEP 03
    package com.itheima.dao;
    import com.itheima.pojo.Worker;
    import org.apache.ibatis.annotations.Select;
    public interface WorkerMapper {
    @Select(“select * from tb_worker where id = #{id}”)
    Worker selectWorker(int id);
    }
    加载配置文件:在核心配置文件mybatis-config.xml中的元素下引入
  • STEP 04
    WorkerMapper接口,将WorkerMapper.java接口加载到核心配置文件中。

@Insert注解

下面通过一个案例演示@Insert注解的使用,要求实现员工信息的插入,案例具体实现步骤如下。
添加注解:在WorkerMapper接口中添加向tb_worker数据表插入数据的方法insertWorker(),并在方法上添加@Insert注解。

  • STEP 01
    @lnsert(“insert into tb_worker(name,age,sex,worker_id) values(#{name},#{age},#{sex},#{worker_id})”)
    int insertWorker(Worker worker);

编写测试类:在测试类MyBatisTest中,编写测试方法insertWorkerTest()。

  • STEP 02
    public void insertWorkerTest() {
    //1.生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    Worker worker = new Worker();
    worker.setld(4);
    worker.setName(“赵六”);
    worker.setAge(36);
    worker.setSex(“女”);
    worker.setWorker_id(“1004”);
    WorkerMapper mapper = session.getMapper(WorkerMapper.class);
    //⒉插入员工信息
    int result = mapper.insertWorker(worker);
    //输出语句省略..
    session.commit();
    session.close(); // 3.关闭SqlSession}

@Update注解

下面通过一个案例演示@Update注解的使用,该案例要求实现员工信息的修改,案例具体实现步骤如下。

  • STEP 01
    添加注解:在WorkerMapper接口中添加更新tb_worker表中数据的方法,并在方法上添加@Update注解。
    @Update(“update tb_worker set name = #{name},age = #{age} where id = #{id}”)
    int updateWorker(Worker worker);

@Delete注解

下面通过一个案例演示@Delete注解的使用,该案例要求实现员工信息的删除
案例具体实现步骤如下。
添加注解:在WorkerMapper接口中添加删除数据库中数据的方法,并在方法上添加@Delete注解。

  • STEP 01
    @Delete(“delete from tb_worker where id = #{id}”)
    int deleteWorker(int id);

@Param注解

下面通过一个案例演示@Param注解的使用,该案例要求根据员工的id和姓名查询员工信息,案例具体实现步骤如下。

  • STEP 01
    添加注解:在WorkerMapper接口中添加多条件查询的方法。
    @Select(“select * from tb_worker where id = #{param01}
    and name = #{param02}”)
    Worker selectWorkerByldAndName(@Param(“param01”) int id,
    Param(“param02”) String name);

编写测试类:在测试类MyBatisTest中,编写测试方法selectWorkerByldAndNameTest()。

  • STEP 02
    public void selectWorkerByldAndNameTest() {
    //1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    WorkerMapper mapper = session.getMapper(WorkerMapper.class);
    //⒉查询id为3姓名为王五的员工的信息
    Worker worker = mapperselectWorkerByldAndName(3,”王五”);
    System.out.println(worker.toString());
    session.close();
    }

一对一查询

接下来,以4.2节中使用的tb_idcard和tb_person数据表为例,详细讲解基于注解@One实现tb_idcard和tb_person数据表之间的一对一关联查询,具体步骤如下。

  • STEP 01 创建持久化类:本案例使用4.2节中的ldCard类和Person类作为持久类。

  • @Result注解的三个属性及含义
    (1) property属性用来指定关联属性,这里为card。
    (2) column属性用来指定关联的数据库表中的字段,这里为card_id。
    (3) one属性用来指定数据表之间属于哪种关联关系,通过@One注解表明数据表tb_idcard和tb_person之间是一对一关联关系。

一对多查询

  • STEP 01 创建持久化类:本案例使用4.3节中的Users类和Orders类作为持久类。

编写接口方法:(1)在项目的com.itheima.dao包下创建OrdersMapp该接口中编写selectOrdersByUserld()方法,通过user_id查询用户对应的

  • STEP 02
    public interface OrdersMapper {
    @Select(“select * from tb_orders where user_id=#{id} “)
    @Results({@Result(id = true,column = “id”,property = “id”),
    @Result(column = “number” ,property = “number”) })
    List selectOrdersByUserld(int user_id);
    }

编写接口方法:(2)在项目的com.itheima.dao包下创建UsersMappe接口中编写selectUserByld()方法,通过id查询用户信息。

  • STEP 02

public interface UsersMapper
Select(“select * from tb_user where id=#{id} “)
@Results({@Result(id = true,column = “id”,property = “id”),
@Result(column = “username” ,property = “username”),
@Result(column = “address”,property = “address”),
@Result(column = “id”,property = “ordersList”,
many = @Many(select =
“com.itheima.dao.OrdersMapper.selectOrdersByUserld”))})
Users selectUserByld(int id);

引入接口:在核心配置文件mybatis-config.xml中的元素

  • STEP 03
    和OrdersMapper接口。
    l心

编写测试类:在测试类MyBatisTest中,编写测试方法selectUserByld

  • STEP 04
    public void selectUserByldTest() {
    //1.通过工具类生成SqlSession对象
    SqlSession session = MyBatisUtils.getSession();
    UsersMapper mapper = session.getMapper(UsersMapper.class);
    //⒉.查询id为1的人的信息
    Users users = mapper.selectUserByld(1);
    System.out.println(users);
    session.close();}

多对多查询

  • 多对多关联使用中间表
    在数据库中,表与表之间的多对多关联关系通常使用一个中间表来维开以4.4节中使用的订单表tb_orders和商品表tb_product为例,这网个表之间的大联大系使用了一个中间表tb_ordersitem来维护,订单表tb_orders和商品表tb_product,都
    与中间表tb_ordersitem形成了一对多关联关系,即中间表tb_ordersitem将订单表tb_orders和商品表tb_product拆分成了两个一对多的关联关系。

案例:基于MyBatis注解的学生管理程序

使用MyBatis注解实现下列要求
(1) MyBatis注解实现查询操作。根据表1和表2在数据库分别创建一个学生表s_student和一个班级表c_class,并查询id为2的学生的信息。
(2)MyBatis注解实现修改操作。修改id为4的学生的姓名修改为李雷,年龄修改为21。
(3)MyBatis注解实现一对多查询。查询出二班所有学生的信息。

  • STEP 02
    数据库准备:在名为mybatis的数据库中,创建两个数据表,和班级表c_class,同时在表中预先插入几条测试数据。
    USE mybatis;

#创建一个名称为c_class的表CREATE TABLE c_class
id int(32)PRIMARY KEY AUTO_INCREMENT,
classname varchar(40);
INSERT INTO c_class VALUES (1,’一班’);INSERTINTO c_class VALUES (2,’二班’);
#创建s student表同理

  • STEP 03
    POJO类准备:在项目的src/main/java目录下创建com.itheima.poio包.在com.itheima.pojo包中创建持久化类IClass,并在类中定义相头以
    封装lClass对象的id、班级名称以及关联的学生集合等属性。

public class lClass
private Integer id;
private String classname;
//主键id,班级名称
private List studentList;
@Override
public String toStringreturn “IClass{“ +”id=” + id +
“ , classname=’” + classname +”, studentList=” + studentList + ‘Y ;
}

  • STEP 04
    创建Mapper接口:在项目的src/main/java目录下创建com.itheincom.itheima.dao包下创建lStudentMapper接口,用于编与@ vrec
    查询语句。
    package com.itheima.dao;
    import com.itheima.pojo.IStudent;
    import org.apache.ibatis.annotations.Select;public interface lStudentMapper
    @Select(“select * from s_student where id = #{id}”)lStudent selectStudent(int id);

Spring

Spring概述

String框架的核心技术
Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以loC( lnverse OfControl:反转控制)和AOP ( Aspect Oriented Programming :面向切面编程)为内核

提供了展现层SpringMVC和持久层Spring JDBCTemplate以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的JavaEE企业应用开源框架.

Spring的体系结构

Spring的入门程序

下面通过一个简单的入门程序演示Spring框架的使用,要求在控制台打印“张三,欢迎来到Spring”实现步骤具体如下。

  • STEP 01
    在IDEA中创建名称为chapter06的Maven项目,然后在pom.xml文件中加载需使用到的Spring四个基础包以及Spring依赖包。

    org.springframework
    spring-expression
    5.2.8.RELEASE

    commons-logging commons-logging 1.2
  • STEP 02
    在chapter06项目的src/main/java目录下中创建com.itheima包,并在该包下创建名为HelloSpring的类。在HelloSpring类中定义userName属性和show()方法。
    package com.itheima;

public class HelloSpring {
private String userName;
public void setUserName(String userName){
this.userName=userName;}
public void show() {
System.out.printIn(userName+”:欢迎来到Spring”);}
}

  • STEP 03
    在chapter06项目的src/main/resources目录下新建applicationContext.xml文件作为HelloSpring类的配置文件,并在该配置文件中创建id为helloSpring的Bean。

  • STEP 04
    在chapter06项目的com.itheima文件夹下创建测试类TestHelloSpring,在main()方法中初始化Spring容器并加载applicationContext.xml配置文件,通过Spring容器获取HelloSpring类的helloSpring实例,调用HelloSpring类中的show)方法在控制台输出信息。

public class TestHelloSpring {
public static void main(String[] args){
//初始化spring容器,加载applicationContext.xml配置ApplicationContext applicationContext=new
ClassPathXmlApplicationContext(“applicationContext.xml”);
//通过容器获取配置中helloSpring的实例
HelloSpring helloSpring=
(HelloSpring)applicationContext.getBean(“helloSpring”);
helloSpring.show();
//调用方法}
}

  • STEP 05
    在IDEA中启动测试类TestHelloSpring,控制台会输出结果。

控制反转的概念

依赖注入的概念

什么是依赖注入
依赖注入(Dependency Inject,缩写DI)就是由loC容器在运行期间动态地将某种依赖资源注入对象之中。例如,将对象B注入(赋值)给对象A的成员变量。依赖注入的基本思想是:明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行。

依赖注入和控制反转的比较
依赖注入(DI)和控制反转(loC)是从不同角度来描述了同一件事情。依赖注入是从应用程序的角度描述,即应用程序依赖loC容器创建并注入它所需要的外部资源;而控制反转是从loC容器的角度描述,即loC容器控制应用程序,由loC容器反向地向应用程序注入应用程序所需要的外部资源。这里所说的外部资源可以是外部实例对象,也可以是外部文件对象等。

依赖注入的类型–构造方法注入

依赖注入的实现方式
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中。依赖注入通常有两种实现方式,一种是构造方法注入,另一种是属性setter方法注入。这两种实现方式具体介绍如下。

  1. 构造方法注入
    构造方法注入是指Spring容器调用构造方法注入被依赖的实例,构造方法可以是有参的或者是无参的。Spring在读取配置信息后,会通过反射方式调用实例的构造方法,如果是有参构造方法,可以在构造方法中传入所需的参数值,最后创建类对象。
    下面通过案例演示构造方法注入的实现,具体步骤如下。
  • STEP 01
    编写用户类:在项目chapter06的com.itheima包下新建User1类,在User1类中定义id、name和password三个属性。
    public class User1 {
    private int id;
    private Strilg name;
    private String password;
    public User1(int id, String name, String password){
    this.id=id;this.name=name;
    this.password=password; }
    public String toString(){
    return “id=”+id+”,name=”+name+”,password=”+password;
    }
    }
  • STEP 02
    获取Bean的配置信息:在chapter06项目的src文件夹下创建applicationContext-User.xml文件,在该文件中添加User1类的配置信息。

bean id=”user1” class=”com.itheima.User1”>


<constructor-arg name=”password”value=”123”>

  • STEP 03
    编写测试类:在项目chapter06的com.itheima包下创建测试类TestUser1。
    public class TestUser1 {
    public static void main(String[] args)throws Exception{//加载applicationContext.xml配置
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext-User.xml”);
    //获取配置中的User1实例
    User1 user1,applicationContext.getBean(“user1”, User1.class);System.out.println(user1);
    }
    }

依赖注入的类型–属性setter方法注入

属性setter方法注入
属性setter方法注入是Spring最主流的注入方法,这种注入方法简单、直观,它是在被注入的类中声明一个setter方法,通过setter方法的参数注入对应的值。

下面通过案例演示属性setter方法注入的实现,具体步骤如下。

  • STEP 01
    编写用户类:在项目chapter06的com.itheima包下新建User2类,在User2类中定义id、name和password三个属性。
    public class User2 {
    private int id; private String name;private String password;
    省略getter/setter方法public String toStringo{
    return “id=”+id+”,name=”+name+ “,password=”+password;
    }
    }

  • STEP 02
    获取Bean的配置信息:在chapter06项目的src文件夹下创建applicationContext-User2.xml文件,并在该文件的bean元素中添加User2类的配置信息。

  • STEP 03
    编写测试类:在项目chapter06的com.itheima包下创建测试类TestUser2。
    public class TestUser2{
    public static void main(String[]args)throws Exception {
    //加载applicationContext.xml配置
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(“applicationContext-User2.xml”);
    //获取配置中的User2实例
    User2 user2 = applicationContext.getBean(“user2”, User2.class);
    System.out.printIn(user2);
    }
    }

依赖注入的应用

下面以属性setter方法注入为例,实现一个简单的登录验证。具体实现步骤如下所示。

  • STEP 01
    (1)编写DAO层:在项目chapter06的com.itheima包下新建dao包,在dao包下创建接口UserDao.java,在UserDao.java接口中添加方法login),用于实现登录功能。

public interface UserDao {
public boolean login(String name,String password);}

(2)编写DAO层的实现类:在com.itheima.dao包下创建impl包,在impl包下创建UserDao接口的实现类UserDaolmpl,在UserDaolmpl类中实现login()方法。
public class UserDaolmpl implements UserDao {
@Override
public boolean login(String name, String password) {if (name.equals(“张三”)&&password.equals( “123”)){
return true;
}
return false;}
}

  • STEP 02
    (1)编写Service层:在项目chapter06的com.itheima包下新建service包,在service包下创建接口UserService.java,在接口中添加方法login().
    public interface UserService {
    public boolean login(String name,String password);}

(2)编写Service层实现类:在com.itheima.service包下创建impl包,在impl包下创建UserService接口的实现类UserServicelmpl,在UserServicelmpl类中实现login()方法。

public class UserServicelmpl implements UserService {UserDao userDao;
public void setUserDao(UserDaouserDao){ this.userDao=userDao; }
}
@Override
public boolean login(String name, String password) {return userDao.login(name,password);}

  • STEP 03
    编写applicationContext.xml配置文件:使用元素添加创建的UserDaolmpl类和UserServicelmpl类的实例,并配置其相关属性。


  • STEP 04
    编写测试类:在com.itheima包中新建测试类TestSpring。
    public class TestSpring {
    public static void main(String[] args){//加载applicationContext.xml配置
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“ applicationContext.xml”);
    UserService userService=(UserService)//获取配置中的UserService实例applicationContext.getBean(“userService”);
    boolean flag =userService.login(“张三”,”123””);if (flag) { System.out.printIn(“登录成功”);
    }else { System.out.printIn(“登录失败”);}
    }
    }

BeanFactory

BeanFactory接口实例的语法格式
Spring提供了几个BeanFactory接口的实现类,其中最常用的是XmIBeanFactory,它可以读取XML文件并根据XML文件中的配置信息生成BeanFactory接口的实例,BeanFactory接口的实例用于管理Bean。XmlBeanFactory类读取XML文件生成BeanFactory接口实例的具体语法格式如下。
BeanFactory beanFactory=new XmlBeanFactory
(new FileSystemResource(“D:/bean.xml”));

ApplicationContext接口

ApplicationContext接口的特点
ApplicationContext接口建立在BeanFactory接口的基础之上,它丰富了BeanFactory接口的特性,例如,添加了对国际化、资源访问、事件传播等方面的支持。
ApplicationContext接口可以为单例的Bean实行预初始化,并根据元素执行setter方法,单例的Bean可以直接使用,提升了程序获取Bean实例的性能。

Bean的配置

Spring容器所支持的配置文件格式
Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常用的是XML格式的配置文件。XML是标准的数据传输和存储格式,方便查看和操作数据。在Spring中,XML配置文件的根元素是, 元素包含子元素,每个
子元素可以定义一个Bean,通过元素将Bean注册到Spring容器中。

普通的Bean通常只需定义id(或者name)和class两个属性

构造方法实例化

下面通过一个案例演示Spring容器如何通过构造方法实例化Bean。
在IDEA中创建一个名为chapter07的Maven项目,然后在项目的pom.xml文件中配置需使用

  • STEP 01
    到的Spring四个基础包和Spring的依赖包。
    <-这里只展示了一个依赖包x-> org.springframeworkspring-expression5.2.8.RELEASE

在chapter07项目的src/main/java目录下,创建一个名称为com.itheima的包,在该包中创

  • STEP 02
    建Bean1类。
    package com.itheima;public class Bean1
    public Bean1){
    System.out.println(“这是Bean1”);}

在chapter07项目的src/main/resources目录下新建applicationBean1.xml作为Bean1类的

  • STEP 03
    配置文件,在该配置文件中定义一个id为bean1的Bean,并通过class属性指定其对应的实现类为Bean1。

在chapter07项目的com.itheima包中创建测试类Bean1Test,在main()方法中通过加载applicationBean1.xml配置文件初始化Spring容器,再通过Spring容器生成Bean1类的实例bean1,用来测试构造方法是否能实例化Bean1。

  • STEP 04

public class Bean1Test {
public static void main(String[] args){//加载applicationBean1.xml配置
ApplicationContext applicationContext=new
ClassPathXmlApplicationContext(“applicationBean1.xml”);通过容器获取配置中bean1的实例
Bean1 bean=(Bean1) applicationContext.getBean(“bean1”);System.out.print(bean);}
}

  • STEP 05 在IDEA中启动Bean1Test类,控制台会输出结果。

静态工厂实例化

下面通过一个案例演示如何使用静态工厂方式实例化Bean。
在chapter07项目的com.itheima包中创建Bean2类,该类与Bean1类一样,只定义一个构造方法,不需额外添加任何方法。

  • STEP 01
    package com.itheima;
    public class Bean2 {public Bean2(){
    System.out.println(“这是Bean2”);}
    }

在com.itheima包中,创建一个MyBean2Factory类,在该类中定义一个静态方法createBean(),用于创建Bean的实例。createBean()方法返回Bean2实例。

  • STEP 02

package com.itheima;
public class MyBean2Factory {
//使用MyBean2Factory类的工厂创建Bean2实例
public static Bean2 createBean(){
return new Bean2();
}
}

在chapter07项目的src/main/resources目录下新建applicationBean2.xml文件,作为

  • STEP 03
    MyBean2Factory类的配置文件。 <bean id=”bean2”
    class=”com.itheima.MyBean2Factory”factory-method=”createBean”/>

在com.itheima包中,创建一个测试类Bean2Test,用于测试使用静态工厂方式是否能实例化Bean。

  • STEP 04
    public class Bean2Test {
    public static void main(String[] args){
    llApplicationContext在加载配置文件时,对Bean进行实例化ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext(“ applicationBean2.xml”);System.out.printIn(applicationContext.getBean(“ bean2”));}
    }

实例工厂实例化

下面通过一个案例演示如何使用实例工厂方式实例化Bean.在chapter07项目的com.itheima包中创建Bean3类,该类与Bean1一样,不需添加任何方法。

  • STEP 01
    package com.itheima;
    public class Bean3 {public Bean3(){
    System.out.println(“这是Bean3”);}
    }

在com.itheima包中,创建一个MyBean3Factory类,在该类中定义无参构造方法,并定义createBean()方法用于创建Bean3对象。

  • STEP 02
    package com.itheima;
    public class MyBean3Factory {public MyBean3Factory() {
    System.out.printIn(“bean3工厂实例化中”);}
    public Bean3 createBean() {
    //创建Bean3实例的方法
    return new Bean3();}
    }

在chapter07项目的src/main/resources目录下新建applicationBean3.xml文件,作为MyBean3Factory类的配置文件。

  • STEP 03

在com.itheima包中,创建一个测试类BeanTest3,用于测试使用实例化工厂方式是否能实例化Bean。

  • STEP 04
    public class Bean3Test {
    public static void main(String[] args){
    ll ApplicationContext在加载配置文件时,对Bean进行实例化
    ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext(“applicationBean3.xml”);
    System.out.printIn(applicationContext.getBean(“bean3”));}
    }

singleton作用域


下面将通过案例的方式演示Spring容器中singleton作用域的使用。将id为bean1的作用域设置为singleton。

  • STEP 01

prototype作用域

prototype作用域的使用
在7.4.1节的基础上修改配置文件,将id为bean1的作用域设置为prototype。

基于XML的装配

两种基于XML的装配方式

a.属性setter方法注入
属性setter方法注入要求一个Bean必须满足以下两点要求。
(1) Bean类必须提供一个默认的无参构造方法。
(2) Bean类必须为需要注入的属性提供对应的setter方法。

b.构造方法注入
使用构造方法注入时,在配置文件里,需要使用元素的子元素来定义构造方法的参数,例如,可以使用其value属性(或子元素)来设置该参数的值。

基于注解的装配


下面通过案例演示如何使用注解来装配Bean,具体实现步骤如下。
导入依赖:在项目chapter07的pom.xml文件中导入spring-aop-5.2.8.RELEASE.jar依赖包,导入代码如下所示。

  • STEP 01 org.springframeworkspring-aop 5.2.8.RELEASE

XML配置文件:在chapter07项目的src\main\resources目录下创建

  • STEP 02
    applicationContext.xml,在该文件中引入Context约束并启动Bean的自动扫描功能。

    <context:component-scan base-package=”com.itheima” />

定义实体类:在项目chapter07的com.itheima包下新建entity包,在entity包下创建User实体类。

  • STEP 03
    @Component(“user”)
    @Scope(“singleton”)
    public class User {
    @Value(“1”)
    private int id;
    @Value(“张三”)
    private String name;
    @Value(“123”)
    private String password;
    省略getter/setter方法和toString()方法}

定义dao层:在chapter07项目的com.itheima包下创建dao包,在dao包下创建UserDao接口作为数据访问层接口,并在UserDao接口中声明save()方法,用于查询User实体的对象信息。

  • STEP 04
    package com.itheima.dao;public interface UserDao {
    public void save();
    }

实现dao层: chapter07项目的com.itheima.dao包下创建UserDaolmpl作为UserDao的实现类,并在UserDaolmpl类中实现UserDao接口中的save()方法。

  • STEP 05
    @Repository(“userDao”)
    public class UserDaolmpl implements UserDao {
    public void save(){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    User user=(User) applicationContext.getBean(“user”);
    System.out.println(user);
    System.out.println(“执行UserDaolmpl.save()”);}
    }

定义service层:在chapter07项目的com.itheima包下新建service包,在service包下创建UserService接口作为业务逻辑层接口,并在UserService接口中定义save()方法。

  • STEP 06
    package com.itheima.service;
    public interface UserService {
    public void save();
    }

实现service层:在chapter07项目的com.itheima.service包下创建UserServicelmpl作为

  • STEP 07
    UserService的实现类,并在UserServicelmpl类中实现UserService接口中的save()方法。
    @Service(“userService”)
    public class UserServicelmpl implements UserService {
    //使用@Resource注解注入UserDao
    Resource(name=”userDao”)
    private UserDao userDao;
    public void save(){
    this.userDao.save();
    System.out.println(“执行UserServicelmpl.save()”);}
    }

定义controller层: 在chapter07项目的com.itheima的包下新建controller包,在该包下创建UserController类作为控制层。

  • STEP 08
    @Controller
    public class UserController {
    //使用@Resource注解注入UserService
    @Resource(name=”userService”)
    private UserService userService;public void save(){
    this.userService.save();
    System.out.println(“执行UserController.save()”);}
    }

定义测试类:在chapter07项目的com.itheima的包下创建测试类AnnotationTest,在该类中编写测试代码,通过Spring容器加载配置文件并获取UserController实例,然后调用实例中的save()方法。

  • STEP 09
    public class AnnotationTest {
    public static void main(String[] args){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    UserController usercontroller=(UserController)
    applicationContext.getBean(“userController”);
    usercontroller.save();}
    }

自动装配

Bean的生命周期

Bean在不同作用域内的生命周期
Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中,Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中,Spring容器可以管理Bean的生命周期,控制着Bean的创建、初始化和销毁。在prototype作用域中,Spring容器只负责创建Bean实例,不会管理其生命周期。

监控时间节点的方式
监控两个节点的方式有两种,一种是使用XML配置文件,一种是使用注解。
Spring容器提供了@PostConstruct用于监控Bean对象初始化节点,
提供了@PreDestroy用于监控Bean对象销毁节点。下面通过案例演示这两个注解的使用。

在chapter07项目的com.itheima包下创建Student类,在类中定义id和name字段,并使用@PostConstruct指定初始化方法,使用@PreDestroy指定Bean销毁前的方法。

  • STEP 01
    @Component(“student”)
    public class Student {
    @Value(“1”)
    private String id;
    @Value(“张三”)
    private String name;/省略getter/setter方法,以及toString()方法
    @postConstruct
    public void init(){System.out.printIn(“Bean的初始化完成,调用init()方法”);}@PreDestroy
    public void destroy(){System.out.printIn(“Bean销毁前调用destroy()方法”);
    l}
    }

在chapter07项目的com.itheima的包下创建测试类StudentTest,在该类中编写测试代码,通过Spring容器加载配置文件并获取Student实例。

  • STEP 03
    public class StudentTest {
    public static void main(Stringargs){
    ApplicationContext applicationContext=new
    ClassPathXmlApplicationContext(“applicationStudent.xml”);
    Student student=(Student)applicationContext.getBean(“student”);
    System.out.printIn(student);
    //销毁Spring容器中的所有Bean
    AbstractApplicationContext ac=(AbstractApplicationContext)
    applicationContext;
    ac.registerShutdownHook();}
    }

Spring AOP概述

AOP概述
AOP的全称是Aspect Oriented Programming,即面向切面编程。和OOP不同,AOP主张将程序中相同的业务逻辑进行横向隔离,并将重复的业务逻辑抽取到一个独立的模块中,以达到提高程序可重用性和开发效率的目的。
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。

Spring AOP术语

AOP术语
AOP并不是一个新的概念,AOP中涉及很多术语,如切面、连接点、切入点、通知/增强处理、目标对象、织入、代理和引介等,下面针对AOP的常用术语进行简单介绍。
切面(Aspect)
切面是指关注点形成的类(关注点是指类中重复的代码),通常是指封装的、用于横向插入系统的功能类(如事务管理、日志记录等)。在实际开发中,该类被Spring容器识别为切面,需要在配置文件中通过元素指定。

JDK动态代理

Spring AOP的默认代理方式
默认情况下,Spring AOP使用JDK动态代理,JDK动态代理是通过
java.lang.reflect.Proxy类实现的,可以调用Proxy类的newProxyInstance()方法创建代理对象。JDK动态代理可以实现无侵入式的代码扩展,并且可以在不修改源代码的情况下,增强某些方法。

接下来,通过一个案例演示Spring中JDK动态代理的实现过程,案例具体实现步骤如下。

  • STEP 01
    在IDEA中创建一个名为chapter08的Maven项目,然后在项目的pom.xml文件中加载需使用到的Spring基础包和Spring的依赖包。

在项目的src/main/java目录下,创建一个com.itheima.demo01包,在该包下创建接口UserDao,在UserDao接口中编写添加和删除的方法。

  • STEP 02
    package com.itheima.demo01;
    .. . .十.
    public interface ‘UserDao {
    public void addUser();
    public void deleteUser();}

在com.itheima.demo1包中,创建UserDao接口的实现类UserDaolmpl,分别实现接口中的方法。

  • STEP 03
    package com.itheima.demo01;
    public class UserDaolmpl implements UserDao {
    public void addUser() {
    System.out.println(“添加用户”);}
    public void deleteUser() {
    System.out.println(“删除用户”);}
    }
    在com.itheima.demo01包下创建切面类MyAspect,在该类中定义一个模拟权限检查的方法和一个模拟日志记录的方法,这两个方法就是切面中的通知。
  • STEP 04
    package com.itheima.demo01;
    public class MyAspect {//切面类:存在多个通知Advice(增强的方法)
    public void check_Permissions(){
    System.out.println(“模拟检查权限…”);
    }
    public void log(){
    System.out.println(“模拟记录日志..”);
    }
    }

在com.itheima.demo01包下创建代理类MyProxy,该类需要实现InvocationHandler接口设置代理类的调用处理程序。在代理类中,通过newProxyInstance()生成代理方法。

  • STEP 05
    public class MyProxy implements lnvocationHandler {
    private UserDao userDao;
    public Object createProxy(UserDao userDao){
    this.userDao = userDao;
    ClassLoader classLoader = MyProxy.class.getClassLoader();
    /1.类加载器
    Classclasses = userDao.getClass().getInterfaces();
    // 2.被代理对象实现的所有接口
    return Proxy.newProxyInstance(classLoader,classes,this);
    // 3.返回代理对象
    }
    }
    /所有动态代理类的方法调用,都会交由invoke()方法去处理。这里省略invoke()方法

newProxyInstance()方法的3个参数
第1个参数是classLoader,表示当前类的类加载器。
第2个参数是classes,表示被代理对象实现的所有接口。
第3个参数是this,表示代理类JdkProxy本身。

在com.itheima.demo01包中,创建测试类JDKTest。在该类中的main()方法中创建代理对象jdkProxy和目标对象userDao,然后从代理对象jdkProxy中获得对目标对象userDao增强后的对象userDao1,最后调用userDao1对象中的添加和删除方法。

  • STEP 06

public class JDKTest {
public static void main(String[] args) {
MyProxy jdkProxy = new MyProxy;
//创建代理对象UserDao userDao = new UserDaolmplO;
/创建目标对象//从代理对象中获取增强后的目标对象
UserDao userDao1 = (UserDao)jdkProxycreateProxy(userDao);
/执行方法
userDao1.addUser();
userDao1.deleteUser();}
}

CGLib动态代理

JDK与CGLib动态代理的比较
JDK动态代理存在缺陷,它只能为接口创建代理对象,当需要为类创建代理对象时,就需要使用CGLib (Code Generation Library)动态代理,CGLib动态代理不要求目标类实现接口,它采用底层的字节码技术,通过继承的方式动态创建代理对象。Spring的核心包已经集成了CGLib所需要的包,所以开发中不需要另外导入JAR包。

接下来通过一个案例演示CGLib动态代理的实现过程,具体步骤如下。
在chapter08项目的src/main/java目录下创建一个com.itheima.demo02包,在该包下创建目标类UserDao,在该类中编写添加用户和删除用户的方法。

  • STEP 01
    package com.itheima.demo02;
    public class UserDao {
    public void addUser(){
    System.out.println(“添加用户”);}public void deleteUser(){
    System.out.println(“删除用户”);}
    }

在com.itheima.demo02包下创建代理类CglibProxy,该代理类需要实现MethodInterceptor接口用于设置代理类的调用处理程序,并实现接口中的intercept()方法。
STEP 02
public class CglibProxy implements MethodInterceptor {
public Object createProxy(Object target){//代理方法
Enhancer enhancer = new Enhancer();//创建一个动态类对象
enhancer.setSuperclass(target.getClass());//确定需要增强的类,设置其父类enhancer.setCallback(this);//添加回调函数
return enhancer.create();//返回创建的代理类
// intercept()方法省略}
}

在com.itheima.demo02包中创建测试类CglibTest,在main()方法中首先创建代理对象cglibProxy和目标对象userDao,然后从代理对象cglibProxy中获得增强后的目标对象userDao1,最后调用userDao1对象的添加和删除方法。

  • STEP 03
    public class CglibTest {
    public static void main(String[] args) {
    CglibProxy cglibProxy = new CglibProxy();//创建代理对象UserDao userDao = new UserDao();//创建目标对象
    /获取增强后的目标对象
    UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);//执行方法
    userDao1.addUser();userDao1.deleteUser();}
    }

基于XML的AOP实现

使用AOP代理对象的好处
因为Spring AOP中的代理对象由loC容器自动生成,所以开发者无须过多关注代理对象生成的过程,只需选择连接点、创建切面、定义切点并在XML文件中添加配置信息即可。Spring提供了一系列配置Spring AOP的XML元素。

Aop配置:在 1.切面 中配置 2.切入点和3.增强的 4.关系

  1. 配置切面
    在Spring的配置文件中,配置切面使用的是aop:aspect元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,因此,在使用aop:aspect元素之前,要在配置文件中先定义一个普通的Spring Bean。Spring Bean定义完成后,通过aop:aspect元素的ref属性即可引用该Bean。配置aop:aspect元素时,通常会指定id和ref两个属性。

aop:aspect元素的id属性和ref属性的描述
属性名称 描述
id 用于定义该切面的唯一标识
ref 用于引用普通的Spring Bean

  1. 配置切入点
    在Spring的配置文件中,切入点是通过aop:pointcut元素来定义的。当
    aop:pointcut元素作为aop:config元素的子元素定义时,表示该切入点是全局的,它可被多个切面共享;当aop:pointcut元素作为aop:aspect元素的子元素时,表示该切入点只对当前切面有效。定义aop:pointcut元素时,通常会指定id、expression属性。

Spring AOP切入点表达式的基本格式
execution(modifiers-pattern ret-type-patterndeclaring-type-pattern?
name-patternbaram-patterr throws-pattern?)

  1. 配置通知
    在Spring的配置文件中,使用aop:aspect元素配置了5种常用通知,分别为前置通知、后置通知、环绕通知、返回通知和异常通知。
  • STEP 01
    接下来通过一个案例演示如何在Spring中使用XML实现Spring AOP,具体实现步骤如下。
    在chapter08项目的pom.xml文件中导入AspectJ框架的相关JAR包。 org.aspectjaspectjrt1.9.1 org.aspectjaspectjweaver1.9.6

在chapter08项目的src/main/java目录下创建一个com.itheima.demo03包,在该包下创建接口UserDao,并在该接口中编写添加、删除、修改和查询的方法。

  • STEP 02
    package com.itheima.demo03;
    public interface UserDao {
    public void insert();
    public void delete();
    public void update();
    public void select();
    }

在com.itheima.demo03包下创建UserDao接口的实现类UserDaolmpl,实现UserDao接口中的方法。

  • STEP 03
    public class UserDaolmpl implements UserDao{public void insert() {
    System.out.println(“添加用户信息”);}public void delete(){
    System.out.println(“删除用户信息”);}public void update() {
    System.out.println(“更新用户信息”);}public void select() {
    System.out.println(“”查询用户信息”);}
    }

在com.itheima.demo03包下创建XmlAdvice类,用于定义通知。

  • STEP 04

public class XmlAdvice {
//前置通知
public void before(JoinPoint joinPoint){
System.out.print(“这是前置通知!”);
System.out.print(“目标类是: “+joinPoint.getTarget());System.out.println(“,被织入增强处理的目标方法为:”+
joinPoint.getSignature().getName();
}
/其他通知省略:返回通知、环绕通知、异常通知、后置通知}

在chapter08项目的resource文件夹下创建applicationContext.xml文件,在该文件中引入AOP命名空间,使用元素添加Spring AOP的配置信息。

  • STEP 05 <aop:pointcut id=”pointcut” expression=”execution(*
    com.itheima.demo03.UserDaolmpl.*(..))”/><aop:aspect ref =”xmlAdvice”>
    <aop:before method=”before” pointcut-ref=”pointcut” /><aop:after-returning method=”afterReturning” pointcut-ref=”pointcut” /><aop:around method=”around” pointcut-ref=”pointcut” />–指定环绕方式–><aop:after-throwing method=”afterException”pointcut-ref=”pointcut” /><aop:after method=”after” pointcut-ref=”pointcut” />

在com.itheima.demo03包中创建测试类TestXml。

  • STEP O6
    public class TestXml{
    public static void main(String[] args){ApplicationContext context=new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    UserDao userDao=context.getBean(“userDao”,UserDao.class);
    userDao.delete();
    System.out.printIn();
    userDao.insert();
    System.out.printIn();
    userDao.select();
    System.out.printIn();
    userDao.update();}
    }

基于注解的AOP实现

下面通过一个案例演示基于注解的AOP的实现,案例具体实现步骤如下。
在chapter08项目的src/main/java目录下创建一个com.itheima.demo04包,在该包下创建AnnoAdvice类,用于定义通知。

  • STEP 01
    @Aspect
    public class AnnoAdvice {/*
    @Pointcut(“execution( * com.itheima.demo03.UserDaolmpl.(..))”)Before(“ poincut0”)
    @AfterReturning(“poincut()”)
    @Around(“poincut()”)
    @AfterThrowing(“poincut()”)
    @After(“poincut()”)
    使用以上注解分别定义切点、前置通知、返回通知、环绕通知、异常通知、后置通知
    /}

在chapter08项目的resource文件夹下创建applicationContext-Anno.xml文件,在该文件中引入AOP命名空间,使用元素添加Spring AOP的配置信息。

JdbcTemplate概述

数据库用于处理持久化业务产生的数据,应用程序在运行过程中经常要操作数据库。一般情况下,数据库的操作由持久层来实现。作为扩展性较强的一站式开发框架,Spring也提供了持久层Spring JDBC功能,Spring JDBC可以管理数据库连接资源,简化传统JDBC的操作,进而提升程序数据库操作的效率。本章将对Spring JDBC相关知识进行详细讲解。

JDBCTemplate作用
针对数据库操作,Spring框架提供了JdbcTemplate类,JdbcTemplate是一个模板类,Spring JDBC中的更高层次的抽象类均在JdbcTemplate模板类的基础上创建。
JdbcTemplate类提供了操作数据库的基本方法,包括添加、删除、查询和更新。在操作数据库时,JdbcTemplate类简化了传统JDBC中的复杂步骤,这可以让开发人员将更多精力投入到业务逻辑中。

抽象类JdbcAccessor的属性
JdbcTemplate类继承自抽象类JdbcAccessor,同时实现了JdbcOperations接口。抽象类JdbcAccessor提供了一些访问数据库时使用的公共属性,具体如下:
. DataSource: DataSource主要功能是获取数据库连接。在具体的数据操作中,它还提供对数据库连接的缓冲池和分布式事务的支持。
. SQLExceptionTranslator: SQLExceptionTranslator是一个接口,它负责对SQLException异常进行转译工作。

Spring JDBC的配置

Spring对数据库的操作都封装在core、dataSource、object和support这4个包,使用Spring JDBC,就要对这些包进行配置。在Spring中,JDBC的配置是在配置文件applicationContext.xml中完成的。

  • STEP 01

配置数据源:包括数据库驱动、连接数据库url.
连接数据库用户名、连接数据库密码。

  • STEP 02
    配置JDBC模板:必须使用默认数据源。

  • STEP 03
    配置注入类

dataSource属性值的设定要求
在dataSource的4个属性中,需要根据数据库类型或者系统配置设置相应的属性值。例如,如果数据库类型不同,需要更改驱动名称;如果数据库不在本地,则需要将地址中的localhost替换成相应的主机IlP;默认情况下,数据库端口号可以省略,但如果修改过MySQL数据库的端口号,则需要加上修改后的端口号。此外,连接数据库的用户名和密码需要与数据库创建时设置的用户名和密码保持一致。

excute()方法

execute()方法用于执行SQL语句,其语法格式如下:
jdTemplate.execute(“SQL语句”);

下面以创建数据表的SQL语句为例,来演示excute()方法的使用,具体步骤如下。

  • STEP 01
    创建数据库:在MySQL中,创建一个名为spring的数据库。

创建项目并引入依赖:在IDEA中创建一个名为chapter09的Maven项目,然后在pom.xml文件中加载使用到的Spring 基础包、Spring依赖包、MySQL数据库的驱动JAR包、Spring JDBC的JAR包和Spring事务处理的JAR包。

  • STEP 02
    <!-这里只展示了其中一个JAR包–> mysql mysql-connector-java5.1.47 runtime

编写配置文件:在chapter09项目的src/main/resources目录下,创建配置文件applicationContext.xml,在该文件中配置数据源Bean和JDBC模板Bean,并将数据源注入到JDBC模板中。

  • STEP 03

编写测试类:在src/main/java目录下创建一个com.itheima包,在该包中创建测试类TestJdbcTemplate,在该类的main()方法中获取JdbcTemplate实例,然后调用execute()方法执行创建数据表的SQL语句。

  • STEP 04
    public class TestdbcTemplate {
    public static void main(Stringl args){
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(“applicationContext.xml”);
    JdbcTemplate jdTemplate =
    (JdbcTemplate) applicationContext.getBean(“jdbcTemplate”);
    jdTemplate.execute(“create table account(“+
    “id int primary key auto_increment,” +“username varchar(50),”+
    “balance double)”);
    System.out.println(“账户表account创建成功!”);}
    }

update()方法

下面通过一个案例演示update()方法的使用,该案例要求添加、更新、删除用户账户。案例具体实现步骤如下所示。
编写实体类:在chapter09项目的com.itheima包中,创建Account类,在该类中定义属性,以及其对应的getter/setter方法。

  • STEP 01
    public class Account {
    private Integer id;
    /账户id
    private String username;
    //用户名
    private Double balance;
    账户余额
    /省略gettter/setter方法
    public String toString0 {
    return “Account [id=” +id + “””
  • “username=” + username + “, balance=” + balance + “]”;}
    }

编写Dao层接口:在com.itheima包中,创建接口AccountDao,并在接口中定义添加、更新和删除账户的方法。

  • STEP 02
    public interface AccountDao {
    //添加
    public int addAccount(Account account);更新
    public int updateAccount(Account account);//删除
    public int deleteAccount(int id);
    }

实现Dao层接口:在com.itheima包中,创建AccountDao接口的实现类AccountDaolmpl,并在类中实现添加、更新和删除账户的方法。

  • STEP 03
    public class AccountDaolmpl implements AccountDao {
    定义JdbcTemplate属性,此处省略setter方法
    private JdbcTemplate jdbcTemplate;
    这里只展示(添加账户)的操作
    public int addAccount(Account account) {
    String sql = “insert into account(username,balance) value(?,?)”;Object[] obj = new Object[]/定义数组来存放SQL语句中的参数
    account.getUsername(), account.getBalance()
    };
    //执行添加操作,返回的是受SQL语句影响的记录条数
    return this.jdbcTemplate.update(sql, obj);
    }

编写配置文件:在applicationContext.xml中,定义一个id为accountDao的Bean用于将jdbcTemplate注入到accountDao实例中。

  • STEP 04

测试添加功能:在com.itheima包中创建测试类TestAddAccount,该类主要用于添加用户账户信息。

  • STEP 05
    public class TestAddAcount {
    public static void main(String[] args){
    ApplicationContext applicationContext =new
    ClassPathXmlApplicationContext(“applicationContext.xml”);AccountDao accountDao =
    (AccountDao) applicationContext.getBean(“accountDao”);Account account = new Account();
    account.setUsername(“tom”); account.setBalance(1000.00);int num = accountDao.addAccount(account);
    if (num > 0) { System.out.println(“成功插入了” + num +”条数据!”);} else { System.out.printIn(“插入操作执行失败!“);}
    }
    }

  • STEP 06
    查看执行第5步后的运行结果:在IDEA中启动TestAddAccount类,控制台会输出结果。此时再次查询spring数据库中的account表。

query()方法


了解了JdbcTemplate类中几个常用的query()方法后,接下来通过一个具体的案例演示query()方法的使用,案例实现步骤如下。插入数据:向数据表account中插入几条数据。

  • STEP 01
    uSE ‘spring;
    DROP TABLE IF EXISTSaccount;CREATE TABLE account( id int(11) NOT NULL AUTO_INCREMENT,'username' varchar(50)DEFAULT NULL,balance double DEFAULT NULL,
    PRIMARY KEY (id)
    )ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;insert into ‘account`(id ; ‘username , balance ) values
    (1;’zhangsan’,100),(3,’lisi’ ,500),(4,’wangwu’,300);

编写查询方法:在前面的AccountDao接口中,声明findAccountByld()方法,通过id查询单个账户信息;声明findAllAccount()方法,用于查询所有账户信息。

  • STEP 02
    //通过id查询
    public Account findAccountByld(int id);查询所有帐户b
    public List findAllAccount();

实现查询方法:在前面的的AccountDaolmpl类中,实现AccountDao接口中的findAccountByld()方法和findAllAccount()方法,并调用query()方法分别进行查询。

  • STEP 03
    /这里只展示了其中一个方法,通过id查询单个账户信息public Account findAccountByld(int id){
    //定义SQL语句
    String sql = “select * from account where id = ?”;/创建一个新的BeanPropertyRowMapper对象RowMapper rowMapper
    new BeanPropertyRowMapper(Account.class);/将id绑定到SQL语句中,通过RowMapper返回Object类型的单行记录return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
    (1Ee2 , 1239)ercrr
    }

测试条件查询:在com.itheima包中创建测试类FindAccountByldTest,用于测试条件查询。
STEP 04
public class FindAccountByldTest {public static void main(String[] args){//加载配置文件
ApplicationContext applicationContext =new
ClassPathXmlApplicationContext(“applicationContext.xml”);//获取AccountDao实例
AccountDao accountDao =
(AccountDao) applicationContext.getBean(“accountDao”);Account account = accountDao.findAccountByld(1);
System.out.println(account); }
}

测试查询所有用户信息:在com.itheima包中创建测试类FindAllAccountTest,用于查询所有用户账户信息。

  • STEP 05

public class FindAllAccountTest {
public static void main(String[]args) {//加载配置文件
ApplicationContext applicationContext =new
ClassPathXmlApplicationContext(“applicationContext.xml”);/获取AccountDao实例
AccountDao accountDao =
(AccountDao) applicationContext.getBean(“accountDao”);List°account = accountDao.findAllAccount();//执行方法for (Account act : account){//循环输出集合中的对象
System.out.println(act); }
}
}

事务管理的核心接口

spring-tx-5.2.8.RELEAS依赖包的3个接口
PlatformTransactionManager接口:可以根据属性管理事务。
TransactionDefinition接口:用于定义事务的属性。
TransactionStatus接口:用于界定事务的状态。



事务的传播行为
事务的传播行为是指处于不同事务中的方法在相互调用时,方法执行期间,事务的维护情况。例如,当一个事务的方法B调用另一个事务的方法A时,可以规定A方法继续在B方法所属的现有事务中运行,也可以规定A方法开启一个新事务,在新事务中运行,B方法所属的现有事务先挂起,等A方法的新事务执行完毕后再恢复。

事务的超时时间
事务的超时时间是指事务执行的时间界限,超过这个时间界限,事务将会回滚。TransactionDefinition接口提供了TIMEOUT_DEFAULT常量定义事务的超时时间。

事务是否只读
当事务为只读时,该事务不修改任何数据,只读事务有助于提升性能,如果在只读事务中修改数据,会引发异常。
TransactionDefinition接口中除了提供事务的隔离级别、事务的传播行为、事务的超时时间和是否为只读事务的常量外,还提供了一系列方法来获取事务的属性。

事务管理的方式

Spring中的事务管理的两种方式
Spring中的事务管理分为两种方式,一种是传统的编程式事务管理,另一种是声明式事务管理。

  • 编程式事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
  • 声明式事务管理:通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码植入到业务目标类中。

基于XML方式的声明式事务

如何实现XML方式的声明式事务
基于XML方式的声明式事务管理是通过在配置文件中配置事务规则的相关声明来实现的。在使用XML文件配置声明式事务管理时,首先要引入tx命名空间,在引入tx命名空间之后,可以使用

tx:advice元素来配置事务管理的通知,进而通过Spring AOP实现事务管理。
配置tx:advice元素时,通常需要指定id和transaction-manager属性,其中,id属性是配置文件中的唯一标识,transaction-manager属性用于指定事务管理器。除此之外,tx:advice元素还包含子元素<tx: attributes>,tx:attributes元素可配置多个
tx:method子元素,tx:method子元素主要用于配置事务的属性。

接下来通过一个案例演示如何通过XML方式实现Spring的声明式事务管理。本案例以9.2小节的项目代码和数据表为基础,编写一个模拟银行转账的程序,要求在转账时通过Spring对事务进行控制。案例具体实现步骤如下。

  • STEP 01
    导入依赖:在chapter09项目的pom.xml文件中加入aspectjweaver依赖包和aopalliance依赖包作为实现切面所需的依赖包。 org.aspectjaspectjweaver1.9.6 runtime aopallianceaopalliance1.0

定义Dao层方法:在com.itheima包的AccountDao接口中声明转账方法transfer()。

  • STEP 02
    //转账
    public void transter(String outUer
    String inUser,
    Double money);

实现Dao层方法:在com.itheima包的AccountDaolmpl实现类中实现AccountDao接口中的transfer()方法。

  • STEP 03
    //转账inUser:收款人; outUser:汇款人; money:收款金额
    public void transfer(String outUser, String inUser, Double money){/收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update(“update account set balance = balance +? “
  • “where username = ?”,money, inUser);
    /模拟系统运行时的突发性问题
    int i = 1/0;
    汇款时,汇款用户的余额=现有余额-所汇金额
    thisjdbcTemplate.update(“update account set balance = balance-? “
  • “where username = ?”,money, outUser);
    }

修改配置文件:修改chapter09项目的配置文件applicationContext.xml,添加命名空间等相关配置代码。

测试系统:在chapter09项目的com.itheima包中创建测试类TransactionTest。

  • STEP 05
    public class TransactionTest {
    public static void main(String[] args) {ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext(“applicationContext.xml”);
    获取AccountDao实例
    AccountDao accountDao =
    (AccountDao)applicationContext.getBean(“accountDao”);/
    调用实例中的转账方法
    accountDao.transfer(“lisi”, “zhangsan”,100.0);
    //输出提示信息
    System.out.println(“转账成功!”);}
    }

  • 未使用事物管理的缺陷
    由XML方式实现声明式事务管理的案例可知,zhangsan的账户余额增加了100,而lisi的账户确没有任何变化,这样的情况显然是不合理的。这就是没有事务管理,系统无法保证数据的安全性与一致性,下面使用事务管理解决该问题。

基于注解方式的声明式服务

接下来对上一小节的案例进行修改,以注解方式来实现项目中的事务管理,具体实现步骤如下。
创建配置文件:在chapter09项目的src/main/resources目录下,创建配置文件applicationContext-annotation.xml,在该文件中声明事务管理器等配置信息。

  • STEP 01
    <!-前四步省略–> <tx:annotation-driven transaction-manager=”transactionManager” />

修改Dao层实现类:在AccountDaolmpl类的transfer()方法上添加事务注解Transactional。

  • STEP 02
    @Transactional(propagation = Propagation.REQUIRED,
    isolation = lsolation.DEFAULT, readOnly = false)
    public void trassfer(String outUser, String inUser,Double money) //收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update(“update account set balance = balance +? “
  • “where username = ?” ,money, inUser);
    /模拟系统运行时的突发性问题
    int i = 1/0;
    /汇款时,汇款用户的余额=现有余额-所汇金额
    this.jdbcTemplate.update(“update account set balance = balance-? “
    +”where username = ?”,money, outUser);

编写测试类:在chapter09项目的com.itheima包中创建测试类AnnotationTest。

  • STEP 03
    public class AnnotationTest {
    public static void main(String[] args){
    ApplicationContext applicationContext =newClassPathXmlApplicationContext(“applicationContext-annotation.xml”);
    /获取AccountDap实例AccountDao accountDao =
    (AccountDao)applicationContext.getBean(“accountDao”);/调用实例中的转账方法
    accountDao.transfer(“lisi”, “zhangsan”,100.0);//输出提示信息
    System.out.println(“转账成功!”);}
    }

案例:实现用户登录

  • 思路分析
    根据学生管理系统及其登录要求,可以分析案例的实现步骤如下。
    1)为了存储学生信息,需要创建一个数据库。
    2)为了程序连接数据库并完成对数据的增删改查操作,需要在XML配置文件中配置数据库连接和事务等信息。
    3)在Dao层实现查询用户信息的方法。
    4)在Controller层处理业务逻辑,如判断用户输入的用户名与密码是否正确。

  • STEP 01
    创建数据库:在MySQL中的spring数据库中创建一个名为student的表。

编写实体类:在chapter09项目的com.itheima包中创建entity包,在 Student类,在该类中定义id、username、password和course属性,应的getter/setter方法。

  • STEP 02
    public class Student {
    //学生ID
    private Integer id;//学生姓名
    private String username;//学生密码
    private String password;//学生班级
    private String course;/省略getter/setter方法
    }

编写配置文件:在chapter09项目的resources文件夹下,创建配置文件applicationContext-student.xml,在该文件中配置id为dataSource的Bean和id为jdbcTemplate的JDBC模板Bean,并将数据源注入到JDBC

  • STEP 03

实现Dao层方法:在com.itheima.dao包下创建lmpl包,在该包下创疑StudentDaolmpl实现类,在StudentDaolmpl类中实现StudenfindAllStudent()方法。

  • STEP 05

public class StudentDaolmpl implements StudentDao {
/声明JdbcTemplate属性,省略了setter方法
private JdbcTemplate jdbcTemplate;
public List findAllStudent() {String sql = “select * from student”;RowMapper rowMapper =
new BeanPropertyRowMapper(Student.class);
/执行静态的SQL查询,并通过RowMapper返回结果
return this.jdbcTemplate.query(sql, rowMapper);l}
}

编写Controller层:在com.itheima包中创建controller包,在contrc

  • STEP 06
    StudentController类,用于实现用户登录操作。
    public class StudentController {
    public static void main(String[] args) {Scanner sca=new Scanner(System.in);String name=sca.nextLine();
    ApplicationContext applicationContext =new
    ClassPathXmlApplicationContext( “ applicationContext-student.xml”);/获取AccourRtDao实例
    StudentDao studentDao =
    (StudentDao) applicationContext.getBean(“studentDao”);List student = studentDao.findAllStudent();
    l/ for循环输出集合中的对象,此处省略}
    }

SpringMVC

SpringMVC概述

模型 视图 控制器

Java EE三层架构
在Java EE开发中,系统经典的三层架构包括表现层业务层持久层。三层架构中每一层各司其职,表现层 (Web层)负责接收客户端请求,并向客户端响应结果;业务层(Service层)负责业务逻辑处理,和项目需求息息相关;持久层(Dao层)负责和数据库交互,对数据库表进行增删改查。

Spring MVC在表现层的作用
Spring MVC作用丰层架构中的表现层用于接收客户端的请求并进行响应。Spring MVC中包含了控制器和视图,控制器接收到客户端的请求后对请求数据进行解析和封装,接着将请求交给业务层处理。业务层会对请求进行处理,最后将处理结果返回给表现层。表现层接收到业务层的处理结果后,再由视图对处理结果进行渲染,渲染完成后响应给客户端。

SpringMVC特点

Spring MVC的特点
(1) Spring MVC是Spring框架的后续产品,可以方便地使用Spring框架所提供的其他功能。
(2) Spring MVC使用简单,很容易设计出干净简洁的Web层。
(3) Spring MVC支持各种请求资源的映射策略。
(4) Spring MVC具有非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的APl。

(5) Spring MVC支持国际化,可以根据用户区域显示多国语言。
(6) Spring Mvq支持多种视图技术扌它支持JSP、Velocity和FreeMarker等视图技术。
(7) Spring MVC灵活性强,易扩展。

SpringMVC入门程序

接下来本节将通过一个简单的入门程序演示Spring MVC的使用。该程序要求在浏览器发起请求由Spring MVC接收请求并响应,具体实现步骤如下。

  • STEP 01
    创建项目:在IDEA中,创建一个名称为chapter10的Maven Web项目。

在下面的步骤6中,“Web resource directory path:”输入框中可以设置项目webapp文件夹的路径。将路径中项目名称后的路径修改为“src\main\webapp”,然后单击“OK”按钮完成项目webapp文件夹路径的设置。单击“OK”按钮系统会回到步骤4所示的设置界面,在步骤4中单击右下角“OK”按钮。

需要注意的是,如果默认创建的Maven项目中没有自动生成webapp文件夹,可以在IDEA中进行设置。

引入Maven依赖:项目创建完成后,为保障项目的正常运行,需要导入项目所需

  • STEP 02
    的依赖到项目的pom.xml文件中。
    <!-这里只展示了其中一个JAR包–> org.springframeworkspring-context5.2.8.RELEASE 需要注意的是,要在IDEA中使用插件运行Maven项目,除了需要在pom.xml文件中配置对应的插件外,还需要在IDEA中进行项目运行的相关配置。

配置前端控制器: Spring MVC通过前端控制器拦截客户端的请求并进行转发

  • STEP 03
    此在使用Spring MVC时,配置前端控制器是必不可少的一步。Spring MVC的前端控制器也是一个Servlet,可以在项目的web.xml文件中进行配置。

DispatcherServlet
org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

classpath:spring-mvc.xml
1
DispatcherServlet/

配置处理器映射信息和视图解析器:在项目的resources文件夹下创建Spring MVC的配置文件spring-mvc.xml,用于配置处理器映射信息和视图解析器。

  • STEP 04 <context:component-scan basepackage=”com.itheima.controller” />

创建处理器:在项目的src/main/java目录下创建一个路径为com.itheima.controller的包。在包中创建处理器FirstController类,用于处理客户端的请求并指定响应时转跳的页面。

  • STEP 05
    //设置当前类为处理器类
    @Controller
    public class FirstController {/设定当前方法的访问映射地址
    @RequestMapping(“ /firstController”)
    /设置当前方法返回值类型为String,用于指定请求完成后跳转的页面public String sayHello() {
    System.out.println(“访问到FirstController!”);//设定具体跳转的页面
    return “success”;
    }

创建视图(View)页面:在项目的Web-INF文件夹下创建名称为page的文件夹并在page文件夹下创建名称为sucdess的jsp文件,用于对客户端请求进行处理后的视图展示。

  • STEP 06

    Spring MVC FirstController!

SpringMVC工作原理

Spring MVC三大组件—处理器映射器
处理器映射器可以理解为一个Map<URL,Hanlder>,HandlerMapping负责根据用户请求的URL找到Handler (处理器),Spring MVC提供了不同的映射器来实现不同的映射方式。

DispatcherServlet类

DispatcherServlet作用
DispatcherServlet是Spring MVC的核心类,也是Spring MVC的流程控制中心,也称为Spring MVC的前端控制器,它可以拦截客户端的请求。拦截客户端请求之后,DispatcherServlet会根据具体规则将请求交给其他组件处理。所有请求都要经过DispatcherServlet进行转发处理,这样就降低了Spring MVC组件之间的耦合性。

DispatcherServlet案例编写说明
DispatcherServlet的本质是一个Servlet,可以在web.xml文件中完成它的配置和映射。参考第10章Spring MVC入门程序,在IDEA中创建一个名称为chapter11的
Maven Web项目。需要注意的是,如无特殊说明,本章的所有案例都将在chapter11项目中开发和运行。项目创建完成之后,在项目web.xml文件中配置DispatcherServlet。

web.xml中对DispatcherServlet的配置分为两个方面。一是配置Spring MVC的前端控制器,二是配置映射的URL路径。
1.配置Spring MVC的前端控制器:

DispatcherServlet
org.springframework.web.servlet.DispatcherServlets -l、

contextConfigLocationclasspath:spring-mvc.xml 1

2.配置映射的URL路径:

DispatcherServlet/

WEB-INF文件夹下默认配置文件命名规则
如果web.xml没有通过元素指定DispatcherServlet初始化时要加载的文件,则应用程序会去WEB-INF文件夹下寻找并加载默认配置文件,默认配置文件的名称规则如下所示。
[servlet-name]-servlet.xml
其中,[servlet-name]指的是web.xml文件中元素的值;“-servlet.xml”是配置文件名的固定拼接。

元素取值
元素取值分为三种情况:
1)如果元素的值为正整数或者0,表示在项目启动时就加载并初始化这个Servlet,值越小,Servlet的优先级越高,就越先被加载;
2)如果元素的值为负数或者没有设置,则Servlet会在被请求时加载和初始化;
3)如果元素的值为1,表明DispatcherServlet会在项目启动时加载并初始化。

@Controller

@Controller注解作用
在Spring MVC框架中,传统的处理器类需要直接或间接地实现Controller接口,这种方式需要在Spring MVC配置文件中定义请求和Controller的映射关系。当后台需要处理的请求较多时,使用传统的处理器类会比较繁琐,且灵活性低,对此,Spring MVC框架提供了@Controller注解。使用@Controller注解,只需要将@Controller注解标注在普通Java类上,然后通过Spring的扫描机制找到标注了该注解的Java类,该Java类就成为了SpringMVC的处理器类。

Spring MVC配置文件的类包扫描配置信息
<beans xmIns=”http://www.springframework.org/schema/beans
xmIns:context=”http://www.springframework.org/schema/context"xmIns:xsi="http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=”http:// www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package=”com.itheima.controller” />

Spring扫描配置文件范围
Spring MVC的配置文件被加载时,Spring会自动扫描com.itheima.controller类包及其子包下的Java类。如果被扫描的Java类中带有@Controller、@Service等注解,则把这些类注册为Bean并存放在Spring中。
与传统的处理器类实现方式相比,使用@Controller注解的方式显然更加简单和灵活。因此,在实际开发中通常使用@Controller注解来定义处理器类。

@RequestMapping注解的使用

@RequestMapping注解作用
@RequestMapping注解用于建立请求URL和Handler(处理器)之间的映射关系,该注解可以标注在方法上和类上。下面分别对@RequestMapping注解的这两种使用方式进行介绍。

方式一 — 标注在方法上
当@RequestMapping注解标注在方法上时,该方法就成了一个可以处理客户端请求的Handler(处理器),它会在Spring MVC接收到对应的URL请求时被执行。Handler在浏览器中对应的访问地址,由项目访问路径+处理方法的映射路径共同组成。

接下来通过一个案例演示@RequestMapping注解标注在方法上的使用。

  • STEP 01
    在chapter11项目的src/main/java目录下创建类包com.itheima.controller,并在类包下创建FirstController类。在类中创建sayHello()方法,用来处理客户端请求.package cpm.itheima.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;@Controller
    public class FirstController {
    @RequestMapping(value=” /firstController”)public void sayHello{
    System.out.printIn(“hello Spring MVC”);
    }
    }

启动项目,在浏览器中访问http://localhost:8080/chapter11/firstController,控制台打印输出信息。

  • STEP 02
    由运行结果可知,sayHello()方法被成功执行,说明
    @RequestMapping注解标注在方法上时,成功建立了请求URL和处理请求方法之间的对应关系。

方式二—标注在类上
当@RequestMapping注解标注在类上时,@RequestMapping的value属性值相当于本处理器类的命名空间,即访问该处理器类下的任意处理器都需要带上这个命名空间。@RequestMapping标注在类上时,其value属性值作为请求URL的第一级访问目录。当处理器类和处理器都使用@RequestMapping注解指定了对应的映射路径,处理器在浏览器中的访问地址,由项目访问路径+处理器类的映射路径+处理器的映射路径共同组成。

  • STEP 01
    接下来通过一个案例演示@RequestMapping注解标注在类上的使用。
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;@Controller
    @RequestMapping(value=” /springMVC”)public class FirstController {
    RequestMapping(value=” /firstController”)public void sayHello(){
    System.out.println(“hello Spring MVC”);}
    }

启动项目,在浏览器中访问http://localhost:8080/chapter11/springMVC/firstController,控制台打印输出信息。

  • STEP 02
    由运行结果可知, sayHello()方法被成功执行,说明
    @RequestMapping注解标注在类上时,成功建立了请求URL和处理请求类之间的对应关系。

@RequestMapping注解的属性

value属性的两种映射路径标注
value属性是@RequestMapping注解的默认属性。当value属性是@RequestMapping注解显式使用的唯一属性时,可以省略value的属性名。例如,下面两种映射路径标注的含义相同。

@RequestMapping(value=” /firstController”)
RequestMapping(“ /firstController”)

使用value属性时,可以指定映射单个的请求URL,也可以将多个请求映射到一个方法上。在value属性中添加一个带有请求路径的列表,就可以将这个请求列表中的路径都映射到对应的方法上。
@Controller
public class AuthController {
//设定当前方法的访问映射地址列表
RequestMapping(value = “ /addUser” ,” /deleteUser”)
public void checkAuth(){
System.out.println(“增删操作校验”);}
}

启动项目,在浏览器中访问地址http://localhost:8080/chapter11/addUser,控制台打印输出信息。

在浏览器中访问地址http://localhost:8080/chapter11/deleteUser,控制台打印输出信息。

method属性限定处理器映射
method属性可以对处理器映射的URL请求方式进行限定。当请求的URL和处理器映射成功,但请求方式和method属性指定的属性值不匹配,处理器也不能正常处理请求。

  • STEP 01
    接下来通过一个案例演示method属性中HTTP请求类型的声明。
    @Controller
    @RequestMapping(“ /method”)
    public class MethodController
    /只展示了处理请求方式为GET的请求,get()方法RequestMapping(method = RequestMethod.GET)public void get() {
    System.out.printIn(“RequestMethod.GET”);
    }
    //处理请求方式为DELETE的请求,delete()方法
    //处理请求方式为POST的请求,post()方法//处理请求方式为PUT的请求,put()方法

启动项目后,在客户端依次以GET方式、DELETE方式、POST方式和PUT方式请求

method属性中有多个HTTP请求类型
如果需要同时支持多个请求方式,则需要将请求方式列表存放在英文大括号中,以数组的形式给method属性赋值,并且多个请求方式之间用英文逗号分隔,示例代码如下所示。
@RequestMapping(value = “/method”,
method = {RequestMethod.GET,RequestMethod.POST})
public void getAndPost() {
System.out.println(“RequestMethod.GET+RequestMethod.POST”);
}

params属性值的定义方式
params属性中定义的值可以将请求映射的定位范围缩小。当客户端进行请求时,如果请求参数的值等于params属性定义的值,可以正常执行所映射到的方法,否则映射到的方法不执行。
@Controller
public class ParamsController
@RequestMapping(value = “ /params”,params = “id=1”)public void findByld(String id) {
System.out.printIn(“id=”+id);}

请求映射方式

请求映射方式的分类
基于注解风格的Spring MVC,通过@RequestMapping主解指定请求映射的URl路径。URL路径映射常用的方式有基于请求方式的URL路径映射、基于Ant风格的URL路径映射和基于REST风格的URL路径映射。接下来分别对这三种请求映射方式进行详细讲解。

a.基于请求方式的URL路径映射
上一节中学习到可以使用@RequestMapping注解的method属性,来限定当前方法匹配哪种类型的请求方式。除了可以使用@RequestMapping注解来限定客户端的请求方式之外,从Spring 4.3版本开始,还可以使用组合注解完成客户端请求方式的限定。组合注解简化了常用的HTTP请求方式的映射,并且更好的表达了被注解方法的语义。

  • Spring MVC组合注解
    @GetMapping:匹配GET方式的请求。
    @PostMapping:匹配POST方式的请求。
    @PutMapping:匹配PUT方式的请求。
    @DeleteMapping:匹配DELETE方式的请求。
    @PatchMapping:匹配PATCH方式的请求。

@GetMapping用法示例
接下来以@GetMapping为例讲解组合注解的用法,@GetMapping是
@RequestMapping(method = RequestMethod.GET)的缩写,使用组合注解替代@RequestMapping注解,可以省略method属性,从而简化代码。@GetMapping用法示例代码如下所示。
@GetMapping(value=” /firstController”)
public void sayHelloo{
}

b.基于Ant风格的URL路径映射
Spring MVC支持Ant风格的URL路径映射,所谓Ant风格其实就是一种通配符风格,可以在处理器映射路径中使用通配符对访问的URL路径进行关联。Ant风格的通配符有以下3种,分别是:?匹配任何单字符;*匹配0或者任意数量的字符;**匹配0或者多级目录。

映射路径使用多个通配符情况
当映射路径中同时使用多个通配符时,会有通配符冲突的情况。当多个通配符冲突时,路径会遵守最长匹配原则(has more characters)去匹配通配符,如果一个请求路径同时满足两个或多个Ant风格的映射路径匹配规则,那么请求路径最终会匹配满足规则.字符最多的路径。例如,y/ant/a/path同时满足/**/path和/ant//path匹配规则,但/ant/a/path最终会匹配“/ant//path”路径。

c.基于RESTful风格的URL路径映射
RESTful是按照REST风格访问网络资源,简单说RESTful就是把请求参数变成请求路径的一种风格。而REST (Representational State Transfer)是一种网络资源的访问风格,规范对了网络资源的访问方式。REST所访问的网络资源可以是一段文本、一首歌曲、一种服务,总之是一个具体的存在。每个网络资源都有一个URI指向它,要获取这个资源,访问它的URI就可以,因此URI即为每一个资源的独一无二的标识符。

传统风格与RESTful风格访问URL格式的不同

传统风格访问的URL格式如下所示。
http:// …/findUserByld?id=1
而采用RESTful风格后,其访问的URL格式如下所示。
http:// ../user/id/1
需要注意的是,RESTful风格中的URL不使用动词形式的路径,例如,findUserByld表示查询用户,是一个动词,而user表示用户,为名词。

RESTful风格的基本请求操作
RESTful风格在HTTP请求中,通过GET 、POST、PUT和DELETE 4个动词对应四种基本请求操作,具体如下所示。
.GET用于获取资源. POST用于新建资源. PUT用于更新资源. DELETE用于删除资源

数据绑定

数据绑定的概念
在程序运行时,Spring MVC接收到客户端的请求后,会根据客户端请求的参数和请求头等数据信息,将参数以特定的方式转换并绑定到处理器的形参中。Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑定。

Spring MVC数据绑定中的信息处理过程
Spring MVC数据绑定中的信息处理过程的步骤描述如下。
(1) Spring MVC将ServletRequest对象传递给DataBinder。
(2)将处理方法的入参对象传递给DrtaBinder。
(3)DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中。
(4)调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。
(5)校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。

默认类型数据绑定

Spring MVC常见的默认类型
当使用Spring MVC默认支持的数据类型作为处理器的形参类型时,Spring MVC的参数处理适配器会默认识别这些类型并进行赋值。Spring MVC常见的默认类型如下所示。

HttpServletRequest:通过request对象获取请求信息。
HttpServletResponse:通过response处理响应信息。
HttpSession:通过session对象得到session中存放的对象。
Model/ModelMap: Model是一个接口,ModelMap是一个类,Model的实现类对象和ModelMap对象都可以设置model数据,model数据会填充到request域。

下面通过案例演示默认类型的数据绑定,该案例要求实现一个HttpServletRequest类型的数据绑定,案例具体实现步骤如下所示。

  • STEP 01
    在IDEA中,创建一个名称为chapter12的Maven Web项目,并参照第10章Spring MVC入门程序的项目搭建,在项目的pom.xml中引入Spring MVC的相关依赖,并在Spring MVC的配置文件spring-mvc.xml中完成相关配置。需要注意的是,如无特殊说明,本章节的所有案例都将在chapter12项目中开发和运行。chapter12项目的初始目录结构和文件组成如图所示。

chapter12项目的Src\mainyava目录卜创建路径名称为com.ithelma.controller的类包,在类包中创建处理器类UserController,在UserController类中定义方法getUserld( ),用于获取客户端请求中userid参数的值。

简单数据类型绑定

简单数据类型绑定的概念
简单数据类型的绑定,就是指Java中基本类型(如int、double、String等)的数据绑定。在Spring MVC中进行简单类型的数据绑定,只需客户端请求参数的名称和处理器的形参名称一致即可,请求参数会自动映射并匹配到处理器的形参完成数据绑定。

下面通过案例演示简单数据类型的数据绑定,该案例要求实现Integer类型和String类型的数据绑定案例具体实现步骤如下所示。

  • STEP 01
    修改l4serController.java文件,在UserController类中新增getUserNameAndld( )方法,用来接收客户端请求中的参数。

@RequestMapping(“/getUserNameAndld”)
public void getUserNameAndld(String username,Integer id){
System.out.printIn(“ username=”+username+”,
id=”+id);
}

参数别名的设置
需要注意的是,有时候客户端请求中参数名称和处理器的形参名称不一致,这就会导致处理器无法正确绑定并接收到客户端请求中的参数。为此,Spring MVC提供了@RequestParam注解来定义参数的别名,完成请求参数名称和处理器的形参名称不一致时的数据绑定。

@RequestParam注解的使用
假设浏览器中的请求地址为http://localhost:8080/chapter12/getUserName?name=Spring,可以在getUserName()方法中使用@RequestParam注解标注参数。

@RequestMapping(“/getUserName”)
public void getUserName(@RequestParam(value=”name”,required = false,defaultValue = “itheima”) String username)
System.out.println(“username=”+username);

上述代码中,@RequestParam注解的value属性,给getUserName()方法中的username形参定义了别名name。此时,客户端请求中名称为name的参数,就会绑定到getUserName()方法中的
username形参上。@RequestParam注解的required属性设定了请求的name参数不是必须的,如果访问时没有携带name参数,会将defaultValue属性设定的值赋给形参username。

@PathVariable注解的两个常用属性
当请求的映射方式是REST风格时,上述对简单类型数据绑定的方式就不适用了。为此,Spring MVC提供了@PathVariable注解,通过@PathVariable注解可以将URL中占位符参数绑定到处理器的形参中。@PathVariable注解有以下两个常用属性。
value:用于指定URL中占位符名称。
required:是否必须提供占位符,默认值为true。

@PathVariable注解的使用
在UserController.java类中新增一个处理方法getPathVariable( ),在该方法中使用@PathVariable注解进行数据绑定,具体代码如下所示。
@RequestMapping(“/user/{name}”)
public void getPathVariable(@PathVariable(value = “name”)String username){
System.out.println(“username=”+username);
上述代码中,通过@PathVariable注解的value属性将占位符参数“name”和处理方法的参数username进行绑定。}

@RequestParam注解value属性可省略的情况
启动chapter12项目,在浏览器中访问地址http://localhost:8080/chapter12/user/Spring,访问后,控制台打印信息如图所示。

从运行结果的打印信息可以看出,控制台打印出了username的值为Spring。这表明访问地址后执行了getPathVariable()方法,@PathVariable注解成功将请求URL中的变量user映射到了方法的形参username上。如果请求路径中占位符的参数名称和方法形参名称一致,那么@PathVariable注解的value属性可以省略。

POJO绑定

POJO数据绑定的使用场景
在使用简单数据类型绑定时,可以很容易的根据具体需求来定义方法中的形参类型和个数,然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较繁琐。为解决这个问题,可以使用POJO类型进行数据绑定。

POJO数据绑定的概念
POJO类型的数据绑定就是将所有关联的请求参数封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定。

下面通过用户注册案例演示POJO的数据绑定,该案例要求表单提交的数据绑定在处理器User类型的形参中,案例具体实现步骤如下所示。

  • STEP 01
    在项目的src\main\java目录下,创建一个com.itheima.pojo包,在该包下创建一个User类用于封装用户信息。
    public class User {
    private String username;
    //用户名
    private String password;
    //用户密码
    省略getter/setter方法}

  • STEP 02
    在UserController.java类中,定义registerUser()方法用于接收用户注册信息。
    /接收表单用户信息
    @RequestMapping(“ /registerUser”)public void registerUser(User user) {
    String username = user.getUsername();String password = user.getPassword();
    System.out.printIn(“username=”+username+ “,password=”
    +password);}

在src\main\webapp目录下,创建register.jsp文件,在register.jsp中编写用户注册表单。

  • STEP 03

在register.jsp所示页面的表单中,分别填写注册的用户名为“heima”,密码为“123”,然后单击“注册”按钮即可完成注册数据的提交。当单击“注册”按钮后,控制台打印信息如图所示。

  • STEP 05
    从图中可以看出,程序成功打印出了用户名和密码。这表明registerUser()方法获取到了客户端请求中的参数username和参数password的值,并将username和password的值分别赋给了
    getUserNameAndld()方法中user形参的username属性和password属性,实现了POJO数据绑定。

解决请求参数中的中文乱码问题
为了防止客户端传入的中文数据出现乱码,可以使用Spring提供的编码过滤器来统一编码。要使用编码过滤器,只需要在web.xml中添加如下代码。
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8

CharacterEncodingFilter/*

自定义类型转换器

自定义类型转换器使用场景
Spring MVC默认提供了一些常用的类型转换器,这些类型转换器,可以将客户端提交的参数自动转换为处理器形参类型的数据。然而默认类型转换器并不能将提交的参数转换为所有的类型。此时,就需要开发者自定义类型转换器,来将参数转换为程序所需要的类型。

Converter接口的使用
Spring框架提供了org.springframework.core.convert.converter.Converter接口作为类型转换器,开发者可以通过实现Converter接口来自定义类型转换器。Converter接口的代码如下所示。
public interface Converter<S,T>
T convert(S source);
在上述代码中,泛型参数中的S表示源类型,T表示目标类型,而convert()方法将源类型转换为目标类型返回,方法内的具体转换规则可由开发者自行定义。

下面通过案例演示自定义类型转换器转换特殊数据类型并完成数据绑定,该案例要求实现Date类型的数据绑定,案例具体实现步骤如下所示。
在项目chapter12的src/main/java目录下,创建com.itheima.convert包,在该包下创建DateConverter类,并在类中定义convert()方法,实现String类型转到Date类型的转换。

  • STEP 01

public class DateConverter implements Converter<String, Date> {
private String datePattern = “yyyy-MM-dd”;//定义日期格式Override
public Date convert(String source){
SimpleDateFormat sdf = new SimpleDateFormat(datePattern);try {
return sdf.parse(source);
} catch (Exception e) {
throw new lllegalArgumentException(
“无效的日期格式,请使用这种格式:”+datePattern); }
}

为了让Spring MVC知道并使用DateConverter转换器类,还需要在配置文件spring-mvc.xml中配置类型转换器。

  • STEP 02 <context:component-scan basepackage=”com.itheima.controller” />

配置类型转换器工厂或配置格式化工厂
要将自定义类型转换器注册到程序中,除了可以将自定义转换器配置在类型转换器工厂ConversionServiceFactoryBean中,也可以将自定义转换器配置在格式化工厂org.springframework.format.support.FormattingConversionServiceFactoryBean中,通过格式化工厂对数据格式化。

在UserController.java类中定义方法getBirthday(),用于绑定客户端请求中的日期数据,getBirthday()方法代码如下所示。

  • STEP 03
    /**
    使用自定义类型数据绑定日期数据/
    @RequestMapping(“ /getBirthday”)
    public void getBirthday(Date birthday){
    System.out.printIn(“ birthday=” + birthday);
    }

数组绑定

数组绑定的使用场景
在实际开发中,可能会遇到客户端请求需要传递多个同名参数到服务器端的情况,这种情况采用前面讲解的简单数据绑定的方式显然是不合适的。此时,可以使用数组来接收客户端的请求参数,完成数据绑定。

接下来通过一个批量提交商品的案例来演示数组的数据绑定,具体实现步骤如下所示。
在chapter12项目的com.itheima.pojo包下创建一个商品类Product,用于封装商品信息。Product类的具体代码如下所示。

  • STEP 01
    package com.itheima.pojo;
    public class Product {
    private String prold;
    //商品id
    private String proName;
    //商品名称
    省略getter/setter方法
    }

在项目的src\main\webapp目录下,创建一个提交商品页面products.jsp,在products.jsp中创建一个展示商品列表的表单,表单提交时向服务器端发送商品列表的所有id。products.jsp的具体代码如下所示。

  • STEP 02
    <%@page language=””java” contentType=”text/html; charset=UTF-8””
    pageEncoding=”UTF-8”%> 提交商品
    SSM框架实战

在com.itheima.controller包中创建一个商品处理器类ProductController,在ProductController类中定义getProducts()方法,用于接收表单提交的商品id。ProductController类的具体代码如下所示。

  • STEP 03
    @Controller
    public class ProductController
    /获取商品列表
    @RequestMapping(“ /getProducts”)
    public void getProducts(String[]prolds){
    for (String prold : prolds){
    System.out.println(“获取到了ld为”+prold+”的商品”);}
    }

集合绑定

集合绑定的使用
集合中存储简单类型数据时,数据的绑定规则和数组的绑定规则相似,需要请求参数名称与处理器的形参名称保持一致。不同的是,使用集合绑定时,处理器的形参名称需要使用@RequestParam注解标注。

接下来使用集合数据绑定来批量提交商品案例,具体实现步骤如下所示。

  • STEP 01
    修改ProductControllerjava类的getProducts()方法,让getProducts()方法使用
    List类型来接受客户端的请求参数,修改后getProducts( )方法的具体代码如下所示。

获取商品列表(使用List绑定数据)RequestMapping(“ /getProducts”)
,public void getProducts(@RequestParam(“ prolds”)
Listprolds{
for (String prold : prolds){
System.out.println(“获取到了ld为”+prold+”的商品”);}
}

复杂POJO绑定一属性为对象类型的数据绑定

复杂POJO数组绑定的使用场景
使用简单POJO类型已经可以完成多数的数据绑定,但有时客户端请求中传递的参数比较复杂。例如,在用户查询订单时,页面传递的参数可能包括订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息。如果将订单和用户的所有查询条件都封装在一个简单POJO中,显然会比较混乱,这时可以考虑使用复杂POJO类型的数据绑定。

复杂POJO的3种属性的类型
所谓的复杂POJO,就是POJO属性的类型不止包含简单数据类型扌还包含对象类型、List类型和Map类型等其他引用类型。接下来分别对复杂POJO中属性为对象类型的数据绑定、属性为List类型的数据绑定和属性为Map类型的数据绑定进行讲解。

属性为对象类型的数据绑定

  • STEP 01
    下面通过一个获取用户订单信息的案例,演示复杂POJO中对象类型的数据绑定,案例具体实现步骤如下。在com.itheima.pojo包中创建一个订单类Order,用于封装订单信息,Order类的具体代码如下所示。
    public class Order {
    private String orderld;
    //订单id
    //省略getter/setter方法}

修改User.java类,在User类中新增Order类型的属性order,并定义相应的getter和setter方法。修改后User类的具体代码如下所示。

  • STEP 02
    public class User {
    private String username;
    //用户名
    private String password;
    //用户密码
    private Order order;
    //订单
    省略getter/setter方法
    }

在UserController.java类中定义方法findOrderWithUser( ),用于获取客户端请求中的User信息,findOrderWithUser()方法的具体代码如下所示。

  • STEP 03
    @RequestMapping(“ /findOrderWithUser”)
    public void findOrderWithUser(User user){
    String username = user.getUsername();
    String orderld = user.getOrder(.getOrderld);System.out.println(“username=”+username+”,orderld=”+orderld);
    }

在项目的src\main\webapp目录下,创建一个订单信息文件order.jsp,在orderjsp文件中创建一个表单,表单中包含用户名和订单编号。表单提交时将用户名和订单编号信息发送到处理器。order.jsp的具体代码如下所示。

  • STEP 04
    <%@page language=”java” contentType=”text/html;
    charset=UTF-8” pageEncoding=”UTF-8”%>

复杂POJO数组绑定的格式
在复杂POJO数据绑定时,如果数据需要绑定到POJO属性对象的属性中,客户端请求的参数名(本例中指form表单内各元素name的属性值)的格式必须为“属性对象名称.属性”,其中“属性对象名称”要和POJO的属性对象名称一致,“属性”要和属性对象所属类的属性一致。

复杂POJO绑定一属性为List类型的数据绑定

属性为List类型的数据绑定
一般订单业务中,用户和订单基本都是一对多的映射关系,即用户的订单属性使用集合类型。接下来通过一个获取用户订单信息的例子,演示复杂POJO中属性为List类型的数据绑定,案例具体实现步骤如下。修改User.java类,将User类中订单属性修改为List类型。由于用户一般拥有多个收货地址,在User类中新增List类型的
STEP 01
public class User {
private String username;
//用户名
private String password;
//用户密码
private List orders;
//用户订单
private List address;
//订单地址
/省略getter/setter方法
}

在com.itheima.controller包中创建一个订单处理器类OrderController,在OrderController类中定义showOrders()方法,用于展示用户的订单信息。OrderController类的具体代码如下所示。

  • STEP 02
    Controller
    public class OrderController
    // 获取用户中的订单信息@RequestMapping(“ /showOrders”)
    public void showOrders(User user)
    List orders = user.getOrders();List addressList = user.getAddress);System.out.println(“订单:”);for (int i = 0; i <orders.size() ; i++){
    Order order = orders.get(i);
    String address = addressList.get(i);
    System.out.println(“订单ld:” +order.getOrderldO);System.out.printIn(“订单配送地址: “+address);}
    }

在项目的src\main\webapp目录下,创建一个订单信息文件orders.jsp,在orders.jsp中创建一个表单用于提交用户的订单信息。表单提交时,表单数据分别封装到User的订单属性orders和地址属性address中。orders.jsp的具体代码如下所示。

  • STEP 03
    <%@page language=”java” contentType=”text/html; charset=UTF-8”
    pageEncoding=”UTF-8” %>订单信息<form action=”${pageContext.request.contextPath }/showOrders”method=”post”>

    订单号订单名称配送地址
  • STEP 05
    在orders.jsp显示效果图所示的页面中,单击左下角“订单信息”按钮,orders.jsp表单中的订单信息发送到服务器端的showOrders()方法进行处理,控制台打印信息如图所示。

复杂POJO数组绑定的编写要求
在复杂POJO数据绑定时,如果数据绑定到List类型的属性,客户端请求的参数名称(本例中指form表单内各元素name的属性值)编写必须符合以下要求。
①如果List的泛型为简单类型,则客户端参数名称必须和POJO类中List属性所属类中的属性名称保持一致。
②如果List的泛型参数为对象类型,则客户端参数名称必须与POJO类的层次结构名称保持一致,并使用数组格式描述对象在List中的位置,即客户端参数名称必须和最终绑定在List中的某个对象的某个属性的名称保持一致。

复杂POJO绑定一属性为Map类型的数据绑定

属性为Map类型的数据绑定

  • STEP 01
    接下来,通过一个获取订单信息的案例,演示复杂POJO中属性为Map类型的数据绑定,具体实现如下。修改Order.java类,在Order类中新增HashMap类型的属性productInfo,用于封装订单中的商品信息,其中productInfo的键用来存放商品的类别,productInfo的值用来存放商品类别对应的商品。
    public class Order {
    private String orderld;
    //订单id
    private HashMap<String,Product> productlnfo;//商品信息
    }

修改OrderController.java类,在OrderController类中新增getOrderlr用于获取客户端提交的订单信息,并将获取到的订单信息打印在控制台。getOrderlnfo()方法的具体代码如下所示。

  • STEP 02
    RequestMapping(“ /orderInfo”)
    public void getOrderlnfo(Order order){String orderld = order.getOrderld();
    //获取订单id
    //获取商品信息
    HashMap<String,Product> orderInfo = order.getProductInfo0;Set keys = orderlnfo.keySet();
    System.out.printIn(“订单id:”+orderld);
    System.out.println(“订单商品信息:”);
    for (String key : keys){ Product product = orderlnfo.get(key);
    String prold = product.getProld();String proName = product.getProName();System.out.printIn( key+”类~”+”商品id:” + prold+”,商品名称: “+ proName);}
    }

在项目的src\main\webapp目录下,创建一个订单信息页面order_infcorder_info.jsp中创建一个表单用于提交订单信息。表单提交时,表单数据分别封装到Order的orderld属性和商品信息属性productlnfo中。

  • STEP 03
订单id:
商品ld 商品名称

JSON数据绑定

消息转换器—HttpMessageConverter接口
客户端不同的请求,HttpServletRequest中数据的MediaType可能会不同,如果想将HttpServletRequest中的数据转换成指定对象,或者将对象转换成指定格式的数据,就需要使用对应的消息转换器来实现。Spring中提供了一个
HttpMessageConverter接口作为消息转换器。因为数据的类型有多种,所以Spring中提供了多个HttpMessageConverter接口的实现类,其中
MappingJackson2HttpMessageConverter是HttpMessageConverter接口的实现类之一,在处理请求时,可以将请求的JSON报文绑定到处理器的形参对象,在响应请求时,将处理器的返回值转换成JSON报文。

HttpMessageConverter与Converter类型转换器的区别
需要注意的是,HttpMessageConverter消息转换器和之前所学习的Converter类型转换器是有区别的。HttpMessageConverter消息转换器用于将请求消息中的报文数据转换成指定对象,或者将对象转换成指定格式的报文进行响应;Converter类型转换器用于对象之间的类型转换。

接下来通过一个异步提交商品信息案例,演示Spring MVC中的JSON数据绑定,案例具体实现步骤如下。在项目的pom.xml文件中导入Jackson的依赖。使用MappingJackson2HttpMessageConverter对JSON数据进行转换和绑定,需要导入Jackson JSON转换核心包、JSON转换的数据绑定包和JSON转换注解包的相关依赖。

  • STEP 01

    com.fasterxml.jackson.corejackson-core 2.9.2
  • STEP 02
    在项目中导入jQuery文件。
    由于本次演示的是异步数据提交,需要使用jQuery,所以需要将jQuery文件导入到项目中,以便发送ajax请求。在项目的/webapp文件夹下创建名称为js的文件夹,在js文件夹中导入jQuery文件。

在项目的src\main\webapp目录下,创建一个商品信息页面product.jsp,在product.jsp中创建一个表单用于填写商品信息,表单提交时,表单发送异步请求将表单的商品信息发送到处理器。product.jsp的部分代码如下所示。

  • STEP 03

    修改ProductController.java类,在ProductController类电新增getProduct()方法和getProductList()方法,分别用于获取客户端提交的单个商品信息和多个商品信息。由于客户端发送的是JSON格式的数据,此时,在处理器中无法直接使用方法形参接收数据,完成数据的自动绑定。对此,可以使用Spring MVC提供的@RequestBody注解。

    • STEP 04
      /**
      只展示了个体Product()方法,获取单个商品信息*/
      RequestMapping(“/getProduct”)
      public void getProduct(@RequestBody Product product) {String prold = product.getProld();
      String proName = product.getProName();
      System.out.println(“获取到了ld为”+prold+”名称为”+ proName+”的商品”);

    在项目的web.xml文件中配置的DispatcherServlet会拦截所有URL,导致项目中的静态资源如css、jsp、js等)也被DispatcherServlet拦截。如果想放行静态资源,可以在Spring MVC的配置文件中进行静态资源配置。Spring MVC配置文件的部分配置代码如下所示。

    • STEP 05 <context:component-scan base-package=”com.itheima.controller” />
      <property name=”prefix”value=”/WEB-INF/pages/“ />
      mvc:annotation-driven/ <mvc:resources mapping=”/js/**” location=”/js/“/>

    <mvcresources ../>的两个重要属性
    属性 说明
    location 用于定位需要访问的本地静态资源文件路径,具体到某个文件夹
    mapping 匹配静态资源全路径,其中“/**”表示文件夹及其子文件夹下的某个具体文件

    在product.jsp的显示效果图所示的页面中,单击右侧“提交单个商品”按钮,product.jsp表单中的单个商品信息以JSON格式异步发送到服务器端getProduct()方法中。提交单个商品时控制台打印信息如图所示。

    • STEP 07
      从图中所示的打印信息可以得出,客户端异步提交的JSON数据,按照形参product属性的格式进行关联映射,并赋值给product对应的属性,完成了JSON数据的绑定。

    多学一招:JSON转换器配置和静态资源访问配置
    JSON转换器配置和静态资源访问配置,除了之前讲解的配置方案之外,还可以通过其他方式完成,下面讲解两种配置方式,使用元素配置JSON转换器和静态资源访问的配置方式。

    使用元素配置JSON转换器
    在配置JSON转换器时,除了常用的<mvc:annotation-driven />元素,还可以使用元素进行显示的配置,元素配置JSON转换器方式具体如下所示。




    返回值为void类型的页面跳转

    返回值为void类型的页面跳转到默认页面
    当Spring MVC方法的返回值为void类型,方法执行后会跳转到默认的页面。默认页面的路径由方法映射路径和视图解析器中的前缀、后缀拼接成,拼接格式为“前缀+方法映射路径+后缀”。如果Spring MVC的配置文件中没有配置视图解析器,则会报HTTP Status 500错误。

    由上面两个图的内容可以得出,访问地址后,执行了showPageByVoid()方法,并且在方式执行后成功跳转到WEB-INF文件夹下的register.jsp页面。页面虽然跳转了,但是浏览器地址栏没有变化,原因是Spring MVC对请求默认按转发的方式进行响应。

    返回值为String类型的页面跳转一不携带数据

    返回值为String类型的页面跳转的转发方式
    当方法的返回值为普通的字符串时,Spring MVC在方法执行后会默认以转发的方式响应给客户端。除了这种默认的转发方式,还可以返回指定前缀的字符串,来设定处理器执行后对请求进行转发还是重定向,设定转发和重定向的字符串格式如下所示。

    @Controller
    public class PageController {
    @RequestMapping(“ /showPageByString”)publicStringfshowPageByString0 {
    System.out.printIn(“showPageByString running”);}return register;
    }

    forward:需要转发到的资源路径
    redirect:需要重定向到的资源路径

    返回值为String类型的页面跳转一携带数据

    返回值为ModelAndView类型的页面跳转

    ModelAndView对象组成部分
    使用方法的返回值可以设定跳转的逻辑视图名称,使用Model等对象实现页面跳转时传输数据。除此之外,Spring MVC还提供了兼顾视图和数据的对象
    ModelAndView,ModelAndView对象包含视图相关内容和模型数据两部分,其中视图相关的内容可以设置逻辑视图的名称,也可以设置具体的View实例;模型数据则会在视图渲染过程中被合并到最终的视图输出。

    接下来通过一个案例演示返回值为ModelAndView类型的页面跳转,案例具体实现步骤如下。修改文件PageController.java,新增showModelAndView()方法,在showModelAndView()方法中使用ModelAndView封装数据和视图,完成页面跳转时传递数据。

    普通字符串的回写

    接下来通过ittpServletResponse输出数据的案例,演示普通字符串的回写,案例具体实现步骤如下。在项目的com.itheima.controller包下创建一个数据回写类DataController,在DataController类中定义showDataByResponse()方法,用于测试在Spring MVC中普通字符串的回写。

    • STEP 01
      Controller
      public class DataController
      @RequestMapping(“showDataByResponse”)
      public void showDataByResponse(HttpServletResponse response){try {
      response.getWriter().print(“response”);} catch (IOException e) {
      e.printStackTrace();}
      }

    启动chapter12项目,在浏览器中访问地址

    JSON数据的回写一对象数据转换成JSON数据后的回写

    项目中已经导入Jackson的依赖
    可以先调用Jackson的JSON转换的相关方法,将对象或集合转换成JSON数据,然后通过HttpServletResponse将JSON数据写入到输出流中完成回写,具体实现步骤如下。

    • STEP 01
      修改文件DataController.java,在DataController类中新增showDataByJSON()方法,用于将对象转换成JSON数据并写入输出流中完成回写。
      @RequestMapping(“showDataByJSON”)
      public void showDataByJsoNHttpServletResponse responsetry ObjectMapper om = new ObjectMapper);
      User user = new User();user.setUsername(“heima”); user.setPassword(“666”)String ujson = om.writeValueAsString(user);
      response.getWriter.print(ujson);
      } catch (IOException e){e.printStackTrace();}

    ResponseBody注解的使用范围
    如果每次回写对象或者集合等数据都需要手动转换成JSON数据,操作就比较繁琐。为此,Spring MVC提供了@ResponseBody注解,@ResponseBody注解的作用是将处理器返回的对象通过适当的转换器转换为指定的格式之后,写入到
    HttpServletResponse对象的body区,@ResponseBody注解通常用来返回JSON数据。@ResponseBody注解可以标注在方法和类上,当标注在类上时,表示该类中的所有方法均应用@ResponseBody注解。如果需要当前类中的所有方法均应用
    @ResponseBody注解,也可以使用@RestController注解,@RestController注解相当于@Controller+@ResponseBody两个注解的结合。

    JSON数据的回写一集合数据转换成JSON数据后的回写

    接下来通过一个案例演示使用@ResponseBody注解回写JSON格式的对象数据和集合数据,案例具体实现步骤如下。修改文件DataControllerjava,在DataController类中新增getUser()方法,用于返回JSON类型的User信息;新增addProducts()方法用于返回JSON类型的Product列表信息。

    • STEP 01
      /只展示getUser()方法
      @RequestMapping(“getUser”)@ResponseBody
      public User getUser) {User user = new User();user.setUsername(“heima2”);return user; }

    在项目的src\main\webapp目录下,创建一个商品添加页面product_add.jsp,在product_add.jsp中创建一个表格,用于显示用户信息和添加商品信息。product_add.jsp的部分代码如下所示。

    • STEP 02

    简单异常处理器

    HandlerExceptionResolver接口
    如果希望对Spring MVC中所有异常进行统一处理,可以使用Spring MVC提供的异常处理器HandlerExceptionResolver接口。Spring MVC内部提供了HandlerExceptionResolver的实现类SimpleMappingExceptionResolver。它实现了简单的异常处理,通过该实现类可以将不同类型的异常映射到不同的页面,当发生异常的时候,实现类根据发生的异常类型跳转到指定的页面处理异常信息。实现类也可以为所有的异常指定一个默认的异常处理页面,当应用程序抛出的异常没有对应的映射页面,则使用默认页面处理异常信息。

    • STEP 01
      下面通过一个案例演示SimpleMappingExceptionResolver对异常的统一处理,案例具体实现步骤如下所示。

    • STEP 02
      src\mainyjava目录下,创建一个名称为com.itheima.controller的包。在包中创建ExceptionController类,ExceptionController类的具体代码如下所示。
      @Controller
      public class ExceptionController {/抛出空指针异常
      @RequestMapping(“showNullPointer”)public void showNullPointer() {
      ArrayList list = new ArrayList<>();System.out.printIn(list.get(2));}
      /抛出IO异常省略,抛出算术异常省略}

      程序执行文件ExceptionController.java中的任意一个方法时,都会抛出异常。在异常发生时,如果要跳转到指定的处理页面,则需要在Spring MVC的配置文件spring-mvc.xml中使用SimpleMappingExceptionResolver指定异常和异常处理页面的映射关系。Spring MVC配置文件的部分配置如下所示。

      • STEP 03 nullPointerExp.jspIOExp.jsp

      在文件spring-mvc.xml中,已经指定了异常类别对应的异常处理页面,接下来创建这些异常处理页面。在此不对异常处理页面做太多处理,只在页面中展示对应的异常信息。

      • STEP 04 <%@page contentType=”text/html;charset=UTF-8” language=”java”%>空指针异常处理页面 空指针异常处理页面-----${exp}

      启动chapter13项目,在浏览器中访问地址

      自定义异常处理器

      resolveException()方法
      除了使用SimpleMappingExceptionResolver进行异常处理,还可以自定义异常处理器统一处理异常。通过实玫HandlerExceptionResolver接口,重写异常处理方法
      resolveException()来定义自定义异常处理器。当Handler执行并且抛出异常时,自定义异常处理器会拦截异常并执行重写的resolveException)方法,该方法返回值是
      ModelAndView类型的对象,可以在ModelAndView对象中存储异常信息,并跳转到异常处理页面。

      接下来通过一个案例演示自定义异常处理器分类别处理自定义异常和系统自带的异
      STEP 01
      常,案例具体实现步骤如下所示。在src\main\java目录,创建一个路径为com.itheima.exception的包,并在包中创建自定义异常类MyException。
      public class MyException extends Exception {
      private String message;//异常信息
      public MyException(String message) {super(message);
      this.message = message;
      }
      @Override
      public String getMessage() {return message;}
      public void setMessage(String message){this.message = message;}

      修改文件ExceptionController.java,在ExceptionController类中,新增方法addData()用于抛出自定义异常,addData()方法的具体代码如下所示。

      • STEP 02
        @RequestMapping(“addData”)
        public void addData() throws MyException {
        throw new MyException(“新增数据异常!”);

      在com.itheima.controller包下,创建名称为MyExceptionHandler的自定义异常处理器。在MyExceptionHandler类中重写resolveException()方法,用于判断当前异常是自定义异常还是系统自带的异常,根据异常的种类不同,resolveException)方法返回不同的异常信息。使用自定义异常处理器,需要先将自定义异常处理器注册到Spring MVC中。MyExceptionHandler类的部分代码如下所示。

      • STEP 03
        只列举了if…else的内容
        if (ex instanceof MyException){//自定义异常,将异常信息直接返回
        msg=ex.getMessage(;
        }else //如果是系统的异常,从堆栈中获取异常信息
        Writer out = new StringWriter(); PrintWriter s = new PrintWriter(out);ex.printStackTrace(s);
        String sysMsg = out.toString();//系统真实异常信息
        msg=”网络异常! “;//向客户隐藏真实的异常信息,仅发送提示信息

      在src\main\webapp目录下,创建一个名称为error.jsp的文件,用作异常处理页面。本案例不对异常处理页面进行过多处理,只将异常信息打印在页面上。

      • STEP 04
        <%@page contentType=”text/html;charset=UTF-8” language=”java”%>异常处理页面 ${msg}

      异常处理注解

      @ControllerAdvice注解的作用
      从Spring 3.2开始,Spring提供了一个新注解@ControllerAdvice,ControllerAdvice有以下两个作用。
      。注解作用在类上时可以增强Controller,对Controller中被@RequestMapping注解标注的方法加一些逻辑处理。
      @ControllerAdvice注解结合方法型注解@ExceptionHandler,可以捕获Controller中抛出的指定类型的异常,从而实现不同类型的异常统一处理。

      接下来通过一个案例演示使用注解实现异常的分类处理,具体实现步骤如下所示。在com.itheima.controller包下,创建名称为ExceptionAdvice的异常处理器。ExceptionAdvice类中定义2个处理不同异常的方法,其中doMyException()方法用来处理Handler执行时抛出的自定义异常, doOtherException()方法用来处理Handler执行时抛出的系统异常。

      • STEP 01
        @ControllerAdvice
        public class ExceptionAdvice { //处理MyException类型的异常@ExceptionHandler(MyException.class)
        public ModelAndView doMyException(MyException ex) throws IOException {ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject(“msg” , ex.getMessage());
        modelAndView.setViewName(“error.jsp”); return modelAndView;}//处理Exception类型的异常doOtherException()省略

      拦截器概述

      什么是拦截器
      拦截器(Interceptor)是一种动态拦截Controller方法调用的对象,它可以在指定的方法调用前或者调用后,执行预先设定的代码。拦截器作用类似于Filter (过滤器),但是它们的技术归属和拦截内容不同。Filter采用Servlet技术,拦截器采用Spring MVC技术;Filter会对所有的请求进行拦截,拦截器只针对Spring MVC的请求进行拦截。

      拦截器的定义方式
      在Spring MVC中定义一个拦截器非常简单,常用的拦截器定义方式有以下两种。第一种方式是通过实现Handlerlnterceptor接口定义拦截器。
      第二种方式是通过继承Handlerlnterceptor接口的实现类F HandlerlnterceptorAdapter 定义拦截器。
      上述两种方式的区别在于,直接实现HandlerInterceptor接口需要重写HandlerInterceptor接口的所有方法;而继承HandlerInterceptorAdapter类的话,允许只重写想要回调的方法。

      自定义拦截器示例
      下面通过实现Handlerlnterceptor接口自定义拦截器,自定义拦截器的代码如下所示。public class Customlnterceptorimplements HandlerInterceptor {
      @Override
      }

      afterCompletion()方法
      afterCompletion()方法可以完成一些资源清理、日志信息记录等工作,它会在整个请求完成后执行,即视图渲染结束之后执行。
      postHandle()方法的前2个参数和preHandler()方法的前2个参数一样,分别是请求对象和响应对象。第3个参数ex是异常对象,如果处理器执行过程中出现异常,会将异常信息封装在该异赏对象中,可以在afterCompletion()方法中针对异常情况进行单独处理。需要注意的是,只有在preHandler()方法的返回值为true时
      postHandle()方法和
      afterCompletion()方法才会按上述执行规则执行。

      拦截器的配置

      配置自定义拦截器
      要使自定义的拦截器生效,还需要在Spring MVC的配置文件中进行配置。配置代码如下所示。
      mvc:interceptors

      mvc:interceptor>
      <mvc:mapping path=” /“ />
      <mvc:exclude-mapping path=””/>!–配置不需要拦截器作用的路径–>

      配置自定义拦截器代码分析
      在上述代码中,mvc:interceptors元素使用2种方式配置了拦截器,其中,使用子元素声明的拦截器,将会对所有的请求进行拦截;而使用元素声明的拦截器,会对指定路径下的请求进行拦截。
      mvc:interceptor元素的子元素mvc:mapping通过path属性配置拦截器作用的路径。如上述代码中path的属性值为“]**”,表示拦截所有路径。如果有不需要拦截的请求,可以通过元素进行配置。需要注意的是,中的子元素必须按照上述代码的配置顺序进行编写,即<mvc:mapping … >→<mvcexclude-mapping … />→<bean …/>的顺序,否则文件会报错。

      拦截器的执行流程一单个拦截器


      下面通过一个案例演示单个拦截器的执行流程,案例具体实现步骤如下所示。在

      • STEP 01
        chapter13项目的com.itheima.controller包下,创建名称为HelloController的控制器类,在HelloControlle类中定义2个方法,其中hello()方法用于正常处理客户端的请求,exp()方法被调用时产生异常。
        @Controller
        public classHelloController@RequestMapping(“hello”)public String hellook
        System.out.printIn(“HelloController…Hello”);
        return “success.jsp”;}
        @RequestMapping(“exp”)
        public Stringfexp0
        System.out.println(1/0);return “success.jsp”;}

      Spring MVC的配置文件spring-mvc.xml中添加MyInterceptor拦截器的配置,具

      • STEP 03
        体配置如下所示。 福~r0 6 出To||×中巴 在spring-mvc.xml中使用元素的子元素来配置拦截器,配置的拦截器会拦截所有映射到Handler的请求。

      拦截器的执行流程一多个拦截器

      下面在单个拦截器案例的基础上新增一个拦截器,来演示多个拦截器的执行,具体
      STEP 01
      步骤如下。在com.itheima.interceptor包中,新增拦截器MyInterceptor2,MyInterceptor2和MyInterceptor一样,也是Handlerlnterceptor接口的实现类,在MyInterceptor2中也要重写Handlerlnterceptor接口的3个方法。
      public class MyInterceptor2 implements HandlerInterceptor {
      @Override
      public boolean preHandle(HttpServletRequest request,
      HttpServletResponse response, Object handler){
      System.out.println(“Mylnterceptor2…preHandle”);
      1/对拦截的请求进行放行处理
      return true;
      }
      ll postHandle()和afterCompletion()方法省略

      在Spring MVC配置文件springmvc-config.xml中的mvc:interceptors元素内,新增拦截器MyInterceptor2,mvc:interceptors元素内的配置代码具体如下所示。


      案例:后台系统登录验证

      案例要求
      本案例主要是对用户登录状态的验证,只有登录成功的用户才可以访问系统中的资源为了保证后台系统的页面不能被客户直接请求访问,本案例中所有的页面都存放在项目的WEB-INF文件夹下,客户需要访问相关页面时,需要在服务器端转发到相关页面。如果没有登录系统而直接访问系统首贡,拦截器会将请求拦截,并转发到登录页面,同时在登录页面中给出提示信息。如果用户登录时提交的用户名或密码错误,也会在登录页面给出相应的提示信息。当已登录的用户在系统页面中单击“退出”链接时,系统同样会回到登录页面。

      了解了案例的验证规则,接下来就在项目中实现后台系统登录验证,如下所示。

      • STEP 01
        在src/main/java目录下,创建一个路径为com.itheima.pojo的包,并在包中创建User类。在User类中,声明username和password属性,分别表示用户名和密码,并定义了每个属性的getter/setter方法。
        public class User {
        private String username;
        1/用户名
        private String password;
        //用户密码
        /省略getter/setter方法
        }

      在com.itheima.controller包中,创建控制器类UserController,并在该类中定义跳转到系统首页、跳转到登录页面、跳转到订单信息页面、用户登录和用户退出五个方法。UserController类的部分代码如下所示。

      • STEP 02
        @Controller
        public class UserController {
        /跳转到系统首页,跳转到登录页面,跳转到订单信息页面,用户登录省略@RequestMapping(“/logout”)
        public String logout(HttpSession session){/用户退出session.invalidate();//清除Session
        return “redirect:tologin”;//退出登录后重定向到登录页面

      文件上传

      文件下载

      案例:文件上传和下载

      常用方式整合思路

      项目基础结构搭建

      Spring和Mybatis整合

      Spring和Spring MVC整合

      纯注解方式整合思路

      纯注解SSM框架整合

      系统概述

      数据库设计

      系统环境搭建

      用户登录

      实现登录验证

      注销登录

      新书推荐

      图书借阅

      当前借阅

      借阅记录

      访问权限控制

      扩展01 -云图书管理系统环境搭建

      登录的业务流程分析

      SSM整合

      • WEB-INF创建 lib包

      5.1.目的

      利用现有框架的优势,将其集中在一个项目中进行使用。

      5.2.整合框架

      5.2.1.整合Spring

      1. 依赖

      2. 添加resource并标记,添加applicationContext.xml

        1
        2
        3
        4
        5
        6
        7
        <?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">

        </beans>

        Spring配置文件

      3. 配置applicationContext.xml

      1. 开启注解扫描
      <context:component-scan base-package="edu.shifan" />
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      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
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">

      <!-- 开启注解扫描-->
      <context:component-scan base-package="edu.shifan" />
      </beans>
      1. 添加edu.shifan.pojo/controller/dao/service

      5.2.2.整合MyBatis

      1. 依赖

      2. 添加mysql.properties
        数据库连接配置

      1
      2
      3
      4
      driver=com.mysql.cj.jdbc.Driver
      url=jdbc:mysql://localhost:3306/schooldb?serverTimezone=UTC
      user=root
      pwd=root
      1. 修改applicationContext.xml

        <context:property-placeholder location=”classpath:mysql.properties” />

      2. MyBatis配置文件

      修改Spring配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:tx="http://www.springframework.org/schema/tx"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns: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
      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">
      <!-- 1、引入外部资源文件-->
      <context:property-placeholder location="classpath:mysql.properties" />

      <!-- 2、配置数据源(使用了阿里的达鲁伊连接池)-->
      <bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
      <property name="driverClassName" value="${driver}" />
      <property name="url" value="${url}" />
      <property name="username" value="${user}" />
      <property name="password" value="${pwd}" />
      </bean>

      <!-- 3、配置MyBAtis -->
      <!-- a、使用配置类配置MyBatis-->
      <bean id="config" class="org.apache.ibatis.session.Configuration">
      <!-- 开启驼峰命名法的自动转换 -->
      <property name="mapUnderscoreToCamelCase" value="true" />
      <!-- 配置日志的输出方法 -->
      <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl" />
      </bean>

      <!-- b、配置SqlSessionFactoryBean(获取SqlSessionFactoryBena)-->
      <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
      <!-- 配置MyBatis的配置内容 -->
      <property name="configuration" ref="config" />
      <!-- 配置类型别名(配置实体类所属的包) -->
      <property name="typeAliasesPackage" value="edu.shifan.pojo" />
      <!-- 配置数据源 -->
      <property name="dataSource" ref="ds" />
      <!-- 配置Mapper映射文件的自动扫描位置 -->
      <property name="mapperLocations" value="classpath:mappers/*.xml" />

      <!-- 配置分页插件-->
      </bean>

      <!-- c、配置映射接口的扫描位置-->
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="edu.shifan.dao" />
      </bean>

      <!-- 4、配置事务管理器 -->
      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="ds" />
      </bean>

      <!-- 5、配置Aop-->
      <!-- a、配置消息:配置(数据库)操作的处理方式-->
      <tx:advice id="txAdvice" transaction-manager="txManager">
      <tx:attributes>
      <!-- 执行CUD时使用事务-->
      <tx:method name="add" propagation="REQUIRED"/>
      <tx:method name="update" propagation="REQUIRED"/>
      <tx:method name="del*" propagation="REQUIRED"/>
      <!-- 查询时设置为只读-->
      <tx:method name="find*" read-only="true"/>
      </tx:attributes>
      </tx:advice>

      <!-- b、配置切点 -->
      <aop:config>
      <!-- 配置切点 -->
      <aop:pointcut id="txPointcut" expression="execution(* edu.shifan.service.*.*(..))"/>
      <!-- 将消息和切点连接在一起-->
      <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
      </aop:config>

      <!-- 6、开启注解扫描-->
      <context:component-scan base-package="edu.shifan" />

      <!-- 7、启动注解驱动-->
      <tx:annotation-driven transaction-manager="txManager" />
      </beans>

      5.2.3.整合SpringMvc

      1. 依赖

      2. 配置SpringMvc

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!-- 1、配置视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
        </bean>

        <!-- 2、开启注解扫描 -->
        <context:component-scan base-package="edu.shifan.controller" />

        <!-- 3、开启注解驱动 -->
        <mvc:annotation-driven/>
        </beans>
      3. 配置前端控制器及监听器

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        <?xml version="1.0" encoding="UTF-8"?>
        <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
        <!--1、 配置全局参数(配置Spring配置文件的位置)-->
        <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
        </context-param>

        <!--2、配置监听器 -->
        <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

        <!-- 3、配置前端控制器-->
        <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:springmvc-config.xml</param-value>
        </init-param>
        </servlet>
        <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
        </servlet-mapping>
        </web-app>

      5.3.测试

      项目搭建

      创建项目

      1. New Project-> javaEE
      • 1.8.0_131jdk
      • javaEE 8
      • tomcat 8.5.7827 D:javaEE\
      • Web Application create web.xml
      1. lib和web.xml 放置到 WEB-INF下 添加Add
      2. rescource粘贴至文件根目录 添加 Rescource Root
      3. src->创建
      • edu.shifan.pojo
      • dao
      • service
      • controller
      1. 更改数据库名称

      项目顺序

      Jsp -> Pojo -> Dao -> Mapper -> Service -> Controller

      编写列表页面

      1. Jsp
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        <%--
        Created by IntelliJ IDEA.
        User: Smithhs
        Date: 2024/6/23
        Time: 13:47
        To change this template use File | Settings | File Templates.
        --%>
        <!-- 设置页面内容类型和编码,以及使用的语言为Java -->
        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
        <!-- 定义HTML文档类型 -->
        <html>
        <head>
        <!-- 设置页面标题 -->
        <title>Title</title>
        <!-- 定义页面样式 -->
        <style>
        /* 设置表格的宽度、边框合并方式 */
        .grid{
        width: 100%;
        border-collapse: collapse;
        }
        /* 设置表格单元格和表头的边框样式 */
        .grid td,th{
        border: solid 1px cadetblue;
        }
        /* 设置表头单元格的边框样式 */
        .grid th{
        background-color: #f2f2f2;
        }
        </style>
        </head>
        <body>
        <!-- 创建一个带有类名"grid"的表格 -->
        <table class="grid">
        <!-- 创建表头部分 -->
        <tr>
        <th>编号</th>
        <th>年级</th>
        <th>说明</th>
        <th>操作</th>
        </tr>
        <!-- 创建数据行 -->
        <c:forEach items="${list}" var="l">
        <tr>
        <td>${l.gradeId}</td>
        <td>${l.gradeName}</td>
        <td>${l.memo}</td>
        <td>
        <!-- 添加修改和删除链接 -->
        <a href="load.jsp?id=${l.gradeId}">修改</a>
        <a href="delete.jsp?id=${l.gradeId}">删除</a>
        </td>
        </tr>
        </c:forEach>
        </table>
        </body>
        </html>

      2. pojo
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        package edu.shifan.dao; // 定义包名为edu.shifan.dao

        import java.io.Serializable; // 导入可序列化的接口

        /**
        * 实体类
        */
        public class Grade implements Serializable { // 定义一个名为Grade的公共类,实现Serializable接口
        private Integer gradeId; // 定义一个私有的整数类型的gradeId变量
        private String gradeName; // 定义一个私有的字符串类型的gradeName变量
        private String memo; // 定义一个私有的字符串类型的memo变量
        }
        //getter 和 setter
      3. dao
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        package edu.shifan.pojo; // 定义包名为edu.shifan.pojo

        import edu.shifan.pojo.Grade; // 导入Grade类,用于操作年级信息
        import org.springframework.stereotype.Repository; // 导入Spring框架的Repository注解,用于标记数据访问组件

        import java.util.List; // 导入Java的List接口,用于存储查询结果

        // 使用@Repository注解,表示这是一个数据访问组件
        @Repository
        public interface GradeDao {
        /**
        * 查询所有年级信息列表
        * @return 返回一个包含所有年级信息的列表
        */
        List<Grade> findAll(); // 定义一个findAll方法,用于查询所有年级信息
        }

      4. mapper mybatis.xml
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        <?xml version="1.0" encoding="UTF-8" ?>
        <!-- 声明XML文档的版本和编码 -->
        <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        <!-- 定义文档类型,指定公共标识符和系统标识符 -->
        <mapper namespace="edu.shifan.dao.GradeDao">
        <!-- 定义一个名为GradeDao的命名空间,用于区分不同的DAO接口 -->
        <!--查询所有年级信息列表-->
        <select id="findAll" resultType="Grade">
        select * from sys_grade
        </select>
        </mapper>

      5. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        package edu.shifan.service; // 定义包名为edu.shifan.service

        import edu.shifan.dao.GradeDao; // 导入GradeDao类,用于操作数据库中的Grade表
        import edu.shifan.pojo.Grade; // 导入Grade类,用于表示Grade表中的数据
        import org.springframework.beans.factory.annotation.Autowired; // 导入Autowired注解,用于自动注入依赖
        import org.springframework.stereotype.Service; // 导入Service注解,用于标识这是一个服务类

        import java.util.List; // 导入List接口,用于存储查询到的Grade数据

        // 使用@Service注解,标识这是一个服务类
        @Service
        public class GradeService {
        // 使用@Autowired注解,自动注入GradeDao对象
        @Autowired
        private GradeDao gradeDao;

        // 定义findAll方法,用于查询所有Grade数据
        public List<Grade> findAll(){
        // 调用gradeDao的findAll方法,查询所有Grade数据,并返回结果
        return gradeDao.findAll();
        }
        }
      6. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        package edu.shifan.controller; // 定义包名为edu.shifan.controller

        import edu.shifan.dao.GradeDao; // 导入edu.shifan.dao包下的GradeDao类
        import edu.shifan.service.GradeService; // 导入edu.shifan.service包下的GradeService类
        import org.springframework.beans.factory.annotation.Autowired; // 导入org.springframework.beans.factory.annotation包下的Autowired注解
        import org.springframework.stereotype.Controller; // 导入org.springframework.stereotype包下的Controller注解
        import org.springframework.stereotype.Service; // 导入org.springframework.stereotype包下的Service注解
        import org.springframework.web.bind.annotation.RequestMapping; // 导入org.springframework.web.bind.annotation包下的RequestMapping注解
        import org.springframework.web.servlet.ModelAndView; // 导入org.springframework.web.servlet包下的ModelAndView类

        @Controller // 使用Controller注解,表示这是一个控制器类
        public class GradeController { // 定义一个名为GradeController的公共类
        @Autowired // 使用Autowired注解,自动注入依赖
        private GradeService gradeService; // 定义一个私有的GradeService类型的变量gradeService

        @RequestMapping("/list") // 使用RequestMapping注解,映射请求路径为/list
        public ModelAndView list(){ // 定义一个名为list的公共方法,返回类型为ModelAndView
        ModelAndView mv =new ModelAndView("list"); // 创建一个ModelAndView对象mv,并设置视图名称为"list"
        // 查询数据并添加到对象
        mv.addObject("list",gradeService.findAll()); // 调用gradeService的findAll方法查询所有数据,并将结果添加到mv对象中,键名为"list"
        return mv; // 返回mv对象
        }
        }

      新增年级信息

      1. list.jsp添加
        新增年级信息

      2. 添加add.jsp

      • 复制list中style格式,修改450px
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        <%--
        Created by IntelliJ IDEA.
        User: Smithhs
        Date: 2024/6/23
        Time: 13:47
        To change this template use File | Settings | File Templates.
        --%>
        <!-- 设置页面内容类型和编码,以及使用的语言为Java -->
        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
        <!-- 定义HTML文档类型 -->
        <html>
        <head>
        <!-- 设置页面标题 -->
        <title>Title</title>
        <!-- 定义页面样式 -->
        <style>
        /* 设置表格的宽度、边框合并方式 */
        .grid{
        width: 450px;
        border-collapse: collapse;
        }
        /* 设置表格单元格和表头的边框样式 */
        .grid td,th{
        border: solid 1px cadetblue;
        }
        /* 设置表头单元格的边框样式 */
        .grid th{
        background-color: #f2f2f2;
        }
        </style>
        </head>
        <body>
        <form method="post" action="add">
        <table class="grid">
        <tr>
        <th>年级名称: </th>
        <td>
        <input type="text" name="gradeName"/>
        </td>
        </tr>

        <tr>
        <th>备注: </th>
        <td>
        <input type="text" name="memo"/>
        </td>
        </tr>

        <tr>
        <th colspan="2">
        <input type="submit" value="提交" />
        <input type="reset" value="重置" />

        </th>

        </tr>

        </table>
        </form>

        </body>
        </html>
      1. 修改Controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        /**
        * 跳转到add.jsp页面
        * @return
        */
        @RequestMapping("/init") // 使用RequestMapping注解,映射请求路径为/init
        public ModelAndView init(){
        ModelAndView mv =new ModelAndView("add");
        return mv; // 返回mv对象
        }
      2. 修改dao
        1
        2
        3
        4
        5
        6
        /**
        * 新增年级信息
        * @param grade
        * @return
        */
        int add(Grade grade);
      3. 修改Mapper
        1
        2
        3
        4
        <!--新增年级信息-->
        <insert id="add" parameterType="Grade">
        insert into sys_grade(grade_name,memo) values(#{gradeName},#{memo})
        </insert>
      4. 修改service
        1
        2
        3
        4
        5
        6
        7
        8
        /**
        * 新增年级信息
        * @param grade
        * @return
        */
        public int add(Grade grade){
        return gradeDao.add(grade);
        }
      5. 修改controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        /**
        * 将年级信息写入到数据库中
        */
        @RequestMapping("/add")
        public String add(Grade grade){
        gradeService.add(grade);

        return "redirect:list";
        }

      按id删除年级信息

      1. list.jsp修改
      1. 修改dao
        1
        2
        3
        4
        /**
        * 按ID删除年级信息
        */
        int delById(int id);
      2. 修改Mapper
        1
        2
        3
        4
        <!--按ID删除年级信息-->
        <delete id="delById" parameterType="int">
        delete from sys_grade where grade_id=#{id}
        </delete>
      3. 修改service
        1
        2
        3
        4
        5
        6
        /**
        * 按ID删除
        */
        public int delById(int id){
        return gradeDao.delById(id);
        }
      4. 修改controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        /**
        *按id删除年级信息
        */
        @RequestMapping("/del")
        public String del(int id){
        gradeService.delById(id);

        return "redirect:list";
        }

      按id更新年级信息

      1. 添加update.jsp
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        <%--
        Created by IntelliJ IDEA.
        User: Smithhs
        Date: 2024/6/23
        Time: 13:47
        To change this template use File | Settings | File Templates.
        --%>
        <!-- 设置页面内容类型和编码,以及使用的语言为Java -->
        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
        <!-- 定义HTML文档类型 -->
        <html>
        <head>
        <!-- 设置页面标题 -->
        <title>Title</title>
        <!-- 定义页面样式 -->
        <style>
        /* 设置表格的宽度、边框合并方式 */
        .grid{
        width: 450px;
        border-collapse: collapse;
        }
        /* 设置表格单元格和表头的边框样式 */
        .grid td,th{
        border: solid 1px cadetblue;
        }
        /* 设置表头单元格的边框样式 */
        .grid th{
        background-color: #f2f2f2;
        }
        </style>
        </head>
        <body>
        <form method="post" action="add">
        <table class="grid">
        <tr>
        <th>年级名称: </th>
        <td>
        <input type="text" name="gradeName"/>
        </td>
        </tr>

        <tr>
        <th>备注: </th>
        <td>
        <input type="text" name="memo"/>
        </td>
        </tr>

        <tr>
        <th colspan="2">
        <input type="submit" value="提交" />
        <input type="reset" value="重置" />

        </th>

        </tr>

        </table>
        </form>

        </body>
        </html>

      2. 修改dao
        1
        2
        3
        4
        5
        6
        7
        8
        9
        /**
        * 按ID修改年级信息
        */
        int update(Grade grade);

        /**
        * 按ID查询年级信息
        */
        Grade findById(int id);
      3. 修改mapper
        1
        2
        3
        4
        5
        6
        7
        8
        9
        <!--按ID更新年级信息-->
        <update id="update" parameterType="Grade">
        update sys_grade set grade_name=#{gradeName},memo=#{memo} where grade_id=#{gradeId}
        </update>

        <!--按ID查询年级信息-->
        <select id="findById" resultType="Grade" parameterType="int">
        select * from sys_grade where grade_id=#{Id}
        </select>
      4. service
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        /**
        * 按ID修改年级信息
        */
        public int update(Grade grade){
        return gradeDao.update(grade);
        }

        /**
        * 按ID查询年级信息
        */
        public Grade findById(int id){
        return gradeDao.findById(id);
        }
      5. controller
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        /**
        * 按id查询年级列表并跳转到update.jsp页面
        */
        @RequestMapping("/load")
        public ModelAndView load(int id){
        ModelAndView mv =new ModelAndView("update");
        // 查询数据并添加到对象
        mv.addObject("grade",gradeService.findById(id));
        return mv; // 返回mv对象
        }
        /**
        * 按ID更新年级列表并跳转到list.jsp页面
        */
        @RequestMapping("/update")
        public String update(Grade grade){
        gradeService.update(grade);

        return "redirect:list";
        }
      头像头像
      Smith
      人生太短,要干的事太多,我要争分夺秒
      打赏作者
      本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Smith
      文章目录
      1. 1. mybatis
        1. 1.1. 创建映射文件
        2. 1.2. 编写测试类
        3. 1.3. Mybatis配置文件的主要元素
        4. 1.4. properties元素
        5. 1.5. setting元素
        6. 1.6. typeAliases元素
        7. 1.7. mappers元素
        8. 1.8. Mybatis映射文件中的常用元素
        9. 1.9. select元素
        10. 1.10. insert元素
        11. 1.11. update元素
        12. 1.12. delete元素
        13. 1.13. sql元素
        14. 1.14. resultMap元素
        15. 1.15. 案例:员工管理系统
        16. 1.16. 动态SQL中的元素
        17. 1.17. if元素
        18. 1.18. choose、when、otherwise元素
        19. 1.19. where元素和trim元素
        20. 1.20. 更新元素
        21. 1.21. foreach元素中的属性
        22. 1.22. foreach元素迭代数组
        23. 1.23. foreach元素迭代List
        24. 1.24. foreach元素迭代Map
        25. 1.25. 案例:学生信息查询系统
        26. 1.26. 关联映射概述
        27. 1.27. 一对一查询
        28. 1.28. 一对多查询
        29. 1.29. 多对多查询
        30. 1.30. 一级缓存
        31. 1.31. 二级缓存
        32. 1.32. 案例:商品的类别
        33. 1.33. @Select注解
        34. 1.34. @Insert注解
        35. 1.35. @Update注解
        36. 1.36. @Delete注解
        37. 1.37. @Param注解
        38. 1.38. 一对一查询
        39. 1.39. 一对多查询
        40. 1.40. 多对多查询
        41. 1.41. 案例:基于MyBatis注解的学生管理程序
      2. 2. Spring
        1. 2.1. Spring概述
        2. 2.2. Spring的体系结构
        3. 2.3. Spring的入门程序
        4. 2.4. 控制反转的概念
        5. 2.5. 依赖注入的概念
        6. 2.6. 依赖注入的类型–构造方法注入
        7. 2.7. 依赖注入的类型–属性setter方法注入
        8. 2.8. 依赖注入的应用
        9. 2.9. BeanFactory
        10. 2.10. ApplicationContext接口
        11. 2.11. Bean的配置
        12. 2.12. 构造方法实例化
        13. 2.13. 静态工厂实例化
        14. 2.14. 实例工厂实例化
        15. 2.15. singleton作用域
        16. 2.16. prototype作用域
        17. 2.17. 基于XML的装配
        18. 2.18. 基于注解的装配
        19. 2.19. 自动装配
        20. 2.20. Bean的生命周期
        21. 2.21. Spring AOP概述
        22. 2.22. Spring AOP术语
        23. 2.23. JDK动态代理
        24. 2.24. CGLib动态代理
        25. 2.25. 基于XML的AOP实现
        26. 2.26. 基于注解的AOP实现
        27. 2.27. JdbcTemplate概述
        28. 2.28. Spring JDBC的配置
        29. 2.29. excute()方法
        30. 2.30. update()方法
        31. 2.31. query()方法
        32. 2.32. 事务管理的核心接口
        33. 2.33. 事务管理的方式
        34. 2.34. 基于XML方式的声明式事务
        35. 2.35. 基于注解方式的声明式服务
        36. 2.36. 案例:实现用户登录
      3. 3. SpringMVC
        1. 3.1. SpringMVC概述
        2. 3.2. SpringMVC特点
        3. 3.3. SpringMVC入门程序
        4. 3.4. Spring MVC FirstController!
        5. 3.5. SpringMVC工作原理
        6. 3.6. DispatcherServlet类
        7. 3.7. @Controller
        8. 3.8. @RequestMapping注解的使用
        9. 3.9. @RequestMapping注解的属性
        10. 3.10. 请求映射方式
        11. 3.11. 数据绑定
        12. 3.12. 默认类型数据绑定
        13. 3.13. 简单数据类型绑定
        14. 3.14. POJO绑定
        15. 3.15. 自定义类型转换器
        16. 3.16. 数组绑定
        17. 3.17. 集合绑定
        18. 3.18. 复杂POJO绑定一属性为对象类型的数据绑定
        19. 3.19. 复杂POJO绑定一属性为List类型的数据绑定
        20. 3.20. 复杂POJO绑定一属性为Map类型的数据绑定
        21. 3.21. JSON数据绑定
        22. 3.22. 返回值为void类型的页面跳转
        23. 3.23. 返回值为String类型的页面跳转一不携带数据
        24. 3.24. 返回值为String类型的页面跳转一携带数据
        25. 3.25. 返回值为ModelAndView类型的页面跳转
        26. 3.26. 普通字符串的回写
        27. 3.27. JSON数据的回写一对象数据转换成JSON数据后的回写
        28. 3.28. JSON数据的回写一集合数据转换成JSON数据后的回写
        29. 3.29. 简单异常处理器
        30. 3.30. 自定义异常处理器
        31. 3.31. 异常处理注解
        32. 3.32. 拦截器概述
        33. 3.33. 拦截器的配置
        34. 3.34. 拦截器的执行流程一单个拦截器
        35. 3.35. 拦截器的执行流程一多个拦截器
        36. 3.36. 案例:后台系统登录验证
        37. 3.37. 文件上传
        38. 3.38. 文件下载
        39. 3.39. 案例:文件上传和下载
        40. 3.40. 常用方式整合思路
        41. 3.41. 项目基础结构搭建
        42. 3.42. Spring和Mybatis整合
        43. 3.43. Spring和Spring MVC整合
        44. 3.44. 纯注解方式整合思路
        45. 3.45. 纯注解SSM框架整合
        46. 3.46. 系统概述
        47. 3.47. 数据库设计
        48. 3.48. 系统环境搭建
        49. 3.49. 用户登录
        50. 3.50. 实现登录验证
        51. 3.51. 注销登录
        52. 3.52. 新书推荐
        53. 3.53. 图书借阅
        54. 3.54. 当前借阅
        55. 3.55. 借阅记录
        56. 3.56. 访问权限控制
        57. 3.57. 扩展01 -云图书管理系统环境搭建
        58. 3.58. 登录的业务流程分析
        59. 3.59.
      4. 4. SSM整合
        1. 4.1. 5.1.目的
        2. 4.2. 5.2.整合框架
          1. 4.2.1. 5.2.1.整合Spring
          2. 4.2.2. 5.2.2.整合MyBatis
          3. 4.2.3. 5.2.3.整合SpringMvc
        3. 4.3. 5.3.测试
      5. 5. 项目搭建
        1. 5.1. 创建项目
        2. 5.2. 项目顺序
        3. 5.3. 编写列表页面
        4. 5.4. 新增年级信息
        5. 5.5. 按id删除年级信息
        6. 5.6. 按id更新年级信息
      引用到评论