一、非注解映射器和适配器(工程springMVC02
)
1.1 非注解映射器
- 非注解映射器
BeanNameUrlHandlerMapping
在之前我们使用的是这个处理器映射器,配置为:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
使用此映射器的时候是将bean
的名字作为url
进行查找,需要在配置Handler
时指定bean
的名字(就是url
)
<bean id="itemsController1" name="/queryItems.action" class="cn.itcast.ssm.controller.ItemsController1"/>
- 非注解映射器
SimpleUrlHandlerMapping
从上面的配置可以看到,如果控制器较多的时候便不是很方便了,于是看使用此种非注解映射器如何配置:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- 对itemsController1进行url映射,一个处理器可以对应多个映射地址 -->
<prop key="/queryItems1.action">itemsController1</prop>
<prop key="/queryItems2.action">itemsController1</prop>
<!-- 配置ItemsController2 -->
<prop key="/queryItems3.action">itemsController2</prop>
</props>
</property>
</bean>
使用此种映射器的时候一个控制器可以对应多个url
,可以看到我们可以同时为多个控制器配置url
,较为方便。这里注意:我们使用<prop>
中的key
指定url
,其值即是相关控制器的id
,但是使用此映射器的时候,配置控制器时则不需要指定name
属性了。
1.2 非注解适配器
- 非注解适配器
SimpleControllerHandlerAdapter
我们看相关的配置:
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
而使用此配置器时,相关的控制器需要实现Controller
接口,我们看相关的类实现ItemsController1.java
:
package cn.itcast.ssm.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import cn.itcast.ssm.pojo.Items;
//实现Controller接口的映射器
public class ItemsController1 implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
//调用service查询数据库,查询商品列表,这里使用静态数据库模拟
List<Items> itemsList = new ArrayList<Items>();
Items items_1 = new Items();
items_1.setName("联想笔记本");
items_1.setPrice(6000f);
items_1.setDetail("ThinkPad T 430");
Items items_2 = new Items();
items_2.setName("苹果手机");
items_2.setPrice(5000f);
items_2.setDetail("iphone6s");
itemsList.add(items_1);
itemsList.add(items_2);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相当于request的setAttribute方法,在jsp页面中就可以通过items取数据了
modelAndView.addObject("itemsList",itemsList);
//指定视图
modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
return modelAndView;
}
}
这个类其实我们在之前的工程中已经给出了。
- 非注解适配器
HttpRequestHandlerAdapter
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
使用此适配器的控制器需要实现HttpRequestHandler
接口,当然映射器可以任选。这里我们给出相关的类ItemsController2.java
:
package cn.itcast.ssm.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import cn.itcast.ssm.pojo.Items;
//实现Controller接口的映射器
public class ItemsController2 implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Items> itemsList = new ArrayList<Items>();
Items items_1 = new Items();
items_1.setName("联想笔记本");
items_1.setPrice(6000f);
items_1.setDetail("ThinkPad T 430");
Items items_2 = new Items();
items_2.setName("苹果手机");
items_2.setPrice(5000f);
items_2.setDetail("iphone6s");
itemsList.add(items_1);
itemsList.add(items_2);
request.setAttribute("itemsList",itemsList);
//视图
request.getRequestDispatcher("/WEB-INF/jsp/items/itemsList.jsp").forward(request, response);
/*
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
*/
}
}
注意:这种方式看起来很原始,但可通过response
修改定义响应的内容,比如返回json
数据。在上面我们已经给出相关的代码。
注意:这里我们配置多个适配器和映射器是不会冲突的。因为这里的映射器和适配器都是非注解的。
二、注解映射器和适配器(工程springMVC03
)
2.1 相关说明
- 1.非注解的多个映射器或适配器不会冲突,但是却不能和注解的适配器同时使用,必须要成对,即注解的适配器必须使用注解的映射器。
- 2.我们发现在上一个工程中我们将相关映射器和适配器删掉之后工程还是可以运行的,这是因为
springMVC
中有默认的配置,我们在包中找到一个资源文件:
前端控制器会从配置文件中加载处理器映射器、适配器、视图解析器等组件,如果不再配置文件中配置这些组件,那么就使用DispatcherServlet.properties
中默认的配置。
2.2 注解的映射器和适配器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
说明:这里我们分别配置了注解形式的映射器和适配器,当然springMVC
给我们提供了更好用的工具,使用下面的配置我们就可以将上面的配置取消掉了:
<mvc:annotation-driven/>
说明:我们在使用此配置的时候报错很可能是schema
文件没有配置上,在后面我们会给出完整的配置文件,可以自行查看。其实在开发中也是使用此种方式。
2.3 控制器编写
使用注解的方式,当然控制器的编写也是不一样了,我们发现在使用非注解的方式时编写控制器总是要继承相关的接口,那必然就要实现相关的方法,那如果我们想编写多个方法就不是很方便了,这里我们看注解方式可以很好的改善这一问题:
ItemsController3.java
package cn.itcast.ssm.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import cn.itcast.ssm.pojo.Items;
//注解的handler
/*使用@Controller标识控制器*/
@Controller
public class ItemsController3{
//商品查询列表,一般建议将url和方法名一样,@RequestMapping实现对下面方法和url进行映射,
//即一个方法对应一个url,url后面可以加.action也可以不加
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception{
List<Items> itemsList = new ArrayList<Items>();
Items items_1 = new Items();
items_1.setName("联想笔记本");
items_1.setPrice(6000f);
items_1.setDetail("ThinkPad T 430");
Items items_2 = new Items();
items_2.setName("苹果手机");
items_2.setPrice(5000f);
items_2.setDetail("iphone6s");
itemsList.add(items_1);
itemsList.add(items_2);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相当于request的setAttribute方法,在jsp页面中就可以通过items取数据了
modelAndView.addObject("itemsList",itemsList);
//指定视图
modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
return modelAndView;
}
//其他方法也可以同时在此类中进行添加
}
说明:这里我们使用注解@Controller
表示此类是一个控制器,使用注解@RequestMapping
表示其对应的url
。同时我们可以看到不需要继承任何接口,于是我们可以在此类中定义多个方法,而相关的url
映射使用注解即可。
2.4 配置控制器
<!-- 配置Handler -->
<!--对于注解的Handler可以单个配置,id不需要了,因为在类中使用注解已经配置好了,在实际开发中使用主键扫描 -->
<!-- <bean class="cn.itcast.ssm.controller.ItemsController3"/> -->
<!-- 主键扫描,可以扫描Controller、service等等,这里是扫描Controller -->
<context:component-scan base-package="cn.itcast.ssm.controller"></context:component-scan>
说明:这里我们给出了两种配置方法,当然如何取舍我们在配置中已经注明。最后我们可以使用地址http://localhost:8080/springMVC03/queryItems.action
进行访问。不管url
中有没有.action
,都使用上面的地址。
三、视图解析器
视图解析器默认是支持jstl
的,但是在配置的时候我们还可以为跳转的jsp
地址配置前缀和后缀:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 默认支持下面的jstl -->
<!-- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> -->
<!-- jsp路径前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- jsp路径后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
这里我们配置了前缀和后缀,那么在控制器ItemsController3.java
中进行跳转的时候就可以这样:
modelAndView.setViewName("items/itemsList");
最后:给出完整的配置文件springmvc.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 配置Handler -->
<!--对于注解的Handler可以单个配置,id不需要了,因为在类中使用注解已经配置好了,在实际开发中使用主键扫描 -->
<!-- <bean class="cn.itcast.ssm.controller.ItemsController3"/> -->
<!-- 主键扫描,可以扫描Controller、service等等,这里是扫描Controller -->
<context:component-scan base-package="cn.itcast.ssm.controller"></context:component-scan>
<!-- **************************注解的处理器映射器************************** -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- **************注解的处理器适配器,所有的处理器适配器都实现HandlerAdapter************** -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!-- 使用下面的注解驱动可以代替上面的注解映射器和注解适配器的配置,同时默认加载了很多的参数绑定方法,比如json转换的解析器
如果使用此种方式就不用配置上面的映射器和适配器了,在实际开发中使用此种方式 -->
<!-- <mvc:annotation-driven/> -->
<!-- ****************视图解析器,解析jsp视图,默认使用jstl,classpath下需要有jstl的包*************** -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 默认支持下面的jstl -->
<!-- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> -->
<!-- jsp路径前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- jsp路径后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
四、跟踪源码
这里我们只是简要的跟踪一下源码,加深对springMVC
执行流程的认识。
1.前端控制器
这里当然我们首先供前端控制器开始,此类中有一个入口方法doDispatch()
,我们从此方法开始。-
2.前端控制器调用映射器查找控制器
我们在doDispatch()
中找到这样一段
这里我们进入getHandler
方法:
可以看到此方法返回一个执行链HandlerExecutionChain
。 -
3.调用处理器处理器调用
handler
映射器返回执行链之后前端加载器就会调用处理器去处理相关方法,在doDispatch()
方法中找到:
我们进入此方法的时候发现出现了很多适配器,我们在之前都见过,它们在执行完之后都会返回一个ModelAndView
。 -
4.视图渲染,将
model
数据填充到request
域
在doDispatch()
方法中找到
进入此方法,在此方法中找到:
可以看到调用的是render
方法,得到一个view
,再调用view
的渲染方法,将model
数据填充到request
域中。在前端控制器类render()
方法中找到:
此类是一个接口,我们进入其抽象方法
再次进入,可以看到:
进入方法renderMergedOutputModel
这里我们可以看到很多渲染类,之前我们配置了一个
InternalResourceViewResolver
,选择InternalResourceView
,看到下面这段代码:点击进入:
这里就将模型数据填充到了视图类中。