使用SpringMVC完成一个简单的登录页面,并实现持久层,业务层和表示层,这样可以更好的理解分层思想。
系统分层思想
MVC是属于表示层的一种软件架构思想,分层是更大的一种架构思想,更广范围更大,层级更灵活。
(1)为什么要分层
为了让系统好维护,采用分层,系统设计应该做到高内聚,低耦合。简单来说就是类的职责要单一,一个类拆成多个类,形成相互调用的层级关系,类之间不要直接依赖,采用依赖注入来完成调用。如写个一交易的方法,里面需要有验证权限、验证安全、对数据库的操作、提交事务、记录日志等逻辑,如果全部写到一个方法里,将让项目不好维护,如果将其拆分开来变成单个的类,然后采用依赖注入的方式完成类调用,将提高代码的可维护性。
(2)如何分层
上一层通过接口调用下一层提供的服务,下层实现类发生改变不会影响上一层,常用的分层结构就是表示层-业务层-持久层。
表示层 数据展现和操作界面,以及请求分发,表示层调用业务层,最好采用接口的方式依赖注入
业务层 封装了业务逻辑处理,业务层调用持久层,最好使用接口方式依赖注入
持久层 封装了数据访问逻辑,需要调用连接池实现类
实现步骤
实现案例先提交准备好素材,本案例写了一个有css美化效果的登录页面,用它来实现MVC登录演示。
(1)导包
本例中需导入Spring-webmvc、junit、mysql驱动包、dbcp连接池包等
1 <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"> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.boe</groupId> 4 <artifactId>SpringMVC02-Login-clyang</artifactId> 5 <version>0.0.1-SNAPSHOT</version> 6 <packaging>war</packaging> 7 8 <dependencies> 9 <!-- 添加Spring-webmvc --> 10 <dependency> 11 <groupId>org.springframework</groupId> 12 <artifactId>spring-webmvc</artifactId> 13 <version>4.2.3.RELEASE</version> 14 </dependency> 15 <dependency> 16 <groupId>org.springframework</groupId> 17 <artifactId>spring-webmvc</artifactId> 18 <version>4.2.3.RELEASE</version> 19 <classifier>sources</classifier> 20 </dependency> 21 <dependency> 22 <groupId>org.springframework</groupId> 23 <artifactId>spring-webmvc</artifactId> 24 <version>4.2.3.RELEASE</version> 25 <classifier>javadoc</classifier> 26 </dependency> 27 <!-- 添加junit测试包 --> 28 <dependency> 29 <groupId>junit</groupId> 30 <artifactId>junit</artifactId> 31 <version>4.12</version> 32 </dependency> 33 <!-- 添加dpcp --> 34 <dependency> 35 <groupId>commons-dbcp</groupId> 36 <artifactId>commons-dbcp</artifactId> 37 <version>1.4</version> 38 </dependency> 39 <!-- 添加mysql驱动包 --> 40 <dependency> 41 <groupId>org.wisdom-framework</groupId> 42 <artifactId>mysql-connector-java</artifactId> 43 <version>5.1.34_1</version> 44 </dependency> 45 <!-- 添加jstl标签包 --> 46 <dependency> 47 <groupId>jstl</groupId> 48 <artifactId>jstl</artifactId> 49 <version>1.2</version> 50 </dependency> 51 52 </dependencies> 53 54 </project>
(2)配置DispatcherServlet
在web.xml中配置即可
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> 3 <display-name>SpringMVC02-Login-clyang</display-name> 4 <welcome-file-list> 5 <welcome-file>index.html</welcome-file> 6 <welcome-file>index.htm</welcome-file> 7 <welcome-file>index.jsp</welcome-file> 8 <welcome-file>default.html</welcome-file> 9 <welcome-file>default.htm</welcome-file> 10 <welcome-file>default.jsp</welcome-file> 11 </welcome-file-list> 12 13 <!-- 配置DispatcherServlet --> 14 <servlet> 15 <servlet-name>springmvc</servlet-name> 16 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 17 <init-param> 18 <param-name>contextConfigLocation</param-name> 19 <param-value>classpath:spring-mvc.xml</param-value> 20 </init-param> 21 <load-on-startup>1</load-on-startup> 22 </servlet> 23 <servlet-mapping> 24 <servlet-name>springmvc</servlet-name> 25 <url-pattern>*.do</url-pattern> 26 </servlet-mapping> 27 28 </web-app>
(3)添加Spring-mvc.xml配置文件
里面需配置组件扫描,注解驱动,视图解析,连接池bean配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" 4 xmlns:jee="http://www.springframework.org /schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" 5 xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:mvc="http://www.springframework.org/schema/mvc" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd 9 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd 10 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd 11 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 12 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd 13 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> 14 15 <!-- 配置组件扫描 --> 16 <context:component-scan base-package="com.boe.*"></context:component-scan> 17 <!-- 配置注解驱动 --> 18 <mvc:annotation-driven></mvc:annotation-driven> 19 <!-- 配置视图解析器 --> 20 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 21 <property name="prefix" value="/WEB-INF/"></property> 22 <property name="suffix" value=".jsp"></property> 23 </bean> 24 <!-- 配置连接池 --> 25 <util:properties id="prop" location="classpath:config.properties"></util:properties> 26 <bean id="dbcp" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 27 <property name="driverClassName" value="#{prop.driver}"></property> 28 <property name="url" value="#{prop.url}"></property> 29 <property name="username" value="#{prop.user}"></property> 30 <property name="password" value="#{prop.pwd}"></property> 31 </bean> 32 33 </beans>
(4)添加db.properties文件
里面包含数据库连接的信息,包括url,driveClassName、user、password等
1 #MySQL路径,其中Admin_ycl代表mysql建表时的scheme名称 2 url=jdbc:mysql://localhost:3306/Admin_ycl 3 #用户名 4 user=root 5 #用户密码 6 pwd=2688 7 #驱动名 8 driver=com.mysql.jdbc.Driver 9 10 nativetoascii config.properties 11 12 initsize=1 13 14 maxactive=200 15 16 maxwait=5000 17 18 minidle=1 19 20 maxidle=1
(5)添加静态资源
包括css文件、登录页面jsp文件和图片素材等
1 <%@page pageEncoding="utf-8"%> 2 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 3 <!doctype html> 4 <html> 5 <head> 6 <title>欢迎登录Color Filter-JavaScript登录验证</title> 7 <meta charset="utf-8"> 8 <link rel="stylesheet" type="text/css" href="css/login.css" /> 9 <script type="text/javascript" src="script/login.js"></script> 10 </head> 11 <body> 12 <!--logo区+段落区--> 13 <div class="logo"> 14 <img src="image/LogoCF.jpg"> 15 <p id="logoDesc">Sign in to Color Filter</p> 16 </div> 17 <!--主体登录区--> 18 <div class="main"> 19 <div id="login"> 20 <!--5行2列--> 21 <!--表单用于输入登录信息,提交给服务器 --> 22 <!-- 23 onsubmit是表单提交事件,在点击提交按钮时触发。触发时先调用onsubmit内的方法,若该方法返回true则会自动提交表单 24 返回false则不提交,此处声明的方法可以起到拦截提交的作用,避免账号名和密码都不对时也能提交到服务器 25 onsubmit="return ((check_username()+check_pwd())==2)" 26 --> 27 <form action="logincheck.do" method="get"> 28 <table> 29 <tr> 30 <td colspan="2" style="text-align: left;text-indent: 29px">Username or email address</td> 31 </tr> 32 <tr> 33 <!-- 增加切换光标确认用户名格式是否正确 --> 34 <td colspan="2"><input type="text" name="user" id="username" onblur="check_username();" value="${username }"></td> 35 </tr> 36 <tr> 37 <td style="text-align: left;text-indent: 29px;width:15%">Password</td> 38 <td style="text-align: left;text-indent: 0px;width:85%"><a href="#">Forget password?</a></td> 39 </tr> 40 <tr> 41 <td colspan="2" style="width: 345px"><input type="password" name="pwd" id="pwd" onblur="check_pwd();" value="${password }"></td> 42 </tr> 43 <tr> 44 <td colspan="2" style="text-align: left;text-indent: 29px">Security Code</td> 45 </tr> 46 <tr> 47 <td style="padding-left:32px;width:60%;text-align:left;"><input type="text" name="valicode" id="valicode"/></td> 48 <!-- <td style="padding-top:3px;width:40%;text-align:right;padding-right:32px"><img alt="验证码" src="createIMG.do" onclick="this.setAttribute(\'src\',\'createIMG.do?x=\'+Math.random())"></td> --> 49 <!-- 添加登录失败提示 --> 50 <td>${login_fail}</td> 51 </tr> 52 <tr> 53 <td colspan="2" id="buttontd"><input type="submit" name="btn" value="Sign in" id="button"></td> 54 </tr> 55 </table> 56 </form> 57 </div> 58 <div id="regist"> 59 <p style="text-align: center;">New to Color Filter? <a href="#">Create an account.</a></p> 60 </div> 61 </div> 62 <!--版权、隐私、法律相关--> 63 <div class="foot"> 64 <p><a href="#">Terms</a> <a href="#">Privacy</a> <a href="#">Security</a> Contact Color Filter</p> 65 </div> 66 </body> 67 68 </html>
(6)写持久层接口和实现类,在写持久层前需准备好对应数据表的实体类,需完成junit测试
1 package com.boe.dao; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import javax.sql.DataSource; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.beans.factory.annotation.Qualifier; 10 import org.springframework.stereotype.Repository; 11 import com.boe.entity.Admin; 12 13 /** 14 * 持久层实现类-JDBC 15 * @author yangchaolin 16 */ 17 @Repository("loginJDBC") 18 public class LoginDAOImpl implements LoginDAO{ 19 @Autowired 20 @Qualifier("dbcp") 21 private DataSource ds; 22 23 //根据用户名查找用户信息 24 public Admin findAdminByAdmincode(String admincode) { 25 Admin admin=null; 26 Connection conn=null; 27 try { 28 conn=ds.getConnection(); 29 if(conn==null) { 30 throw new RuntimeException("网络异常,或数据库被关闭"); 31 } 32 //查询 33 String sql="select * from Admin where admincode=?"; 34 PreparedStatement ps=conn.prepareStatement(sql); 35 ps.setString(1, admincode); 36 ResultSet result=ps.executeQuery(); 37 if(result.next()) { 38 admin=new Admin(); 39 admin.setAdminCode(admincode); 40 admin.setAdminId(result.getInt("ADMINID")); 41 admin.setEmail(result.getString("EMAIL")); 42 admin.setEnrolldate(result.getTimestamp("ENROLLDATE")); 43 admin.setName(result.getString("NAME")); 44 admin.setPassword(result.getString("PASSWORD")); 45 admin.setTelephone(result.getString("TELEPHONE")); 46 } 47 }catch(Exception e) { 48 e.printStackTrace(); 49 throw new RuntimeException(e); 50 }finally { 51 if(conn!=null) { 52 try { 53 conn.close(); 54 } catch (SQLException e) { 55 e.printStackTrace(); 56 throw new RuntimeException(e); 57 } 58 } 59 } 60 return admin; 61 } 62 }
(7)写业务层接口和实现类,在写业务层的同时自定义了一个异常类,实现自定义异常抛出给表示层,需完成junit测试
1 package com.boe.service; 2 3 import javax.annotation.Resource; 4 5 import org.springframework.stereotype.Service; 6 7 import com.boe.dao.LoginDAO; 8 import com.boe.entity.Admin; 9 import com.boe.exception.ApplicationException; 10 11 /** 12 * 业务层实现类 13 * @author yangchaolin 14 */ 15 @Service("loginServiceImpl") 16 public class LoginServiceImpl implements LoginService{ 17 @Resource(name="loginJDBC") 18 private LoginDAO dao; 19 20 public Admin checkLogin(String adminCode, String pwd) { 21 Admin admin=dao.findAdminByAdmincode(adminCode); 22 if(admin==null) { 23 throw new ApplicationException("用户名错误"); 24 } 25 if(!admin.getPassword().equals(pwd)) { 26 throw new ApplicationException("密码错误"); 27 } 28 return admin; 29 } 30 }
(8)写表示层接口和实现类,实现登录验证,并返回登录提示和结果
1 package com.boe.controller; 2 3 import javax.annotation.Resource; 4 5 import org.springframework.stereotype.Controller; 6 import org.springframework.ui.ModelMap; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 9 import com.boe.entity.Admin; 10 import com.boe.entity.userInfo; 11 import com.boe.exception.ApplicationException; 12 import com.boe.service.LoginService; 13 14 @Controller 15 public class mainController { 16 @Resource(name="loginServiceImpl") 17 private LoginService service; 18 19 //去到登录页面 20 @RequestMapping("/toLogin.do") 21 public String toLogin() { 22 System.out.println("去到登录页面"); 23 return "login"; 24 } 25 //验证登录 26 @RequestMapping("/logincheck.do") 27 public String login(userInfo data,ModelMap mm) { 28 System.out.println("登录验证"); 29 System.out.println("用户名为:"+data.getUser()); 30 System.out.println("密码为:"+data.getPwd()); 31 //处理业务层异常 32 try { 33 Admin admin=service.checkLogin(data.getUser(), data.getPwd()); 34 if(admin!=null) { 35 System.out.println("登录成功"); 36 mm.addAttribute("user",data.getUser()); 37 } 38 }catch(Exception e) { 39 //应用错误,即用户填写错误 40 if(e instanceof ApplicationException) { 41 mm.addAttribute("login_fail", e.getMessage()); 42 mm.addAttribute("username",data.getUser()); 43 mm.addAttribute("password", data.getPwd()); 44 return "login"; 45 } 46 //系统错误 47 return "loginNG"; 48 } 49 return "loginOK"; 50 } 51 }
测试结果,正常进入登录页面,并展示css效果。
用户名和密码正确时
用户名错误
密码错误
以上就完成了使用SpringMVC完成登录页面。
另外在写分层时发现上一层调用下一层时,只需要写注解@Resource或@Autowired&@Qualifier就行,两种都能实现依赖注入,并且发现没set或构造器方法,这是怎么实例化对象的呢?因为Spring帮我们完成了set方法或构造器方法,因此看到不需要写也能注入。
参考博文