SpringMVC源码剖析

SpringMVC源码剖析

学习资料:拉勾课程《Java高薪训练营》

  1. 前端控制器DispatcherServlet继承结构

在这里插入图片描述
2. SpringMVC处理请求的大致流程

2.1 Handler⽅法的执⾏时机

打断点
在这里插入图片描述
观察调用栈 : doDispathch⽅法中的1064⾏代码完成handler⽅法的调⽤
在这里插入图片描述

2.2 ⻚⾯渲染时机(打断点并观察调⽤栈)

在这里插入图片描述
观察调用栈,调用的是doDispatch方法,该方法又调用了processDispatchResult方法
在这里插入图片描述

2.3 SpringMVC处理请求的流程即为

org.springframework.web.servlet.DispatcherServlet#doDispatch⽅法的执⾏过程,其中步骤②、 ③、 ④、 ⑤是核⼼步骤

①检查是否是文件上传的请求
②调⽤getHandler()获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截器)
​ 但是如何去getHandler的?后⾯进⾏分析
③调⽤getHandlerAdapter();获取能够执⾏①中Handler的适配器
​ 但是如何去getHandlerAdapter的?后⾯进⾏分析
④适配器调⽤Handler执⾏ha.handle(总会返回⼀个ModelAndView对象)
⑤调⽤processDispatchResult()⽅法完成视图渲染跳转

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   //handler执行器链,包含handler以及一些拦截器
   HandlerExecutionChain mappedHandler = null;
   //是否为多部件请求,是否为文件上传
   boolean multipartRequestParsed = false;
   //异步请求
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
         //1 检查是否是文件上传的请求
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         /*
            2 取得处理当前请求的Controller,这里也称为Handler,即处理器
              这里并不是直接返回 Controller,而是返回 HandlerExecutionChain 请求处理链对象
              该对象封装了Handler和Inteceptor
          */
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            // 如果 handler 为空,则返回404
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
         // 3 获取处理请求的处理器适配器 HandlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         // 处理 last-modified 请求头
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.
         // 4 实际处理器处理请求,返回结果视图对象
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
         // 结果视图对象的处理
         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      //5 跳转视图页面,渲染视图
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      //最终会调用HandlerInterceptor的afterCompletion 方法
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      //最终会调用HandlerInterceptor的afterCompletion 方法
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}
  1. 源码剖析之getHandler方法分析

    遍历两个HandlerMapping,试图获取能够处理当前请求的执⾏链
    在这里插入图片描述

  2. 源码剖析之getHandlerAdapter适配器获取分析

    遍历各个HandlerAdapter,看哪个Adapter⽀持处理当前Handler
    在这里插入图片描述

  3. 源码剖析之Handler方法执行细节剖析

    5.1入口
    在这里插入图片描述
    5.2 断点从⼊⼝进⼊
    在这里插入图片描述
    5.3 invokeHandlerMethod方法

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                     HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
       ServletWebRequest webRequest = new ServletWebRequest(request, response);
       try {
          // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中配置的InitBinder,用于进行参数的绑定
          WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
          // 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
          ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    
          // 将handlerMethod封装为一个ServletInvocableHandlerMethod对象
          ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
          if (this.argumentResolvers != null) {
             // 设置当前容器中配置的所有ArgumentResolver
             invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
          }
          if (this.returnValueHandlers != null) {
             // 设置当前容器中配置的所有ReturnValueHandler
             invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
          }
          // 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
          invocableMethod.setDataBinderFactory(binderFactory);
    
          invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
          ModelAndViewContainer mavContainer = new ModelAndViewContainer();
          mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
          // 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
          // 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
          modelFactory.initModel(webRequest, mavContainer, invocableMethod);
          mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    
          AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
          asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    
          WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
          asyncManager.setTaskExecutor(this.taskExecutor);
          asyncManager.setAsyncWebRequest(asyncWebRequest);
          asyncManager.registerCallableInterceptors(this.callableInterceptors);
          asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    
          if (asyncManager.hasConcurrentResult()) {
             Object result = asyncManager.getConcurrentResult();
             mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
             asyncManager.clearConcurrentResult();
             LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
             });
             invocableMethod = invocableMethod.wrapConcurrentResult(result);
          }
    
          // 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
          invocableMethod.invokeAndHandle(webRequest, mavContainer);
          if (asyncManager.isConcurrentHandlingStarted()) {
             return null;
          }
    
          // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
          // 还会判断是否需要将FlashAttributes封装到新的请求中
          return getModelAndView(mavContainer, modelFactory, webRequest);
       }
       finally {
          webRequest.requestCompleted();
       }
    }
    

    5.4 invokeAndHandle方法
    在这里插入图片描述
    5.5 invokeForRequest方法
    在这里插入图片描述
    5.6 getMethodArgumentValues方法

    /**
     * Get the method argument values for the current request, checking the provided
     * argument values and falling back to the configured argument resolvers.
     * <p>The resulting array will be passed into {@link #doInvoke}.
     * @since 5.1.2
     * 本方法主要是通过当前容器中配置的ArgumentResolver对request中的参数进行转化,
     * 将其处理为目标handler的参数的形式
     */
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                     Object... providedArgs) throws Exception {
    
       // 获取当前handler所声明的所有参数,主要包括参数名,参数类型,参数位置,所标注的注解等等属性
       MethodParameter[] parameters = getMethodParameters();
       if (ObjectUtils.isEmpty(parameters)) {
          return EMPTY_ARGS;
       }
    
       Object[] args = new Object[parameters.length];
       for (int i = 0; i < parameters.length; i++) {
          MethodParameter parameter = parameters[i];
          parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
          // providedArgs是调用方提供的参数,这里主要是判断这些参数中是否有当前类型,如果有,则直接使用调用方提供的参数,对于请求处理而言,默认情况下,
          // 调用方提供的参数都是长度为0的数组
          args[i] = findProvidedArgument(parameter, providedArgs);
          if (args[i] != null) {
             continue;
          }
          // 如果在调用方提供的参数中不能找到当前类型的参数值,则遍历Spring容器中所有的
          // ArgumentResolver,判断哪种类型的Resolver支持对当前参数的解析,这里的判断
          // 方式比较简单,比如RequestParamMethodArgumentResolver就是判断当前参数
          // 是否使用@RequestParam注解进行了标注
          if (!this.resolvers.supportsParameter(parameter)) {
             throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
          }
          try {
             // 如果能够找到对当前参数进行处理的ArgumentResolver,则调用其
             // resolveArgument()方法从request中获取对应的参数值,并且进行转换
             args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
          }
          catch (Exception ex) {
             // Leave stack trace for later, exception may actually be resolved and handled...
             if (logger.isDebugEnabled()) {
                String exMsg = ex.getMessage();
                if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                   logger.debug(formatArgumentError(parameter, exMsg));
                }
             }
             throw ex;
          }
       }
       return args;
    }
    
  4. 源码剖析之视图渲染(processDispatchResult)细节剖析

    6.1 render⽅法完成渲染
    在这里插入图片描述
    在这里插入图片描述
    6.2 resolveViewName方法
    在这里插入图片描述
    6.3 在解析出View视图对象的过程中会判断是否重定向、是否转发等,不同的情况封装的是不同的View实现
    在这里插入图片描述
    6.4 解析出View视图对象的过程中,要将逻辑视图名解析为物理视图名
    在这里插入图片描述
    6.5 封装View视图对象之后,调⽤了view对象的render⽅法
    在这里插入图片描述
    6.6 渲染数据
    在这里插入图片描述
    6.7 把modelMap中的数据暴露到request域中,这也是为什么后台model.add之后在jsp中可以从请求域取出来的根本原因
    在这里插入图片描述
    6.8 将数据设置到请求域中
    在这里插入图片描述

  5. 源码剖析之SpringMVC九大组件初始
    7.1 在DispatcherServlet中定义了九个属性,每⼀个属性都对应⼀种组件
    /** MultipartResolver used by this servlet. */
    //多部件解析器
    @Nullable
    private MultipartResolver multipartResolver;
    
    /** LocaleResolver used by this servlet. */
    //区域化 , 国际化解析器
    @Nullable
    private LocaleResolver localeResolver;
    
    /** ThemeResolver used by this servlet. */
    //主题解析器
    @Nullable
    private ThemeResolver themeResolver;
    
    /** List of HandlerMappings used by this servlet. */
    //处理器映射器组件
    @Nullable
    private List<HandlerMapping> handlerMappings;
    
    /** List of HandlerAdapters used by this servlet. */
    //处理器适配器组件
    @Nullable
    private List<HandlerAdapter> handlerAdapters;
    
    /** List of HandlerExceptionResolvers used by this servlet. */
    //异常解析器组件
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;
    
    /** RequestToViewNameTranslator used by this servlet. */
    //默认视图名转换器组件
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;
    
    /** FlashMapManager used by this servlet. */
    //flash属性管理组件
    @Nullable
    private FlashMapManager flashMapManager;
    
    /** List of ViewResolvers used by this servlet. */
    //视图解析器
    @Nullable
    private List<ViewResolver> viewResolvers;
    

    九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如ViewResolver、 HandlerAdapter等都是接⼝

    7.2源码剖析之SpringMVC九大组件初始化细节

    7.2.1 DispatcherServlet中的onRefresh(),该⽅法中初始化了九⼤组件
    在这里插入图片描述
    7.2.2 观察其中的⼀个组件initHandlerMappings(context)

    private void initHandlerMappings(ApplicationContext context) {
       this.handlerMappings = null;
    
       if (this.detectAllHandlerMappings) {
          // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
          // 按照HandlerMapping.class类型去IOC容器中找到所有的HandlerMapping
          Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
          if (!matchingBeans.isEmpty()) {
             this.handlerMappings = new ArrayList<>(matchingBeans.values());
             // We keep HandlerMappings in sorted order.
             AnnotationAwareOrderComparator.sort(this.handlerMappings);
          }
       }
       else {
          try {
             // 否则在ioc中按照固定名称id(handlerMapping)去找
             HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
             this.handlerMappings = Collections.singletonList(hm);
          }
          catch (NoSuchBeanDefinitionException ex) {
             // Ignore, we'll add a default HandlerMapping later.
          }
       }
    
       // Ensure we have at least one HandlerMapping, by registering
       // a default HandlerMapping if no other mappings are found.
       if (this.handlerMappings == null) {
          // 最后还为空则按照默认策略生成
          //按照默认方式实例化生成HandlerMapping
          this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
          if (logger.isTraceEnabled()) {
             logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                   "': using default strategies from DispatcherServlet.properties");
          }
       }
    }
    

    7.2.3 如果按照类型和按照固定id从ioc容器中找不到对应组件,则会按照默认策略进⾏注册初始化,默认策略在DispatcherServlet.properties⽂件中配置

    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
       String key = strategyInterface.getName();
       String value = defaultStrategies.getProperty(key);
       if (value != null) {
          String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
          List<T> strategies = new ArrayList<>(classNames.length);
          for (String className : classNames) {
             try {
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
             }
             catch (ClassNotFoundException ex) {
                throw new BeanInitializationException(
                      "Could not find DispatcherServlet's default strategy class [" + className +
                            "] for interface [" + key + "]", ex);
             }
             catch (LinkageError err) {
                throw new BeanInitializationException(
                      "Unresolvable class definition for DispatcherServlet's default strategy class [" +
                            className + "] for interface [" + key + "]", err);
             }
          }
          return strategies;
       }
       else {
          return new LinkedList<>();
       }
    }
    

    7.2.4 DispatcherServlet.properties
    在这里插入图片描述
    7.2.5 注意:多部件解析器的初始化(initMultipartResolver)必须按照id注册对象(multipartResolver)

    private void initMultipartResolver(ApplicationContext context) {
       try {
          this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
          if (logger.isTraceEnabled()) {
             logger.trace("Detected " + this.multipartResolver);
          }
          else if (logger.isDebugEnabled()) {
             logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
          }
       }
       catch (NoSuchBeanDefinitionException ex) {
          // Default is no multipart resolver.
          this.multipartResolver = null;
          if (logger.isTraceEnabled()) {
             logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
          }
       }
    }
    
弃用了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、付费专栏及课程。

余额充值