day04 springmvc

news2024/11/15 13:56:01

day04 springmvc

第一章 SpringMVC运行原理

第一节 启动过程

1. Servlet 生命周期回顾

生命周期环节调用的方法时机次数
创建对象无参构造器默认:第一次请求 修改:Web应用启动时一次
初始化init(ServletConfig servletConfig)创建对象后一次
处理请求service(ServletRequest servletRequest, ServletResponse servletResponse)接收到请求后多次
清理操作destroy()Web应用卸载之前一次

2. 初始化操作调用路线图

在这里插入图片描述

3. IOC容器创建

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
                "Fatal initialization error in servlet with name '" + getServletName() +
                "': custom WebApplicationContext class [" + contextClass.getName() +
                "] is not of type ConfigurableWebApplicationContext");
    }
    
    // 通过反射创建 IOC 容器对象
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    wac.setEnvironment(getEnvironment());
    
    // 设置父容器
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);
    return wac;
}

4. 将 IOC 容器对象存入应用域

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 创建 IOC 容器
        wac = createWebApplicationContext(rootContext);
    }
    if (!this.refreshEventReceived) {
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }
    if (this.publishContext) {
        // 获取存入应用域时专用的属性名
        String attrName = getServletContextAttributeName();
        
        // 存入
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}

看到这一点的意义:SpringMVC 有一个工具方法,可以从应用域获取 IOC 容器对象的引用。

工具类:org.springframework.web.context.support.WebApplicationContextUtils

工具方法:getWebApplicationContext()

@Nullable
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
    return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

5. 请求映射初始化

FrameworkServlet.createWebApplicationContext()→configureAndRefreshWebApplicationContext()→wac.refresh()→触发刷新事件→org.springframework.web.servlet.DispatcherServlet.initStrategies()→org.springframework.web.servlet.DispatcherServlet.initHandlerMappings()
在这里插入图片描述

6. 小结

整个启动过程我们关心如下要点:

  • DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。
  • DispatcherServlet 的父类是 FrameworkServlet。
    • FrameworkServlet 负责框架本身相关的创建和初始化。
    • DispatcherServlet 负责请求处理相关的初始化。
  • FrameworkServlet 创建 IOC 容器对象之后会存入应用域。
  • FrameworkServlet 完成初始化会调用 IOC 容器的刷新方法。
  • 刷新方法完成触发刷新事件,在刷新事件的响应函数中,调用 DispatcherServlet 的初始化方法。
  • 在 DispatcherServlet 的初始化方法中初始化了请求映射等。

第二章 请求处理过程

第一节 总体阶段

1. 流程描述

  • 目标 handler 方法执行
    • 建立调用链,确定整个执行流程
    • 确定处理器适配器
    • 拦截器的 preHandle() 方法
    • 注入请求参数
    • 准备目标 handler 方法所需所有参数
  • 调用目标 handler 方法
  • 目标 handler 方法执行
    • 拦截器的 postHandle() 方法
    • 渲染视图
    • 拦截器的 afterCompletion() 方法

2. 核心代码

整个请求处理过程都是 doDispatch() 方法在宏观上协调和调度,把握了这个方法就理解了 SpringMVC 总体上是如何处理请求的。

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:doDispatch()

核心方法中的核心代码:

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

第二节 调用前阶段

1. 建立调用链

1.1 相关组件

全类名:org.springframework.web.servlet.HandlerExecutionChain
在这里插入图片描述

拦截器索引默认是 -1,说明开始的时候,它指向第一个拦截器前面的位置。每执行一个拦截器,就把索引向前移动一个位置。所以这个索引每次都是指向当前拦截器。所以它相当于拦截器的指针

1.2 对应操作

所在类:org.springframework.web.servlet.handler.AbstractHandlerMapping
在这里插入图片描述

结论:调用链是由拦截器和目标 handler 对象组成的。

2. 调用拦截器 preHandle()

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:doDispatch()
在这里插入图片描述

具体调用细节:正序调用

所在类:org.springframework.web.servlet.HandlerExecutionChain

所在方法:applyPreHandle
在这里插入图片描述

从这部分代码我们也能看到,为什么拦截器中的 preHandle() 方法通过返回布尔值能够控制是否放行。

  • 每一个拦截器的 preHandle() 方法都返回 true:applyPreHandle() 方法返回 true,被取反就不执行 if 分支,继续执行后续操作,这就是放行。
  • 任何一个拦截器的 preHandle() 方法返回 false:applyPreHandle() 方法返回 false,被取反执行 if 分支,return,导致 doDispatch() 方法结束,不执行后续操作,就是不放行。

3. 调用handler方法

3.1 相关组件

接口:org.springframework.web.servlet.HandlerAdapter

作用:字面含义是适配器的意思,具体功能有三个

  • 将请求参数绑定到实体类对象中
  • 给目标 handler 方法准备所需的其他参数,例如:
    • Model、ModelMap、Map……
    • 原生 Servlet API:request、response、session……
    • BindingResult
    • @RequestParam 注解标记的零散请求参数
    • @PathVariable 注解标记的路径变量
  • 调用目标 handler 方法
3.2 创建并获取这个组件

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:doDispatch()
在这里插入图片描述

3.3 具体操作:调用目标 handler 方法

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:doDispatch()
在这里插入图片描述

3.4 具体操作:注入请求参数

在这里插入图片描述

通过反射给对应属性注入请求参数应该是下面的过程:

  • 获取请求参数名称
  • 将请求参数名称首字母设定为大写
  • 在首字母大写后的名称前附加 set,得到目标方法名
  • 通过反射调用 setXxx() 方法

4. 准备其他参数

以 Model 为例来进行说明。

4.1 背景

在 handler 方法中,如果需要 Model、ModelMap、Map 等对象用来存放模型数据,那么直接在 handler 方法中声明这些类型的形参即可。

而不管我们声明 Model、ModelMap、Map 三者中的任何一个,其实实际传入的对象都是 BindingAwareModelMap 类型的。

4.2 相关组件

组件类:org.springframework.web.method.support.ModelAndViewContainer

相关属性:defaultModel

private final ModelMap defaultModel = new BindingAwareModelMap();

从这个属性的声明能够看出:defaultModel 直接就是用 BindingAwareModelMap 对象来初始化的。

4.3 相关操作

相关接口:org.springframework.web.servlet.HandlerAdapter

所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

所在方法:invokeHandlerMethod()

操作1:创建 ModelAndViewContainer 对象
在这里插入图片描述

操作2:把 ModelAndViewContainer 对象传给 invokeAndHandle() 方法
在这里插入图片描述

第三节 调用后阶段

1. 调用拦截器的 postHandle() 方法

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:doDispatch()
在这里插入图片描述

调用细节:从拦截器集合长度 - 1 开始循环,循环到 0 为止。所以是倒序执行
在这里插入图片描述

2. 渲染视图

2.1 所有后续操作的入口

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:doDispatch()
在这里插入图片描述

2.2 后续细节1:处理异常

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:processDispatchResult()
在这里插入图片描述

2.3 后续细节2:渲染视图

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:processDispatchResult()
在这里插入图片描述

补充细节:模型数据存入请求域的具体位置

所在类:org.thymeleaf.context.WebEngineContext.RequestAttributesVariablesMap

所在方法:setVariable()
在这里插入图片描述

3. 调用拦截器的 afterCompletion() 方法

所在类:org.springframework.web.servlet.DispatcherServlet

所在方法:processDispatchResult()
在这里插入图片描述

调用细节:从拦截器索引开始循环,直到循环变量 i 被减到 0 为止。这样的效果是前面执行拦截器到哪里,就从哪里倒回去执行;前面没有执行的拦截器,现在也不执行。
在这里插入图片描述

第三章 ContextLoaderListener

第一节 概述

目前情况:DispatcherServlet 加载 spring-mvc.xml,此时整个 Web 应用中只创建一个 IOC 容器。将来整合Mybatis、配置声明式事务,全部在 spring-mvc.xml 配置文件中配置也是可以的。可是这样会导致配置文件太长,不容易维护。

所以想到把配置文件分开:

  • 处理浏览器请求相关:spring-mvc.xml 配置文件
  • 声明式事务和整合Mybatis相关:spring-persist.xml 配置文件

配置文件分开之后,可以让 DispatcherServlet 加载多个配置文件。例如:

<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

如果希望这两个配置文件使用不同的机制来加载:

  • DispatcherServlet 加载 spring-mvc.xml 配置文件:它们和处理浏览器请求相关
  • ContextLoaderListener 加载 spring-persist.xml 配置文件:不需要处理浏览器请求,需要配置持久化层相关功能

此时会带来一个新的问题:在 Web 一个应用中就会出现两个 IOC 容器

  • DispatcherServlet 创建一个 IOC 容器
  • ContextLoaderListener 创建一个 IOC 容器

注意:本节我们探讨的这个技术方案并不是**『必须』这样做,而仅仅是『可以』**这样做。

第二节 配置 ContextLoaderListener

1. 在web.xml中配置ContextLoaderListener

<!--    配置监听器(监听服务器启动)-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </context-param>
方法名执行时机作用
contextInitialized()Web 应用启动时执行创建并初始化 IOC 容器
contextDestroyed()Web 应用卸载时执行关闭 IOC 容器

2.在spring-web.xml中也配置包扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


<!--    包扫描-->
    <context:component-scan base-package="com.atguigu"></context:component-scan>

    <!--    加载mvc的注解驱动-->
    <mvc:annotation-driven></mvc:annotation-driven>

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

    <!-- Thymeleaf视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">

                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/>

                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>

                        <!--模板类型-->
                        <property name="templateMode" value="HTML5"/>
                        <!--模板的字符编码-->
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

<!--    使用import标签导入其他的xml配置文件-->
<!--    <import resource="spring.xml"></import>-->

    <mvc:view-com.atguigu.controller path="/" view-name="index.html"></mvc:view-com.atguigu.controller>
    <mvc:view-com.atguigu.controller path="/add.html" view-name="add"></mvc:view-com.atguigu.controller>

</beans>

第三节 探讨两个IOC容器之间的古纳西

结论:两个组件分别创建的 IOC 容器是父子关系。

  • 父容器:ContextLoaderListener 创建的 IOC 容器
  • 子容器:DispatcherServlet 创建的 IOC 容器

父子关系是如何决定的?

  • ContextLoaderListener 初始化时如果检查到有已经存在的根级别 IOC 容器,那么会抛出异常。
  • DispatcherServlet 创建的 IOC 容器会在初始化时先检查当前环境下是否存在已经创建好的 IOC 容器。
    • 如果有:则将已存在的这个 IOC 容器设置为自己的父容器
    • 如果没有:则将自己设置为 root 级别的 IOC 容器
  • 同时 Tomcat 在读取 web.xml 之后,加载组件的顺序就是监听器、过滤器、Servlet。

DispatcherServlet 创建的 IOC 容器设置父容器的源码截图:

所在类:org.springframework.web.servlet.FrameworkServlet

所在方法:createWebApplicationContext()
在这里插入图片描述

第四节 探讨两个IOC容器之间bean的互相访问

分析:“子可用父,父不能用子”的根本原因是子容器中有一个属性 getParent() 可以获取到父容器这个对象的引用。

源码依据:

  • 在 AbstractApplicationContext 类中,有 parent 属性
  • 在 AbstractApplicationContext 类中,有获取 parent 属性的 getParent() 方法
  • 子容器可以通过 getParent() 方法获取到父容器对象的引用
  • 进而调用父容器中类似 “getBean()” 这样的方法获取到需要的 bean 完成装配
  • 而父容器中并没有类似 “getChildren()“ 这样的方法,所以没法拿到子容器对象的引用
    在这里插入图片描述

第五节 有可能重复创建对象

1. 重复创建对象的问题

  • 浪费内层空间

  • 两个IOC容器能力是不同的

    • spring-mvc.xml:仅配置和处理请求相关的功能。所以不能给 service 类附加声明式事务功能。

      结论:基于 spring-mvc.xml 配置文件创建的 EmpService 的 bean 不带有声明式事务的功能

      影响:DispatcherServlet 处理浏览器请求时会调用自己创建的 EmpController,然后再调用自己创建的EmpService,而这个 EmpService 是没有事务的,所以处理请求时没有事务功能的支持

    • spring-persist.xml:配置声明式事务。所以可以给 service 类附加声明式事务功能。

      结论:基于 spring-persist.xml 配置文件创建的 EmpService 有声明式事务的功能

      影响:由于 DispatcherServlet 的 IOC 容器会优先使用自己创建的 EmpController,进而装配自己创建的EmpService,所以基于 spring-persist.xml 配置文件创建的有声明式事务的 EmpService 用不上。

2.解决重复创建对象的问题

2.1 解决方案一

让两个配置文件配置自动扫描的包时,各自扫描各自的组件

  • SpringMVC就扫描XxxController
  • Spring扫描XxxService和XxxDao
2.2 解决方案二

如果由于某种原因,必须扫描同一个包,确实存在重复创建对象的问题,可以采取下面的办法处理.

  • spring-web.xml配置文件在整体扫描的基础上进一步配置:仅包含被@Controller注解标记的类
  • spring.xml配置在整体扫描的基础上进一步配置:排除被@Controller注解标记的类。

具体spring-web.xml配置文件中的配置方式如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!--    包扫描 只扫描Controller-->
<!--    两个Spring的配置文件扫描相同的包-->
<!--    为了解决重复创建对象的问题,需要进一步制定扫描组件时的规则-->
<!--    目标:仅包含@Controller注解标记的类-->
<!--    use-default-filters="false"表示关闭默认规则,表示什么都不扫描,此时不会把任何组件加入IOC容器;-->
<!--    再配合context:include-filter实现仅包含效果-->
    <context:component-scan base-package="com.atguigu" use-default-filters="false">
<!--        context:include-filter标签配置一个扫描组件时要包含的类的规则,追加到默认规则中-->
<!--        type属性:制定规则的类型,根据什么找到要包含的类,现在使用annotation表示基于注解来查找-->
<!--        expression属性:规则的表达式。如果type属性选择了annotation,那么expression属性配置注解的全类名-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--    加载mvc的注解驱动-->
    <mvc:annotation-driven></mvc:annotation-driven>

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

    <!-- Thymeleaf视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">

                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/>

                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>

                        <!--模板类型-->
                        <property name="templateMode" value="HTML5"/>
                        <!--模板的字符编码-->
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
<!--    使用import标签导入其他的xml配置文件-->
<!--    <import resource="spring.xml"></import>-->
    <mvc:view-controller path="/" view-name="index.html"></mvc:view-controller>
    <mvc:view-controller path="/add.html" view-name="add"></mvc:view-controller>
</beans>

具体spring.xml配置文件中的配置方式如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://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 https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--   1. 包扫描 扫描除了Controller的注解-->
    <context:component-scan base-package="com.atguigu">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

<!--    2.配置JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    3.配置DataSource对象-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${datasource.username}"></property>
        <property name="password" value="${datasource.password}"></property>
        <property name="url" value="${datasource.url}"></property>
        <property name="driverClassName" value="${datasource.driver}"></property>
    </bean>

<!--    4.声明事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <tx:annotation-driven></tx:annotation-driven>

<!--    5.aop切面-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

第六节 小结

  • DispatcherServlet 和 ContextLoaderListener 并存
    • DispatcherServlet 负责加载 SpringMVC 的配置文件,例如:spring-mvc.xml
    • ContextLoaderListener 负责加载 Spring 的配置文件,例如:spring-persist.xml
  • 两个 IOC 容器的关系:
    • ContextLoaderListener 创建的容器是父容器
    • DispatcherServlet 创建的容器是子容器
  • bean 的装配
    • 子容器可以访问父容器中的 bean
    • 父容器不能访问子容器中的 bean
  • 两个容器扫描同一个包会导致重复创建对象
    • 解决办法一:各自扫描各自的包
    • 解决办法二:
      • DispatcherServlet 创建的容器仅扫描 handler
      • ContextLoaderListener 创建的容器不扫描 handler

第四章 SSM整合

第一节Spring和Mybatis整合

1.思路

在这里插入图片描述

  1. 实现步骤:
    1.1 引入Spring和Mybatis整合的依赖、Mybatis的依赖
    1.2 持久层改造:持久层技术使用Mybatis
    1.2.1 创建全局配置文件
    (1). 类型别名配置的包扫描
    (2). 全局懒加载配置
    (3). 驼峰映射
    1.2.2 创建映射配置文件(每一个持久层接口创建一个映射配置文件)

    1.3 Spring整合Mybatis:
    1.3.1 在spring的持久层配置文件中做如下配置:
    (1). 配置SqlSessionFactoryBean(底层使用FactoryBean机制,其实创建在IOC容器中的是SqlSessionFactory对象)
    (1.1). 注入DataSource
    (1.2). 注入Mybatis的全局配置文件的路径
    (1.3). 注入要加载的映射配置文件的所在的路径
    (2). 配置MapperScannerConfigurer对象(用于扫描所有的持久层接口,创建持久层接口的代理对象)
    (2.1). 注入要扫描的持久层接口的包名

    1.4 删除所有的持久层实现类,在单元测试中使用Spring整合单元测试,测试Mybatis

2.Mybatis-Spring技术

官方介绍

相关技术之间版本匹配说明:
在这里插入图片描述

Mybatis-Spring 的依赖:

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

3.总体SSM整合所需依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.atguigu</groupId>  
  <artifactId>springmvc04-soldier-crudRestful-01</artifactId>
  <version>1.0-SNAPSHOT</version>  
  <packaging>war</packaging>
  <dependencies> 
    <!-- SpringMVC -->  
    <dependency> 
      <groupId>org.springframework</groupId>  
      <artifactId>spring-webmvc</artifactId>  
      <version>5.3.1</version> 
    </dependency>  
    <!-- 日志 -->  
    <dependency> 
      <groupId>ch.qos.logback</groupId>  
      <artifactId>logback-classic</artifactId>  
      <version>1.2.3</version> 
    </dependency>  
    <!-- ServletAPI -->  
    <dependency> 
      <groupId>javax.servlet</groupId>  
      <artifactId>javax.servlet-api</artifactId>  
      <version>3.1.0</version>  
      <scope>provided</scope> 
    </dependency>  
    <!-- Spring5和Thymeleaf整合包 -->  
    <dependency> 
      <groupId>org.thymeleaf</groupId>  
      <artifactId>thymeleaf-spring5</artifactId>  
      <version>3.0.12.RELEASE</version> 
    </dependency>  
    <dependency> 
      <groupId>org.junit.jupiter</groupId>  
      <artifactId>junit-jupiter-api</artifactId>  
      <version>5.7.0</version>  
      <scope>test</scope> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework</groupId>  
      <artifactId>spring-test</artifactId>  
      <version>5.3.1</version> 
    </dependency>  
    <!--引入LomBok的依赖-->  
    <dependency> 
      <groupId>org.projectlombok</groupId>  
      <artifactId>lombok</artifactId>  
      <version>1.18.8</version>  
      <scope>provided</scope> 
    </dependency>  
    <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->  
    <dependency> 
      <groupId>org.springframework</groupId>  
      <artifactId>spring-orm</artifactId>  
      <version>5.3.1</version> 
    </dependency>  
    <!-- MySQL驱动 -->  
    <dependency> 
      <groupId>mysql</groupId>  
      <artifactId>mysql-connector-java</artifactId>  
      <version>8.0.27</version> 
    </dependency>  
    <!-- 数据源 -->  
    <dependency> 
      <groupId>com.alibaba</groupId>  
      <artifactId>druid</artifactId>  
      <version>1.0.31</version> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework</groupId>  
      <artifactId>spring-aspects</artifactId>  
      <version>5.3.1</version> 
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>


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

  </dependencies> 
</project>

4.创建Mapper配置文件

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.atguigu.dao.SoldierDao">
    <delete id="deleteById">
        delete from t_soldier where soldier_id=#{id}
    </delete>

    <update id="update">
        update t_soldier
        <set>
            <if test="soldierName != null and soldierName != ''">
                soldier_name=#{soldierName},
            </if>
            <if test="soldierWeapon != null and soldierWeapon != ''">
                soldier_weapon=#{soldierWeapon}
            </if>
        </set>
        where soldier_id=#{soldierId}
    </update>

    <insert id="add">
        insert into t_soldier(soldier_name,soldier_weapon) values(#{soldierName},#{soldierWeapon})
    </insert>

    <sql id="columns">
        select soldier_id,soldier_name,soldier_weapon from t_soldier
    </sql>

    <select id="getSoldierById" resultType="Soldier">
        <include refid="columns"></include>
        where soldier_id=#{soldierId}
    </select>

    <select id="findAll" resultType="Soldier">
        <include refid="columns"></include>
    </select>

    <select id="findByName" resultType="Soldier">
        <include refid="columns"></include>
        where soldier_name=#{soldierName}
    </select>
</mapper>

5.在spring中配置SqlSessionFactoryBean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://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
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--   1. 包扫描 扫描除了Controller的注解-->
    <context:component-scan base-package="com.atguigu">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--    2.配置JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <!--    3.配置DataSource对象-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${datasource.username}"></property>
        <property name="password" value="${datasource.password}"></property>
        <property name="url" value="${datasource.url}"></property>
        <property name="driverClassName" value="${datasource.driver}"></property>
    </bean>


    <!--    4.声明事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <tx:annotation-driven></tx:annotation-driven>

    <!--    5.aop切面-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!--    6.spring整合mybatis-->
    <!--    6.1 配置SqlSessionFactoryBean-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--        注入数据源-->
        <property name="dataSource" ref="dataSource"></property>

        <!--        加载配置文件-->
        <property name="mapperLocations" value="classpath:mappers/*.xml"></property>
        <!--        类型别名注入的包扫描-->
        <property name="typeAliasesPackage" value="com.atguigu.pojo"></property>

<!--        settings设置-->
        <property name="configuration">
            <bean class="org.apache.ibatis.session.Configuration">
<!--                驼峰映射-->
                <property name="mapUnderscoreToCamelCase" value="true"></property>
<!--                全局加载-->
                <property name="lazyLoadingEnabled" value="true"></property>
<!--                二级缓存-->
                <property name="cacheEnabled" value="true"></property>
             </bean>
        </property>
    </bean>

<!--    6.2 配置MapperScannerConfigurer扫描持久层接口,创建持久层接口的代理对象-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.atguigu.dao"></property>
    </bean>
</beans>

6.测试

继承后能够正常执行
在这里插入图片描述

第二节 SSM整合总结

  1. SSM整合的简化
    2.1 彻底舍弃Mybatis的全局配置文件: Mybatis全局配置文件要做的配置全交给Spring中的SqlSessionFactoryBean里面进行配置
    (1). 类型别名配置的包扫描:
    (2). settings配置:








    2.2 采用<mybatis-spring:scan base-package=“com.atguigu.mapper”/> 代替MapperScannerConfigurer进行持久层的包扫描
    2.2.1 缺点: IDEA无法识别这个配置,所以IDEA认为持久层没有做包扫描,所以在注入持久层对象的时候IDEA会报红色
    2.2.2 怎么解决上述缺点呢?
    方法一: 使用@Resource注入代替@Autowired注入
    方法二: 欺骗IDEA,在持久层接口上添加@Repository注解

第五章 分页

第一节 分页的概述

1. 为什么要分页

如果应用程序显示数据不分页,会有三个问题:

  • 用户查看数据非常不方便。
  • 所有数据不分冷热全部显示出来,冷数据白白占用存储空间,浪费内存。
  • 在服务器端查询全部数据占用内存很大,给整个系统增加了很大压力。

2. 分页本身的概念

把系统中要显示的数据分成较小的单元,每个单元作为『一页』显示给用户。每次访问服务器只查询一页数据。

分页的好处:

  • 用户体验较好。
  • 服务器端每次只查询一部分数据,内存压力减小。
  • 对冷数据减少查询的次数,据此对系统性能进行优化。

3. 分页的细节

在这里插入图片描述

4. 实现分页的基本逻辑

4.1 物理分页

具体数据库不同,分页语法有区别。下面我们以 MySQL 为例来说明。MySQL 的分页需要借助 LIMIT 子句来完成。

select emp_id,emp_name,emp_salary from t_emp limit 0,5; # 查询第一页数据
select emp_id,emp_name,emp_salary from t_emp limit 5,5; # 查询第二页数据
select emp_id,emp_name,emp_salary from t_emp limit 10,5;# 查询第三页数据

LIMIT 子句的公式:

limit (pageNo-1)*pageSize,pageSize

注意:在 SQL 的语法中,LIMIT 子句必须出现在 SQL 语句最后。

4.2 逻辑分页
4.2.1 需求

为了能够在页面上全面显示分页相关的细节数据,总页数需要计算得到。

4.2.2 总页数计算方式

在这里插入图片描述

4.2.3 页码的合理化

页码的有效范围:1~总页数。修正方式:

  • 用户输入的页码 < 1:将页码设定为第一页
  • 用户输入的页码 > 总页数:将页码设定为最后一页
4.2.4 分页执行流程
  • 查询总记录数(用count()函数)
  • 查询当前页数据(使用limit查询)
  • 根据总记录数和每页条数计算总页数
  • 在1~总页数之间修正页码
  • 封装上述所有数据,发送到页面显示

第二节 实现分页

1. Mybatis的分页插件

具体使用细节可以参考:官方文档

1.1 引入依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>
1.2 配置
<!-- 配置 SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 
    ……
 
    <!-- 在 plugins 属性中配置 Mybatis 插件 -->
    <property name="plugins">
        <array>
            <bean class="com.github.pagehelper.PageInterceptor">
                <property name="properties">
                    <props>
                        <!-- 设置 reasonable 为 true 表示将页码进行合理化修正。页码的有效范围:1~总页数 -->
                        <prop key="reasonable">true</prop>
                        
                        <!-- 数据库方言:同样都是 SQL 语句,拿到不同数据库中,在语法上会有差异 -->
                        <!-- 默认情况下,按照 MySQL 作为数据库方言来运行 -->
                        <prop key="helperDialect">mysql</prop>
                    </props>
                </property>
            </bean>
        </array>
    </property>
 
</bean>

2.具体代码

2.1 首页添加超链接
<a th:href="@{/soldier/1/3}">分页查询士兵信息</a>
2.2 hadnler方法
package com.atguigu.controller;

import com.atguigu.pojo.Soldier;
import com.atguigu.service.SoldierService;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Controller
@RequestMapping("/soldier")
public class SoldierController {
    public static final String LIST_ACTION = "redirect:/soldier";
    public static final String PAGE_EDIT = "edit";
    public static final String PAGE_LIST = "list";
    private static final String PAGE_PAGINATION = "pagination";

    @Autowired
    private SoldierService soldierService;

    @DeleteMapping("/{id}")
    public String deleteById(@PathVariable("id") Integer soldierId){
        soldierService.deleteById(soldierId);
        // 重新查询所有,重定向查询所有方法
        return LIST_ACTION;
    }

    @PutMapping
    public String addOrUpdate(Soldier soldier){
        soldierService.addOrUpdate(soldier);
        return LIST_ACTION;
    }

    @GetMapping("/{id}")
    public String getSoldierById(@PathVariable("id") Integer soldierId, Model model){
        Soldier soldier = soldierService.getSoldierById(soldierId);
        model.addAttribute("soldier", soldier);
        return PAGE_EDIT;

    }

    @GetMapping
    public String findAll(Model model){
        List<Soldier> soldierList = soldierService.findAll();
        model.addAttribute("soldierList", soldierList);
        return PAGE_LIST;
    }
    
    @RequestMapping("/{pageNum}/{pageSize}")
    public String findPage(@PathVariable("pageNum") Integer pageNum,
                           @PathVariable("pageSize") Integer pageSize,
                           Model model){
        // 调用业务层的方法进行分页查询
        PageInfo<Soldier> pageInfo = soldierService.findPage(pageNum, pageSize);
        // 将分页信息存储到请求域
        model.addAttribute("pageInfo", pageInfo);
        return PAGE_PAGINATION;
    }
}
2.3 ServiceDao接口和ServiceDaoImpl实现类中创建findPage方法
    /**
     * 分页查询
     * @param pageNum
     * @param pageSize
     * @return
     */
    PageInfo<Soldier> findPage(Integer pageNum, Integer pageSize);
    @Override
    public PageInfo<Soldier> findPage(Integer pageNum, Integer pageSize) {
        // 1. 调用分页插件的方法,开启分页
        PageHelper.startPage(pageNum, pageSize);
        // 2.调用持久层的方法执行查询语句
        List<Soldier> soldierList = soldierDao.findAll();
        return new PageInfo<>(soldierList);
    }
2.4 显示翻页和数据展示
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>分页显示页面</title>
    <style type="text/css">
        table {
            border-collapse: collapse;
            margin: 0px auto 0px auto;
        }
        table th,td {
            border: 1px solid black;
            text-align: center;
        }
    </style>
</head>
<body>
<!--    第一部分显示当前页的士兵列表-->
    <table>
        <tr>
            <th>士兵序号</th>
            <th>士兵名称</th>
            <th>士兵武器</th>
        </tr>
        <tr th:each="soldier,status:${pageInfo.list}">
            <td th:text="${status.count}"></td>
            <td th:text="${soldier.soldierName}"></td>
            <td th:text="${soldier.soldierWeapon}"></td>
        </tr>
        <tr>
            <td colspan="3">
                总共有 <span th:text="${pageInfo.total}"></span>条数据,
                总共有 <span th:text="${pageInfo.pages}"></span>页.
                每页有 <span th:text="${pageInfo.pageSize}"></span>条数据
            </td>
        </tr>

        <tr>
            <td colspan="3">
                <span th:if="${pageInfo.pageNum > 1}">
                    <a th:href="@{/soldier/}+${pageInfo.navigateFirstPage}+'/'+${pageInfo.pageSize}">首页</a>
                    <a th:href="@{/soldier/}+${pageInfo.prePage}+'/'+${pageInfo.pageSize}">上一页</a>
                </span>

                <span th:each="num:${pageInfo.navigatepageNums}">
                    <a th:if="${num == pageInfo.pageNum}" th:text="${num}"></a>
                    <a th:unless="${num == pageInfo.pageNum}" th:href="@{/soldier/}+${num}+'/'+${pageInfo.pageSize}" th:text="${num}"></a>
                </span>

                <span th:if="${pageInfo.pageNum < pageInfo.pages}">
                    <a th:href="@{/soldier/}+${pageInfo.nextPage}+'/'+${pageInfo.pageSize}">下一页</a>
                    <a th:href="@{/soldier/}+${pageInfo.navigateLastPage}+'/'+${pageInfo.pageSize}">尾页</a>
                </span>
            </td>
        </tr>
    </table>
</body>
</html>
2.5 打印的sql语句

在这里插入图片描述

2.6 展示效果

在这里插入图片描述

第三节 为什么是 PageInfo 而不是 Page

1. List接口的具体实现

当我们开启了分页功能后,查询一个 List 集合,实际返回的是:com.github.pagehelper.Page 类型。这个 Page 类继承了 ArrayList,所以也兼容 List 接口类型。

2. 提出问题

如果我们将 Page 类型的对象存入模型,转发到视图模板上显示数据,会存在一个问题:视图模板技术只承认这个对象是一个 List 集合,不识别 List 集合之外的其它属性。

这一点在其他场合也需要注意:我们开发时尽量不要继承 ArrayList、HashMap 等类似的集合实现类。如果继承了,那么页面视图模板技术或其他表达式往往只能识别我们的对象是一个集合,而无法访问额外封装的其他属性。

所以 Page 对象需要封装为 PageInfo,让 list、pageNum 等等数据作为 PageInfo 对象的属性;PageInfo 本身并不是一个 List 类型的集合。

3. PageHelper 非侵入式的体现

PageHelper.startPage(pageNo, pageSize);

开启分页功能,就在 SQL 语句后面附加 LIMIT 子句并查询总记录数;不开启就还是按照原样查询。分页功能对原有的 Mapper 接口、SQL 语句没有任何影响。这个效果可以称之为是非侵入式,也可以说是可插拔的。

总结

  1. ContextLoaderListener: 在服务器启动的时候加载配置文件创建IOC容器
    1. ContextLoaderListener创建的IOC是DispatcherServlet 创建的IOC容器的父容器;
    2. 子容器中可以拿到父容器中的对象
    3. 在我们项目中DispatchServlet只负责表现层,只扫描Controller或者是RestController
    4. ContextLoaderListener负责其它的
  2. Spring与Mybatis整合
    1. 引入mybatis-spring的整合的依赖
    2. mybatis的使用和以前一样,只是不用写全局配置文件,并且也不用写创建持久层代理对象的那一堆代码
    3. 整合的目的: 在Spring的IOC容器中持有持久层的代理对象
    4. 整合的步骤:
      1. 在spring的配置文件中配置SqlSessionFactoryBean
        1. 注入dataSource
        2. 别名包扫描
        3. 驼峰配置
        4. 懒加载等等配置
        5. 指定映射配置文件的路径
      2. 扫描持久层接口所在的包
  3. PageHelper分页插件
    1. 目标: 以非侵入的方式在后端进行分页
    2. 使用步骤:
      1. 引入分页插件的依赖
      2. 在SqlSessionFactoryBean的配置中,配置分页插件
      3. 在业务层中:
        1. 调用PageHelper.startPage(pageNo,pageSize)开启分页
        2. 调用查询所有的持久层方法
        3. 使用PageInfo封装分页数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/23270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式Linux系统中ARM汇编语言的使用方法

大家好&#xff0c;今天主要大家聊一聊&#xff0c;如何在ARM中使用汇编语言的方法。 目录 第一&#xff1a;汇编基础简介 第二&#xff1a;处理器内部数据传输指令 第三&#xff1a;存储器访问指令 第一&#xff1a;汇编基础简介 我们在学习嵌入式Linux开发的时候是绝…

【用户画像】Redis_Jedis测试、将人群包存放到Redis中、挖掘类标签处理过程、决策树、用SparkMLLib实现决策树

文章目录一 Redis_Jedis_测试1 Jedis所需要的jar包2 连接Redis注意事项3 测试相关数据类型&#xff08;0&#xff09;测试连接&#xff08;1&#xff09;Key&#xff08;2&#xff09;String&#xff08;3&#xff09;List&#xff08;4&#xff09;set&#xff08;5&#xff0…

shiro-第一篇-基本介绍及使用

shiro 概述 shior的话&#xff0c;在第一次听说的时候单纯的任务它就是一个安全框架&#xff0c;可以对访问接口的用户进行验证等工作&#xff0c;类似拦截器或过滤器的东西&#xff0c;但是在学习后&#xff0c;发现远远不止这些&#xff0c;它的灵活性和易用性让我震惊&…

408 | 【计网】第二章 物理层 回顾

自用冲刺笔记整理。 部分图片来自王道。 加油ヾ(◍∇◍)ノ゙ (一)通信基础 1.信道、信号、带宽、码元、波特、速率/数据率、信源与信宿等基本概念 单向通信、半双工通信(双方都可接发,不能同时)、全双工通信码元:用一个固定时长(码元宽度)的信号波形表示一位k进制数字。 …

高项 风险管理论文

六个过程&#xff1a; 1&#xff0c;规划风险管理&#xff1a;决定如何进行规划和实施项目风险管理活动。 2&#xff0c;识别风险&#xff1a;判断哪些风险会影响项目&#xff0c;并以书面形式记录其特点。 3&#xff0c;实施定性风险分析&#xff1a;对风险概率和影响进行评…

通信原理学习笔记5-2:数字调制——连续相位和恒包络问题(非线性功放、连续相位CP FSK信号、最小频移键控MSK、GMSK)

为了最大程度利用非线性功放&#xff0c;需要降低信号PAPR&#xff0c;这要求信号具有恒包络特性信道带宽有限&#xff0c;需要降低信号带外泄露&#xff08;进而传输失真小&#xff09;&#xff0c;要求信号具有连续相位特性&#xff08;从而高频成分少&#xff09; 波形连续…

[附源码]java毕业设计文章管理系统查重PPT

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

让你不在为设计商品详情页而烦恼的工具

不想使用之前的详情页设计模板想更换怎么办&#xff1f;不满意自己用软件设计的详情页模板怎么办&#xff1f;下面跟着小编&#xff0c;教你使用这个在线设计工具乔拓云&#xff0c;在工具内不仅有大量的详情页设计模板&#xff0c;还有海量的详情页免扣设计素材能直接使用&…

[附源码]java毕业设计小区物业管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

springmvc-ssm整合

前言:在座的各位大佬好&#xff0c;最近学习了ssm&#xff0c;然后这是一篇整合ssm的笔记&#xff0c;参考的网上某马视频课的笔记嘿嘿~SSM整合需要掌握↓↓↓↓↓↓↓↓一、SSM整合【重点】1 SSM整合配置问题导入1.1 SSM整合流程1.2 SSM整合配置1.2.1 创建工程&#xff0c;添加…

2022年度中国PCB百强榜单公布

近日&#xff0c;2022慕尼黑华南电子展在深圳圆满举办。电巢直播作为电子工程领域流量前沿、专业度高的在线直播平台&#xff0c;参与了本次展会并搭建了“云观展”平台&#xff0c;对展会进行了全程实时直播。 在这场全国性的“电子企业盛会”中&#xff0c;有1100余家来自不…

你真的了解Spring的依赖查找吗?

1.写在前面 前面的博客我们介绍了Spring的总览&#xff0c;今天我们来了解下Spring的依赖查找的相关的内容&#xff0c;我们会从它的前世今生来带你了解下&#xff0c;以及各种类型的查找的方式&#xff0c;同时介绍对应的相对比较安全的查找的方式。以及会介绍一些比较常见的…

分布式系统设计模式和一致性协议,你用过哪些?

1、布隆过滤器 Bloom过滤器是一种节省空间的概率数据结构&#xff0c;用于测试元素是否为某集合的成员。它用于我们只需要检查元素是否属于对象的场景。 在BigTable&#xff08;和Cassandra&#xff09;中&#xff0c;任何读取操作都必须从组成Tablet的SSTable中读取。如果这些…

用于光波导耦合的倾斜光栅分析

1. 摘要 因其在确定衍射级上的高衍射效率&#xff0c;倾斜光栅广泛用于将光耦合到光波导中。如今&#xff0c;倾斜光栅广泛用于增强现实和混合现实应用中。本示例中将示范如何使用VirtualLab Fusion分析文献中具有特定参数的某些倾斜光栅的几何形状&#xff08;例如倾斜角、填充…

Word控件Spire.Doc 【图像形状】教程(1) ;如何在 Word 中插入图像(C#/VB.NET)

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

使用OpenCV计算两幅图像的协方差

要计算协方差首先要知道协方差的数学原理。 定义 Cov(X,Y) E{ [X-E(X)][Y-E(Y)] }为随机量X与Y的协方差。 其中E(X)为随机变量X的期望(均值)&#xff0c;E(Y)为随机变量Y的期望(均值)。 我们通常用下面的这个公式计算协方差。 Cov(X,Y)E(XY)-E(X)E(Y) 另外&#xff0c;大家…

【苹果家庭群发推送】软件安装最新的Appletweetios.macosimessage是用于发送Apple文本消息

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…

单商户商城系统功能拆解33—营销中心—包邮活动

单商户商城系统&#xff0c;也称为B2C自营电商模式单店商城系统。可以快速帮助个人、机构和企业搭建自己的私域交易线上商城。 单商户商城系统完美契合私域流量变现闭环交易使用。通常拥有丰富的营销玩法&#xff0c;例如拼团&#xff0c;秒杀&#xff0c;砍价&#xff0c;包邮…

热门Java开发工具IDEA入门指南——从Eclipse迁移到IntelliJ IDEA(三)

IntelliJ IDEA&#xff0c;是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能是非常强大的。 本文…

React 基础

React 基础 1.React概述 目标&#xff1a;了解 React 是什么 官方文档&#xff08;英文&#xff09;,官方文档&#xff08;中文&#xff09; 官方释义&#xff1a; A JavaScript library for building user interfaces &#xff08;一个用于构建用户界面的 JavaScript 库&a…