SpringMVC3-启动-源码跟踪

环境搭建

目前搭建一个标准的maven-webapp工程,结构如下:
这里写图片描述

依赖包
SpringMvc的环境搭建,用maven构建,只需要引入如下maven依赖,spring版本3.1.0.RELEASE(公司老项目,很多都是3.x版本 orz)


    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>3.1.0.RELEASE</version>
    </dependency>

    <!--tomcat 默认使用3.1,Tomcat7默认使用3.0 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>

web.xml
web.xml配置也十分简单,只需要配置org.springframework.web.servlet.DispatcherServlet即可。

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:/spring/application-*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

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

xml 配置
ContextLoaderListener 和 DispatcherServlet 个各创建一个WebApplicationContext;
ContextLoaderListener 默认加载 context-param为contextConfigLocation中的配置文件信息,它是DispatcherServlet创建容器的父容器。 为了防止交叉定义bean:

  • ContextLoaderListener: 配置所有非MVC组件:Service,dao…
  • DispatcherServlet : 配置所有MVC相关组件 Controller,Handler,Resovler…

application-context.xml

   <!-- use-default-filters默认为true, 表示配置扫描所有bean ...
   exclude-filter	排除所有controller -->
    <context:component-scan base-package="cn.jhs.mvc" >
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>

application-mvc.xml

    <mvc:annotation-driven> </mvc:annotation-driven>
    <!-- include-filter ,仅扫描 Controller-->
    <context:component-scan base-package="cn.jhs.mvc" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/html"/>
        <property name="suffix" value=".jsp"/>
    </bean>


    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/*"/>
            <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
        </mvc:interceptor>

        <mvc:interceptor>
            <mvc:mapping path="/*"/>
            <!--   默认paramName为theme-->
            <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

创建Controller,view等

public class HiController {
	
	@RequestMapping("/index")
	public String index(){
		return "/hi/index";
	}

	@RequestMapping("/say/{msg}")
	@ResponseBody
	public String say(@PathVariable("msg") String msg){
		return "hi:"+msg;
	}
}

完成如上步骤,环境搭建完成。



SpringMVC启动

web容器启动的过程,首先加载web.xml中listener -> filter -> servlet.init(),由现有配置可知,程序的“入口”,是org.springframework.web.servlet.DispatcherServlet.init(ServletConfig config),但是观察DispatcherServlet并没有发现它有init方法,这是为什么呢?
当web容器未找到目标方法时,会向DispatcherServlet的父类中寻找该方法。

SpringMVC servlet结构图
这里写图片描述
代码结构如下:

public class DispatcherServlet extends FrameworkServlet {}

public abstract class FrameworkServlet extends HttpServletBean {}

public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware {}


public abstract class HttpServlet extends GenericServlet{}

public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable{}

其中EnvironmentAware接口的代码如下:

public interface EnvironmentAware extends Aware {
    void setEnvironment(Environment var1);
}

常见的还有ApplicationContextAware同样的它有setApplicationContext(ApplicationContext var1)接口XXXAware都会通过setXXX方法为实现类提供XXX的使用能力。


Servlet

javax.servlet.Servlet,是Serve+Applet的缩写,表示一个服务器应用的接口规范。

public interface Servlet {
 /**
 * init方法在容器启动时被调用,`当load-on-setup设置为负数或者未设置时,
 * 是在Servlet第一次访问时才会被调用`,且只调用一次。
 *
 * @Param config:contextConfigLocation等参数就是封装才此
 */
 public void init(ServletConfig config) throws ServletException;
 
 //service方法用来处理具体的请求
 public void service(ServletRequest req, ServletResponse res)
	throws ServletException, IOException;

}

GenericServlet

GenericServlet 实现了javax.servlet.Servlet.init(ServletConfig config)方法,并提供了无参的init(),模板方法供子类实现,以及提供了新方法getServletContext()

public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable
{
    public void init(ServletConfig config) throws ServletException {
		this.config = config;
		this.init();
    }
    
    //模板方法,供子类实现
	 public void init() throws ServletException {

    }
    
    public ServletContext getServletContext() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getServletContext();
    }

}

HttpServlet

HttpServlet是用HTTP协议实现的Servlet基类,它主要关心的如何处理请求,所以它主要重写了service(ServletRequest req, ServletResponse res)

public abstract class HttpServlet extends GenericServlet
{
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        //....省略
        //转换,调用service(HttpServletRequest req, HttpServletResponse resp)
        service((HttpServletRequest) req, (HttpServletResponse) res);
    }
    
	 protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
	         String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
	         doGet(req, resp);
         } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } //....省略
	}

	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	        throws ServletException, IOException{
		//....省略
	}
}

HttpServlet主要关注请求的处理,所以在启动时,无需过于关注此类。

HttpServletBean

//GenericServlet.init()可知,下一步需要调用DispatcherServlet无参的init()方法,此类在HttpServletBean定义实现

public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware {

	@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		try {
			//将Servlet配置的参数封装到pvs中
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			
			//this即DispatcherServlet
			//通过PropertyAccessorFactory封装成一个BeanWrapper对象,通过BeanWrapper对DispatcherServlet进行属性设置等操作。
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));

			//模板方法,可在子类实现调用。做一些初始化工作
			initBeanWrapper(bw);
			
			//将pv配置,即servletConfig包含web.xml中配置的init-param给设置到DispatcherServlet中
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			throw ex;
		}

		//模板方法,子类初始化入口
		initServletBean();
	}
}

FrameworkServlet

由上可知,FrameworkServlet方法入口是initServletBean();

public abstract class FrameworkServlet extends HttpServletBean {
	
	@Override
	protected final void initServletBean() throws ServletException {
		//log,try-catch省略.....
		
		//a.初始化webApplicationContext 
		this.webApplicationContext = initWebApplicationContext();
		
		//b.初始化FrameworkServlet模板方法---当前为空
		initFrameworkServlet();
	}
}

可以得知FrameworkServlet的构建过程中,主要的工作是初始化webApplicationContext :initWebApplicationContext()

protected WebApplicationContext initWebApplicationContext() {
		//a1.获取spring的根容器rootContext		
		WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		
		if (this.webApplicationContext != null) {
			//如果context已经通过构造方法设置了,直接使用;
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					//如果context没有被refreshed,按照如下操作进行;如设置parent context
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					//a2.通过构造函数中的webApplicationContext做一些设置
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			//a3 当WebApplicationContext已经存在于ServletContext中,通过配置在Servlet中Attribute获取
			wac = findWebApplicationContext();
		}
		if (wac == null) {
		   //a4 如果context还没有创建,创建一个
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// 当refreshEventReceived没有被触发,调用此方法。模板方法,由子类即DispatcherServlet实现。
			onRefresh(wac);
		}

		if (this.publishContext) {
			//a6 把WebApplicationContext作为ServletContext的一个属性
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

initWebApplicationContext方法主要做了三件事:

  1. 获取Spring根容器rootContext
  2. 设置webApplicationContext并根据情况调用onRefresh方法
  3. 将webApplicationContext设置到ServletContext中。

获取spring的根容器rootContext
默认情况下spring会将自己的容器设置成ServletContext的属性。

//org.springframework.web.context.support.WebApplicationContextUtils

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
	//WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE =WebApplicationContext.class.getName()+".ROOT"
	//即“org.springframework.web.context.WebApplicationContext.ROOT”
	return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
	
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
	//省略assert,try-catch...
	Object attr = sc.getAttribute(attrName);
	return (WebApplicationContext) attr;
}

设置webApplicationContext并根据情况调用onRefresh方法
设置webApplicationContext一共有三种方法。

  • 第一种是在构造方法中已经传递了webApplicationContext参数,此时只需要进行一些设置即可。如configureAndRefreshWebApplicationContext(cwac);

这种方法主要用于Servlet3.0以后的环境中,Servlet3.0之后可以在程序中使用ServletContext.addServlet方式注册Servlet,此时就可以在新建FrameworkServlet和其子类的时候通过构造方法传递已经准备好的webApplicationContext。

  • 第二种方法是findWebApplicationContext():此时webApplicationContext已经存在于ServletContext中了。只需要在配置Servlet的时候,将ServletContext中的webApplicationContext中name配置到contextAttribute属性即可。通常不这样做,会报错。
    这里写图片描述
protected WebApplicationContext findWebApplicationContext() {
	String attrName = getContextAttribute();
	WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
	return wac;
}
  • 第三种方式是在前两种方法都无效的情况下,自己创建一个。正常情况下就是使用这种方式。创建过程是在createWebApplicationContext方法中,方法内部同第一种方式也调用了configureAndRefreshWebApplicationContext(cwac);
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
	//获取创建类型:默认值 XmlWebApplicationContext.class
	Class<?> contextClass = getContextClass();
	
	//检查类型
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException();
		//省略..... throw内容
	}
	
	//创建
	ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

	wac.setParent(parent);
	//将web.xml设置的conextConfigLocation设置进wac,默认值为"/WEB-INF/"+namespace+".xml" ,即 /WEB-INF/[servleName]-servlet.xml
	//此段逻辑见:XmlWebApplicationContext.getDefaultConfigLocations()
	wac.setConfigLocation(getContextConfigLocation());

	configureAndRefreshWebApplicationContext(wac);

	return wac;
}


protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		//wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName());
							
		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		
		//添加监听ContextRefreshListener()监听器,监听ContextRefreshEvent事件。
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
	
		//模板方法---此处为空
		postProcessWebApplicationContext(wac);

		//执行配置在web.xml <servlet><init-param>contextInitializerClasses<init-param>的类,这些类必须实现ApplicationContextInitializer接口,的initialize()方法。
		applyInitializers(wac);
		
		//springcontext-refresh,默认值为XmlApplicationContext
		wac.refresh();
	}

其中ContextRefreshListener是FrameworkServletd的一个子类:

	//org.springframework.web.servlet.FrameworkServlet
	private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

		public void onApplicationEvent(ContextRefreshedEvent event) {
			//当事件触发时,调用父类的onApplicationEvent方法
			FrameworkServlet.this.onApplicationEvent(event);
		}
	}
	
	public void onApplicationEvent(ContextRefreshedEvent event) {
		//将refreshEventReceived设为true,表示已经refresh过。
		this.refreshEventReceived = true;
		
		//调用子类的onRefresh方法,即DispatcherServlet中该方法。
		onRefresh(event.getApplicationContext());
	}

第三种方法初始化时已经refresh,不再调用后续的onRefresh方法。同样第一种方法也调用了configureAndRefreshWebApplicationContext()所以也不再onRefresh了,只有第二种方法初始化时才会调用。
b

if (!this.refreshEventReceived) {
	//调用子类的onRefresh方法,即DispatcherServlet中该方法。
	onRefresh(wac);
}

将webApplicationContext设置到ServletContext中
最后根据publishContext标识来判断是否将创建出来的webApplicationContext设置到ServletContext属性中。

	if (this.publishContext) {
		// FrameworkServlet.class.getName() + ".CONTEXT."+getServletName();
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}

DispatcherServlet

由上可知onRefresh()是DispatcherServlet 的入口。onRefresh简单的调用了initStrategies()方法。

protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

//初始化供servlet时候用的策略对象。 可以被子类覆盖以实现其他策略。
protected void initStrategies(ApplicationContext context) {
	//初始化MultipartResolver处理文件上传。 默认没有配置,即不处理。
	initMultipartResolver(context);
	
	//初始化LocaleResolver 配置国际化的i18n,默认AcceptHeaderLocaleResolver
	initLocaleResolver(context);
	
	//初始化ThemeResolver 通过界面主题
	initThemeResolver(context);
	
	//初始化HandlerMappings:处理映射器,为用户发送的请求找到合适的Handler Adapter
	initHandlerMappings(context);
	
	//初始化HandlerAdapters:处理器适配器,为请求寻找实际调用处理函数
	initHandlerAdapters(context);
	
	//初始化HandlerExceptionResolvers 异常处理器:根据异常设置ModelAndView,并通过render方法渲染。
	initHandlerExceptionResolvers(context);
	
	//初始化RequestToViewNameTranslator,从request中获取viewName
	initRequestToViewNameTranslator(context);
	
	//初始化ViewResolvers,视图解析,通过Stirng类型viewName找到View
	initViewResolvers(context);
	
	//初始化FlashMapManager
	initFlashMapManager(context);
}

除了MultipartResolver没有设置默认值,其他配置全部都设有默认值,这些默认值是存放在DispatcherServlet.class同目录下的DispatcherServlet.properties中的。

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.DefaultFlashMapManager

以initThemeResolver()为例

	private void initThemeResolver(ApplicationContext context) {
		try {
			//加载context中配置的ThemeResolver
			this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
		}
		catch (NoSuchBeanDefinitionException ex) {
			//如果没有配置,则使用默认配置
			this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
		}
	}

	//获取默认配置方法
	protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
		List<T> strategies = getDefaultStrategies(context, strategyInterface);
		//throw 省略.....
		return strategies.get(0);
	}

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		String key = strategyInterface.getName();
		//获取默认配置,defaultStrategies在DispacherServlet static{}即<clinit>方法中,读取了上面的DispatcherServlet.properties中的配置信息。
		String value = defaultStrategies.getProperty(key);
		
		//省略if,try-catch,throw ....
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		for (String className : classNames) {
			Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
			
			//通过context容器,创建默认配置bean.
			Object strategy = createDefaultStrategy(context, clazz);
			strategies.add((T) strategy);
		}
		return strategies;
	}

自此SpringMVC容器的启动过程完成。

其中FrameworkServlet.configureAndRefreshWebApplicationContext()中调用wac.refresh();这段逻辑,参见Spring容器启动。



ContextLoaderListener

通常我们还有奖springMVC和spring的bean分用两个不同的容器管理bean,即

  • SpringMVC容器管理controller,viewerResolver等
  • Spring管理其他bean

这是需要使用ContextLoaderListener,配置如下:

<web-app>
  <display-name>springmvc-empty</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:/spring/application-context.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:/spring/application-mvc.xml</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
  </servlet>


  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

类结构图
这里写图片描述
由类图可知ContextLoaderListener实现了ServletContextListener,方法的入口是:contextInitialized(ServletContextEvent sce)

//org.springframework.web.context.ContextLoaderListener
public void contextInitialized(ServletContextEvent event) {
	this.contextLoader = createContextLoader();
	if (this.contextLoader == null) {
		this.contextLoader = this;
	}
	this.contextLoader.initWebApplicationContext(event.getServletContext());
}

它主要逻辑都交由ContextLoader.initWebApplicationContext(ServletContext )处理:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		/**
		 * 如果servlet已经存在WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE则抛出异常
		 */
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException("..........");	
		}

		//try-catch,log.....略


		// 创建WebApplicationContext
		if (this.context == null) {
			this.context = createWebApplicationContext(servletContext);
		}
		
		//转换为ConfigurableWebApplicationContext
		if (this.context instanceof ConfigurableWebApplicationContext) {
			configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
		}
		
		// 设置到servletContext的WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl == ContextLoader.class.getClassLoader()) {
			currentContext = this.context;
		}
		else if (ccl != null) {
			currentContextPerThread.put(ccl, this.context);
		}
		return this.context;
}

上述代码最重要的一句是:servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
在后续的DispatcherServlet初始化过程中,出示过程中,通过调用FrameworkServlet#initWebApplicationContext()获取spring-Root容器逻辑中,便可以获取Spring容器,并设为springmvc容器的parentContext;

此时创建的Spring容器,默认类型为XmlWebApplicationContext,由ContextLoader同级的ContextLoader.properties配置:
这里写图片描述
也可以由web.xml参数context-param : contextClass自主配置。

XmlWebApplicationContext默认加载配置文件位置是:/WEB-INF/applicationContext.xml
可以通过context-param : contextConfigLocation配置

弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,现在这一篇补上。下面开始贴代码。 文中用的框架版本:spring 3,hibernate 3,没有的,自己上网下。 先说web.xml配置: [java] view plaincopy 01.<?xml version="1.0" encoding="UTF-8"?> 02.<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> 03. <display-name>s3h3</display-name> 04. <context-param> 05. <param-name>contextConfigLocation</param-name> 06. <param-value>classpath:applicationContext*.xml</param-value> 07. </context-param> 08. <listener> 09. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 10. </listener> 11. 12. <servlet> 13. <servlet-name>spring</servlet-name> 14. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 15. <load-on-startup>1</load-on-startup> 16. </servlet> 17. <servlet-mapping> 18. <servlet-name>spring</servlet-name> <!-- 这里在配成spring,下边也要写一个名为spring-servlet.xml的文件,主要用来配置它的controller --> 19. <url-pattern>*.do</url-pattern> 20. </servlet-mapping> 21. <welcome-file-list> 22. <welcome-file>index.jsp</welcome-file> 23. </welcome-file-list> 24.</web-app> spring-servlet,主要配置controller的信息 [java] view plaincopy 01.<?xml version="1.0" encoding="UTF-8"?> 02. <beans 03. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 04. xmlns:context="http://www.springframework.org/schema/context" 05. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 06. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 07. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 08. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 09. 10. <context:annotation-config /> 11. <!-- 把标记了@Controller注解的类转换为bean --> 12. <context:component-scan base-package="com.mvc.controller" /> 13. <!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 --> 14. <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> 15. 16. <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --> 17. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 18. p:prefix="/WEB-INF/view/" p:suffix=".jsp" /> 19. 20. <bean id="multipartResolver" 21. class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 22. p:defaultEncoding="utf-8" /> 23. </beans> applicationContext.xml代码 [java] view plaincopy 01.<?xml version="1.0" encoding="UTF-8"?> 02.<beans 03. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 04. xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" 05. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 06. xsi:schemaLocation=" 07. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 08. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 09. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 10. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> 11. 12. <context:annotation-config /> 13. <context:component-scan base-package="com.mvc" /> <!-- 自动扫描所有注解该路径 --> 14. 15. <context:property-placeholder location="classpath:/hibernate.properties" /> 16. 17. <bean id="sessionFactory" 18. class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 19. <property name="dataSource" ref="dataSource" /> 20. <property name="hibernateProperties"> 21. <props> 22. <prop key="hibernate.dialect">${dataSource.dialect}</prop> 23. <prop key="hibernate.hbm2ddl.auto">${dataSource.hbm2ddl.auto}</prop> 24. <prop key="hibernate.hbm2ddl.auto">update</prop> 25. </props> 26. </property> 27. <property name="packagesToScan"> 28. <list> 29. <value>com.mvc.entity</value><!-- 扫描实体类,也就是平时所说的model --> 30. </list> 31. </property> 32. </bean> 33. 34. <bean id="transactionManager" 35. class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 36. <property name="sessionFactory" ref="sessionFactory" /> 37. <property name="dataSource" ref="dataSource" /> 38. </bean> 39. 40. <bean id="dataSource" 41. class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 42. <property name="driverClassName" value="${dataSource.driverClassName}" /> 43. <property name="url" value="${dataSource.url}" /> 44. <property name="username" value="${dataSource.username}" /> 45. <property name="password" value="${dataSource.password}" /> 46. </bean> 47. <!-- Dao的实现 --> 48. <bean id="entityDao" class="com.mvc.dao.EntityDaoImpl"> 49. <property name="sessionFactory" ref="sessionFactory" /> 50. </bean> 51. <tx:annotation-driven transaction-manager="transactionManager" /> 52. <tx:annotation-driven mode="aspectj"/> 53. 54. <aop:aspectj-autoproxy/> 55.</beans> hibernate.properties数据库连接配置 [java] view plaincopy 01.dataSource.password=123 02.dataSource.username=root 03.dataSource.databaseName=test 04.dataSource.driverClassName=com.mysql.jdbc.Driver 05.dataSource.dialect=org.hibernate.dialect.MySQL5Dialect 06.dataSource.serverName=localhost:3306 07.dataSource.url=jdbc:mysql://localhost:3306/test 08.dataSource.properties=user=${dataSource.username};databaseName=${dataSource.databaseName};serverName=${dataSource.serverName};password=${dataSource.password} 09.dataSource.hbm2ddl.auto=update 配置已经完成,下面开始例子 先在数据库建表,例子用的是mysql数据库 [java] view plaincopy 01.CREATE TABLE `test`.`student` ( 02. `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 03. `name` varchar(45) NOT NULL, 04. `psw` varchar(45) NOT NULL, 05. PRIMARY KEY (`id`) 06.) 建好表后,生成实体类 [java] view plaincopy 01.package com.mvc.entity; 02. 03.import java.io.Serializable; 04. 05.import javax.persistence.Basic; 06.import javax.persistence.Column; 07.import javax.persistence.Entity; 08.import javax.persistence.GeneratedValue; 09.import javax.persistence.GenerationType; 10.import javax.persistence.Id; 11.import javax.persistence.Table; 12. 13.@Entity 14.@Table(name = "student") 15.public class Student implements Serializable { 16. private static final long serialVersionUID = 1L; 17. @Id 18. @Basic(optional = false) 19. @GeneratedValue(strategy = GenerationType.IDENTITY) 20. @Column(name = "id", nullable = false) 21. private Integer id; 22. @Column(name = "name") 23. private String user; 24. @Column(name = "psw") 25. private String psw; 26. public Integer getId() { 27. return id; 28. } 29. public void setId(Integer id) { 30. this.id = id; 31. } 32. 33. public String getUser() { 34. return user; 35. } 36. public void setUser(String user) { 37. this.user = user; 38. } 39. public String getPsw() { 40. return psw; 41. } 42. public void setPsw(String psw) { 43. this.psw = psw; 44. } 45.} Dao层实现 [java] view plaincopy 01.package com.mvc.dao; 02. 03.import java.util.List; 04. 05.public interface EntityDao { 06. public List<Object> createQuery(final String queryString); 07. public Object save(final Object model); 08. public void update(final Object model); 09. public void delete(final Object model); 10.} [java] view plaincopy 01.package com.mvc.dao; 02. 03.import java.util.List; 04. 05.import org.hibernate.Query; 06.import org.springframework.orm.hibernate3.HibernateCallback; 07.import org.springframework.orm.hibernate3.support.HibernateDaoSupport; 08. 09.public class EntityDaoImpl extends HibernateDaoSupport implements EntityDao{ 10. public List<Object> createQuery(final String queryString) { 11. return (List<Object>) getHibernateTemplate().execute( 12. new HibernateCallback<Object>() { 13. public Object doInHibernate(org.hibernate.Session session) 14. throws org.hibernate.HibernateException { 15. Query query = session.createQuery(queryString); 16. List<Object> rows = query.list(); 17. return rows; 18. } 19. }); 20. } 21. public Object save(final Object model) { 22. return getHibernateTemplate().execute( 23. new HibernateCallback<Object>() { 24. public Object doInHibernate(org.hibernate.Session session) 25. throws org.hibernate.HibernateException { 26. session.save(model); 27. return null; 28. } 29. }); 30. } 31. public void update(final Object model) { 32. getHibernateTemplate().execute(new HibernateCallback<Object>() { 33. public Object doInHibernate(org.hibernate.Session session) 34. throws org.hibernate.HibernateException { 35. session.update(model); 36. return null; 37. } 38. }); 39. } 40. public void delete(final Object model) { 41. getHibernateTemplate().execute(new HibernateCallback<Object>() { 42. public Object doInHibernate(org.hibernate.Session session) 43. throws org.hibernate.HibernateException { 44. session.delete(model); 45. return null; 46. } 47. }); 48. } 49.} Dao在applicationContext.xml注入 <bean id="entityDao" class="com.mvc.dao.EntityDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> Dao只有一个类的实现,直接供其它service层调用,如果你想更换为其它的Dao实现,也只需修改这里的配置就行了。 开始写view页面,WEB-INF/view下新建页面student.jsp,WEB-INF/view这路径是在spring-servlet.xml文件配置的,你可以配置成其它,也可以多个路径。student.jsp代码 [xhtml] view plaincopy 01.<%@ page language="java" contentType="text/html; charset=UTF-8" 02. pageEncoding="UTF-8"%> 03.<%@ include file="/include/head.jsp"%> 04.<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 05.<html> 06.<head> 07.<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 08.<title>添加</title> 09.<mce:script language="javascript" src="<%=request.getContextPath()%><!-- 10./script/jquery.min.js"> 11.// --></mce:script> 12.<mce:style><!-- 13.table{ border-collapse:collapse; } 14.td{ border:1px solid #f00; } 15.--></mce:style><style mce_bogus="1">table{ border-collapse:collapse; } 16.td{ border:1px solid #f00; }</style> 17.<mce:script type="text/javascript"><!-- 18.function add(){ 19. [removed].href="<%=request.getContextPath() %>/student.do?method=add"; 20.} 21. 22.function del(id){ 23.$.ajax( { 24. type : "POST", 25. url : "<%=request.getContextPath()%>/student.do?method=del&id;=" + id, 26. dataType: "json", 27. success : function(data) { 28. if(data.del == "true"){ 29. alert("删除成功!"); 30. $("#" + id).remove(); 31. } 32. else{ 33. alert("删除失败!"); 34. } 35. }, 36. error :function(){ 37. alert("网络连接出错!"); 38. } 39.}); 40.} 41.// --></mce:script> 42.</head> 43.<body> 44. 45.<input id="add" type="button" value="添加"/> 46.<table > 47. <tr> 48. <td>序号</td> 49. <td>姓名</td> 50. <td>密码</td> 51. <td>操作</td> 52. </tr> 53. <c:forEach items="${list}" var="student"> 54. <tr id="<c:out value="${student.id}"/>"> 55. <td><c:out value="${student.id}"/></td> 56. <td><c:out value="${student.user}"/></td> 57. <td><c:out value="${student.psw}"/></td> 58. <td> 59. <input type="button" value="编辑"/> 60. <input type="button" value="${student.id}"/>')" value="删除"/> 61. </td> 62. </tr> 63. </c:forEach> 64. 65.</table> 66.</body> 67.</html> student_add.jsp [xhtml] view plaincopy 01.<%@ page language="java" contentType="text/html; charset=UTF-8" 02. pageEncoding="UTF-8"%> 03.<%@ include file="/include/head.jsp"%> 04.<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 05.<html> 06.<head> 07.<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 08.<title>学生添加</title> 09.<mce:script type="text/javascript"><!-- 10.function turnback(){ 11. [removed].href="<%=request.getContextPath() %>/student.do"; 12.} 13.// --></mce:script> 14.</head> 15.<body> 16.<form method="post" action="<%=request.getContextPath() %>/student.do?method=save"> 17.<div><c:out value="${addstate}"></c:out></div> 18.<table> 19. <tr><td>姓名</td><td><input id="user" name="user" type="text" /></td></tr> 20. <tr><td>密码</td><td><input id="psw" name="psw" type="text" /></td></tr> 21. <tr><td colSpan="2" align="center"><input type="submit" value="提交"/><input type="button" value="返回" /> </td></tr> 22.</table> 23. 24.</form> 25.</body> 26.</html> controller类实现,只需把注解写上,spring就会自动帮你找到相应的bean,相应的注解标记意义,不明白的,可以自己查下@Service,@Controller,@Entity等等的内容。 [java] view plaincopy 01.package com.mvc.controller; 02. 03.import java.util.List; 04. 05.import javax.servlet.http.HttpServletRequest; 06.import javax.servlet.http.HttpServletResponse; 07. 08.import org.apache.commons.logging.Log; 09.import org.apache.commons.logging.LogFactory; 10.import org.springframework.beans.factory.annotation.Autowired; 11.import org.springframework.stereotype.Controller; 12.import org.springframework.ui.ModelMap; 13.import org.springframework.web.bind.annotation.RequestMapping; 14.import org.springframework.web.bind.annotation.RequestMethod; 15.import org.springframework.web.bind.annotation.RequestParam; 16.import org.springframework.web.servlet.ModelAndView; 17. 18.import com.mvc.entity.Student; 19.import com.mvc.service.StudentService; 20. 21.@Controller 22.@RequestMapping("/student.do") 23.public class StudentController { 24. protected final transient Log log = LogFactory 25. .getLog(StudentController.class); 26. @Autowired 27. private StudentService studentService; 28. public StudentController(){ 29. 30. } 31. 32. @RequestMapping 33. public String load(ModelMap modelMap){ 34. List<Object> list = studentService.getStudentList(); 35. modelMap.put("list", list); 36. return "student"; 37. } 38. 39. @RequestMapping(params = "method=add") 40. public String add(HttpServletRequest request, ModelMap modelMap) throws Exception{ 41. return "student_add"; 42. } 43. 44. @RequestMapping(params = "method=save") 45. public String save(HttpServletRequest request, ModelMap modelMap){ 46. String user = request.getParameter("user"); 47. String psw = request.getParameter("psw"); 48. Student st = new Student(); 49. st.setUser(user); 50. st.setPsw(psw); 51. try{ 52. studentService.save(st); 53. modelMap.put("addstate", "添加成功"); 54. } 55. catch(Exception e){ 56. log.error(e.getMessage()); 57. modelMap.put("addstate", "添加失败"); 58. } 59. 60. return "student_add"; 61. } 62. 63. @RequestMapping(params = "method=del") 64. public void del(@RequestParam("id") String id, HttpServletResponse response){ 65. try{ 66. Student st = new Student(); 67. st.setId(Integer.valueOf(id)); 68. studentService.delete(st); 69. response.getWriter().print("{/"del/":/"true/"}"); 70. } 71. catch(Exception e){ 72. log.error(e.getMessage()); 73. e.printStackTrace(); 74. } 75. } 76.} service类实现 [java] view plaincopy 01.package com.mvc.service; 02. 03.import java.util.List; 04. 05.import org.springframework.beans.factory.annotation.Autowired; 06.import org.springframework.stereotype.Service; 07.import org.springframework.transaction.annotation.Transactional; 08. 09.import com.mvc.dao.EntityDao; 10.import com.mvc.entity.Student; 11. 12.@Service 13.public class StudentService { 14. @Autowired 15. private EntityDao entityDao; 16. 17. @Transactional 18. public List<Object> getStudentList(){ 19. StringBuffer sff = new StringBuffer(); 20. sff.append("select a from ").append(Student.class.getSimpleName()).append(" a "); 21. List<Object> list = entityDao.createQuery(sff.toString()); 22. return list; 23. } 24. 25. public void save(Student st){ 26. entityDao.save(st); 27. } 28. public void delete(Object obj){ 29. entityDao.delete(obj); 30. } 31.} OK,例子写完。有其它业务内容,只需直接新建view,并实现相应comtroller和service就行了,配置和dao层的内容基本不变,也就是每次只需写jsp(view),controller和service调用dao就行了。 怎样,看了这个,spring mvc是不是比ssh实现更方便灵活。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值