现在的位置: 首页 > 综合 > 正文

Spring框架研究之MultiActionController

2013年12月03日 ⁄ 综合 ⁄ 共 19773字 ⁄ 字号 评论关闭

最近一段时间Spring MVC吵得挺火的,项目中也有用到,于是趁这个机会研究了下MultiActionController.

MultiActionController的用途:

在使用Spring提供的控制器时,AbstractController和SimpleFormController是应用得最多的。AbstractController是最基本的Controller,可以给予用户最大的灵活性。

SimpleFormController则用于典型的表单编辑和提交。在一个需要增,删,改,查的需求中,增加和修改扩展SimpleFormController完成,删除和查询则扩展AbstractController完成。

但是像上面那样完成某一业务对象的增,删,改,查,都属于一类相关的业务。把一类相关的操作分布到不同的类去完成,违返“高内聚”的设计原则。这样四个业务操作需要四个类来完成,造成太多的类文件,难以维护和配置。

所以Spring借鉴Struts的DispatchAction提供了类似功能的MultiActionController。可以实现不同的请求路径对应MultiActionController中的不同方法,这样就可以把相关的操作都在一个类的相关方法中完成。这样使得这个类具有“高内聚”,也利于系统的维护,还避免了重复代码。增加和修改操作的数据验证逻辑是很相似的,使用MultiActionController后就可以让增加和修改操作共用一段数据验证逻辑代码。

MultiActioncontroller的结构:

public class MultiActionController extends AbstractController implements LastModified {

本文查看的spring3.1.0的源代码,由上述声明可知,MultiActionController类扩展至AbstractController对象,主要业务逻辑在重写方法:

/**
	 * Template method. Subclasses must implement this.
	 * The contract is the same as for <code>handleRequest</code>.
	 * @see #handleRequest
	 */
	protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
	    throws Exception;

MultiActionController中对上述方面的具体实现如下:

/**
	 * Determine a handler method and invoke it.
	 * @see MethodNameResolver#getHandlerMethodName
	 * @see #invokeNamedMethod
	 * @see #handleNoSuchRequestHandlingMethod
	 */
	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		try {
			String methodName = this.methodNameResolver.getHandlerMethodName(request);
			return invokeNamedMethod(methodName, request, response);
		}
		catch (NoSuchRequestHandlingMethodException ex) {
			return handleNoSuchRequestHandlingMethod(ex, request, response);
		}
	}

由上述代码可知,具体的业务逻辑是:

String methodName = this.methodNameResolver.getHandlerMethodName(request);

1) 通过配置的methodNameResolver确定要引用的方法名。

return invokeNamedMethod(methodName, request, response);

2) 根据确定的方法名调用响应的方法,执行业务逻辑。

1。1)  methodNameResolver的确定,

methodNameResolver变量类型为接口 MethodNameResolver, 实现MethodNameResolver接口的常用的有以下三个:
 A)  InternalPathMethodNameResolver   该类型为默认实现,即若没有配置methodNameResolver属性时使用。 它的主要用法如下:

Maps the resource name after the last slash, ignoring an extension. 
E.g. "/foo/bar/baz.html" to "baz", assuming a "/foo/bar/baz.html" controller mapping to the corresponding MultiActionController handler. method. 


中文意思就是: 以最后一个斜杠为分界线,取得最后的部分,去掉后缀名就是你的映射方法名。

 B)  ParameterMethodNameResolver 这个用的很普遍, 默认的paramName为action。 具体用法如下:

配置该类的paramName为你想要用来指定方法属性的名字,比如什么action、method、command都行,什么都行,不配置的话,就是默认的action。

名称定位器就会获取该属性(名称为paramName配置的值)的值作为方法名。

 C)  PropertiesMethodNameResolver 这个可以单独配置properties文件来指定url--methodName的映射。每个都需要配置,重用性不好。具体用法:

The most flexible out-of-the-box implementation of the MethodNameResolver interface. 
Uses java.util.Properties to define the mapping between the URL of incoming requests and the corresponding method name. 
Such properties can be held in an XML document. 

Properties format is /welcome.html=displayGenresPage Note that method overloading isn't allowed, so there's no need to specify arguments. 

Supports direct matches, e.g. a registered "/test" matches "/test", and a various Ant-style pattern matches,
 e.g. a registered "/t*" matches both "/test" and "/team". For details, see the AntPathMatcher javadoc.

2)invokeNamedMethod(methodName, request, response)的详细流程:

/**
	 * Invokes the named method.
	 * <p>Uses a custom exception handler if possible; otherwise, throw an
	 * unchecked exception; wrap a checked exception or Throwable.
	 */
	protected final ModelAndView invokeNamedMethod(
			String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {

		Method method = this.handlerMethodMap.get(methodName);
		if (method == null) {
			throw new NoSuchRequestHandlingMethodException(methodName, getClass());
		}

		try {
			Class[] paramTypes = method.getParameterTypes();
			List<Object> params = new ArrayList<Object>(4);
			params.add(request);
			params.add(response);

			if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
				HttpSession session = request.getSession(false);
				if (session == null) {
					throw new HttpSessionRequiredException(
							"Pre-existing session required for handler method '" + methodName + "'");
				}
				params.add(session);
			}

			// If last parameter isn't of HttpSession type, it's a command.
			if (paramTypes.length >= 3 &&
					!paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
				Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
				params.add(command);
				bind(request, command);
			}

			Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));
			return massageReturnValueIfNecessary(returnValue);
		}
		catch (InvocationTargetException ex) {
			// The handler method threw an exception.
			return handleException(request, response, ex.getTargetException());
		}
		catch (Exception ex) {
			// The binding process threw an exception.
			return handleException(request, response, ex);
		}
	}

2.1) 获取该methodName对应的Method方法对象

Method method = this.handlerMethodMap.get(methodName);

2.2) 准备方法调用前的参数

                        Class[] paramTypes = method.getParameterTypes();
			List<Object> params = new ArrayList<Object>(4);
			params.add(request);
			params.add(response);

			if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
				HttpSession session = request.getSession(false);
				if (session == null) {
					throw new HttpSessionRequiredException(
							"Pre-existing session required for handler method '" + methodName + "'");
				}
				params.add(session);
			}

			// If last parameter isn't of HttpSession type, it's a command.
			if (paramTypes.length >= 3 &&
					!paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
				Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
				params.add(command);
				bind(request, command);
			}

由MultiActionController的注释可知:她支持如下类型的方法的声明:

public (ModelAndView | Map | String | void) actionName(HttpServletRequest request, HttpServletResponse response, [,HttpSession] [,AnyObject]);

若第三个参数或第三个以后的参数包含了非HttpSession类型的参数,则为绑定的Command对象,支持绑定类型的Command类型的校验。

bind(request, command);

该类的具体实现如下:

/**
	 * Bind request parameters onto the given command bean
	 * @param request request from which parameters will be bound
	 * @param command command object, that must be a JavaBean
	 * @throws Exception in case of invalid state or arguments
	 */
	protected void bind(HttpServletRequest request, Object command) throws Exception {
		logger.debug("Binding request parameters onto MultiActionController command");
		ServletRequestDataBinder binder = createBinder(request, command);
		binder.bind(request);
		if (this.validators != null) {
			for (Validator validator : this.validators) {
				if (validator.supports(command.getClass())) {
					ValidationUtils.invokeValidator(validator, command, binder.getBindingResult());
				}
			}
		}
		binder.closeNoCatch();
	}

在该方法里实现了:

A) 对command对象的绑定。

                ServletRequestDataBinder binder = createBinder(request, command);
		binder.bind(request);

B) 对command对象的检验

                if (this.validators != null) {
			for (Validator validator : this.validators) {
				if (validator.supports(command.getClass())) {
					ValidationUtils.invokeValidator(validator, command, binder.getBindingResult());
				}
			}
		}

要检验command对象的话,需要配置validators属性,Validator的实现类需要实现Validator接口的两个方法。

public interface Validator {

	/**
	 * Can this {@link Validator} {@link #validate(Object, Errors) validate}
	 * instances of the supplied <code>clazz</code>?
	 * <p>This method is <i>typically</i> implemented like so:
	 * <pre class="code">return Foo.class.isAssignableFrom(clazz);</pre>
	 * (Where <code>Foo</code> is the class (or superclass) of the actual
	 * object instance that is to be {@link #validate(Object, Errors) validated}.)
	 * @param clazz the {@link Class} that this {@link Validator} is
	 * being asked if it can {@link #validate(Object, Errors) validate}
	 * @return <code>true</code> if this {@link Validator} can indeed
	 * {@link #validate(Object, Errors) validate} instances of the
	 * supplied <code>clazz</code> 
	 */
	boolean supports(Class<?> clazz);

	/**
	 * Validate the supplied <code>target</code> object, which must be
	 * of a {@link Class} for which the {@link #supports(Class)} method
	 * typically has (or would) return <code>true</code>.
	 * <p>The supplied {@link Errors errors} instance can be used to report
	 * any resulting validation errors.
	 * @param target the object that is to be validated (can be <code>null</code>) 
	 * @param errors contextual state about the validation process (never <code>null</code>) 
	 * @see ValidationUtils
	 */
	void validate(Object target, Errors errors);

}

3) 检验是否有校验的错误信息,若有错误抛出异常。

binder.closeNoCatch();

具体实现如下:

        /**
	 * Treats errors as fatal.
	 * <p>Use this method only if it's an error if the input isn't valid.
	 * This might be appropriate if all input is from dropdowns, for example.
	 * @throws ServletRequestBindingException subclass of ServletException on any binding problem
	 */
	public void closeNoCatch() throws ServletRequestBindingException {
		if (getBindingResult().hasErrors()) {
			throw new ServletRequestBindingException(
					"Errors binding onto object '" + getBindingResult().getObjectName() + "'",
					new BindException(getBindingResult()));
		}
	}

在校验有错误信息时,抛出ServeltRequestBindingException异常。然后在

               catch (Exception ex) {
			// The binding process threw an exception.
			return handleException(request, response, ex);
		}

catch语句块截获异常信息,然后调用handleException(*)方法处理,校验失败的信息。handleException()方法如下:

private ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, Throwable ex)
			throws Exception {

		Method handler = getExceptionHandler(ex);
		if (handler != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking exception handler [" + handler + "] for exception: " + ex);
			}
			try {
				Object returnValue = handler.invoke(this.delegate, request, response, ex);
				return massageReturnValueIfNecessary(returnValue);
			}
			catch (InvocationTargetException ex2) {
				logger.error("Original exception overridden by exception handling failure", ex);
				ReflectionUtils.rethrowException(ex2.getTargetException());
			}
			catch (Exception ex2) {
				logger.error("Failed to invoke exception handler method", ex2);
			}
		}
		else {
			// If we get here, there was no custom handler or we couldn't invoke it.
			ReflectionUtils.rethrowException(ex);
		}
		throw new IllegalStateException("Should never get here");
	}

它先通过getExceptionHandler(ex)方法查找处理异常的方法。方法位于

/** Methods, keyed by exception class */

    private final Map<Class, Method> exceptionHandlerMap = new HashMap<Class, Method>();

这里的class就是,校验抛出的Exception的具体类。若找到则用找到的handler来处理,找不到则继续抛出异常。

因此在检验Command对象出现错误时,需要有 ServletRequestBindingException参数类型的方法来处理。形如:

public ModelAndView checkFailed(HttpServletRequest request, HttpServletResponse response, ServletRequestBindingException srbe){
		logger.debug("check failed.");
		
		BindException be = (BindException)srbe.getCause();
		
		return new ModelAndView("login", be.getModel());
	}

方法名称可以任意。

2.3 ) 若校验没有异常则直接调用下面的方法:

Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));

2.4) 然后根据返回结果生成视图模型对象。

return massageReturnValueIfNecessary(returnValue);

然后的工作就是视图识别器的事了。

看了这么多代码,有点理不清出逻辑,下面简要总结下处理流程:

1)  调用methodNameResolver识别request需要要调用的方法名。

2.1) 根据第一步获得的方法名,获得实际准备调用的Method方法类。

2.2) 准备调用Method方法的参数。若有Command对象,提供校验。

2.3) 若检验通过,则调用Method方法来出来request请求。

         若校验没通过,则在catch(Exception...)中的handleException(request, response, ex)来处理。

2.4) 根据返回对象生成ViewAndModel对象,供视图识别器使用。

MultiActionController使用的具体事例:

下面通过一个使用的具体事例来体会下上面的流程, 我们一个简单的登陆为例进行说明。

创建一个动态web工程, 名称: mySpring.

login.jsp 

位置: /WEB-INF/jsp/login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"	pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/jsp/include.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工信息文件导入</title>
<style type="text/css">
.InputText {
	font-size: 12px;
	color: #000000;
	font-weight: normal;
	font-family: "宋体";
}
.error {
	color: red;
}
</style>
<script type="text/javascript">
	function toSubmit() {
		var form1 = document.getElementById("form1");
		if(form1.userName.value==""){
			alert("请输入用户名");
			form1.userName.focus();
			return false;
		}
		if(form1.password.value==""){
			alert("请输入密码");
			form1.password.focus();
			return false;
		}
		return true;
	}
</script>
</head>

<body>
	<form:form name="form1" id="form1" method="post" action="login.do" onsubmit="return toSubmit();">
		<table width="100%" height="100%">
			<tr>
				<td style="color:red;"><form:errors path="*" />
				</td>
			</tr>
			<tr>
				<td valign="middle" align="center">
					<table border="0" width="252" align="center" cellspacing="0" cellpadding="0">
						<tr id="trUser">
							<td colspan="2" align="left" valign="middle" class="InputText">
								工号  <input name="userName" style="border: 1 solid #4579C5; width: 120px" value="${command.userName}" size="10">
							</td>
						</tr>
						<tr id="trPassword">
							<td colspan="2" align="left" valign="middle" class="InputText">
								密码  <input type="password" name="password" style="border: 1 solid #4579C5; width: 120px" value="${command.password}" size="10">
							</td>
						</tr>
						<tr>
							<td colspan="2" align="right">
								<input type="hidden" name="command" id="command" value="login" />
								<input type="submit" size="100" value="登 陆" />
							</td>
						</tr>
					</table>
			</td>
			</tr>
		</table>
	</form:form>
</body>
</html>

注意:

1) 要使用<form:errors />标签的话,必须位于<form:form>标签才能有效,否则不能显示。

2) 使用<form:from>标签时,要是嫌它给定的commandName模式不方便,可以直接使用以前的form标签的属性。

include.jsp  位置: /WEB-INF/jsp/include.jsp

<%@ taglib prefix="c" uri="/c" %>
<%@ taglib prefix="fmt" uri="/fmt" %>
<%@ taglib prefix="form" uri="/form" %>

登陆成功后显示的页面: success.jsp  位置: /WEB-INF/jsp/success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"	pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/jsp/include.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆成功页面</title>
</head>

<body>
登陆成功页面。
</body>
</html>

处理login.do的Controller方法, LoginController.java

package com.study.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.validation.BindException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

import com.study.service.LoginService;
import com.study.model.User;

public class LoginController extends MultiActionController {

	private LoginService loginService;
	
	public LoginService getLoginService() {
		return loginService;
	}

	public void setLoginService(LoginService loginService) {
		this.loginService = loginService;
	}

	/**
	 * 默认方法,找不到方法参数时调用该方法。
	 * 
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	public String defaultMethod(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if(logger.isDebugEnabled()){
			logger.debug(" inovke defaultMethod ");
		}
		
		return "login";
	}
	
	/**
	 * 处理登陆校验的方法。
	 * 
	 * @param request
	 * @param response
	 * @param user
	 * @return
	 * @throws Exception
	 */
	public String login(HttpServletRequest request, HttpServletResponse response, User user)
			throws Exception {
		if(logger.isDebugEnabled()){
			logger.debug(" inovke login ");
		}
		
		if(loginService.checkUser(user)){
			HttpSession session = request.getSession(true);
			session.setAttribute("user", user);
		}
		return "success";
	}
	
	/**
	 * 校验失败时调用的方法。
	 * 
	 * @param request
	 * @param response
	 * @param srbe
	 * @return
	 */
	public ModelAndView checkFailed(HttpServletRequest request, HttpServletResponse response, ServletRequestBindingException srbe){
		logger.debug("check failed.");
		
		BindException be = (BindException)srbe.getCause();
		
		return new ModelAndView("login", be.getModel());
	}
	
}

校验的具体业务逻辑在LoginService中实现 LoginService.java

package com.study.service;

import com.study.model.User;

public class LoginService {
	
	public boolean checkUser(User user){
		return true;
	}
}

LoginValidator.java

package com.study.validator;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.study.model.User;

public class UserValidator implements Validator {

	private static final int MIN_PASSWORD_LEN = 6;

	public boolean supports(Class<?> clazz) {
		return User.class.isAssignableFrom(clazz);
	}

	public void validate(Object target, Errors errors) {
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "field.required", "工号输入为空");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "field.required", "密码输入为空");
		
		User user = (User)target;
		String password = user.getPassword();
		if(password != null && password.length()< MIN_PASSWORD_LEN){
			errors.rejectValue("password", "password.length.lessThanMin", 
					new Object[]{Integer.valueOf(MIN_PASSWORD_LEN)},
					"密码长度至少为 "+MIN_PASSWORD_LEN+"位");
		}
	}

}

下面给出具体的配置文件,集成各个模块。

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>mySpring</display-name>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<servlet>
		<servlet-name>mySpring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>mySpring</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

	<jsp-config>
		<taglib>
			<taglib-uri>/c</taglib-uri>
			<taglib-location>/WEB-INF/tld/c.tld</taglib-location>
		</taglib>
		<taglib>
			<taglib-uri>/fmt</taglib-uri>
			<taglib-location>/WEB-INF/tld/fmt.tld</taglib-location>
		</taglib>
		<taglib>
			<taglib-uri>/form</taglib-uri>
			<taglib-location>/WEB-INF/tld/spring-form.tld</taglib-location>
		</taglib>
	</jsp-config>
	
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	
</web-app>

spring-servlet.xml 位置:/WEB-INF/spring-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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<!-- the application context definition for the springapp DispatcherServlet -->
	<bean name="/login.do" class="com.study.controller.LoginController">
		<property name="methodNameResolver" ref="parameterMethodNameResolver" />
		<property name="validators">
			<list>
				<bean class="com.study.validator.UserValidator" />
			</list>
		</property>
		<property name="loginService" ref="loginService" />
	</bean>
		
	<bean id="parameterMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
		<property name="paramName" value="command" />
		<property name="defaultMethodName" value="defaultMethod" />
	</bean>	
	
	<!--  view resolver -->
	<bean id="viewResolver"
		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>	
	
</beans>

applicationContext.xml 位置: /WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
	Application context definition for PetClinic on Hibernate.
-->
<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:p="http://www.springframework.org/schema/p"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
			http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<!--
		the parent application context definition for the springapp
		application
	-->	
	<!--  data source for connection pool -->
	<!-- 
	<bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">
		<property name="driver" value="${jdbc.driverClassName}" />
		<property name="driverUrl" value="${jdbc.url}" />
		<property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="alias" value="dbPool" />
		<property name="maximumConnectionCount" value="100" />
		<property name="prototypeCount" value="5" />
		<property name="minimumConnectionCount" value="10" />
		<property name="trace" value="true" />
		<property name="verbose" value="true" />
	</bean>
		
	<bean id="propertyConfiger"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:jdbc.properties</value>
			</list>
		</property>
	</bean>

         <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	-->
	
	<bean id="loginService" class="com.study.service.LoginService">
	</bean>
	
</beans>

文中用到的 c.tld, fmt.tld位于spring3.1发布包里的standard.jar的META-INF文件夹里。spring-form.tld记不清楚放在哪里了,它就是spring的标签库,网上去搜下,
好了,该有的都有了,把spring3.1.0的所有的发布包放到/WEB-INF/lib/目录下,启动tomcat测试下。

抱歉!评论已关闭.