上一节主要记了SpringMVC在开发控制器过程中传接参的要点,接到参数后,下一步就要去使用参数去调用业务对象,这里很明显就需要去整合SSM了,整合SSM的开发在之前Spring的学习中使用XML配置文件和注解分别实现过,可以参考Spring(8)Spring(完),当时整合SSM时使用的是Struts2,这里再写一个demo,使用SpringMVC编写控制器。

一、SpringMVC控制器调用业务对象【SSM整合】

思路分析:

主要是需要在Controller层注入Service对象,在Service层注入DAO层对象并且做事务控制,DAO层对应mybatis的xml配置,最后再在工厂里面声明bean对象。

19.png

编码Demo:

dispatcher.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:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--设置注解扫描的路径-->
    <context:component-scan base-package="com.jin" />

    <!--引入SpringMVC的核心功能-->
    <mvc:annotation-driven />

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--    连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_mybatis?useSSL=false&allowPublicKeyRetrieval=true"></property>
        <property name="username" value="q"></property>
        <property name="password" value="w"></property>
    </bean>

    <!--    创建SqlSessionFactory  SqlSessionFactoryBean-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="typeAliasesPackage" value="com.jin.entity"></property>
        <!--        指定 实体类所在的包 com.jin.entity User-->
        <!--                                              Product-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com.jin.mapper/*Mapper.xml</value>
                <!--                指定 配置⽂件(映射⽂件)的路径 还有通⽤配置-->
                <!--                com.jin.mapper/*Mapper.xml-->
            </list>
        </property>
    </bean>

    <!--    创建DAO对象 MapperScannerConfigure-->
    <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
        <property name="basePackage" value="com.jin.dao"></property>
        <!--        指定 DAO接⼝放置的包 com.baizhiedu.dao-->
    </bean>

    <!--    DataSourceTransactionManager-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <tx:annotation-driven transaction-manager="dataSourceTransactionManager" />


</beans>

DAO

public class User implements Serializable {
    private Integer id;
    private String name;
    private String password;

    public User() {
    }

    public User(Integer id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
    get set
}

public interface UserDAO {
    public void save(User user);
}

<?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.jin.dao.UserDAO">
    <insert id="save" parameterType="user">
        insert into t_users(name,password) values (#{name},#{password})
    </insert>
</mapper>

create  table t_user(
    id integer primary key auto_increment,
    name varchar(12),
    password varchar(12)
);


Service

package com.jin.service;

import com.jin.dao.UserDAO;
import com.jin.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author jinyunlong
 * @date 2021/12/14 10:19
 * @profession ICBC锅炉房保安
 */
//@Transactional(isolation = Isolation.SERIALIZABLE)
//@Transactional(timeout = 2)
@Transactional
@Service
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDAO userDAO;


    @Override
    public void register(User user) {
//        try {
//            Thread.currentThread().sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        userDAO.save(user);
//        throw new RuntimeException("测试");
    }

}

public interface UserService {
    public void register(User user);
}

Controller

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/register")
    public String register(User user){
        userService.register(user);
        return "regOK";
    }

}

<body>
<form method="post" action="${pageContext.request.contextPath}/user/register">
    UserName<input type="text" name="name" id="name" /></br>
    PassWord<input type="text" name="password" id="password" /></br>
    <input type="submit" value="提交">
</form>
</body>

<body>
    <h1>regOk</h1>
</body
二、父子工厂(父子容器)拆分

现有SSM开发存在的问题,把MVC层的对象(Controller,mvc:annotation-driven/,视图解析器)与非MVC层的(连接池 DAO Service 事务),都配置在dispatcher.xml中,最终交给DispatcherServlet创建的工厂来进行实例化,存在着耦合问题,后续一旦替换MVC的实现(比如换成Struts2),代码都会受到影响,不利于项目的维护。

解决方法:就是把非MVC层的配置交给父工厂applicationcontext.xml管理即可

1. 把目前单一的工厂进行父子工厂的拆分

	1. 	子工厂(容器)(DispatcherServlet),读取dispatcher.xml
			完成与SpringMVC相关对象的创建:视图解析器、自定义类型转换器、静态资源排除、拦截器、视图控制器等
	2. 	父工厂(容器)(ContextLoaderListener),读取applicationContext.xml
			完成与非SpringMVC相关对象的创建:连接池、DAO、Service、事务、MQ、ES、Redis等

2. 工厂(容器)在需要获取对象进行操作时,首先获取子容器中所创建的对象,如果没有继续获得父容器中所创建的对象

21.png

编码配置略,把原先dispatcher.xml拆开即可,web.xml中配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!--创建工厂-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--指定SpringMVC配置文件的路径-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:dispatcher.xml</param-value>
    </init-param>
    <!--本Servlet会在tomcat启动的时候就会被创建-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
三、父子工厂存在的问题

如上配置的话,事务不生效,原因:

20.PNG

在子工厂dispathcher.xml中配置了包扫描,扫描的是项目路径下的所有类,所以当我们访问Controller时会先进子工厂去找Service对象,这时dispatcher.xml也是可以扫描到Service注解的,所以子工厂中也存在Service对象,但是没有对应的事务标签,所以生成的是Service的实现类对象,并非代理对象,事务不生效。解决方法是让子工厂只扫描Controller类,这样Controller在子工厂中找不到Service对象会去父工厂找,父工厂扫描Controller类以外的类,由于已配置事务标签,所以生成的Service对象为代理对象供子工厂使用。

1. 子容器dispatcher.xml:只扫描控制器及与MVC相关的内容
	 <context:component-scan base-package="com.jin.controller"/>
	 
2. 父容器applicationcontext.xml:不扫描控制器与MVC相关的内容
   <context:component-scan base-package="com.jin">
  		<context:exclude-filter type="aspectj" expression="com.jin.controller.*"/>
   </context:component-scan>

3. 注意:在SpringBoot中不会出现父子容器的问题。 
四、整合编码

将一、二、三整合,略


标题:SpringMVC(3)
作者:jyl
地址:http://jinyunlong.xyz/articles/2022/02/11/1644546108436.html