很久之前写了一篇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','123');
相应的,我们可以在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方便一些。
最后,附上项目组织层级结构树
由于知识水平有限,如有遗漏错误之处,请指正,非常感谢。