spirng之springMVC实例详解

1、Spring Web MVC是什么

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。

 

另外还有一种基于组件的、事件驱动的Web框架在此就不介绍了,如Tapestry、JSF等。

 

Spring Web MVC也是服务到工作者模式的实现,但进行可优化。前端控制器是DispatcherServlet;应用控制器其实拆为处理器映射器(Handler Mapping)进行处理器管理和视图解析器(View Resolver)进行视图管理;页面控制器/动作/处理器为Controller接口(仅包含ModelAndView handleRequest(request, response) 方法)的实现(也可以是任何的POJO类);支持本地化(Locale)解析、主题(Theme)解析及文件上传等;提供了非常灵活的数据验证、格式化和数据绑定机制;提供了强大的约定大于配置(惯例优先原则)的契约式编程支持。


2、Spring Web MVC能帮我们做什么

√让我们能非常简单的设计出干净的Web层和薄薄的Web层;

√进行更简洁的Web层的开发;

√天生与Spring框架集成(如IoC容器、AOP等);

√提供强大的约定大于配置的契约式编程支持;

√能简单的进行Web层的单元测试;

√支持灵活的URL到页面控制器的映射;

√非常容易与其他视图技术集成,如Velocity、FreeMarker等等,因为模型数据不放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用);

√非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API;

√提供一套强大的JSP标签库,简化JSP开发;

√支持灵活的本地化、主题等解析;

√更加简单的异常处理;

√对静态资源的支持;

√支持Restful风格。


3、准备开发环境和运行环境:

☆开发工具:eclipse

☆运行环境:resin-pro-3.0.28

☆工程:web工程(springmvc-spring3.0)

☆spring框架下载:

spring-framework-3.1.1.RELEASE-with-docs.zip(下载地址略)

☆依赖jar包:

为了简单,将spring-framework-3.1.1.RELEASE-with-docs.zip/dist/下的所有jar包拷贝到项目的WEB-INF/lib目录下;

并且添加依赖包commons、log4j等,这里就不一一列出来了。


4、准备数据

CREATE TABLE USER(
	user_id INT AUTO_INCREMENT PRIMARY KEY,
	user_name VARCHAR,
	PASSWORD VARCHAR,
	credits INT,
	last_ip VARCHAR,
	last_date DATE
)

//记录用户登录日志
CREATE TABLE loginlog(
	loginlog_id INT AUTO_INCREMENT PRIMARY KEY,
	user_id INT ,
	ip VARCHAR,
	login_time DATE
)

在user表中添加测试数据:admin,密码为123456


5、创建工程


工程目录如下图所示:




5.1  创建领域对象


领域对象,也就是我们通常所说的实体类,它代表了业务的状态,一般来说,领域对象属于业务层,但他贯穿展示层,业务层和持久层,并最终被持久化到数据库中。


用户领域对象:


public class User implements Serializable {

	public User() {
		// TODO Auto-generated constructor stub
	}

	private int userid;
	private String username;
	private String password;
	private int credits;
	private String lastIp;
	private Date lastDate;

	public int getUserid() {
		return userid;
	}

	public void setUserid(int userid) {
		this.userid = userid;
	}

	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;
	}

	public String getLastIp() {
		return lastIp;
	}

	public void setLastIp(String lastIp) {
		this.lastIp = lastIp;
	}

	public Date getLastDate() {
		return lastDate;
	}

	public void setLastDate(Date lastDate) {
		this.lastDate = lastDate;
	}

	public int getCredits() {
		return credits;
	}

	public void setCredits(int credits) {
		this.credits = credits;
	}

}

登陆日志领域对象:

public class Loginlog implements Serializable{

	public Loginlog() {
		// TODO Auto-generated constructor stub
	}

	private int loginlogId;
	private int userId;
	private String ip;
	private Date loginTime;

	public int getLoginlogId() {
		return loginlogId;
	}

	public void setLoginlogId(int loginlogId) {
		this.loginlogId = loginlogId;
	}

	public int getUserId() {
		return userId;
	}

	public void setUserId(int userId) {
		this.userId = userId;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public Date getLoginTime() {
		return loginTime;
	}

	public void setLoginTime(Date loginTime) {
		this.loginTime = loginTime;
	}

}

5.2  UserDao


首先,我们来定义访问user的Dao,它包括3个方法

  • getMatchCount():根据用户名和密码获取匹配的用户数。1表示正确,0表示没有此用户或账户密码不对。
  • findUserByUsername();根据用户名找到用户。
  • updateLoginInfo();更新用户积分,记录用户最后登录的时间,ip等信息

@Repository
// 通过spring注释定义一个Dao
public class UserDao {

	@Autowired
	// 自动注入JdbcTemplate的bean
	private JdbcTemplate jdbcTemplate;

	// 根据用户名和密码,获取匹配的用户数,有返回1,没有返回0
	public int getMatchCount(String username, String password) {

		String QUERY = "select count(*) from user where user_name = ? and password = ?";
		return jdbcTemplate.queryForInt(QUERY, new Object[] { username,
				password });
	}

	// 根据用户名获得user对象
	public User findUserByUsername(final String username) {

		String QUERY = "select * from user where user_name = ?";

		final User user = new User();

		jdbcTemplate.query(QUERY, new Object[] { username },

		new RowCallbackHandler() {

			public void processRow(ResultSet rs) throws SQLException {
				// TODO Auto-generated method stub

				user.setUserid(rs.getInt("user_id"));

				user.setPassword(rs.getString("password"));

				user.setCredits(rs.getInt("credits"));
				
				user.setLastDate(rs.getDate("last_date"));
				
				user.setLastIp(rs.getString("last_ip"));
				
				user.setUsername(rs.getString("user_name"));

			}

		});

		return user;
	}
	
	//更新用户积分、最后登录时间、登陆ip
	public void updateLoginInfo(User user){
		
		String UPDATE = "update user set credits = ? , last_ip = ?,last_date = ? where user_id = ?";
		
		jdbcTemplate.update(UPDATE, new Object[]{user.getCredits(),user.getLastIp(),user.getLastDate(),user.getUserid()});
}
}


其中findUserByUsername()方法有点复杂,其中我们用到了JdbcTemplate.query(String sql,Object[] args,RowCallBackHandler rch)方法,他有3个参数:

  • sql:sql语句,允许带“?”的占位符
  • args:sql中占位符对应的参数数组,按顺序排放
  • rch:查询结果的处理回调接口,该回调接口有一个方法processRow(Result rs),负责将查询的结果从Result中装载到类似于领域对象的对象实例中。


5.3  LoginlogDao


LoginlogDao负责记录用户的登陆日志,它仅有一个insertLoginLog()方法


@Repository
public class LoginlogDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public void insertLoginlog(Loginlog loginlog){
		
		String insert = "insert into loginlog (user_id,ip,login_time) values (?,?,?) ";
		jdbcTemplate.update(insert, new Object[]{loginlog.getUserId(),loginlog.getIp(),loginlog.getLoginTime()});
		
	}
}

5.4 在spring中装配Dao

在项目工程的src目录下创建一个applicationContext.xml文件,配置基本结构如下:


<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入aop及tx命名空间所对应的Schema文件 -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-3.1.xsd 
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">      ... </beans>

DaoBean的配置如下:

 <!-- 扫描类包,将标注spring注解的类自动转化为Bean,同时完成Bean的注入 -->

	<!-- spring2.5 以后才支持 -->
	<context:component-scan base-package="com.huitong.spring.dao" />
	<!-- 定义一个使用dbcp实现的数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"
		p:url="jdbc:mysql://localhost:3306/test" p:username="root" p:password="1234" />
	<!-- 定义jdbc模板bean -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
		p:dataSource-ref="dataSource" />

我们使用spring的context:component-scan扫描制定类包下的所有类,这样再类中定义的spring注解(如@Repository、@Autowired等)才能生效。好了,这样2个Dao就完成了,现在先放到一边。


5.4  业务层


UserService业务接口有3个业务方法:

  • hasMatchUser();用于检查用户名密码的正确性
  • findUserByUsername();以用户名为条件,加载User对象
  • loginSuccess();在用户登陆成功后调用,更新用户最后登陆时间和ip等信息,同时记录用户登陆日志

UserService类:


@Service //将userservice标注为一个服务层的bean

public class UserService {
	
	@Autowired
	private UserDao userDaoImpl;
	
	@Autowired
	private LoginlogDao loginlogDao;
	
	public boolean hasMatchUser(String username,String password){
		
		return userDaoImpl.getMatchCount(username, password)>0;
	} 
	
	public User findUserByUsername(String username){
		
		return userDaoImpl.findUserByUsername(username);
		
	}
	
	public void loginSuccess(User user){
		
		user.setCredits(5+user.getCredits());
		
		Loginlog loginlog = new Loginlog();
		
		loginlog.setUserId(user.getUserid());
		loginlog.setLoginTime(user.getLastDate());
		loginlog.setIp(user.getLastIp());
		
		userDaoImpl.updateLoginInfo(user);
		
		loginlogDao.insertLoginlog(loginlog);
	}
}

前两个方法通过简单的调用Dao完成对应的功能;loginSuccess()根据入参user对象构造出loginLog对象,并将user.credits递增5,即用户没登陆一次赚取5个积分,然后更新到t_user中,在往t_loginlog中,添加一条记录。


5.5  在spring中装配service


打开原来的applicationContext.xml文件,添加如下代码:


	<!-- 扫描service类包,应用spring的注解配置 -->
	<context:component-scan base-package="com.huitong.spring.service" />

	<!-- 配置事物管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		p:dataSource-ref="dataSource" />

	<!-- 通过aop配置提供事物增强,让service包下所有bean的所有方法拥有事物 -->
	<aop:config proxy-target-class="true">
		<aop:pointcut expression="execution(* com.huitong.spring.service.*.*(..))"
			id="serviceMethod" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
	</aop:config>
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>

5.6  单元测试


到这里,我们可以使用JUnit进行单元测试,验证一下,由于很简单,过程我就不写了。


5.7  配置springMVC框架


首先,对web.xml文件配置,如下:

<!-- 启动spring容器的监听器 ,他将引用上面的上下文参数获得spring的配置文件地址 -->
	<listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	</listener>

	<!-- springMVC 的servlet -->
	<!-- <servlet-name>标签配的值为springServlet(<servlet-name>springServlet</servlet-name>),再加上“-servlet”后缀而形成的springServlet-servlet.xml文件名 
		并且这个路径在/WEB-INF/下 -->
	<servlet>
		<servlet-name>springServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-do.xml</param-value> 
			</init-param> -->
		<load-on-startup>2</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>springServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
	<!-- 从类路径下加载spring配置文件,classpath关键字默认类路径下加载 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>

5.8 处理登陆请求--POJO控制器类

//标注成为一个springMVC的controller
@Controller
public class LoginController {

	@Autowired
	public UserService serviceImpl;

	// 负责处理login.do的请求
	@RequestMapping(value = "/loginPage.do")
	public ModelAndView loginPage(HttpServletRequest request,
			HttpServletResponse response) {

		return new ModelAndView("/login");
	}

	// 负责处理loginCheck.do的请求
	@RequestMapping(value = "/loginCheck.do")
	public ModelAndView loginCheck(HttpServletRequest request,
			HttpServletResponse response, LoginCommand loginCommand) {


		boolean isValideUser = serviceImpl.hasMatchUser(
				loginCommand.getUserName(), loginCommand.getPassword());

		if (!isValideUser) {
			System.out.println("转到login页面。。。");
			return new ModelAndView("login", "error", "用户名或密码错误");
		} else {
			User user = serviceImpl.findUserByUsername(loginCommand
					.getUserName());

			user.setLastIp(request.getRemoteAddr());
			user.setLastDate(new Date());
			serviceImpl.loginSuccess(user);
			request.getSession().setAttribute("user", user);
			return new ModelAndView("main");
		}
	}
}

通过spring的@Controller注解可以将POJO的任何一个类标注为springMVC的控制器,处理HTTP请求。一个控制器可以对应多个不同的HTTP请求,通过@RequestMapping指定如何映射请求路径,其中,请求的参数会根据参数名称默认契约自动绑定到响应方法的入参中,如这里的loginCommand。


请求响应方法可以返回一个ModerAndView,或直接返回一个字符串,springMVC回解析并转向目标响应页面。这里我们只要知道它代表一个视图。前面我们用到的loginCommand对象,他是一个POJO,没有继承特定的父类或实现特定的接口,并且只有username和password两个属性。如下:

public class LoginCommand {
	private String userName;

	private String password;

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}
}

5.9 springMVC配置文件


编写好loginCommand之后,我们需要申明该控制器servletSpring-servlet.xml,扫描web路径,指定springMVC的视图解析器,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入aop及tx命名空间所对应的Schema文件 -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-3.1.xsd 
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	http://www.springframework.org/schema/mvc 
	http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

	<!-- 启用spring mvc 注解 -->
	<mvc:annotation-driven />
	<!-- 扫描web包,添加spring注解 -->
	<context:component-scan base-package="com.huitong.spring.web"></context:component-scan>

	<!-- 配置视图解析器,将ModelAndView解析为具体的页面 对转向页面的路径解析。 -->
	<!-- 前缀 prefix 后缀 suffix -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>

	<bean id="exceptionResolver"
		class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<property name="defaultErrorView" value="failure" />
		<property name="exceptionMappings">
			<props>
				<prop key="java.sql.SQLException">/error/error.jsp</prop>
				<prop key="java.lang.RuntimeException">/error/error.jsp</prop>
			</props>
		</property>
	</bean>
</beans>


5.10 jsp前台页面


login.jsp,如下:


<%@ page language="java" contentType="text/html;charset=utf-8" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>my spring3.0</title>
    
    <title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
  </head>
  
  <body>
   	<c:if test="${!empty error}">
   		<font color="red"><c:out value="${error }" /></font>
   	</c:if>
   	<form action="<c:url value="/loginCheck.do"/> " method="post">
   	
   		用户名:<input type="text" name="userName" /><br/>
   		密    码:<input type="password" name="password" /><br/>
   		<input type="submit" value="登陆" />
   		<input type="reset" value="重置" />
   	</form>
  </body>
</html>

main.jsp登陆之后的显示页面,如下:

<%@ page language="java" contentType="text/html;charset=utf-8"
	import="java.util.*" pageEncoding="utf-8"%>	
<%@ page isELIgnored="false" %>
...
</head>
<br /> ${sessionScope.user.username},欢迎您进入,您的积分为${user.credits}.
<body>

</body>
</html>

这里需要使用<%@ page isELIgnored="false" %>设置el表达式生效。


好了,到这里这个项目就完成了,可以运行查看效果了。


PS:由于本人水平有限,文中难免出现错误之处,恳请大家批评指正,我会及时修改更新,谢谢。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值