spring-MVC

1. SpringMVC

java开源框架,Spring Framework的一个独立模块。

MVC框架,在项目中开辟MVC层次架构

对控制器中的功能 包装 简化 扩展践行工厂模式,功能架构在工厂之上

MVC : Model View Controller

模型 视图 控制器

模型:即业务模型,负责完成业务中的数据通信处理,对应项目中的 service和dao

视图:渲染数据,生成页面。对应项目中的Jsp

控制器:直接对接请求,控制MVC流程,调度模型,选择视图。对应项目中的Servlet

MVC是现下软件开发中的最流行的代码结构形态;

人们根据负责的不同逻辑,将项目中的代码分成 M V C 3个层次;

层次内部职责单一,层次之间耦合度低;

符合低耦合 高内聚的设计理念。也实际有利于项目的长期维护。

2. 开发流程

2.1 导入依赖

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

2.2 配置核心(前端)控制器

作为一个MVC框架,首先要解决的是:如何能够收到请求!

所以MVC框架大都会设计一款前端控制器,选型在 Servlet 或 Filter两者之一,在框架最前沿率先工作,接收所有请求。

此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。

<servlet>
    <servlet-name>mvc9</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- Servlet启动时刻:可选 -->
    <load-on-startup>1</load-on-startup>
    <!-- 局部参数:声明配置文件位置 -->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:mvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>mvc9</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

2.3 后端控制器

等价于之前定义的Servlet

@Controller //声明这是一个控制器
@RequestMapping("/hello")  //访问路径 ,等价于url-pattern
public class HelloController {
	@RequestMapping("/test1")  //访问路径
	public String hello1(){
		System.out.println("hello world");
		return "index"; // 跳转:/index.jsp  
	}
	@RequestMapping("/test2") //访问路径
	public String hello2(){
		System.out.println("hello c9");
		return "views/users";//  跳转:/views/user.jsp
	}
}

2.4 配置文件

默认名称:核心控制器名-servet.xml 默认位置:WEB-INF

随意名称:mvc.xml 随意位置:resources 但需要配置在核心控制器中

<beans 	xmlns="http://www.springframework.org/schema/beans"
		xmlns:context="http://www.springframework.org/schema/context"
		xmlns:mvc="http://www.springframework.org/schema/mvc" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.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">

	<!-- 告知springmvc  哪些包中 存在 被注解的类 -->
	<context:component-scan base-package="com.zhj.controller"></context:component-scan>
	<!-- 注册注解开发驱动 -->
	<mvc:annotation-driven></mvc:annotation-driven>
	<!-- 视图解析器
	     作用:1.捕获后端控制器的返回值="index"
	          2.解析: 在返回值的前后 拼接 ==> "/index.jsp"
	 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 前缀 -->
		<property name="prefix" value="/"></property>
		<!-- 后缀 -->
		<property name="suffix" value=".jsp"></property>
	</bean>
</beans>

2.5 访问

http://localhost:8989/hello/test1
http://localhost:8989/hello/test2

3. 接收请求参数

通过控制器中方法的形参 接收请求参数

请求参数 要和 方法的形参同名

3.1 零散接收

// id  name gender
// springMVC默认可以识别的日期字符串格式为: YYYY/MM/dd HH:mm:ss
// http://localhost:8989/xxx/../text1?id=1&name=zzz&gender=false&birth=2018-12-12 12:20:30
@RequestMapping("/test1")
public String testParam1(Integer id,
                         String name,
                         Boolean gender,
                         @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")Date birth){
    System.out.println("test param1");
    return "index";
}

3.2 实体收参(建议)

请求参数和实体的属性 同名即可

public class User {
	private Integer id;
	private String name;
	@DateTimeFormat(pattern="yyyy-MM-dd")
	private Date birth;
	private Boolean gender;
	//set/get ...
}

//http://localhost:8989/.../test2?id=1&name=zzz&gender=false&birth=2018-12-12 12:20:30
@RequestMapping("/test2")
public String testParam2(User user){
    System.out.println("test param2");
    System.out.println("user:"+user);
    return "index";
}

3.3 数组收参 ( 了解 )

简单类型的 数组

<form>
    ......
    <input type="checkbox" name="hobby" value="fb"/>足球 
    <input type="checkbox" name="hobby" value="bb"/>篮球 
    <input type="checkbox" name="hobby" value="vb"/>排球
    
</form>
//http://localhost:8989/.../test3?hobby=football&hobby=basketball
@RequestMapping("/test3")
public String testParam3(String[] hobby){
    for(String h:hobby){
        System.out.print(h+" ");
    }
    return "index";
}

3.4 集合收参 (了解)

public class VO9 {
	//private User[] users;
	private List<User> users;
	//set/get..
}

// <input type="text" name="users[0].id"/>
//http://...?users[0].id=1&users[0].name=zhangsan&users[0].birth=2018-12-12&users[1].id=2&....
@RequestMapping("/test4")
public String testParam4(VO9 vo){
    for(User user:vo.getUsers()){
        System.out.println(user);
    }
    return "index";
}

3.5 中文乱码

首先,页面中字符集统一

JSP : <%@page  pageEncoding="utf-8" %>
HTML : <meta charset="UTF-8">

其次,tomcat中字符集设置,对get请求中,中文参数乱码有效

Tomcat配置:URIEncoding=utf-8

最后,设置此filter,对post请求中,中文参数乱码有效

request.setCharactorEncoding("utf-8");
request.getParameter("name");
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4. 跳转

跳转关键字 forward: redirect:

4.1 C–>V

【C-->V】
@RequestMapping("/test1")
public String testJump1(){
    System.out.println("test jump1");
    //重定向跳转   /index.jsp
    //return "redirect:/index.jsp";
    //转发跳转  /views/users.jsp
    return "forward:/views/users.jsp";
}

4.2 C–>C

【C-->C】
@RequestMapping("/test2")
public String testJump2(){
    System.out.println("test jump2");
    //转发跳转到  /jump/test1
    //return "forward:/jump/test1";
    //跳转到当前类下的 路径为test1的方法中
    //return "forward:test1";
    //重定向到 /jump/test1
    return "redirect:test1";
}

4.3 跳转细节

1.在增删改之后,为了防止请求重复提交,重定向跳转

2.在查询之后,可以做转发跳转

3.C到C,一般是增删改后衔接一个查询

4.C到V,一般是查询后衔接一个视图

4.4 JSP细节

1.不应该直接访问jsp,应该先过C,查到数据后,在转发jsp

2.可以将所有jsp都放入 WEB-INF目录下,即可强制不接受外界直接访问,只能由C转发

5. 传值

C得到数据后,转发V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面

转发跳转:Request作用域

重定向跳转:Session作用域

5.1 获得Request和Session

//形参中 即可获得 request 和 session对象
@RequestMapping("/test1")
public String testData(HttpSession session,HttpServletRequest req,Integer id){
    session.setAttribute("user",new User());
    req.setAttribute("age", 18);
    req.setAttribute("users",Arrays.asList(new User(),new User()));
    //return "test2";
    return "forward:/WEB-INF/test2.jsp";
}

5.2 JSP中取值

建议:重点复习 EL C标签

//jsp中用EL表达式 取值即可
<fmt:formatDate value="${sessionScope.user.birth}" pattern="yyyy-MM-dd"/> <br/>
${sessionScope.user.birth} <br>
${requestScope.age}

6. 了解:Model, ModelAndView

//model中的数据,会在V渲染之前,将数据复制一份给request
@RequestMapping("/test2")
public String testData2(Model model){
     model.addAttribute("name", "张三");
     return "forward:/WEB-INF/test2.jsp";
}

//jsp中用EL表达式 取值即可
${requestScope.name}
//modelandview 可以集中管理 跳转和数据
@RequestMapping("/test3")
public ModelAndView testData3(){
     ModelAndView mv = new ModelAndView();
     mv.setViewName("forward:/WEB-INF/test2.jsp");
     Map<String,Integer> map = new HashMap<String,Integer>();
     map.put("age",18);
     mv.addAllObjects(map);
     return mv;
}

//jsp中用EL表达式 取值即可
${requestScope.age}

7. 静态资源

静态资源:html,js文件,css文件,图片文件

静态文件没有url-pattern,所以默认是访问不到的,之所以可以访问,是因为,tomcat中有一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 “/”,是全局默认的Servlet. 所以每个项目中不能匹配的静态资源的请求,有这个Servlet来处理即可。

但,在SpringMVC中DispatcherServlet也采用了 “/” 作为url-pattern, 则项目中不会再使用全局的Serlvet,

则静态资源不能完成访问。

7.1 解决方案1

DispathcerServlet采用其他的url-pattern

此时,所有访问handler的路径都要以 action结尾!!

<servlet>
  	<servlet-name>mvc9</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>mvc9</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

7.2 解决方案2

DispathcerServlet的url-pattern依然采用 “/”,但追加配置

<!-- 
  额外的增加一个handler,且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
  所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
  处理方式:将请求转会到tomcat中名为default的Servlet

  request-mapping: /*  /a  /b   /c   /ssss
                 : /**   /a   /b   /c    /a/b/c/df/d/f/fe/fefef/d
  -->
<mvc:default-servlet-handler/>

8. Json处理

springMVC默认的Json解决方案选择是 Jackson,所以只需要导入jackson的jar,即可使用。

注意:spring4中,要在tomcat中选择jdk7或jdk8

8.1 导入依赖

<!-- Jackson -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>
<!-- FastJson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.54</version>
</dependency>

8.2 使用@ResponseBody

@RequestMapping("/test1")
@ResponseBody //将handler的返回值,转换成json(jackson),并将json响应给客户端。
public User hello1(){
    System.out.println("hello world");
    User user = new User();
    return user;
}
//@ResponseBody还可以用在handler的返回值上
@RequestMapping("/test2")
public @ResponseBody List<User> hello2(){
    System.out.println("hello world");
    List<User> users = Arrays.asList(new User(),new User());
    return users;
}

8.3 日期格式化

@JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”)

public class User implements Serializable{
	private Integer id;
	private String name;
	@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
	private Date birth;
    ....

8.4 FastJson

如果不想使用Jackson,则也可以安装其他的 Json处理方案:FastJson

8.4.1 安装FastJson

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <!-- 声明转换类型:json -->
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

8.4.2 使用

@RequestMapping("/test1")
@ResponseBody //将handler的返回值,转换成json(jackson),并将json响应给客户端。
public User hello1(){
    System.out.println("hello world");
    User user = new User();
    return user;
}
//@ResponseBody还可以用在handler的返回值上
@RequestMapping("/test2")
public @ResponseBody List<User> hello2(){
    System.out.println("hello world");
    List<User> users = Arrays.asList(new User(),new User());
    return users;
}

// produces 设置响应头,避免中文乱码
@RequestMapping(value="/test3",produces = "text/html;charset=utf-8")
@ResponseBody //方法的返回值,会被自动转成json,并相应给客户端
public String test3(){
    System.out.println("json test3");
    return "臧红久";
}

8.4.3 日期格式化

@JSONField(format=“yyyy/MM/dd”)

public class User implements Serializable{
	private Integer id;
	private String name;
	@JSONField(format="yyyy/MM/dd")
	private Date birth;
	...

8.5 @RestController

@RestController // 等价于在本类的所有Handler中都添加了@ResponseBody
@RequestMapping("/json")
public class MyController {

    @RequestMapping("/test1")
    public User queryOneUser(Integer id){
        ....
    }
}

8.6 使用@RequestBody

@RequestBody, 接收Json参数
@RequestMapping("/users")
public String addUser(@RequestBody User user){//@RequestBody将请求体中的json数据转换为java对象
    System.out.println("cap2");
    System.out.println("Post user :"+user);
    return "index";
}
var xhr = new XMLHttpRequest();
xhr.open("post","${pageContext.request.contextPath}/users?"+new Date().getTime());
xhr.setRequestHeader("content-type","application/json");//设置请求头
xhr.send('{"id":1,"name":"臧红久","gender":"true","registTime":"2019-12-12"}');//传递json串

9. 异常解析器

项目中会面临各种异常。

  • 运行时异常,大多会在开发测试阶段解决掉。
  • 非运行时异常,是必须要处理的。
  • 业务异常,是根据业务要求,在出现非法情况时,人为抛出的。

9.1 异常处理方案

DAO,Service的所有异常,必须无条件上抛,全部集中到C

C中处理所有异常,将不同的异常转发到不同的错误处理结果中

方案1:

C中的每个Handler自己处理异常

public String xxx(){
 try{
     ...
 }catch(Exception1 e){
     e.printStackTrace();
	    return "redirect:/xx/xx1";
 }catch(Exception2 e){
     e.printStackTrace();
	    return "redirect:/xx/xx2";
 }
}

此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理

方案2:

C中的每个Handler不再自己处理异常,而是直接上抛所有异常。

定义一个“异常解析器” 集中捕获处理 所有异常

public class MyExResolver implements HandlerExceptionResolver{
	/**
	 * 异常解析器:主体逻辑
	 * 执行时刻:当handler中抛出异常时,会执行:捕获异常,并可以跳到错误页面
	 */
	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		ex.printStackTrace();//打印异常栈
		//创建一个ModelAndView
		ModelAndView mv = new ModelAndView();
		//识别异常
		if (ex instanceof Exception1) {
			mv.setViewName("redirect:/dispatch/login/page");
		}else if(ex instanceof Exception2){
			mv.setViewName("redirect:/dispatch/error1/page");
		}else{
			mv.setViewName("redirect:/dispatch/error");
		}
		return mv;
	}
}
<!-- 声明异常解析器 -->	
<bean class="com.baizhi.exception.resolver.MyExResolver"></bean>

此种方案,在集中管理异常方面,更有优势!

10. 拦截器(非重点)

重要但不紧急

作用:抽取C中的冗余功能

10.1 定义拦截器

执行顺序: preHandle–postHandle–afterCompletion

public class MyInter1 implements HandlerInterceptor{
	//主要逻辑:在handler之前执行:抽取handler中的冗余代码
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		System.out.println("pre~~~");
		if(1==2){
			response.sendRedirect("/springMVC_day2/index.jsp");//响应
			return false;//中断请求
		}
		return true;//放行,后续的拦截器或handler就会执行
	}
	//在handler之后执行:进一步的响应定制
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("post~~");
	}
	//在页面渲染完毕之后,执行:资源回收
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("after~~");
	}
}

10.2 配置拦截路径

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/inter/test1"/>
        <mvc:mapping path="/inter/test2"/>
        <mvc:mapping path="/inter/test*"/>  /inter/testaa  /inter/test22  /inter/testssssss
        <mvc:mapping path="/inter/**"/> /inter/a/b/c/d   /inter/cc/ss/dfd
        <mvc:exclude-mapping path="/inter/a/**"/> 不拦截此路径
        <bean class="com.baizhi.interceptor.MyInter1"></bean> 拦截器类
    </mvc:interceptor>
</mvc:interceptors>

多个拦截器,拦截相同的位置: A–>B -->Handler --> B–> A

11. spring整合

11.1 整合思路

此时项目中有两个工厂

  • DispatcherServlet 启动的springMVC工厂==负责生产C及springMVC自己的系统组件
  • ContextLoaderListener 启动的spring工厂==负责生产其他所有组件
  • springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件
  • 整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可

11.2 整合技巧

两个工厂不能有彼此侵入,即,生产的组件不能有重合。

<!-- 告知SpringMVC  哪些包中 存在 被注解的类
	use-default-filters=true 凡是被 @Controller @Service  @Repository注解的类,都会被扫描
	use-default-filters=false 默认不扫描包内的任何类, 只扫描include-filter中指定的类
	只扫描被@Controller注解的类
-->
<context:component-scan base-package="com.zhj" use-default-filters="false">
 	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 告知Spring
     唯独不扫描@Controller注解的类 -->
<context:component-scan base-package="com.zhj" use-default-filters="true">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值