JavaWeb到SSM整合过程
创始人
2025-05-29 10:58:43
0

本篇分别讲解了Mybatis、SpringMVC、Spring,最后又将三者整合,成就SSM框架

每个内容都是比较细的,适用于新手,也适用于会写代码但不知道SSM每一部分如何整合的同学。

本文需要一些初始代码和sql(比如一套UserInfo实体类和user_info数据库以及对应的Servlet代码),这部分很少,大家用自己原有的JavaWeb项目也可以。如有需要留言邮箱即可,我看到会发过去

看完本篇你可以了解到什么:

  1. JavaWeb变成SSM的过程
  2. SSM每一部分的含义
  3. SSM所有配置,SSM项目建立

本文内容很不错,值得一看。有不了解的地方可以留言

文章目录

    • 一、JavaWeb项目
      • 1. 创建项目
      • 2. 添加依赖
      • 3. 配置tomcat
      • 4. 启动EL表达式配置
      • 5. 配置lombok
        • 5.1 添加依赖
        • 5.2 设置idea支持lombok
        • 5.3 常用注解
    • 二、Mybatis
      • 1. 前言、理论
        • 1.1 原生CRUD的问题
        • 1.2 ORM
        • 1.3 介绍
      • 2. 添加依赖
      • 3. 配置文件
        • 3.1 核心文件配置
          • 1. properties标签
          • 2. typeAliases标签
        • 3.2 映射文件配置
          • 1. 新建bean对应的xml
          • 2. 核心文件配置
      • 4. 初试sql
        • 4.1 映射文件
        • 4.2 Dao层代码
      • 5. 数据映射问题
      • 6. 日志管理Log4j
      • 7. 传统模式开发
        • 7.1 返回(单个\多个\map)
        • 7.2 mybatis参数传递
        • 7.3 insert时主键回填
      • 8. 基于接口代理模式开发
        • 8.1 修改代码、两模式对比
        • 8.2 参数
          • 1. 单参数
          • 2. 多参数
        • 8.3 模糊查询
      • 9. 动态SQL
        • 9.1 IF标签
        • 9.2 choose when
        • 9.3 set标签
        • 9.4 trim标签
        • 9.5 foreach标签
      • 10. 一对一查询
        • 总结
      • 11. 一对多查询
        • 总结
      • 12. 多对多
      • 13. 级联查询
        • **懒汉加载和饿汉加载**
      • 14. Mybatis注解开发
    • 三、SpringMVC
      • 1. 分析理解MVC
      • 2. SpringMVC引入
      • 3. SpringMVC初步配置
      • 4. 静态资源问题
      • 5. 常用注解
        • 5.1 @RequestMapping
        • 5.2 @PathVariable与RESTFUL风格
        • 5.3 @RequestHeader
        • 5.4 @CookieValue
      • 6. 参数注入
        • 6.1 乱码问题
        • 6.2 **时间Date**
      • 7. 响应返回
        • 7.1 转发和重定向
        • 7.2 响应Json
      • 8. 数据校验
      • 9. 文件上传
      • 10. 文件下载
      • 11. 拦截器
    • 四、Spring
      • 1. IOC
        • 1.1 XML方式
        • 1.2 注解方式
        • 1.3 配置类方式
      • 2. AOP
    • 五、SSM整合
      • 1. M
      • 2. Spring加入M
      • 3. Spring容器配置
      • 4. 全部配置文件
        • 4.1 Spring配置
        • 4.2 SpringMVC配置
        • 4.3 web.xml
        • 4.4 依赖
        • 4.5 其他

一、JavaWeb项目

1. 创建项目

使用maven骨架创建,选择webapp,如图所示

在这里插入图片描述

在创建完成后,会有从仓库中拉取web骨架,需要等一段时间,注意maven已经是否已经配置了国内的数据源。

完成后是这样的:在这里插入图片描述

这样没有java文件夹,右击新建文件夹,会有提示直接将java文件夹创建出来。

在这里插入图片描述

2. 添加依赖

javax.servletjavax.servlet-api4.0.1mysqlmysql-connector-java8.0.27javax.servletjstl1.2junitjunit4.12

3. 配置tomcat

不赘述

4. 启动EL表达式配置

使用骨架创建的web项目是不能用EL表达式的

原因:idea给我们提供的代码模板默认使用的2.3版本,可以修改版本,也可以在jsp中添加代码,如下所示

解决方案选择其一

1.在jsp页面中增加
<%@ page isELIgnored="false"%>2.修改web.xml



5. 配置lombok

5.1 添加依赖


org.projectlomboklombok1.18.26provided

5.2 设置idea支持lombok

在这里插入图片描述

在这里插入图片描述

开启编译注解功能在这里插入图片描述

5.3 常用注解

@Setter/@Setter :注解在属性上;为属性提供 setter/getter 方法
@ToString:为类生成 tostring 方法,输出类名和所有属性
@EqualsAndHashCode:生成 equal 和 hashCode 方法,可以通过参数,排除一些属性
@NoArgsConstructor :注解在类上;为类提供一个无参的构造方法
@RequiredArgsConstructor:会生成一个包含常量(final 修饰),和标识了 NotNull 的变量的构造方
法 。
@AllArgsConstructor :注解在类上;为类提供一个全参的构造方法
@Data ;注解在类上相当于 @Getter、@Setter、@ToString 等几个注解的合集。
@Log4j :注解在类上,为类提供一个属性名为 log 的 log4j 日志对象

二、Mybatis

1. 前言、理论

1.1 原生CRUD的问题

  1. 编码过于繁琐在这里插入图片描述

  2. 结果集映射成对象需要我们自己编写代码

  3. 性能一般

  4. Sql语句与Java代码高度耦合

1.2 ORM

Object-Relationl Mapping,对象关系映射,它的作用是在关系型数据库和对象之间作一个映射,这样我们在具体的操作数据库的时候,只要像平时操作对象一样操作它就可以了,ORM框架会根据映射完成对数据库的操作,就不需要再去和复杂的SQL语句打交道了。

Hibernate

是全自动ORM框架

自动进行对象和结果集的转换;sql自动生成和执行 hql

重量级;更新慢

Mybatis

是半自动ORM框架

开发人员手动写sql

轻量级;更新快;灵活性好

1.3 介绍

MyBatis 本是Apache的一个开源项目iBatis, 2010年这个项目由Apache Software Foundation 迁移到了Google Code,且改名为MyBatis 。2013年11月迁移到GitHub。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

官网:https://mybatis.org/mybatis-3/zh/index.html

2. 添加依赖

org.mybatismybatis3.5.11

3. 配置文件

3.1 核心文件配置

在resourdces目录下新建:sqlMapConfig.xml,如下图所示

在这里插入图片描述

内容可在官网处查看,或下面代码,官网:https://mybatis.org/mybatis-3/zh/getting-started.html



         一会此处需要修改
一会需要添加两个标签:
1.properties    配置文件,存放配置参数
2.typeAliases	自动扫描beans包,自动将类起别名,别名是:类名首字母小写
1. properties标签

可以直接在在value中配置,也可以单独放到一个properties文件中,若希望将配置数据放到jdbc.properties

在xml中添加

    

如图所示

在这里插入图片描述

jdbc.properties文件内容:

jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/数据库名?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=root
jdbc_password=root
2. typeAliases标签

大概的意义是,如果不加typeAliases,那么在映射文件中需要将实体类的地址写全,也就是com.qst.binzhou2022.beans.UserInfo

加上之后,只需要写:userInfo 就知道对应的实体类是哪一个

具体作用很快就描述

    
在这里插入图片描述

3.2 映射文件配置

1. 新建bean对应的xml

在resources下新建相同的包名创建一个与user_info表对应的xml映射文件;

注意:

  1. 路径必须和beans一致
  2. resources中创建时一级一级新建文件夹,不要直接用com.qst.binzhou2022这样,resource下会将.作为文件名的一部分
在这里插入图片描述

UserInfoMapper.xml内容: 摘至官网

(后面再自己写需要的内容)



 
注意映射文件的命名空间:
 
在传统模式开发下,命名空间写什么都可以,没有用,如果觉得爆红不好看可以写成beans的目录,idea就不会爆红了(但注意,实际没有其他用处)

具体应该写什么在 基于接口代理模式开发中说明

在这里插入图片描述

2. 核心文件配置

刚刚的核心的文件是这样的:




需要修改:对应刚刚新建的xml

4. 初试sql

4.1 映射文件

再映射文件中编写查询sql



由于我们之前配置了typeAliases标签,可以这样写,resultType代表返回类型



4.2 Dao层代码

	public  List getUserInfo(UserInfo userInfo) throws SQLException {boolean flag = false;// 获取主配置文件String resource = "sqlMapConfig.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}// 由建造者模式建筑一个工厂里类SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 由工厂类建造一个产品SqlSession sqlSession = sqlSessionFactory.openSession();// 以上完全可以写一个工具类,可复用代码--单例模式// sqlSession有很多方法,因为xml中写的是查询多条/* 这里怎么知道findUserInfoAll呢?1.加载了主配置文件2.主配置文件的mappers标签中写明了映射文件的地址*/List   userInfoList =  sqlSession.selectList("findUserInfoAll");for (UserInfo info : userInfoList) {System.out.println(info);}return userInfoList;}

5. 数据映射问题

目前为止,是可以查出来数据了,但是又有些问题,运行此时的代码,会发现查出来的数据全是null

原因:虽然从数据库中查询到了数据,但是数据的列名是这样的:

user_id user_name user_sex user_pass

而我们定义了resultType中返回的是User实体类,实体类的属性是这样的:

userId userName userSex userPass

Mybatis并不能自动的将user_id映射成userId

解决思路:主要是让Mybatis知道user_id到底是哪一个数据,它该放到哪里,那么需要我们制定映射方案

解决方案1:为列起一个别名,让查出来的user_id改名为userId

    备注:通过使用 SQL,可以为表名称或列名称指定别名。
SELECT column_name AS alias_name FROM table_name;
as可省略

解决方案2:我们来指定一个映射方案

写一个resultMap标签 设置idtype属性,在内部编写映射方案

子元素说明:

  • id元素 ,用于设置主键字段与实体类属性的映射关系
  • result元素 ,用于设置普通字段与实体类属性的映射关系
    

6. 日志管理Log4j

在开发过程中我们很多时候需要对sql进行调试,查看参数或者查看最后的sql什么样子,之前我们可以使用debug或者使用在控制台打印的方式查看,但是现在我们放到了xml中??怎么跟进sql??

需要借助日志工具Log4j

添加依赖:

        org.apache.logging.log4jlog4j-core2.20.0

resources下添加log4j2.xml
具体log4j2的配置可自行了解



在这里插入图片描述

7. 传统模式开发

普通模式,也称为传统DAO模式,就是在传统DAO模式下,定义接口和实现类,如 interface EmpDao class EmpDaoImpl implements EmpDao. 在实现类中,用SQLSession对象调用 select insert delete update 等方法实现.目前极为少见.在传统模式下,我们需要知道SqlSession对象 实现CURD和 参数传递的处理

7.1 返回(单个\多个\map)

1.1 返回单个 selectOne

修改映射文件:

    

修改dao代码:

		List userInfoList = new ArrayList<>();UserInfo userInfoResult = sqlSession.selectOne("findUserInfoSingle");userInfoList.add(userInfoResult);userInfoList.forEach(System.out::println);

1.2 返回对象List集合 selectList,在4.1与4.2中

1.3 返回对象Map集合

		List userInfoList = new ArrayList<>();Map map = sqlSession.selectMap("findUserInfoAllMap", "userId");Set userIds = map.keySet();for (Integer s : userIds) {System.out.println(s + " ---  " + map.get(s));}
注意:1. map键值的类型2. selectMap属性(1)第一个是映射文件中查询标签的id(2)第二个属性指定 键
    
或者

7.2 mybatis参数传递

2.1 传递单个值参数

parameterType 在有参数情况下也是可以省略不写 mybatis 可以根据实际情况自动判断;定义号参数后在标签内sql中可以使用 #{} 和 ${} 进行占位

并且在单个值传递的时候{}中值 随便写

#{} 代表mybatis底层使用的preparedStatment语句对象,参数使用?作为占位符处理  	---	常用
${} 代表mybatis底层使用Statment语句对象,参数是以字符串拼接的形式设置
    
        userInfoList = sqlSession.selectList("getUserInfo", "1");selectList支持第二个参数

2.2 传递多个参数使用Map

        Map rap = new HashMap<>();rap.put("userId", "1");rap.put("userName", "a");userInfoList = sqlSession.selectList("getUserInfoByMap", rap);selectList第二个参数放map
    

2.3 传递对象参数

        userInfo.setUserId(1);userInfo.setUserName("a");UserInfo userInfo1 = sqlSession.selectOne("getUserInfoByBean", userInfo);System.out.println(userInfo1.toString());
    

Mybatis很灵活, 单个实体类不加parameterType也可以 (单个值随便写)

注意:在xml中<等符号有特殊含义,不能直接使用,需要转义

比如:

在这里插入图片描述

sql是没有问题的,但在xml中需要改成

    

网站:https://www.w3school.com.cn/html/html_entities.asp

空格  
<小于号<<
>大于号>>
&和号&&
"引号""
撇号' (IE不支持)'
分(cent)¢¢
£镑(pound)££
¥元(yen)¥¥
欧元(euro)
§小节§§
©版权(copyright)©©
®注册商标®®
商标
×乘号××
÷除号÷÷

7.3 insert时主键回填

在实验中我们发现主键是自增的,我们并没有传入主键,那我们如何获取到插入的数据的主键呢??

  1. insert into user_info (user_name, user_pass, user_sex, user_age)VALUES (#{userName}, #{userPass}, #{userSex}, #{userAge})
    添加几个参数useGeneratedKeys keyColumn keyProperty
    
  2.     select @@identityinsert into user_info (user_name, user_pass, user_sex, user_age)VALUES (#{userName}, #{userPass}, #{userSex}, #{userAge})添加selectKey标签
    

8. 基于接口代理模式开发

8.1 修改代码、两模式对比

先解释再看下方步骤,舍弃dao

  1. Mybatis要求使用mapper作为接口文件夹名称(保证了编译后在同一文件夹),且接口名需要和映射文件同名。

  2. mapper文件夹下放接口,接口方法名和映射文件中的id名相同

  3. 映射文件的命名空间namespace, 在3.2.1中大概说明了命名空间,在基于接口代理模式开发时,需要指定mapper文件夹下的接口文件

-

下面是修改过程

在com.qst.binzhou2022目录下新增一个mapper包;此处要注意mapper包路径要跟resources下的mapper一致

在这里插入图片描述

在com.qst.binzhou2022.mapper下新增一个接口文件 命名为UserInfoMapper, 并且新增一个方法(方法名要与映射文件中的sql的id一致)

在这里插入图片描述

修改映射文件的命名空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XaqEm9Xn-1678960789411)(D:\其他\桌面\毕设\JavaWeb项目.assets\image-20230302223000061.png)]

service层:修改代码

    /*
Mybatis接口代理*/public boolean getUserInfo(UserInfo userInfo) throws SQLException, IOException {//获取String resource = "sqlMapConfig.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 通过getMapper方法获得UserInfoMapper接口的实现类的对象// 具体是怎么实现的我们不需要管UserInfoMapper userInfoMapper = sqlSession.getMapper(UserInfoMapper.class);boolean flag = false;userInfo = userInfoMapper.getUserInfoById(1);   // 这个在8.2中System.out.println(userInfo);return flag;}
接口模式开发总结1. 接口的名字和Mapper映射文件名字必须保持一致2. Mapper映射文件的namespace必须是接口的全限定类名3. sql语句的id必须与接口对应方法的名称一致4. 映射文件应该和接口编译之后放在同一个目录下优点1. 接口使各个模块之间更加规范2. 接口中方法参数由开发人员制定,更加灵活3. 通过代理模式由mybatis提供接口的实现类对象 无需编写实现类

8.2 参数

1. 单参数
    注:实际上单参数怎么写都行,可以没有parameterType,#{}里面也可以随便写
mapper接口方法:
UserInfo getUserInfoById(int userId);
service层:
/*
Mybatis接口代理*/public boolean getUserInfo(UserInfo userInfo) throws SQLException, IOException {//获取String resource = "sqlMapConfig.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();// 通过getMapper方法获得UserInfoMapper接口的实现类的对象// 具体是怎么实现的我们不需要管UserInfoMapper userInfoMapper = sqlSession.getMapper(UserInfoMapper.class);boolean flag = false;userInfo = userInfoMapper.getUserInfoById(1);   // 这个在8.2中System.out.println(userInfo);return flag;}
2. 多参数

使用多参数出现的问题:

  1. 映射文件上面怎么写?一个参数可以用parameterType定义类型,多个怎么办
  2. 方法参数名和映射文件中怎么对应?
  3. mapper接口直接写上多个参数吗?

*解决方案一:可以使用arg (arg0 arg1 arg2 …) **

在mapper接口方法中正常写参数,参数名也正常起

而映射文件中对应的参数就是,arg (arg0 arg1 arg2 …)*
下标从0开始

解决方案二:可以使用param(param1 param2 param3 …)*

同方案一,只是下标从1开始

在这里插入图片描述

在这里插入图片描述

映射文件:   这两种是固定写法,Mybatis底层帮我们处理了,直接用,但是缺点很明显,可读性差

解决方案三:可以使用@Param注解标明参数

在mapper接口中:

    UserInfo getUserInfoByIdAndName(@Param("user_id") int userId,@Param("user_name") String username);

映射文件:

    

这样一来,user_iduserId就可以对应上了

解决方案四:定义map和对象传入

map:与7.2.3相同,#{}中直接写map定义的键就可以对应,如果参数多,这个方法也很好

对象:与7.2.4相同,#{}中直接写实体类的变量名

8.3 模糊查询

在上面的例子中我们使用的是= 如果我们希望用户名模糊查询呢?? 如果拼接字符串?

解决方案一:在传入参数的时候拼接%%

映射文件:
接口: 
List getUserLikeName(int userId);
service:List list;list = userInfoMapper.getUserLikeName("%"+"a"+"%");list.forEach(System.out::println);

缺点很明显,需要在service中用字符串的形式拼接,也不利于可读性

解决方案二:在sql中使用concat函数拼接%%

    

9. 动态SQL

一个表字段比较多的时候,根据不同的情况会需要不同的查询条件进行查询;需要我们写很多sql语句

MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中使用特定标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。

9.1 IF标签

好处,此条SQL支持查询多种条件,如果我们需要根据name和pass查询,那么在userInfo赋值时只给它这俩属性的值

那么,如果根据其他条件查询,同样可以使用这个

第一种:为什么有一个1=1?
根据条件,第一条判断userName可能为空,语句不执行,那么and如何放呢,第一条判断中加不加and呢,所以在最前面加一个1=1恒true,后面放心加and
第二种:去掉1=1,用框架内部的方法解决上述问题,添加where标签

在这里插入图片描述

接口中的代码就不放了,和之前一样的操作。

9.2 choose when

IF条件直接换成when

service代码同IF

    

与IF的区别

通过查看log,可以看出虽然在service中给userInfo定义了两个参数,但是在choose中只判断了一个,而IF中两个都进行了判断

即:choose when结构在找到满足条件拼接sql后余下的条件即便满足也不会拼接(只判断第一个条件)

在这里插入图片描述

9.3 set标签

service写法:

        userInfo.setUserName("a");userInfo.setUserSex("1");userInfo.setUserId(2);int i = userInfoMapper.updateUserById(userInfo);sqlSession.commit();记得要执行sqlSession.commit();否则数据不会更改

正常的写法:

    update user_infoset user_name = #{userName},user_sex  = #{userSex},user_pass = #{userPass},user_age  = #{userAge}where user_id = #{userId}

使用set标签

    update user_infouser_name = #{userName},user_sex = #{userSex},user_pass = #{userPass},user_age = #{userAge},where user_id = #{userId}

可以发现执行时自动去掉了无用的

在这里插入图片描述

9.4 trim标签

trim是用来动态拼接字符的一种特殊标签;set where都是一些特殊的trim

有一些属性

			prefix 要增加的前缀prefixOverrides 要去除的前缀suffix 要增加的后缀suffixOverrides 要去除的后缀

演示,如果要实现9.3中的set

前缀增加set,后缀去掉,
update user_infouser_name = #{userName},user_sex = #{userSex},user_pass = #{userPass},user_age = #{userAge},where user_id = #{userId}那么演示执行的sql是:update user_info set user_name = 'a', user_sex = '1', user_age = 100 where user_id = 2
执行成功

如果改为prefixOverrides呢,前缀去除,

        

结果是失败的:

在这里插入图片描述

可以发现sql语句出了问题,where前面的,没有去掉

其实如果是这样,是可以的:

需要根据具体,放的位置,来定义用哪一个属性,对于set标签,,放前面后面都可以

    update user_info,user_name = #{userName},user_sex = #{userSex},user_pass = #{userPass},user_age = #{userAge}where user_id = #{userId}

9.5 foreach标签

sql时会遇到下面情况:

        select * from user_info where user_name = 'a' or user_name = 'b' or user_name = 'c'
或select * from user_info where user_name in ('a','b','c')
 collection=""  遍历的集合或者是数组参数是数组,collection中名字指定为array参数是List集合,collection中名字指定为listseparator=""   多个元素取出的时候 用什么文字分隔open=""        以什么开头close=""       以什么结尾item=""        中间变量名
service:List list = new ArrayList<>();list.add("a");list.add("b");list.add("c");List userInfoList= userInfoMapper.getUserInfoForeach(list);userInfoList.forEach(System.out::println);
接口:List getUserInfoForeach(List list);
    

在这里插入图片描述

10. 一对一查询

业务需求是查询某个学生信息和学生所在班级信息

在这里插入图片描述

在这里插入图片描述

学生用StuInfo对象存储,班级用ClassInfo对象存储

学生和班级是一对一的关系

若通过查询学生且查询他班级的信息,结果如何保存呢

可以在StuInfo属性中加入ClassInfo对象,这样java对象方面的问题就解决了

Mybatis的问题:

之前虽然有resultMap映射,可以也仅是映射了StuInfo的属性,ClassInfo的属性怎么办呢

有解决方案,在内部加入association标签

好了,简单介绍完毕,开始准备工作:

准备工作1:创建相应的实体类

准备工作2:创建StuInfoMapper接口

准备工作3:创建映射文件



    注意这里			注意这里

准备工作4:编写StuInfoService

可以发现,StuInfoServiceUserInfoService中获取sqlSession的代码是重复的,可以单独做一个工具类

package com.qst.binzhou2022.utils;import lombok.Data;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;/*** @author spengda* @date 2022/4/51:34*/
@Data
public class MybatisUtils {SqlSessionFactory sqlSessionFactory;private static MybatisUtils instance = new MybatisUtils();private MybatisUtils (){//获取String resource = "sqlMapConfig.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}public static MybatisUtils getInstance() {return instance;}public SqlSession getSqlSession(){SqlSession sqlSession = sqlSessionFactory.openSession();return sqlSession;}public Object getMapper(Class classInfo){return  getSqlSession().getMapper(classInfo);}}

那么service:

package com.qst.binzhou2022.services;import com.qst.binzhou2022.beans.StuInfo;
import com.qst.binzhou2022.beans.UserInfo;
import com.qst.binzhou2022.mapper.StuInfoMapper;
import com.qst.binzhou2022.mapper.UserInfoMapper;
import com.qst.binzhou2022.utils.MybatisUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;public class StuInfoService {/*
Mybatis接口代理*/public StuInfo getStuInfo(String stuCode) throws SQLException, IOException {StuInfoMapper stuInfoMapper = (StuInfoMapper) MybatisUtils.getInstance().getMapper(StuInfoMapper.class);StuInfo stuInfo = stuInfoMapper.getStuInfo(stuCode);System.out.println(stuInfo.toString());System.out.println(stuInfo.getClassInfo().toString());return stuInfo;}}

准备工作5:随便编写一下jspservlet

那么结果:

在这里插入图片描述

总结

  1. 映射文件的resultMap映射标签添加association标签,作为另一个表的映射 , 处理一对一
    注意association的属性

property 类的属性名
javaType  用哪个类的对象给属性赋值
  1. 在实体类中增加对应的类关系;以属性体现
public class StuInfo {private int stuId;private String stuCode;private String stuName;private int classId;private ClassInfo classInfo; 			// 这里哦
}

这样一看,更改的地方并不多,也不困难

11. 一对多查询

业务需求是查询某个班级的信息和这个班级所有学生的信息

对应sql

        select *from class_infoleft join stu_info si on class_info.class_id = si.class_idwhere si.class_id = #{classId}

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zfhwauuk-1678960789413)(D:\其他\桌面\毕设\JavaWeb项目.assets\image-20230306164604722.png)]

准备工作一:增加ClassInfo属性:List

准备工作二:创建ClassInfoMapper接口

准备工作三:创建映射文件





准备工作四:编写ClassInfoService

package com.qst.binzhou2022.services;import com.qst.binzhou2022.beans.ClassInfo;
import com.qst.binzhou2022.beans.StuInfo;
import com.qst.binzhou2022.mapper.ClassInfoMapper;
import com.qst.binzhou2022.mapper.StuInfoMapper;
import com.qst.binzhou2022.utils.MybatisUtils;import java.io.IOException;
import java.sql.SQLException;public class ClassInfoService {/*
Mybatis接口代理*/public ClassInfo getClassInfo(int classId) throws SQLException, IOException {ClassInfoMapper classInfoMapper = (ClassInfoMapper) MybatisUtils.getInstance().getMapper(ClassInfoMapper.class);ClassInfo classInfo = classInfoMapper.getClassJoinStu(classId);return classInfo;}}

准备工作五:编写相应的jsp和servlet

效果演示:

在这里插入图片描述

总结

  1. 在实体类中增加对应的类关系;以属性体现;注意是集合
public class ClassInfo {private int classId;private String className;private String classGrade;List stuInfoList;
}
  1. collection标签 处理一对多
       property 类的属性名javaType  可以省略;也可以写集合类型ofType 用哪个类的对象给属性赋值(list中的类型)

12. 多对多

查询某个学生信息和该学生所选课信息

对应关系

在这里插入图片描述

实体类对应关系

在这里插入图片描述

编写映射文件SQL

    因为Stu和StuCourse是一对多,所以用collection
因为StuCourse和Course是一对一,所以用association

其他的:增加实体类属性,编写接口、service方法,修改servlet、jsp。这些就不写了,和前面俩差不多。

13. 级联查询

级联查询,是利于数据库表间的外键关联关系进行自动的级联查询操作,除了实体类增加关联属性外,还需要在映射文件中进行配置。

以一对多为例;根据查询一个班级的所有学生信息;我们之前写的sql是通过多表连接查询;但是实际上我们可以将该SQL变更为2个sql

1、先查询班级信息

2、在根据班级信息查询学生信息

        select *from class_infoleft join stu_info si on class_info.class_id = si.class_idwhere si.class_id = #{classId}

将上述sql转为两个sql,上述sql的结果是下面两个sql结果之和,他们都用classId查询

        select * from class_info where class_id = #{classId}select * from stu_info where class_id = #{classId}发现classId是关键数据
  1. 首先编写StuInfoMapper增加根据class_id查询学生信息的sql
    映射在自己内部实现,可以这样,也可以用resultMap
  1. 编写ClassInfoMapper中 根据classid查询班级信息的sql;这里用到级联查询 所以并不能使用简单的返回类型

因为我们的目的是和一对多一样,要classInfolist,所以不是简单的resultMap

    

发现,实际上是执行了两条SQL在这里插入图片描述

懒汉加载和饿汉加载

默认是饿汉模式:前面我们使用的一个级联查询的方式是饿汉加载;无论如何都会进行2次查询

懒汉加载(延迟加载)

延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中。

延迟加载的好处:先从单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在时间上的分配更加均匀,而且单表要比关联查询多张表速度要快。

设置属性fetchTypelazy

    

验证:第一种虽然使用了classInfo, 但没有对StuInfoList做调用,所以仅需要一个sql

在这里插入图片描述

第二种:调用了StuInfoList,那么在调用时去查询,即实现了延迟查询

在这里插入图片描述

		javaType="list"  实体类的属性数据类型column="class_id"  给另一个SQL语句传入的参数列jdbcType="INTEGER" 参数对应JDBC的数据类型(实验,不加也没事)fetchType="eager"  加载方式 eager饿汉加载  lazy延迟加载

aggressiveLazyLoading属性介绍 (翻译:有点亢奋的懒😎)

在懒加载模式下;只有当我们调用到级联查询后者数据的时候才会按需加载;

那如果我们希望只要该对象被使用就加载该对象所有数据呢??

使用aggressiveLazyLoading属性;该属性默认false

主配置文件中进行配置

在这里插入图片描述

效果:

在这里插入图片描述

在这里插入图片描述

以前是按需加载,现在是用即加载。

14. Mybatis注解开发

在接口中方法上添加注解

在简单sql时,可以直接这样,方便

    @Select("select class_id classId, class_name className  from class_info where class_id=#{classId}")ClassInfo getById(int classId);

甚至,如果全用注解,可以不用映射文件,直接一个接口
需要在核心文件中添加接口的地址(类用.,resource用/),此方法了解即可

在这里插入图片描述

三、SpringMVC

1. 分析理解MVC

Mybatis替换的是 DAO连接数据库
SpringMVC替换的是 前端和servlet这部分

大致:
model:数据库的数据,也就是类
view:展示的界面
controller:控制数据展示到视图上

SpringMVC工作流程

在这里插入图片描述

在这里插入图片描述

HandlerMapping处理映射处理器

DispatcherServlet不处理任何请求,但是接收全部请求
HandlerMapping处理请求,处理器HandlerURL的映射关系
这样DispatcherServlet就知道了往哪里转发请求,即给哪个Handler

在这里插入图片描述

HandlerAdapter处理器适配器

调用Handler(或说servlet),中间有个适配器是为了解耦,Handler是我们编写的业务代码(Controller)

ViewReslover视图解析器

返回视图时做一个解析操作,在返回真正的视图字符串之前找到视图字符串在哪里,jsp?xml?

2. SpringMVC引入

依赖:

   org.springframeworkspring-webmvc5.3.18

配置前端控制器,dispatcherServlet就是一个Servlet,之前说了,dispatcherServlet需要接收全部请求
注意:/拦截所有的非jsp/*拦截所有请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Emb2FdBL-1678960789414)(D:\其他\桌面\毕设\JavaWeb项目.assets\image-20230312161534737.png)]


dispatcherServletorg.springframework.web.servlet.DispatcherServlet这里一会需要加东西哦dispatcherServlet/上面很清晰,就是一个正常的servlet配置,(一个servlet配置俩标签)

设置mvc配置文件,这样创建可以直接生成一些属性

在这里插入图片描述

配置文件中是对Springmvc各个组件配置的,现在我们什么都没有配置,但是需要加载这个xml配置文件,即springmvc怎么知道这个文件是他的配置文件呢

DispatcherServlet会默认加载/WEB-INF下的指定名字的配置文件(默认名字为《Servlet-name》的值 拼接上 -servlet.xml)
例如在我们例子中会拼接/WEB-INF/dispatcherServlet-servlet.xml — 不明白!!!!!!!!!!!

正常情况下DispatcherServlet在初始化的时候我们可以通过给其contextConfigLocation传递参数进行设置 配置文件所在路径

在这里插入图片描述


dispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc-config.xml  这样就知道了mvc的配置文件在哪里1dispatcherServlet/

3. SpringMVC初步配置

基础配置;配置三大组件和我们业务控制器

新建一个Controller包;并且在该包下面创建一个TestUserInfoController

实际上controller不会这样使用,这里只是做一个测试(如果这样一个handler写一个类,不和servlet一样了吗)

在这里插入图片描述

在springmvc-config.xml中添加业务控制器以及三大组件的基础配置

在这里插入图片描述

目前项目内部既有之前servlet的精准映射(如/UserInfoServlet),也有刚刚配置的dispatcherServlet/映射
如果调用/UserInfoServlet,也是可以成功的;这因为一个Servlet配置优先级的问题精准的映射和/的优先级。一定是精准url优先级高

完善视图解析器

正常开发jsp我们一般会放到安全目录下;因此我们在WEB-INF下新建一个jsp目录存放我们的jsp页面
那么这样,jsp只能通过handler去访问

在这里插入图片描述

把我们原先的jsp页面都移动过去

同时修改TestUserInfoController代码

在这里插入图片描述

现在我们设置jsp的路径还是挺长(/WEB-INF/jsp/login.jsp),感觉有点麻烦,这个时候我们就可以使用视图解析器的配置对返回值进行优化,修改配置文件中视图解析器;增加两个属性

这样在访问的路径上自动添加前缀和后缀

在这里插入图片描述

基于SpringMVC注解改造项目

上面基础的改造我们发现实际上并没有比Servlet简化多少

org.springframework.stereotype.Controller注解类型用于将类的实例指定为控制器,使用@Controller注解标注的类无需继承特定的类或实现特定的接口

1、在SpringMVC配置文件中引入spring-context

2、使用元素配置包的扫描范围;

3、在对应的类中使用@Controller对类进行注解。

在这里插入图片描述




新建一个Controller测试

在这里插入图片描述

出500错误原因:如没有请忽略

我们都知道,在idea中如果写List,idea会给我们推荐import含有List的包,我们一般选择的是util包

如果选择错,那么List就不是我们常用的那个List,就会出错。同理,我们在配置mvc文件时,也有可能出现上述问题

在这里插入图片描述

在这里插入图片描述

4. 静态资源问题

我们前端的常见资源有js css img等等 这些都会被dispatcherServlet拦截到

在这里插入图片描述

对资源放行

在这里插入图片描述




在这里插入图片描述

5. 常用注解

5.1 @RequestMapping

在这里插入图片描述

5.2 @PathVariable与RESTFUL风格

传统的url:*****/contextPath/getUserInfo?userId=10&userName=a

restful风格的url:*****/contextPath/getUserInfo/10/a

SpringMVC中实现restful风格的url访问

在这里插入图片描述

HTTP中RESTFULE支持

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhY8Xjio-1678960789415)(D:\其他\桌面\毕设\JavaWeb项目.assets\e531cee5722d4584825551a282635ae3.png)]

在SpringMVC中实现

创建一个conreoller测试类 写上四个方法
在controller中标明method属性@Controller
public class RestFulController {@RequestMapping(value = "/userInfo/{userId}", method = RequestMethod.GET)public String getUserInfo(@PathVariable("userId") String userId) {System.out.println("--------GET");return "restful";}@RequestMapping(value = "/userInfo/{userId}", method = RequestMethod.POST)public String addUserInfo(@PathVariable("userId") String userId) {System.out.println("--------POST");return "restful";}@RequestMapping(value = "/userInfo/{userId}", method = RequestMethod.DELETE)public String delUserInfo(@PathVariable("userId") String userId) {System.out.println("--------DELETE");return "restful";}@RequestMapping(value = "/userInfo/{userId}", method = RequestMethod.PUT)public String updateUserInfo(@PathVariable("userId") String userId) {System.out.println("--------PUT");return "restful";}@RequestMapping("restful")public String restful(){return "restful";}
}
创建一个jsp
put和delete因为form的method不支持 采用传递参数的方式;但是单纯传参无法转换请求类型<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>




注意isErrorPage="true",不加会出现405错误

现在还是不可以的,原因有二,

一是 html本身就没有put和delete方法

二是 虽然我们用hidden设置了,但请求中的method属性仍不会改变,在controller哪里仍不知道

解决:添加一个拦截器,拦截请求根据参数进行转化

在这里插入图片描述

    hiddenorg.springframework.web.filter.HiddenHttpMethodFilterhidden/*

5.3 @RequestHeader

用于快捷获取请求头中信息

在这里插入图片描述

    @RequestMapping("/toLogin")public String toLogin(@RequestHeader("Accept") String accept, @RequestHeader("Accept-Language") String lan) {System.out.println(accept);System.out.println(lan);return "login";}

5.4 @CookieValue

快捷获取cookie信息

6. 参数注入

6.1 乱码问题

get方式如果乱码可以修改server.xml

这里使用tomcat版本较高并没有问题

将表单修改为post, 提交中文后台获取是乱码

web.xml中用过滤器转码

此处要注意一定要写到所有过滤器之前

encodingorg.springframework.web.filter.CharacterEncodingFilterencodingUTF-8forceEncodingtrueencoding/*

6.2 时间Date

@DateTimeFormat(pattern = "yyyy-MM-dd")

单独时间

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

实体类中有时间属性

在bean中添加注解

bean:@DateTimeFormat(pattern = "yyyy-MM-dd")private Date userTime;controller中正常:@RequestMapping("timeClass")public String timeClass(UserInfo userInfo) {System.out.println(userInfo.toString());return "restful";}

7. 响应返回

7.1 转发和重定向

转发到其他Controller

return "forward:/restful";

重定向

return "redirect:/restful";

7.2 响应Json

@ResponseBody:可以直接将返回的字符串数据作为响应内容,即设置controller返回的是一个Json数据,而不是视图

添加依赖:这个依赖我们直接用不到,是SpringMVC去调用的,如果不加会406错误

    com.fasterxml.jackson.corejackson-databind2.13.1
@Controller
public class JsonController {@ResponseBody@RequestMapping("getUserInfoJson")public UserInfo getUserInfoJson() {return new UserInfo(1,"zhangsan1",12,"1","",new Date());}@ResponseBody@RequestMapping("/getUserInfoJsonList")public List json(){List list = new ArrayList();list.add(new UserInfo(1,"zhangsan1",12,"1","",new Date()));list.add(new UserInfo(2,"zhangsan2",12,"1","",new Date()));list.add(new UserInfo(3,"zhangsan3",12,"1","",new Date()));return list;}
}在需要范围Json数据的方法上设置ResponseBody

如果整个Controller类的方法都是返回Json
@RestController的作用等同于@Controller + @ResponseBody

@RestController
//@Controller
public class JsonController {
//    @ResponseBody@RequestMapping("getUserInfoJson")public UserInfo getUserInfoJson() {return new UserInfo(1,"zhangsan1",12,"1","",new Date());}//    @ResponseBody@RequestMapping("/getUserInfoJsonList")public List json(){List list = new ArrayList();list.add(new UserInfo(1,"zhangsan1",12,"1","",new Date()));list.add(new UserInfo(2,"zhangsan2",12,"1","",new Date()));list.add(new UserInfo(3,"zhangsan3",12,"1","",new Date()));return list;}
}

在这里插入图片描述

在这里插入图片描述

8. 数据校验

一般情况下我们都会使用前端校验+后端校验的方式,在springmvc中如何进行后端数据校验。

JSR303是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 。通过在 Bean 属性上标注类似于 @NotNull、@max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。

spring中拥有自己的数据校验框架,同时支持JSR303标准的校验框架,可以在通过添加注解的方式进行数据校验。在spring中本身没有提供JSR303的实现,需要导入依赖的包。

  		org.hibernatehibernate-validator5.1.0.Final

在这里插入图片描述

在这里插入图片描述

在实体类中添加约束

在这里插入图片描述

在方法属性设置此类需要被检查@Valid,且设置了接受错误信息的BindingResult

    @RequestMapping("register")public String register(@Valid UserInfo userInfo, BindingResult bindingResult) {if (bindingResult.hasErrors()) {List errors = bindingResult.getFieldErrors();for (FieldError error : errors) {System.out.println(error.getDefaultMessage());}}System.out.println("-----------");System.out.println(userInfo.toString());return "login";}

message可设置错误返回的消息

在这里插入图片描述

9. 文件上传

SpringMVC为文件上传提供了直接支持,即MultipartResolver接口,具有即插即用特征。SpringMVC提供了CommonsMultipartResolver类,该类通过Commons FileUpload技术实现了MultipartResolver接口。因此使用SpringMVC上传功能时需要提供Commons FileUpload依赖支持。

JavaWeb:前端–数据流–转成文件

MVC:前端–注入成MultipartResolver

        commons-iocommons-io2.6commons-fileuploadcommons-fileupload1.4

在SpringMVC配置文件中增加配置

    

在这里插入图片描述
在这里插入图片描述

    @RequestMapping("register")public String register(MultipartFile userImg) throws IOException {System.out.println(userImg.getOriginalFilename());String fileUrl = "D:\\" + userImg.getOriginalFilename();File file = new File(fileUrl);//正常我们文件会选择放到本项目路径下或者选择一个文件服务器作为存储userImg.transferTo(file);return "login";}

10. 文件下载

没什么可说的,都是套路

    @RequestMapping("/downloadFile")public ResponseEntity download(HttpServletRequest request) throws Exception {//获取要下载文件的路径及输入流对象ServletContext servletContext = request.getServletContext();String realPath = servletContext.getRealPath("/static/img/logo.jpg");FileInputStream fileInputStream = new FileInputStream(realPath);byte[] bytes = new byte[fileInputStream.available()];fileInputStream.read(bytes);fileInputStream.close();//将要下载文件内容返回HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.set("Content-Disposition","attachment;filename=logo.jpg");return  new ResponseEntity(bytes,httpHeaders, HttpStatus.OK);}

11. 拦截器

SpringMVC中提供了Interceptor拦截器,用于拦截用户的请求并进行相应的处理,如权限认证、判断用户是否登录等。

SpringMVC拦截器是一种可插拔式的设计,当需要使用某个拦截器时,只需要在配置文件中应用该拦截器即可;当不需要该拦截器时,取消配置文件中的配置。

SpringMVC中的拦截器是通过实现HandlerInterceptor接口或继承HandlerInterceptorAdapter抽象类来完成的。

重写三个方法

preHandle

这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false

当preHandle()方法返回true时,继续向下执行处理器中的方法,否则不再向下执行

postHandle

当preHandle()方法返回true时,且在Controller方法被调用之后被调用; postHandle()方法在DispatcherServlet进行视图返回渲染之前被调用,因此在方法中可以对Controller处理之后的ModelAndView对象进行操作;

afterCompletion

这个方法在DispatcherServlet完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

public class TestInterceptor1 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("一:1111111111");return HandlerInterceptor.super.preHandle(request, response, handler); // true}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("一:2222222222");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("一:3333333333");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
springMVC配置:

如果配置多个拦截器;拦截器的执行顺序是根据配置顺序来的

在这里插入图片描述

四、Spring

spring是一个开源框架。
spring是为了简化企业开发而生的,使得开发变得更加优雅和简洁。
spring是一个IOC和AOP的容器框架。
容器:包含并管理应用对象的生命周期,就好比用桶装水一样,spring就是桶,而对象就是水 servletcontext

使用Spring的优点

​ 1、Spring通过DI、AOP和消除样板式代码来简化企业级Java开发
​ 2、Spring框架之外还存在一个构建在核心框架之上的庞大生态圈,它将Spring扩展到不同的领域,如Web服务、REST、移动开发以及NoSQL
​ 3、低侵入式设计,代码的污染极低
​ 4、独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺
​ 5、Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦
​ 6、Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理,从而提供了更好的复用
​ 7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合,并简化了底层的数据库访问
​ 8、Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

1. IOC

将对象交给IOC容器管理,使用时从IOC容器中获取。

容器也是一个对象,只不过内部管理着其他的对象
IOC是一种设计原则;降低代码耦合;
DI是最常见的实现方式

IOC站在业务对象的角度上:业务对象将获取业务对象依赖的资源的控制权反转给容器。
DI站在容器的角度上:容器将业务对象依赖的资源注入到业务对象中

在这里插入图片描述

如何实现IOC?

Spring框架如何帮助我们实现用容器管理Bean(业务对象)
提供管理业务类的容器接口,BeanFactoryApplicationContext

在这里插入图片描述

如何告诉容器管理哪些类?

1.1 XML方式

① 属性注入

  1. 普通的不带属性的类

创建新配置文件

在这里插入图片描述

创建测试类(注意,上面的文件改了名字,将applicationContext改为了springConfig,其实无所谓,我觉得后者清晰一些)

public  class TestSpring {@Testpublic void  testSpringIOCXML(){ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("springConfig.xml"); // 配置文件名UserInfoController userInfoController = classPathXmlApplicationContext.getBean("userInfoController",UserInfoController.class);
//        UserInfoController userInfoController = (UserInfoController) classPathXmlApplicationContext.getBean("userInfoController");System.out.println(userInfoController);}
}
  1. 带有属性的类,且属性中有其他类

加入一些属性

在这里插入图片描述

配置文件

 

层级很明显
bean中的基本属性用value设置值,引用类型用ref(也可以不引用,再套一个bean),数组用listlist里面再根据类型继续嵌套写。list里面的userInfo用了两种,一种是rel标签,一种直接写了一个bean

结果:

UserInfoController(name=我是Name, userInfo=UserInfo(userId=null, userName=111, userAge=null, userSex=null, userPass=null, userTime=null), userList=[UserInfo(userId=null, userName=111, userAge=null, userSex=null, userPass=null, userTime=null), UserInfo(userId=null, userName=222, userAge=null, userSex=null, userPass=null, userTime=null)])
  1. 标签的继承、依赖

如果有相同值的属性,可以继承parent
依赖:像userInfoController就依赖了userInfo111、userInfo222、userInfo333
即:在执行本体之前需要加载这三个,用 depends-on(不加也正常)










UserInfoController(name=我是Name, userInfo=UserInfo(userId=null, userName=111, userAge=1888888888, userSex=null, userPass=null, userTime=null), userList=[UserInfo(userId=null, userName=111, userAge=1888888888, userSex=null, userPass=null, userTime=null), UserInfo(userId=null, userName=222, userAge=1888888888, userSex=null, userPass=null, userTime=null), UserInfo(userId=null, userName=222, userAge=1888888888, userSex=null, userPass=null, userTime=null)])

在这里插入图片描述

在这里插入图片描述

② 构造器注入

类中需要有全参构造函数

    

③ 自动注入

A需要调用B,B是A的依赖资源,这时候需要自动注入。autowire

常用byType:根据对应属性进行注入

    
UserInfoController(name=111, userInfo=UserInfo(userId=null, userName=111, userAge=1888888888, userSex=null, userPass=null, userTime=null), userList=[UserInfo(userId=null, userName=111, userAge=1888888888, userSex=null, userPass=null, userTime=null)])

1.2 注解方式

主要使用注解的方式,注解可以代替xm几乎的所有操作(包扫描需要在xml中)。

组件扫描(component scanning): Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件.

@Component: 标识了一个受 Spring 管理的基本组件(不推荐使用),代表自动创建了一个bean标签

@Repository: 标识持久层组件类

@Service: 标识服务层(业务层)组件类

@Controller: 标识控制层组件类

后三者和Component作用相同,不过有清晰的分层含义

对于扫描到的组件, Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写. 也可以在注解中通过 value 属性值标识组件的名称

首先,在xml中配置包扫描,同SpringMVC的包扫描
不过配置的范围需要扩大,因为不止是Controller需要IOC控制反转,(当然也可以多写几条context标签)

在这里插入图片描述

    

任务:在UserInfoController中加入UserInfoService属性,并对进行自动注入

  1. UserInfoService是引用类型,那么需要将他也加入到容器管理:在UserInfoService上添加@Service注解,代表之前的一个bean标签。

在这里插入图片描述

  1. @Autowired:标记userInfoService属性需要自动注入
    1. 自动注入的前提是在IOC容器中已经存在了userInfoService,上步加入的@Service注解帮我们实现了
    2. 是否还记得xml中的Autowire自动注入呢?当时我们说常用byType(根据属性进行注入),这里的**@Autowired**就是根据属性进行注入(根据name用另一种注解,不常用)
    3. @Value设置基本类型的默认值,不常用

在这里插入图片描述

这样继续执行之前的测试类:

结果:获取到了userInfoService
UserInfoController(name=设置注入的默认值, userInfoService=com.qst.binzhou2022.services.UserInfoService@7068e664)

如果不对UserInfoService@Service(加入IOC容器)以及@Autowired(标记自动注入),那么他的结果是null

注意:如果不在spring配置文件中配置包扫描,会直接报错,因为根据找不到对应的类

其实,这四个注解无论用哪一个,其实都可以得到UserInfoService,都是根据UserInfoService类路径生成一个对应的bean标签。。。。。。。。。。。。。了解即可

在这里插入图片描述

了解:@Qualifier:根据name自动注入

@Autowired默认是根据类型进行配置的,并且也无法修改为ByName,这个时候如果容器中该类型的对象有多个则会出错(一般不会有多个)

例我们在UserInfoService中增加一个属性,同时添加该Service 为userInfoService2

在这里插入图片描述

在这里插入图片描述

配置文件中我们在配置一个UserInfoService的对象 id设置为userInfoService1
那么目前IOC容器中存在有两个UserInfoService,这样启动测试类,出现错误

使用@Qualifier指定注入的bean是哪一个
在Controller中增加 @Qualifier("userInfoService1")

了解:@Resource

@Resource并不是Spring的注解,他的包是javax.annotation.Resource 需要导入。但是Spring支持该注解的注入。

@Resource有两个中重要的属性:name和type ,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用 byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略

可以直接把@Autowired替换为@Resource;演示结果也是一样的;这里不做记录

1.3 配置类方式

了解即可 完全去除xml配置

Spring3.0支持将配置文件使用java类进行替代

我们新建一个config目录,在目录下新建一个SpringConfig类

给这个类添加@Configuration告诉容器这是一个配置类

然后通过ComponentScan告诉容器要扫描的路径

@Configuration
@ComponentScan(basePackages = {"com.qst.binzhou2022"})
public class SpringConfig {
}

在配置文件中声明bean对象 @Bean注解

@Configuration
@ComponentScan(basePackages = {"com.qst.binzhou2022"})
public class SpringConfig {@Beanpublic StuInfo getStuInfo() {StuInfo stuInfo = new StuInfo();stuInfo.setStuName("苏小白");return stuInfo;}
}

测试类

    @Testpublic void test() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.qst.binzhou2022");StuInfo stuInfo = context.getBean(StuInfo.class);System.out.println(stuInfo);}

2. AOP

概念:链接点

从宏观角度来看,java程序执行的最基本单位是方法的调用。程序的运行过程是方法调用的过程。

链接点是发生方法调用部分的代码。比如A方法中有调用B方法的代码,那段代码就是链接点。

在这里插入图片描述

概念:AOP的作用

假如有一个写好的程序(程序中有方法A、B、C、D、E),现在临时需要在一些方法(B、D)之前添加一个相同的操作,比如统计某些方法的运行时间。

如果这样,我们可以在B、D方法内部修改其代码,但如果之后还有类似的操作,比如这次是A、D方法,我们仍需要在方法体内部添加、修改代码

这样的缺点是我们不断修改已经写好的代码

AOP可以解决这个问题,如果需要在B、D方法运行前、运行后做一些操作,我们可以创建一个代理类,用这个类来调用B、D方法,在执行这些方法前做相应的操作。好处是不用修改已经写好的B、D方法了

概念:切入点

在这里插入图片描述

继续用上次的例子,假如有一个写好的程序(程序中有方法A、B、C、D、E),现在临时需要在一些方法(B、D)之前添加一个相同的操作,比如统计某些方法的运行时间。

B、D就是切入点,B、D加起来就是一个切面(需要添加的功能相同)121
如果是A、C方法,同理,A、C就是切入点,A、C加起来就是一个切面(需要添加的功能相同)

这两个切面的功能不同,这就是切面(添加相同功能点的集合)、切入点(需要添加功能的点)

在这里插入图片描述

概念:代理类

在这里插入图片描述

代理类有三种实现方法,主要是后面两种。但我们其实不需要关注那么多,实际使用有Spring提供的注解
Spring会根据我们的对象,自动在两者间转换(有些场景适用于JDK…有些适用于CGLib)

在这里插入图片描述

在这里插入图片描述

代理类继承目标类,就可以获取到目标方法的所有信息,包括参数

在这里插入图片描述

具体使用

Aspect扩展了标准ava语言,提供了整套AOP理论的完整实现

    org.springframeworkspring-aspects5.2.3.RELEASE

在这里插入图片描述

execution:匹配执行方法的连接点,是 Spring AOP中最主要的切入点指示符(也有其他的,但用这个就够了)

execution(方法的修饰符 返回值类型 所属类.方法名(参数类型))

在这里插入图片描述在这里插入图片描述

@Component
@Aspect
public class TimeAdvice {//调用实际业务类方法之前  前置通知 链接点执行之前执行@Before("execution(public *  com.qst.binzhou2022.services.UserInfoService.getUserInfo(*))")public void printBeforeTime(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();Object[]  params = joinPoint.getArgs();System.out.println("调用的方法名" + methodName +"参数" + params.length + "--" + "第一个参数:" + params[0].toString() );System.out.println("前置通知 printBeforeTime  <<<<<<<<<<<<" + new Date());}//调用实际业务类方法之后    后置通知是在连接点完成之后执行的, 无论实际业务方法是否出现异常,执行完都会执行后置通知,//在此通知中,无法获取实际业务方法的返回值@After("execution(public *  com.qst.binzhou2022.services.UserInfoService.getUserInfo(*))")public void printAfterTime() {System.out.println("后置通知 printAfterTime   >>>>>>>>>>>>"+new Date());}//返回通知  在方法正常结束后执行,可以获取实际业务方法的返回值//@AfterReturning("execution(public int springAspectjAOP.aopclass.MathInterImpl.chu(int, int))")@AfterReturning(value = "execution(public *  com.qst.binzhou2022.services.UserInfoService.getUserInfo(*))",returning = "returnValue")public void printReturnTime(JoinPoint joinPoint,Object returnValue) {System.out.println("返回结果:" + returnValue);System.out.println("返回通知 printReturnTime  -----"+new Date());}//调用实际业务类方法出现异常的时候执行   异常通知  只在连接点抛出异常时才执行异常通知@AfterThrowing(value="execution(public *  com.qst.binzhou2022.services.UserInfoService.getUserInfo(*))",throwing="e")public void printThrowTime(JoinPoint joinPoint, FileNotFoundException e) {System.out.println("出现的异常" + e);System.out.println("异常通知 printThrowTime   xxxxxxxxxxxxxxxxxxxxxx" + new Date());}//环绕通知  相当于动态代理的实现  需要在环绕通知中指定目标方法执行,//如何让目标方法执行,我们需要一个参数ProceedingJoinPoint类型的参数,通过该参数可以帮助我们获取执行的实际业务类方法名和参数,同时可以控制目标方法的执行与否@Around("execution(public *  com.qst.binzhou2022.services.UserInfoService.getUserInfo(*))")public Object printAroundTime(ProceedingJoinPoint pj) {System.out.println("环绕通知 printAroundTime   =============="+new Date());Object returnData = null;//通过ProceedingJoinPoint参数获取方法名和参数String methodName = pj.getSignature().getName();Object [] args = pj.getArgs();//通过ProceedingJoinPoint参数的proceed方法执行目标方法try {//前置通知System.out.println("环绕--前置通知");returnData = pj.proceed();//返回通知System.out.println("环绕--返回通知");} catch (Throwable e) {//异常通知System.out.println("环绕异常通知");e.printStackTrace();}//后置通知System.out.println("环绕--后置通知");return returnData;}}

五、SSM整合

1. M

三个框架整合到一起

目前来说,Spring主要帮我们解决的是依赖关系:Controller-->Service-->Dao(现在Dao层不用,而mapper接口也不用我们new,他是代理类帮我们创建的,除非将控制权再交给IOC )

所以目前IOC帮我们的是:Controller-->Service

回想:现在我们在Service中用的是手写的Mybatis工具类,工具类的流程是:SqlSessionFactoryBuilder-->SqlSessionFactory-->SqlSession-->getMapper

原先我们使用Mybatis就是在service层中使用sqlsessionfactorysqlsession获取mapper代理类
既然我们要解决依赖关系;需要解决Service调用代理类的依赖
所以我们可以思考出来
我们需要把sqlsessionfactorysqlsession获取mapper代理类的创建都交给Spring
mapper代理类可以由SqlSession创建
所以最终我们只需要让Spring管理我们sqlsessionfactorysqlsession对象即可

我们的最终目的是IOC可以帮我们管理Controller、Service、mapper

引入依赖

  1. mybatis写的帮助我们在IOC中管理sqlsessionfactorysqlsession
  2. 数据库连接词,用别的也可以
  3. 既然在IOC中管理Mybatis的东西,那么关系数据库的连接信息也要在IOC中设置。这个是spring连接数据库相关
   org.mybatismybatis-spring2.0.4com.alibabadruid1.1.10org.springframeworkspring-jdbc5.2.3.RELEASE

2. Spring加入M

创建一个applicationContext.xml(名字不太重要)配置文件,进行Spring配置文件配置。之前的SpringConfig没有用处了。

  1. 数据库信息

我们需要让IOC创建sqlsessionfactory和sqlSession;必然要链接数据库,所以数据库信息需要配置到IOC中

数据库信息配置在jdbc.properties中,使用引入属性文件

将数据库链接信息放到一个对象中,方便注入,这里使用的是DruidDataSource

    
  1. 创建sqlsessionfactory

这里不需要直接创建Mybaits核心包中的类(SqlSessionFactory)
我们引入了mybatis整合Spring的包,使用其中的SqlSessionFactoryBean更加适合(mybatis专门为了spring准备的)
同时配置需要注入必须的数据源,以及常用的别名扫描配置

    

在这里插入图片描述

在这里插入图片描述

  1. 创建sqlSession

同样的,这里我们也不需要单独配置管理sqlsession,而选择使用整合包给我们提供的一个映射文件扫描器类MapperScannerConfigurer

一个映射文件扫描器
主要的作用就是扫描映射文件,生成mapper代理类。因此我们需要至少配置两个属性

  1. 要扫描哪些映射文件
  2. 扫描出来的映射文件使用哪个sqlSessionFactory去处理生成代理类
    
  1. 添加扫描service层的配置
    

Controller层已经在springmvc-config.xml中扫描过了,不需要再扫描,如果再写一个,SpringIOC会生成两个Controller对象。(SpringMVC在创建时就有一个SpringIOC容器)

3. Spring容器配置

容器配置好了以后,我们要思考一下容器存放位置,在servletContext容器启动后就加载SpringIOC容器
经过思考放到应用域是最合适的
所以当应用域启动的时候我们就应该创建我们的ioc容器加载对象资源

在这里插入图片描述

web.xml中增加项目启动创建spring容器的配置
这里要使用到监听器监听应用域启动创建IOC容器
ContextLoaderListenerSpring框架帮我们提供好的监听器直接配置使用即可

    contextConfigLocationclasspath:applicationContext.xml Spring配置文件地址org.springframework.web.context.ContextLoaderListener

4. 全部配置文件

4.1 Spring配置

applicationContext.xml



jdbc.properties

jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/guangxissmdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=root
jdbc_password=root

4.2 SpringMVC配置

springmvc-config.xml



4.3 web.xml


contextConfigLocationclasspath:applicationContext.xmlorg.springframework.web.context.ContextLoaderListenerencodingorg.springframework.web.filter.CharacterEncodingFilterencodingUTF-8forceEncodingtrueencoding/*dispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc-config.xml1dispatcherServlet/hiddenorg.springframework.web.filter.HiddenHttpMethodFilterhidden/*

4.4 依赖

        junitjunit3.8.1testjavax.servletjavax.servlet-api4.0.1mysqlmysql-connector-java8.0.27javax.servletjstl1.2org.projectlomboklombok1.18.26providedorg.mybatismybatis3.5.11org.apache.logging.log4jlog4j-core2.20.0org.springframeworkspring-webmvc5.3.18com.fasterxml.jackson.corejackson-databind2.13.1org.hibernatehibernate-validator5.1.0.Finalcommons-iocommons-io2.6commons-fileuploadcommons-fileupload1.4junitjunit4.12org.springframeworkspring-aspects5.2.3.RELEASEorg.mybatismybatis-spring2.0.4com.alibabadruid1.1.10org.springframeworkspring-jdbc5.2.3.RELEASE

4.5 其他

  1. log4j2二.6

上一篇:day9-集合作业

下一篇:es6+正则扩展

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...