上一节主要记了SpringMVC在开发控制器过程中传接参的要点,接到参数后,下一步就要去使用参数去调用业务对象,这里很明显就需要去整合SSM了,整合SSM的开发在之前Spring的学习中使用XML配置文件和注解分别实现过,可以参考Spring(8)和Spring(完),当时整合SSM时使用的是Struts2,这里再写一个demo,使用SpringMVC编写控制器。
一、SpringMVC控制器调用业务对象【SSM整合】
思路分析:
主要是需要在Controller层注入Service对象,在Service层注入DAO层对象并且做事务控制,DAO层对应mybatis的xml配置,最后再在工厂里面声明bean对象。
编码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. 工厂(容器)在需要获取对象进行操作时,首先获取子容器中所创建的对象,如果没有继续获得父容器中所创建的对象
编码配置略,把原先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>
三、父子工厂存在的问题
如上配置的话,事务不生效,原因:
在子工厂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中不会出现父子容器的问题。
四、整合编码
将一、二、三整合,略