很久之前写了一篇SSH搭建例子,由于工作原因已经转到SpringMVC+Mybatis,就以之前SSH实现简单登陆的例子,总结看看SpringMVC+Mybatis怎么实现。
Spring一开始是轻量级的框架,在SSH中,处于中间粘合剂的作用,核心作用是IoC(控制反转)、DI(依赖注入),IoC和DI是同一个概念,只是以不同角度进行解释。简单的说,就是Spring帮助你管理Bean,只要写好了配置文件或者Spring注解,那么Spring可以自动帮你创建Bean,不需要手动new。经过后来的发展出现的SpringMVC框架,与Struts一样,同属于MVC框架的一种,SpringMVC可以说和Spring是两回事了,已经是一个比较重的框架了,我的理解是SpringMVC=Struts+Spring了。
Spring还有另外一个重要组成部分是AOP(Aspect-Oriented Programming 面向切面编程),目前主要使用在拦截器方面。实际上Struts也有拦截器,概念是类似的。
Hibernate、Mybatis、ibatis都是常用的持久层框架,它们都遵从ORM设计,可以简单的认为Hibernate比较重,Mybatis、ibatis比较轻量,Mybatis又是由ibatis发展来的,所以Mybatis与ibatis非常相似,阿里用的就是ibatis(毕竟当时还没有Mybatis),三者之间的区别网上可以搜一下,就不展开说了。
IDE集成开发工具我也从MyEclipse转到IntelliJ IDEA了(版本是2017.1.3),工具上使用肯定有一些区别,但是写代码上是没有区别的。
一、建立Maven SpringMVC项目
首先还是要下载安装好JDK,目前JDK版本已经到9,也不用特意去下载J2EE的版本,就下载一个J2SE版本的就可以,我下载的是JDK8的,开发环境是Windows,所以下载了一个Windows x64的jdk-8u161-windows-x64.exe(下载地址 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html),安装好之后配置好环境变量,主要要设置JAVA_HOME和PATH两个环境变量,CMD命令行敲java -version有版本信息展示就可以了。
另外为了更好的管理jar,我使用了maven(下载地址 https://maven.apache.org/download.cgi),网上搜索一下如何配置即可,主要配置MAVEN_HOME和PATH环境变量,在IDEA里还要在setting->Build,Execution,Deployment->Build Tools->Maven里设置Maven home directory、User setting file、Local repository。
使用IDEA创建maven SpringMVC项目的详细过程请参考博文https://www.cnblogs.com/Sinte-Beuve/p/5730553.html,下面简述步骤:
打开IDEA之后,File->new->Project,选择如下maven-archetype-webapp
next,输入GroupId和ArtifactId,next,next,输入Project name
点击finish,就创建好项目了,这时maven会首先去下载maven骨架,也就是项目组织结构配置文件,还有一些必要的jar包。
等maven下载完成之后,就得到了基本项目框架,参照上面博文,建立java目录并标记source,建立相关pakage,项目框架就基本完成了,我的项目结构如下图所示:
这时候,我们要利用maven加入springmvc、mybatis相关jar包,打开pom.xml,仿照如下添加properties和dependencies
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>springmvctest</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springmvctest Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- spring版本号 -->
<spring.version>4.1.6.RELEASE</spring.version>
<!-- mybatis版本号 -->
<mybatis.version>3.2.6</mybatis.version>
<!-- log4j日志文件管理包版本 -->
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>1.2.17</log4j.version>
<!-- jackson包版本 -->
<jackson.version>2.5.0</jackson.version> </properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--spring单元测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency> <!-- springMVC核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency> <!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.0.9.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency> <!-- AOP begin -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
<!-- AOP end --> <!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency> <!--mybatis spring 插件 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency> <!-- Mysql数据库驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency> <!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> </dependencies>
<build>
<finalName>springmvctest</finalName>
</build>
</project>
如果后面还有需要下载的jar包,比如log4j等,可以去这个网站http://mvnrepository.com搜索相关jar包和版本,找到相关信息,也同样写一个dependency,maven就自动去下载了。
二、使用Spring
WEB-INF下的web.xml可以理解为整个项目的入口,无论是Struts还是Spring都是先从这个web.xml找到相关配置,再进入struts或是spring的框架中的。打开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_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
以上就添加了Spring框架能力,DispatcherServlet顾名思义,就是转发的意思,mapping的url-pattern写的是/,说明所有请求都使用这个DispatcherServlet进行转发。大家可以对比一下struts的配置文件,struts是使用filter的,而spring使用的是servlet。有了Dispatcher转发,就需要再添加一个写怎么样转发的配置文件dispatcher-servlet.xml,同样在WEB-INF底下添加这个文件即可,dispatcher-servlet.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"
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 http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<description>Spring Configuration</description>
<!-- 静态资源(js、image等)的访问 -->
<mvc:default-servlet-handler/>
<!-- 配置注解驱动 可以将request参数与绑定到controller参数上 -->
<mvc:annotation-driven />
<!-- 开启组件自动扫描;使用Annotation自动注册Bean,解决事物失效问题:在主容器中不扫描@Controller注解,在SpringMvc中只扫描@Controller注解。 -->
<!-- base-package 如果多个,用“,”分隔 -->
<context:component-scan base-package="com.test.dao,com.test.service,com.test.controller" />
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP-->
<!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view/"/><!--设置JSP文件的目录位置-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
<mvc:annotation-driven />这一句说明启用自动扫描Spring注解,<context:component-scan base-package="com.test.dao,com.test.service,com.test.controller" />这句说明扫描的位置。defaultViewResolver这个bean主要是用来方便controller返回寻找jsp视图,定义了jsp目录位置是/WEB-INF/view/下面。
那么,这个时候,我们来先写一个controller,名字就叫IndexController吧,用来处理Index主页,其实就是登陆页面。
package com.test.controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; /**
* Created by jeff on 2018/2/13.
*/
@Controller
@RequestMapping("/")
public class IndexController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String loginview(){
return "loginview";
} @RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(@ModelAttribute("username") String username, @ModelAttribute("password") String password){
if(username.equals("jeff") && password.equals("123"))
return "index";
return "loginview";
}
}
可以看到,Class IndexController上面有两个注解,一个是@Controller,说明这个Class是一个Controller,另一个是@RequestMapping,相当于访问路径。
在IndexController定义了两个方法,loginview和login,方法上面同样有@RequestMapping注解,细分访问路径,还定义了它是GET还是POST方法。
在login方法的参数username和password前面还有@ModelAttribute注解,这是对应页面元素的name的,用于获取页面元素的值。这里其实就是依赖注入了,我们没有初始化username和passowrd,就直接使用了,其实就是Spring帮我们获取并初始化设置了页面对应的值进username和password里了。
那么,return "loginview"和return "index"又是什么意思呢?这两个方法的返回值都是String类型,返回给谁呢?刚刚上面dispatcher-servlet.xml文件中,咱们定义了defaultViewResolver这个bean,就是用来处理这个String的,返回一个index,那么Spring就会去你定义好的/WEB-INF/view/下面,找对应的jsp文件。所以,应该可以猜到,我们需要在/WEB-INF/view/下面新建两个jsp文件,一个叫loginview.jsp,一个叫index.jsp。
loginview.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="zh-CN">
<body>
<form method="get" action="/login">
登录名:<input name='username' type="text" /><br/>
密码:<input name ='password' type="password" /><br/>
<button type="submit" style="width: 70px; height: 20px;">确定</button>
</form>
</body>
</html>
index.jsp
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
完成了这些,还不能运行起来,我们还有一个重要的东西还没有,那就是tomcat或者jboss这些容器。初学者学习了几个月,说不定其实还没有了解什么是容器,容器是用来做什么的。首先,容器是指servlet容器,就是存放我们写的servlet的。至于什么作用,其实大家从C写面向过程过来的话,可以想一想,C里面必须有一个main函数,不然程序从哪里开始呢?学C++使用MFC框架时,我就发现没有了main函数,觉得很奇怪,程序怎么知道从哪里进去呢?我们上面写的代码,配置文件,也没有指定任何的main函数。其实main函数就是容器里的,可以想象成这个main函数一直在循环等待触发事件,一旦收到点击打开某个网页之类的指令,那么容器就会去找相应的servlet进行处理。
也许您还会问:那什么又是servlet呢?简单的说,就是实现了servlet接口的类或是继承HttpServlet、GernericServlet的类都叫做servlet(通常继承HttpServlet),可以覆写doPost()、doGet()等方法。但是我们会发现,我们上面没有一个类实现了servlet接口或继承自HttpServlet、GernericServlet,那么http如何找到我们的Controller的呢?如上面提到的DispatcherServlet,它实现了servlet接口。这下大家应该明白,容器接收到http请求,找到web.xml看到配置了的DispatcherServlet,进入DispatcherServlet,由它转到我们的Controller,进行后面的处理,Controller将处理结果返回给DispatcherServlet,然后通过doPost()或者doGet()返回页面。
了解了这些,就去下载一个tomcat或者jboss容器,在idea里配置好就行了,我这里使用常用的tomcat,网上搜一下idea配置tomcat就可以了。
run之后,打开浏览器,输入http://localhost:8080/就可以看到登陆页面了
输入jeff登录名和123密码,即可跳转到index首页,其他登陆名密码则不会跳转
三、使用Mybits
既然Mybatis是一个持久层框架,使用Mybatis之前,我们需要有一个数据库,那么下载一个常用的开源免费的MySQL安装配置好即可(https://dev.mysql.com/downloads/mysql/)。
安装好之后,使用MySQLWorkbench连进去,进行创建数据库、建表等操作。
create database springtest;
use springtest; create table users
(
id int PRIMARY KEY AUTO_INCREMENT,
username varchar(20),
password varchar(50)
) auto_increment = 1; insert into users(username,password) values('jeff','');
相应的,我们可以在entity包下新建一个Users类,这个类就是常说的Model层的javaBean(但没有实现序列化接口Serializable)
package com.test.entity; import org.springframework.stereotype.Repository; /**
* Created by jeff on 2018/2/22.
*/
public class Users { private Integer id;
private String username;
private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password;} }
这时候在项目中需要连接MySQL数据库,在resources下添加jdbc.properties文件,内容如下:
jdbc_driverClassName=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://localhost:3306/springtest
jdbc_username=root
jdbc_password=888888
在dispatcher-servlet.xml增加以下内容:
...
<!-- 配置数据源 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
<!--要是有多个配置文件,只需在这里继续添加即可 -->
</list>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc_driverClassName}</value>
</property>
<property name="url">
<value>${jdbc_url}</value>
</property>
<property name="username">
<value>${jdbc_username}</value>
</property>
<property name="password">
<value>${jdbc_password}</value>
</property>
</bean>
<!-- 配置Mybatis的文件 ,mapperLocations配置**Mapper.xml文件位置,configLocation配置mybatis-config文件位置-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
...
propertyConfigurer这个bean定义了配置文件路径,dataSource这个bean找到配置文件就自动注入property。
在jdbc.properties中配置了jdbc_driverClassName=com.mysql.jdbc.Driver,这就是咱们连接MySQL的jar包包含的数据库驱动,我们在前面pom.xml已经写了一个MySQL的dependency,已经将该jar包下载下来了,现在就可以直接使用了,如果使用别的数据库,那么就换成别的驱动包即可,相应的jdbc_driverClassName就需要修改。
sqlSessionFactory这个工厂类bean是mybatis推荐的使用方式,mapperLocations定义了mybatis的mapper xml文件的位置,可以看到我这是定义在resources下mapper目录下的。
所以,我们需要在mapper目录下新建一个UsersMapper.xml文件,内容如下:
<?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.test.mapper.UsersMapper">
<!--设置domain类和数据库中表的字段一一对应,注意数据库字段和domain类中的字段名称不致,此处一定要!-->
<resultMap id="BaseUsers" type="com.test.entity.Users">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="username" property="username" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
</resultMap>
<!-- 查询所有记录 -->
<select id="selectAllUsers" resultMap="BaseUsers">
SELECT * FROM users
</select>
<!-- 查询单个记录 -->
<select id="selectUsersByUsername" parameterType="java.lang.String" resultMap="BaseUsers">
SELECT * FROM users WHERE username=#{username}
</select>
</mapper>
这样就定义了数据库表与entity类的映射关系,所谓ORM,就是对象于数据库表映射的意思。完成了这个,我们就需要写dao、service接口类和实现类了
dao接口类
package com.test.dao; import com.test.entity.Users; /**
* Created by jeff on 2018/2/22.
*/
public interface UsersDao {
Users getUsersByUsername(String username);
}
dao实现类
package com.test.dao.impl; import com.test.dao.UsersDao;
import com.test.entity.Users;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.stereotype.Repository; import javax.annotation.Resource; /**
* Created by jeff on 2018/2/22.
*/
@Repository
public class UsersDaoImpl extends SqlSessionDaoSupport implements UsersDao {
@Resource
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
// TODO Auto-generated method stub
super.setSqlSessionFactory(sqlSessionFactory);
}
@Override
public Users getUsersByUsername(String username){
return this.getSqlSession().selectOne("com.test.mapper.UsersMapper.selectUsersByUsername", username);
}
}
service接口类
package com.test.service; import com.test.entity.Users; /**
* Created by jeff on 2018/2/22.
*/
public interface UsersService {
Users getUsersByUsername(String username);
}
service实现类
package com.test.service.impl; import com.test.dao.UsersDao;
import com.test.entity.Users;
import com.test.service.UsersService;
import org.springframework.stereotype.Service; import javax.annotation.Resource; /**
* Created by jeff on 2018/2/22.
*/
@Service
public class UsersServiceImpl implements UsersService {
@Resource
private UsersDao usersDao;
@Override
public Users getUsersByUsername(String username){
return usersDao.getUsersByUsername(username);
}
}
这时候IndexController需要修改为如下:
package com.test.controller; import com.test.entity.Users;
import com.test.service.UsersService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import javax.annotation.Resource; /**
* Created by jeff on 2018/2/13.
*/
@Controller
@RequestMapping("/")
public class IndexController {
@Resource
private UsersService usersService; @RequestMapping(value = "/", method = RequestMethod.GET)
public String loginview(){
return "loginview";
} @RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(@ModelAttribute("username") String username, @ModelAttribute("password") String password){
Users user = usersService.getUsersByUsername(username);
if(user != null && user.getPassword().equals(password))
return "index";
return "loginview";
}
}
这样就完成了使用MyBatis连接数据库查询用户信息跳转index主页的功能。
这里根据上面使用的Spring注解解释一下,@Repository意思是仓库、储藏,这里就是持久层的意思,专门用来注解这是持久层bean,@Service意思就是服务层,专门用来注解服务层,还有一种注解是@Component,意思是组件,用来注解无法定义是服务层还是持久层的其他bean。这三个注解效果是一样的,随便换哪一个都是可以的,但为了方便阅读和规范,请正确使用在对应的bean上。
@Controller的注解类似于struts的@Action注解,一般定义web层,直接与页面交互。
@Resource注解标记了该属性、方法需要被依赖注入。值得一提的是该注解并非Spring注解,而是J2EE的注解,只是Spring也支持使用该注解,Spring自己也实现了一个@AutoWired注解,两个注解都能实现依赖注入,但使用起来有细微的区别,我通常使用@Resource方便一些。
最后,附上项目组织层级结构树
由于知识水平有限,如有遗漏错误之处,请指正,非常感谢。