zoukankan      html  css  js  c++  java
  • 逐步理解SpringMVC

    1.MVC(Model-View-Controller)

    用慕课网上的一个图来看看MVC

    Front Controller(前端控制器):把客户的请求分发给不同的控制器去生成业务数据,将生成的业务数据分发给恰当的视图模板生成我们最终的图形界面

    Controller(控制层):实现业务数据的抽取,Controller-->Service-->dao-->ORM-->数据持久化

    Model(模型层):我认为像是一个中间件,如果需要数据持久化的时候,model将会成为PO(persistant object),和dao层进行数据持久化;如果需要给View层展示的话,model将会成为VO(value object),不一定和数据库中的表对应,将View需要的数据封装传过去;有时候一个VO可能满足不了View层的需求,所以BO(business objec)就出来了;最后就是Pojo了,VO和PO应该都属于它。 java的几种对象(PO,VO,DAO,BO,POJO)解释

    View(视图层):显示呗

    2.SpringMVC

    a.DispatcherServlet

    SpringMVC很好的体现了MVC的核心风格:业务数据抽取同业务数据呈现相分离,只不过DispatcherServlet成了我们(1)中提出的前端控制器。DispatcherServlet拦截了我们的请求,将请求分发给不同的Controller,但他是怎么知道不同的Controller的呢?所以有一个东西出现了

    b.HandlerAdapter

     

    HandlerAdapter是一个接口,有5个实现类,我们在springMVC中大多数用的是@Controller,所以我们看看它的实现类SimpleControllerHandlerAdapter,在Dispatcher中我们看一下这个:

    /**
         * Initialize the HandlerAdapters used by this class.
         * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
         * we default to SimpleControllerHandlerAdapter.(如果工厂中没有这个handlerAdapter这个命名空间的话,我们就默认使用SimpleControllerHandlerAdapter)
         */
        private void initHandlerAdapters(ApplicationContext context) {
            this.handlerAdapters = null;
    
            if (this.detectAllHandlerAdapters) {
                // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
                Map<String, HandlerAdapter> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
                    // We keep HandlerAdapters in sorted order.
                    OrderComparator.sort(this.handlerAdapters);
                }
            }
            else {
                try {
                    HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
                    this.handlerAdapters = Collections.singletonList(ha);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Ignore, we'll add a default HandlerAdapter later.
                }
            }
    
            // Ensure we have at least some HandlerAdapters, by registering
            // default HandlerAdapters if no other adapters are found.
            if (this.handlerAdapters == null) {
                this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
                }
            }
        }

    就这样知道了我们写的Controller,本人小白,理解有误,请大神指点!

    c.HandlerInterceptor

    接口,在调用Controller之前或者之后干点什么,类似AOP的思想。

    d.HandlerMapping

    既然用HandlerAdapter知道了那个对应的那个Controller,这个HandlerMapping就是分发请求的啦

    e.HandlerExecutionChain

    这个就是controller处理完与HandlerInterceptor生成handler传给DispatcherServlet

    f.ModeAndView

    Model的具体表现,我们在controller中使用map或者model,这个东西的作用就是将所有的东西转换成modelandview

    g.ViewResolver

    视图解析器,决定要用那个对象,如jstl,json

    来,我们上个图理解一下:

     

    总结一下:当请求传到DispatcherServlet上的时候,由HandlerMapping分发请求到Controller,Controller处理完相应的操作,如业务逻辑,数据的持久化啊等等,然后和HandlerInterceptor把结果生成ModelandView返还给DispatcherServlet,然后DispatcherServlet决定用那个Resolver来解析数据,如jstl,xml,json啊等等,最后让view层显示就行了。

    3.SpringMVC中的过程具体实操

    a.先看看demo结构

    b.上码

    a.web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 如果为2.4版本的,就会默认支持EL -->
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
      <display-name>Archetype Created Web Application</display-name>
      
      <!-- Spring的应用上下文,理解层次化的ApplicationContext -->
      <!-- 定时任务都建议放在全局监听的<context-param>中,而不建议放在<servlet>的<init-param>中. -->
      <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value>
      </context-param>
      
      <listener>
      <listener-class>
      org.springframework.web.context.ContextLoaderListener
      </listener-class>
      </listener>
      
      
      
      <!-- DispatcherServlet,Spring MVC的核心 -->
      <servlet>
      <servlet-name>mvc-dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- DispatcherServlet对应的上下文配置,如果不配置下面的<init-param>,默认为/WEB-INF/${servlet-name}-servlet.xml -->
      <init-param>
      <!-- contextConfigLocation为固定的写法 -->
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>
      
      </init-param>
       <!-- 1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
            2)它的值必须是一个整数,表示servlet应该被载入的顺序
            2)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
            3)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
            4)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
            5)当值相同时,容器就会自己选择顺序来加载。 -->
      <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
      <servlet-name>mvc-dispatcher</servlet-name>
      <!-- 表示拦截所有请求 -->
      <url-pattern>/</url-pattern>
      </servlet-mapping>
      
      
    </web-app>

    b.mvc-dispatcher-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" xmlns:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 本配置文件是工名为mvc-dispatcher的DispatcherServlet使用,提供与其相关的Spring MVC的配置 -->
    
        <!--标签支持,生命周期的管理 ,激活了我们spring关于annotation的DI,当自动扫描启动后,该配置可以去掉-->
        <context:annotation-config />
    
        <!-- 自动扫描,只搜索@Controller标注的类,不搜索其他标注的类 -->
        <context:component-scan base-package="com.huhu.spring">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
    
    
    
        <!--HandlerMapping,会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter两个bean,
                    是spring MVC为@Controllers分发请求所必须的,无需配置,spring MVC可以默认启动
          RequestMappingHandlerMapping   处理@RequestMapping注解的
          RequestMappingHandlerAdapter   处理@Controller注解的控制器类-->
        <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 (也就是说,URL查询参数的某一个变量可以直接映射到@Controller的某个方法的输入参数)-->
        <mvc:annotation-driven />
        
       <!-- 静态资源处理,css,js,images -->
       <!-- 
       <mvc:resources mapping="/resources/**" location="/resources/"/>
        -->
        
        <!--配置ViewResolver,可以使用多个ViewResolver,使用order排序,但是InternalResourceViewResolver放在最后-->
        <!-- ViewResolver用那个viewresolver来获取bean -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/jsps/"></property>
        <property name="suffix" value=".jsp"></property>
        </bean>
        
        
        <!-- 文件上传 -->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="maxUploadSize" value="10485760"/> <!-- 10m -->
            <property name="maxInMemorySize" value="4096" />
            <property name="defaultEncoding" value="UTF-8"></property>
        </bean>
        
        <!-- viewResolver -->
        <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">    
        <property name="mediaTypes">    
          <map>    
            <entry key="html" value="text/html"/>    
            <entry key="xml" value="application/xml"/>  
            <entry key="json" value="application/json"/>    
          </map>    
        </property> 
        <!-- ViewResolver 负责怎么去解析, 而View只代表一种 视图层的技术 --> 
        <!--<property name="viewResolvers"> <list>   </list>  </property>-->
        <property name="defaultViews">  
            <list>  
                <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>  
            </list>  
        </property>  
    </bean>  
        
    </beans>

    c.applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
        xmlns:cache="http://www.springframework.org/schema/cache"  
        xsi:schemaLocation="  
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context.xsd  
        http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/mvc  
        http://www.springframework.org/schema/mvc/spring-mvc.xsd" default-autowire="byName">  
      
        <!--
                        自动扫描web包 ,将带有注解的类 纳入spring容器管理 
                        默认执行<context:annotation-config /> 
        -->  
        <context:component-scan base-package="com.huhu" >
        <!-- 不把@Controller纳入到spring容器中 -->
        <context:exclude-filter type="annotation" 
        expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
         
        
        
    
    </beans> 

    具体的xml语句的含义在文件注释中已经标注,我想谈谈xml语句的运行流程:

    web.xml的运行流程(context-param >> listener  >> fileter  >> servlet):

    1. 在启动Web项目时,容器(比如Tomcat)会读web.xml配置文件中的两个节点<listener>和<contex-param>。
    2. 接着容器会创建一个ServletContext(上下文),应用范围内即整个WEB项目都能使用这个上下文。
    3. 接着容器会将读取到<context-param>转化为键值对,并交给ServletContext。
    4. 容器创建<listener></listener>中的类实例,即创建监听(备注:listener定义的类可以是自定义的类但必须需要继承ServletContextListener)。
    5. 在监听的类中会有一个contextInitialized(ServletContextEvent event)初始化方法,在这个方法中可以通event.getServletContext().getInitParameter("contextConfigLocation"来得到context-param 设定的值。在这个类中还必须有一个contextDestroyed(ServletContextEvent event) 销毁方法.用于关闭应用前释放资源,比如说数据库连接的关闭。
    6. 得到这个context-param的值之后,你就可以做一些操作了.注意,这个时候你的WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早

    web.xml中的bean加载(ContextLoaderListener,ContextLoaderServlet,servlet.DispatcherServlet):

    1. ContextLoaderListener 和 ContextLoaderServlet : 本质上是等同的,都是调用ContextLoader来加载web程序的上下文,加载完成以后,都是在ServletContext中,只不过listener需要Servlet2.3及以上支持。
    2. ContextLoaderListene与DispatcherServlet : 用DispatcherServlet载入的Bean是隶属于此Servlet的(所以spring可以配置多个分别拥有各自环境的DispatcherServlet),因此其他servlet无法获取到该Context。

    d.TestController.java

    @Controller
    @RequestMapping("/test")
    public class TestController {
        
        private static Logger logger =LoggerFactory.getLogger(TestController.class);
        @Autowired
        private TestService testService;
        
       @RequestMapping("/{s}")
       @ResponseBody
       public String test(@PathVariable("s") String s) {
           logger.info(s);
           String str1 = testService.test(s);
           return str1;
       }
       
       //用于文件上传,需要在pom.xml中添加依赖commons-fileupload
       
       @RequestMapping("/upload")
       public String upLoad(@RequestParam("file") MultipartFile file) throws IOException {
           if(!file.isEmpty()) {
               FileUtils.copyInputStreamToFile(file.getInputStream(), new File("D:\\demo\\image",System.currentTimeMillis()+".jpg"));   
           }
           return "success";
           
       }
       
       //json测试,需要在pom.xml中添加依赖jackson-databind
       @RequestMapping(value="/json/{id}",method=RequestMethod.GET)
       @ResponseBody
       public Map<String,String> json(@PathVariable("id") String id ) {
           
        Map<String, String> map=testService.jsonTest();
    
            return map;
    
       }
        
    }

    e.TestService.java

    public interface TestService {
        public String test(String s);
        Map<String, String>  jsonTest();
    }

    f.TestServiceImpl.java

    @Service("testService")
    public class TestServiceImpl implements TestService{
    
        public String test(String s) {
            
            return "hello"+s;
        }
        
        public Map<String, String>  jsonTest(){
            Map<String, String> map=new HashMap();
            map.put("id", "1");
            map.put("name", "huhu");
            map.put("age", "23");
            return map;
        }
        
        
    }

    g.pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>huhu</groupId>
        <artifactId>test</artifactId>
      <version>1.0-SNAPSHOT</version>
      <name>test Maven Webapp</name>
      <url>http://maven.apache.org</url>
      
      <!-- 变量,来替代一些工程版本 -->
      <properties>
      <commons-lang.version>2.6</commons-lang.version>
      <slf4j.version>1.7.6</slf4j.version>
      <spring.version>4.1.3.RELEASE</spring.version>
      </properties>
      
      <!-- 统一管理jar包的版本 -->
      <dependencyManagement>
      <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>${spring.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
         </dependencies>
      </dependencyManagement>
    
      <dependencies>
      <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
       <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
       </dependency>
      
      <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        </dependency>
      
        <dependency>
          <groupId>commons-lang</groupId>
          <artifactId>commons-lang</artifactId>
          <version>${commons-lang.version}</version>
        </dependency>
        
        <dependency>    
       <groupId>javax.servlet</groupId>    
       <artifactId>jstl</artifactId>    
       <version>1.2</version>    
       <scope>runtime</scope>    
        </dependency> 
        
        <!-- 文件上传需要依赖的包-->
        <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
        </dependency>
        
        <!-- viewResolver中所需要的json -->
        <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.8.5</version>
        </dependency>
    
      </dependencies>
      
      <!-- 运行项目的插件  如mvn jetty:run -->
      <build>
      <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-maven-plugin -->
        <plugins>
          <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.2.2.v20140723</version>
          </plugin>
         </plugins>
      </build>
    
    </project>

    文件都上传了,练练手吧!

    4.SpringMVC中的数据绑定

    a.什么是数据绑定?
    将界面用户所填的属性和程序内部设定的类型绑定
    b.基本类型和包装类型
    1.基本类型
    key是必须传的,只能是int类型
    2.包装类型
    可以不传key,可以通过注解@RequestParam()可以指定默认值
    @RequestParam():
    value() 对传入参数指定名称例如ecif
    required() @RequestParam配置的传入参数是否一定要传
    required=false表示可以不传,默认给参数赋值为null注意(参数为基本的数据类型int时不能使用 因为null不能赋值给int )
    required=true 就是必须要有传入参数,不传会报400参数错误
    value与required配合使用如没有配置任何信息为默认值 必须输入对应参数名
    3.数组
    页面可以传相同属性的多个值,如name=qw&name=er
    c.简单对象,多层级对象,同属性对象
    简单对象绑定:地址栏传对象的属性就好了
    多层级对象(对象中含有一个属性是另外一种对象)绑定:只需要地址栏中输入对象的属性.属性就可以了
    同属性对象(拥有相同属性的对象,地址栏该怎么输入呢):使用@InitBinder("")
    函数体为:
    @InitBinder("user")
    public void initUser(WebDataBinder binder){
    binder.setFieldDefaultPrefix("user.");
    }
    ps:如果没有使用@InitBinder(""),那么同属性对象该怎么赋值呢

    d.list,set,map的数据绑定
    1.list
    可以通过索引下标进行绑定,地址栏直接写对象的属性,如users[0].name=qw&users[1].name=er
    如果前台请求,users[0].name=qw&users[20].name=er的时候,中间会出现一些空的对象,浪费资源空间,所以当我们请求的时候注意索引下标
    2.set
    ps:set需要在运行的时候进行初始化,主要作用是在实际运用中进行对象的重复判断,以及排除重复
    直接请求会出现500错误,说set of size 0,创建对象的时候给他申请空间
    set需要重写equals()和hashcode()方法,无序,不重复,hashcode是唯一的,但是不符合实际,所以重写object的hashcode()方法,重写的话会出现相同属性的对象产生相同的hashcode(),所以还要通过equals()方法来判断是否是同一个对象。
    3.map
    通过键值对来存储值
    场景:一个班级,不需要重名的
    URL请求:map.do?users['X'].name=qw&users['X'].age=23

    ------------------------------------------------------------------------------------------------补充-----------------------------------------------------------------------------

    1.是谁关联了****Mapper.xml和****.java的?

    当然是通过xml关联的,下面放一个mybatis.xml的配置文件。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 读入properties配置文件 -->
        <properties resource="init.properties">
    
        </properties>
        <!-- 对象别名 -->
        <typeAliases>
            <typeAlias type="com.huhu.pojo.Person" alias="Person"/>
        </typeAliases>
        <!-- 数据源配置 -->
        <environments default="development">
            <environment id="development">
                <!-- 事务类型 -->
                <transactionManager type="JDBC"/>
                <!-- 数据源 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>
        <!-- 注册xml文件 -->
        <mappers>
            <!-- 注册PersonMapper.xml文件,PersonMapper.xml位于com.huhu.dao.mapper这个包下,
                所以resource写成com/huhu/dao/mapper/PersonMapper.xml-->
            <mapper resource="com/huhu/dao/mapper/PersonMapper.xml"/>
            <!-- 将map下的*.java和*.xml都注册到SqlSession实例中 -->
            <package name="com.huhu.dao.PersonMapper"/>
        </mappers>
    </configuration>

    2.那是谁注入了mybatis.xml呢?

    web.xml中注入的

    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        classpath:spring/applicationContext.xml,
        classpath:mybatis/mybatis.xml
      </param-value>
    </context-param>

    真正的加载顺序为:context-param -> listener -> filter -> servlet

    参考资料:

    慕课网-Spring MVC起步

    pring3.0中返回Json

    spring web.xml 难点配置总结【转】

    解析mediaTypes+viewResolvers+viewResolvers

    谢谢大家的阅读,博客不易,请转载注明地址:http://www.cnblogs.com/huhu1203/p/7633060.html

  • 相关阅读:
    学习、发现和创造一切皆有规律
    Ubuntu12.04下建立交叉编译环境、使用QEMU模拟CortexA9、QEMU运行uboot
    基于ARM的SoC设计入门[zz]
    ARM指令集详解[zz]
    电子工程自学步骤与书籍非电子专业
    IC设计的前端和后端[zz]
    [转]用C#获取IE临时文件
    二行代码解决全部网页木马(含iframe/script木马)(zt)
    winform 分页控件,源码下载
    在UpdatePanel中GridView导出EXECL问题
  • 原文地址:https://www.cnblogs.com/huhu1203/p/7633060.html
Copyright © 2011-2022 走看看