当前位置: 首页 > news >正文

【SSM-SSM整合】将Spring、SpringMVC、Mybatis三者进行整合;本文阐述了几个核心原理知识点,附带对应的源码以及描述解析

SSM整合的基础jar包

在这里插入图片描述

需要创建的层级:

  • controller层
    • 该层下需要创建对应的控制器Servlet
  • POJO文件夹
    • 该层下需要创建与数据库对应的POJO类
  • mapper层
    • 该层下需要创建Mapper的接口实现
  • service层
    • 该层下需要创建业务层的接口及其接口实现

需要创建的配置文件:

ApplicationContext.xml(Spring核心配置文件)
  • 组件扫描

    <context:component-scan base-package="com.demonode"><!-- 排除controller层;因为controller层是由springMvc进行管理 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
  • 配置数据源

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.cj.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm_integration"/><property name="user" value="root"/><property name="password" value="root"/>
    </bean>
    
  • 配置mybatis

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!--        加载数据源--><property name="dataSource" ref="dataSource"></property><!--        加载mybatis核心配置文件--><property name="configLocation" value="classpath:configuration.xml"></property>
    </bean>
    <!-- 扫描Mapper · 如果是注解,那么直接扫描接口的包 · 如果是xml配置文件,请扫描对应的包 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="configurer"><property name="basePackage" value="com.demonode.mapper"></property>
    </bean>
    
  • 配置事务控制

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"><property name="dataSource" ref="dataSource"></property>
    </bean><!--配置事务增强方法 - *默认全部-->
    <tx:advice id="interceptor"><tx:attributes><tx:method name="*"/></tx:attributes>
    </tx:advice><aop:config><!--配置impl服务层下实现接口的所有方法都进行事务控制--><aop:advisor advice-ref="interceptor" pointcut="execution(* com.demonode.service.impl.*.*(..))"></aop:advisor>
    </aop:config>
    
Spring-mvc.xml(SpringMVC配置文件)
  • 配置组件扫描

    <!-- mvc只管理controller层就好 -->
    <context:component-scan base-package="com.demonode.controller"></context:component-scan>
    
  • 配置MVC的注解驱动

    <mvc:annotation-driven></mvc:annotation-driven>
    
  • 配置内部视图解析器

    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/page/"></property><property name="suffix" value=".jsp"></property>
    </bean>
    
  • 开放内部资源

    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    
web.xml(WEB环境需要的配置文件)
  • 配置Spring的核心配置文件

    <context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    
  • 配置监听器

    <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
  • 配置Spring的核心控制器

    <!-- 核心控制器Servlet -->
    <servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup>
    </servlet>
    <!-- servlet的映射地址 -->
    <servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern>
    </servlet-mapping>
    
  • 配置过滤器

    <!-- 过滤器配置 -->
    <filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
    </filter>
    <!-- 映射过滤器地址 -->
    <filter-mapping><filter-name>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
    </filter-mapping>
    
configuration.xml(Mybatis核心配置文件)
  • 由于Mybatis的配置都交由Spring去管理了,所以该配置文件所能使用的功能挺少的。

  • 配置变量

    <typeAliases><typeAlias type="com.demonode.entityclass.Person" alias="person"></typeAlias>
    </typeAliases>
    

流程

流程图

在这里插入图片描述

  • 其核心在于java的反射机制
    • Spring核心:Java反射
    • Mybatis:动态代理,而动态代理又是基于反射的。
    • 所以,SSM框架核心原理在反射。

原理

Spring(对象工厂):

  • 平时开发接触最多的估计就是这个IOC容器,它可以装载bean(也就是Java中的类,当然也包括service、dao里面的),有了这个机制,就不用在每次使用这个类的时候为它初始化,很少看到关键字new。

SpringMVC(视图控制器):

  • 核心为一个DispatcherServlet,控制所有请求。

  • DispatcherServlet源码:

    package servlet;import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Properties;import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;import annotation.HController;
    import annotation.HRequestMapping;/*** 手写SpringMVC*/
    public class MyDispatcherServlet extends HttpServlet {//加载属性文件private Properties properties = new Properties();//装载beannameprivate List<String> classNames = new ArrayList<String>();//ioc容器private Map<String, Object> ioc = new HashMap<String, Object>();//类似于以前自定义的cache缓存容器,这里也是起到一个容器的作用//用于加载各个mappingprivate Map<String, Method> handlerMapping = new HashMap<String, Method>();//容器加载所有的controllerprivate Map<String, Object> controllerMap = new HashMap<String, Object>();@Overridepublic void init(ServletConfig config) throws ServletException {// 1.加载配置文件doLoadConfig(config.getInitParameter("contextConfigLocation"));// 2.初始化所有相关联的类,扫描用户设定的包下面所有的类doScanner(properties.getProperty("scanPackage"));// 3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中beanName默认是首字母小写doInstance();// 4.初始化HandlerMapping(将url和method对应上)initHandlerMapping();}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {// 处理请求doDispatch(req, resp);} catch (Exception e) {resp.getWriter().write("500!! Server Exception");}}private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {if (handlerMapping.isEmpty()) {return;}String url = req.getRequestURI();String contextPath = req.getContextPath();url = url.replace(contextPath, "").replaceAll("/+", "/");if (!this.handlerMapping.containsKey(url)) {resp.getWriter().write("404 NOT FOUND!");return;}Method method = this.handlerMapping.get(url);// 获取方法的参数列表Class<?>[] parameterTypes = method.getParameterTypes();// 获取请求的参数Map<String, String[]> parameterMap = req.getParameterMap();// 保存参数值Object[] paramValues = new Object[parameterTypes.length];// 方法的参数列表for (int i = 0; i < parameterTypes.length; i++) {// 根据参数名称,做某些处理String requestParam = parameterTypes[i].getSimpleName();if (requestParam.equals("HttpServletRequest")) {// 参数类型已明确,这边强转类型paramValues[i] = req;continue;}if (requestParam.equals("HttpServletResponse")) {paramValues[i] = resp;continue;}if (requestParam.equals("String")) {for (Entry<String, String[]> param : parameterMap.entrySet()) {String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");paramValues[i] = value;}}}// 利用反射机制来调用try {method.invoke(this.controllerMap.get(url), paramValues);// 第一个参数是method所对应的实例// 在ioc容器中} catch (Exception e) {e.printStackTrace();}}private void doLoadConfig(String location) {// 把web.xml中的contextConfigLocation对应value值的文件加载到流里面InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);try {// 用Properties文件加载文件里的内容properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();} finally {// 关流if (null != resourceAsStream) {try {resourceAsStream.close();} catch (IOException e) {e.printStackTrace();}}}}private void doScanner(String packageName) {// 把所有的.替换成/URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));File dir = new File(url.getFile());for (File file : dir.listFiles()) {if (file.isDirectory()) {// 递归读取包doScanner(packageName + "." + file.getName());} else {String className = packageName + "." + file.getName().replace(".class", "");classNames.add(className);}}}//利用java的反射机制private void doInstance() {if (classNames.isEmpty()) {return;}for (String className : classNames) {try {// 把类搞出来,反射来实例化Class<?> clazz = Class.forName(className);if (clazz.isAnnotationPresent(HController.class)) {ioc.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());} else {continue;}} catch (Exception e) {e.printStackTrace();continue;}}}private void initHandlerMapping() {if (ioc.isEmpty()) {return;}try {for (Entry<String, Object> entry : ioc.entrySet()) {Class<? extends Object> clazz = entry.getValue().getClass();if (!clazz.isAnnotationPresent(HController.class)) {continue;}// 拼url时,是controller头的url拼上方法上的urlString baseUrl = "";if (clazz.isAnnotationPresent(HRequestMapping.class)) {HRequestMapping annotation = clazz.getAnnotation(HRequestMapping.class);baseUrl = annotation.value();}Method[] methods = clazz.getMethods();for (Method method : methods) {if (!method.isAnnotationPresent(HRequestMapping.class)) {continue;}HRequestMapping annotation = method.getAnnotation(HRequestMapping.class);String url = annotation.value();url = (baseUrl + "/" + url).replaceAll("/+", "/");handlerMapping.put(url, method);controllerMap.put(url, clazz.newInstance());System.out.println(url + "," + method);}}} catch (Exception e) {e.printStackTrace();}}/*** 把字符串的首字母小写* * @param name* @return*/private String toLowerFirstWord(String name) {char[] charArray = name.toCharArray();charArray[0] += 32;return String.valueOf(charArray);}}
    
  • 从源码及可看出流程:

    1. 客户端发送请求到DispacherServlet(由web.xml拦截所有请求到改servlet);
    2. 由DispacherServlet(核心servlet)控制器查询HanderMapping,找到处理请求的Controller;(这里我用了一个map类似于缓存容器,装载所有的mapping即映射)
    3. Controller层调用业务逻辑处理后,返回ModelAndView,即下一层(往往是service、serviceimpl,mapper层)返回的的数据;
    4. DispacherServlet查询视图解析器,找到ModelAndView指定的视图;
    5. 视图负责将结果显示到客户端。

Mybatis(持久层框架)

  • mybatis是对jdbc的封装,它让数据库底层操作变的透明。

  • mybatis的操作都是围绕一个sqlSessionFactory实例展开的。

    • mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。
    • 在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再通过得到Mapper文件sqlSession.getMapper()
    • 最后调用的是JDK的动态代理
    protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy)
    };
    
  • 因此开发人员只需要创建Mapper接口,并使用Mapper接口即可。

  • Mybatis会对Mapper接口产生动态代理对象,这个动态代理对象实现了Mapper接口,拥有Mapper中定义的所有方法,并对这些方法进行了增强。增强的逻辑是获得sql语句和执行sql语句。

  • 动态代理:

    1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
    2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
    3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
    4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

框架核心原理

  • AOP 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。

    • 除了类(classes)以外,AOP提供了切面。切面对关注点进行模块化,例如横切多个类型和对象的事务管理。Spring的一个关键的组件就是AOP框架,可以自由选择是否使用AOP。提供声明式企业服务,特别是为了替代EJB声明式服务。
    • 最重要的服务是声明性事务管理,这个服务建立在Spring的抽象事物管理之上。允许用户实现自定义切面,用AOP来完善OOP的使用可以把Spring AOP看作是对Spring的一种增强。AOP的实现乃至spring框架基本上核心代码都是基于Java语言的反射机制(所谓的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。)。
    • AOP主要作用就是不通过修改源代码的方式、将非核心功能代码织入来实现对方法的增强。
    • 那么Spring AOP的底层如何实现对方法的增强?实现的关键在于使用了代理模式。代理模式的作用就是为其它对象提供一种代理,以控制对这个对象的访问,用于解决在直接访问对象时带来的各种问题,比如要访问的对象在远程的机器上。
    • 在面向对象系统中,由于其他某些原因(对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问)等
    • 代理主要分为两种方式:静态代理和动态代理
  • IOC :IOC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。

    • 有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象。

    • 所以相较于传统的java servlet需要自己request.getParamiter等需要一系列取值,转换中文,转换值类型的繁琐,

    • 更重要的是使得程序的整个体系结构变得非常灵活。自定义一个IOC容器的思路:

      • Map做一个容器,然后用解析xml文件的工具解析出需要扫描的包。利用Java反射机制拿到锁具有的方法,属性,注入到Map容器
  • DI 依赖注入,是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态地将某个依赖关系注入到组件之中。

    • 依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
    • 通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

😍❤️SSM 专栏前篇

  • 【SSM-Spring(一)】初上手Spring?别急!从最底部开始学习Spring吧!从Bean开始!本文介绍Spring程序的开发、Bean的声明周期与依赖注入概念
  • 【SSM-Spring(二)】初上手Spring?别急!从最底部开始学习Spring吧!Spring连接数据库、注解开发、Junit测试
  • 【SSM-SpringMVC(一)】Spring接入Web环境!本篇开始研究SpringMVC的使用!本文介绍了SpringMVC,以及SpringMVC的执行流程和常用注解解析
  • 【SSM-SpringMVC(二)】Spring接入Web环境!本篇开始研究SpringMVC的使用!SpringMVC数据响应和获取请求数据
  • 【SSM-Spring(三)】初上手Spring?别急!从最底部开始学习Spring吧!详细介绍SpringAOP切面编程操作和Spring编程式事务控制操作
  • 【SSM-Mybatis(一)】java持久层框架-Mybatis!本文涵盖介绍Mybatis和基本使用,分析Mybatis核心配置文件
  • 【SSM-Mybatis(二)】java持久层框架-Mybatis!本文重点对mybatis的映射文件进行了分析!涵盖if、foreach、choose、sql片段抽取、多表查询、注解开发等内容!

💕👉博客专栏

  • 英语专栏-涵盖绝大多数的英语语法~~基于B站英语兔视频所作的学习笔记

  • Golang专栏-包含基础、Gin、Goam等知识

  • 云原生专栏-包含k8s、docker等知识

  • 从0开始学习云计算-华为HCIP证书

  • JUC专栏-带你快速领悟JUC的知识!

  • JVM专栏-深入Java虚拟机,理解JVM的原理

  • 基于Java研究 数据结构与算法-包含贪心算法、加权图、最短路径算法等知识

  • Docker专栏-上手热门容器技术Docker

  • SpringBoot专栏-学习SpringBoot快速开发后端

  • 项目管理工具的学习-设计技术:Maven、Git、Gradle等相关管理工具

  • JavaSE-全面了解Java基础

  • JS专栏-使用JS作的一部分实例~

  • 使用CSS所作的一部分案例

相关文章:

  • pdf 不是扫描件,但却无法搜索关键词【问题尝试解决未果记录】
  • Java中堆栈
  • 一个极简单的 VUE3 + Element-Plus 查询表单展开收起功能组件
  • 基于HTTP头部字段的SQL注入:SQLi-labs第17-20关
  • spring中的@PropertySource注解详解
  • 记录裁员后的半年前端求职经历
  • 【氮化镓】GaN在不同电子能量损失的SHI辐射下的损伤
  • 歌曲《忘尘谷》基于C语言的歌曲调性检测技术解析
  • Linux常用命令详解(下):打包压缩、文本编辑与查找命令
  • Codeforces Round 1024 (Div. 2)(A-D)
  • 五、Hive表类型、分区及数据加载
  • [Java][Leetcode simple] 189. 轮转数组
  • 中国黄土高原中部XF剖面磁化率和粒度数据
  • 信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(十八)
  • 数据库分区与分表详解
  • Java实现MCP server,配合DeepSeek和达梦数据库,实现基于企业数据库的智能问答
  • MACH-ETH:汽车网络接口的卓越之选
  • 数据库实验报告 系统E-R图设计 2
  • 堆复习(C语言版)
  • Matlab 234-锂电池充放电仿真
  • 脑血管支架:救命神器还是定时炸弹?听听医生的大实话
  • 中国人民抗日战争暨世界反法西斯战争胜利80周年纪念活动标识发布
  • 应急部:正在积极推动各地逐步科学建设改造应急避难场所
  • 听企业聊感受,《外企聊营商》5月13日起推出
  • 年轻小将绽放光芒!中国短跑男女接力队直通东京世锦赛
  • 黄土是他们的气质:打破宁夏当代油画创作的沉寂