注:本文不包含怎么配置 idea ssm 项目,仅做个人向配置好之后,对于各个文件的分析及跳转之间的的浅解析(之前照着配的文章找不到了qwq)。
叠甲:新手,刚学,不太会,如有错请指出,谢谢!
目录
项目结构:
pom.xml
web.xml
转发器
过滤器
application.xml
spring-dao.xml
mybatis-config.xml
db.properties 数据库配置文件
spring-mvc.xml
spring-service.xml
代码部分:
dao 层文件(以 user 为例):
controller 层文件:
service 层文件:
controller 层:
一个完整的请求过程
项目结构:
先逐段分析,最后会给出我的配置文件。
pom.xml
注意:jdk 不要用 17.0.2 版本的,spring 会报错(版本太高的锅,不过现在是 2022 年 11 月 24 日,可能以后就能用了)
注意*2:我的数据库连接池用的是 c3p0(只用知道是数据库连接池的一种,了解到这里就行了)
仔细看看这个配置文件,应该没有什么看不懂的配置包吧 然后结合代码说一下那个 resources 标签:配置的时候会加载你列出来的 directory 里的 xml,properties 文件 ,你看那个 <filtering> 是 false,就是说过滤器别过滤这些目录里的文件,这些文件有用!!就这个意思。
<?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>org.example</groupId>
<artifactId>spring_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
<dependencies>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Servlet - JSP -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
</dependency>
</dependencies>
</project>
web.xml
这个项目应该是创建时直接选择的 maven + spring mvc 架构。
先从 web.xml 看起,这是整个项目的入口,因为是 ssm 项目,所以在 web.xml 里配置的是 spring 相关的环境。而mybatis,view 之类的都交给 spring 去管,这个后面再说。
转发器
带有 <servlet> 标签配置的是转发器。也就是说到时候 html,jsp 表单中,form action 提交的请求都会被这个东东处理后,再根据你定义的转发规则发给不同的 controller。
看 <init-param> 标签,这个转发器不是也属于 spring 的一部分吗,所以对它单独的配置并不在整体项目的配置文件 web.xml 中,而是放在另外一个文件中。学过 C++ 的话,web.xml 意思就像是C++里先把函数在文件开始时声明一下,再在后面写具体实现的函数体一样;没学过的话 就是说这个东西先声明一下有,但是具体的配置细节不在这里说,在哪里说呢?在classpath:声明的文件里,这个文件一会儿再说,你也可以根据目录快速跳转到对应文件看个大概。
<load-on-startup> 我简单查了一下,就是说正数值越小加载越快,一般都是写1,这个不用纠结。
<!-- 转发器的配置,这个转发器叫做 dispatcher-->
<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:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
这样,<servlet> 标签就配置完了。但是请求可能千千万,接下来就要看的是它要拦截并处理哪些请求了,请看 <servlet-mapping> 标签。
首先是名字。这个名字一定要是上面你配过的 servlet 的名字之一(你可能配好多 servlet),而且这个 mapping 和 servlet 的先后顺序不能颠倒,得先有东西,再有东西的使用范围吧?
<url-pattern> 就是对应的要拦截请求的特征。比如说 *.do 就是拦截任意以 .do 结尾的请求,有人说 .do 格式的是啥文件我怎么从没听说过呢?这就是 spring 拦截器和之前学的 Java web 里 form action 不同的地方:表单的请求不会直接转到某一个文件,而是先交给转发器,由它进行处理,这样一来,你的 form action 就不用限定格式了。
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
<url-pattern>*.in</url-pattern>
</servlet-mapping>
过滤器
<filter>标签代表的是过滤器,就是对于特定文件的处理。
<filter-name> 是过滤器名。
<filter-class> 是过滤器的类,这个直接用官网的就行。
<init-param> 是对过滤器的初始化。就是这个项目一启动,web 不是会给你自动配置一些量吗,过滤器就是在这时启动。encoding,utf-8这里应该能看懂。
<!-- 为预防中文乱码设置的字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
过滤器对应的转发器,可以看到它的作用对象是刚才的转发器名,这里根据自己的也要配好。
<!--过滤器作用的转发器-->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>
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">
<!-- 为预防中文乱码设置的字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!--过滤器作用的转发器-->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>
<!-- 转发器的配置,这个转发器叫做 dispatcher-->
<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:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
<url-pattern>*.in</url-pattern>
</servlet-mapping>
</web-app>
web.xml配置好了,接下来的配置文件就都放在 resources 目录下了。
前面也说了,web.xml 是对整个项目的配置,而 spring 相关的文件是资源文件,自然放到资源目录下。
application.xml
接下来看 application.xml ,曾记否?它首次出现是在配置 spring 的转发器 distacher 中,作为它的初始化文件路径出现的,现在我们来看看它的结构。
我这里使用的是 <import resource> 标签,就是说我又来了一层套娃,对该文件的配置又引用了三个配置文件。注意看项目结构,因为我 spring 相关的配置文件都放在 spring 目录下了,所以加了一个 spring/。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring/spring-dao.xml"/>
<import resource="spring/spring-service.xml"/>
<import resource="spring/spring-mvc.xml"/>
</beans>
spring-dao.xml
DAO(Data Access Object)是一个数据访问接口,数据访问:顾名思义就是与数据库打交道。
所以这个配置文件就相当于 mybatis 中的 mybatis-config.xml,用来配置与数据库的连接。
使用的是配置文件的方式来决定数据库,所以这样写。
数据库连接池的部分我也是抄的,结合注释能看个89不离10,也不解释了;
直接看 sqlSessionFactory 部分。这个在学 mybatis 的时候应该有印象,当时好像还要手动在 Java 类里获取 sqlSessionFactory 吧,你看现在在这里配置即可。我放在 mybatis-config.xml 文件里写了,想想 sqlSessionFactory 也就是要获取对象的位置和映射文件的位置,对不对?
下面那个获得映射文件是我报错找不到之后加上去的,现在想想,是不是可以直接修改 mybatis-config.xml 里的数值呢。
第四点就是将这个 mybatis 的设置统合到 spring 的管理之下啦。
<?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"
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">
<!-- 配置整合mybatis -->
<!-- 1.关联数据库文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 2.数据库连接池 -->
<!--数据库连接池
dbcp 半自动化操作 不能自动连接
c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置连接池属性 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 获取连接超时时间 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 当获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!-- 3.配置 SqlSessionFactory 对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置 MyBatis 全局配置文件:mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 配置 mapper 文件-->
<property name="mapperLocations" value="classpath:/com/test/**/*.xml"/>
</bean>
<!-- 4.配置扫描 Dao 接口包,动态实现 Dao 接口注入到 spring 容器中 -->
<!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入 sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 给出需要扫描 Dao 接口包 -->
<property name="basePackage" value="com.test.dao"/>
</bean>
</beans>
mybatis-config.xml
<typeAliases> 是类的小名,就是说到时候你写返回类型的时候,只要是在这个路径下的 pojo 类,直接写类名就行了,不需要再连着全类名一起写。
<mappers> 就是 mybatis 那些 xml 映射文件在哪里写的,你就写哪个包,到时候 mybatis 就知道噢,在 dao 这个包下找 xml 映射文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根标签 -->
<configuration>
<typeAliases>
<package name="com.test.pojo"/>
</typeAliases>
<mappers>
<package name="com.test.dao"/>
</mappers>
</configuration>
db.properties 数据库配置文件
useSSL=false 是因为我配置 MySQL 的时候就开启了免密登录,你如果也开启了的话这里一定要写 false,不然后面会报错~
因为配了免密登录,所以密码随便写了
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/(你的数据库名)?useSSL=false&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
spring-mvc.xml
mvc mvc,就是要处理视图层!
1.注解就算不会用也见别人用过吧,@RequestMapping 什么的
2.注解的意思能看个大概后,照抄即可
3.这个就是到时候一个请求结束后,可能会跳到一个界面,那这个界面在哪找?就按这里的规则找:prefix 是前缀,也就是返回界面的目录,这里我直接写到 web 主目录下了,所以可以不写,有的是 WEB/INF,就是说页面在 WEB/INF 这个路径下。
suffix 是后缀,就是到时候如果我返回一个以 loginOK 为文件名的字符串,再加上这个后缀就是 loginOK.jsp 了,这个系统给你组装好之后,就是说在 prefix 指定的目录下,结合你自己返回的文件名再加上 suffix 找文件。
<?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"
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">
<!-- 配置 SpringMVC -->
<!-- 1.开启 SpringMVC 注解驱动 -->
<mvc:annotation-driven />
<!-- 2.静态资源默认 servlet 配置-->
<mvc:default-servlet-handler/>
<!-- 3.配置 jsp 显示 ViewResolver视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value=""/>
<property name="suffix" value=".jsp" />
</bean>
<!-- 4.扫描 web 相关的 bean -->
<context:component-scan base-package="com.nxn.controller" />
</beans>
spring-service.xml
service 是什么的 service?想想结合数据库后就是增删改查,那肯定是针对数据库的操作。所以service 是对数据库的服务。
再想想纯用 mybatis 的时候,是不是先定义一个接口,由接口对应 xml 映射文件,再写一个接口实现类写具体的逻辑?这里就是那个意思,只不过这次不用你自己去调用 sqlSessionFactory 初始化接口了,你在这里配好,spring 帮你初始化,这就是所谓的 service。
事务管理器和数据库连接池那个看个大概然后 CTRL CV 就行了(狗头)
<?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"
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">
<!-- 扫描 service 相关的 bean -->
<context:component-scan base-package="com.test.service" />
<!-- ServiceImpl 注入到 IOC 容器中-->
<bean id="userServiceImpl" class="com.test.service.UserServiceImpl">
<property name="UserDao" ref="userDao"/>
</bean>
<bean id="messageServiceImpl" class="com.test.service.MessageServiceImpl">
<property name="MessageDao" ref="messageDao"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
先剧透一下,bean id 那里不是胡写的,要和 controller 里的注解名对应上才行。
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
代码部分:
dao 层文件(以 user 为例):
你看,和 mybatis 那里无差,这个就略过了。
controller 层文件:
service 层文件:
这里你可能就有点迷糊了,怎么这个 userService 类和上面数据库操作的 dao 类是一样的呢?还有实现类,这不是重复了吗?听我讲完,这是有逻辑在里面的。
首先 userService 这个接口要实现登录功能,所以这么写是没问题的,代码重复的问题先放一下;其次你看接口的实现类。它的确是实现了接口的方法,但是你看它的成员变量是什么?虽然代码一样,但是它是 dao 层控制数据库的 userDao 而不是 userService,而方法是怎么实现的?是使用在配置文件中注入的 userDao,借助它调用的 dao 层的方法,从而使用 xml 映射完成的对数据库的操作!
还记得吗?spring-service.xml 文件里,你在这里让 spring 帮你注入 userDao
在 spring-mvc.xml 里,你配置的 controller
说白了,userService 就是 mybatis 里接口实现类的套皮。因为按 mvc 的转发规则, controller 就是把请求的操作转给 service 处理,service 虽然不得不处理,但是它哪儿会啊?它一个 service 层怎么控制数据库呢?这时候就要交给能接触到数据库层的 userDao 了。service不会,但是 service 可以 new 一个 dao 的对象,用 dao 的对象调用 dao 的方法,获得数据库那边返回的结果,然后就当是它自己的结果(有点鸠占鹊巢那味了),之后把这个结果返回 controller,让 controller 根据结果再做决定。
所以差别就在这里,service 里的代码虽然和 dao 层有重复,但是就只是调用了下数据库的操作,之后就去处理了下自己的逻辑了,因为转发规则让他不得不处理数据库的东西,所以这部分代码虽然有点重复,还真不能删。
controller 层:
userController 类,@Controller 注解表示它的身份接收 service 返回的结果并做处理,根据不同的情况选择不同的文件名返回。
目录和后缀在 prefix 和 suffix 中配了,所以没事;userServiceImpl 也在之前的 xml 文件里配好了。
requestMapping 里的值和 login.jsp 里的 form action 保持一致,就能找到该方法。
package com.test.controller;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.test.service.UserService;
@Controller
public class UserController
{
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
@RequestMapping("/login.in")
public String login(@Param("userName") String userName, @Param("password") String password)
{
if (userService.login(userName, password) != null) {
return "leaveMessage";
}
return "login";
}
}
一个完整的请求过程
到此为止,全部文件就都解析了一遍了,接下来以具体的请求为例,看看请求是怎么走的。
逻辑:用户尝试登录,若数据库里有这个用户就跳转到留言界面,留言后将留言插入到数据库中并返回留言界面,否则就回到登录界面...
启动 tomcat,如图
login.jsp提交逻辑(注意看,在配置转发器的时候标注了拦截.in 结尾的类型的请求):
所以该请求会被处理,输入用户名和密码后跳转到 controller里(假设用户名和密码是正确的,其实无所谓,逻辑处理是类似的),看到这个方法上的值和 login 里的是一样的,所以好的,确认是这个方法来处理这个表单的请求。
controller 调用 userService 来处理这个请求,而 userService 是接口,调用它的方法就是调用接口实现类的方法,所以调用的是这个方法。(userDao)是在spring-mvc.xml 中 spring 注入的,有点多态的感觉:
userService 的实现类里,为了访问数据库,它让自己的 userDao 查下数据库,查到一个 user 对象,返回之(因为是正确的用户名和密码,所以能查到对应的实体类);
现在再回到 userService,看代码,我们拿到了非空的对象,所以返回“leaveMessage”。
mvc 视图层给我们加上前后缀,找到一个文件:
然后在相应的页面上也跳转了过来,此时显示的是 leaveMessage.jsp,form action 如下:
输入数据(夹带私货是吧:
点提交,就会走相似的路,这次把这条留言插到数据库里。
查看数据库,发现的确进去了。
好了,这个项目我解析完了。
参考资料:使用Idea创建一个JavaWeb的SSM(maven)项目~(史上最详细,傻瓜式教学,跟着我的做,不会你找我)_少年阿枫的博客-CSDN博客_idea创建ssm项目https://blog.csdn.net/MaNongXf/article/details/83418353
【极简上手】1小时学会SSM框架整合_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1hE411F77L/?vd_source=0403439485a748d1bdb6965301e61622