springmvc入门

223 阅读5分钟

springmvc的hello world

1、导包(自行导包)

2、配置web.xml文件

<pre>
    springmvc基于servlet,配置的第一步是在WEB-INF/web.xml中配置servlet。
</pre>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>springmvc</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 
  		1:配置核心控制器servlet
  		2:再配置servlet的映射地址
   -->
  <servlet>
  	<servlet-name>springmvc</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<!-- 
  	load-on-startup可以指定Servlet被创建的时机,而且这里的数字越小,初始化就越靠前,
  	通常写1,会在当前的应用被初始化加载到tomcat之后立马进行实例化
  	-->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/springmvc.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<!-- 
  		1:通配符:只能有两种固定的格式:
  		一种格式是"*.扩展名"。另一种格式是以正斜杠(/)开头并以"/*"结尾
  		注意:既带/又带扩展名的不合法。
  	 -->
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

3、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:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		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-4.3.xsd">

    <!-- 采用注解方式实现,扫描带有@Controller的注解 -->
    <context:component-scan base-package="com.bjlemon.controller"></context:component-scan>

    <!-- 静态资源映射。当<url-pattern>/</url-pattern>时,该配置必须出现 -->
    <mvc:resources mapping="/script/**" location="/script/"></mvc:resources>
    <mvc:resources mapping="/style/**" location="/style/"></mvc:resources>
    <mvc:resources mapping="/images/**" location="/images/"></mvc:resources>

    <!-- 开启注解驱动:会自动采用系统默认的handlerMapping和handlerAdapter进行处理我们的请求 -->
    <mvc:annotation-driven></mvc:annotation-driven>
	
	<!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver
    ">
    	<property name="prefix" value="/WEB-INF/views/"></property>
    	<property name="suffix" value=".jsp"></property>
    </bean>
</beans>

4、写一个HelloController类

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public ModelAndView hello() {
        System.out.println("hello world");
        ModelAndView model=new ModelAndView();
        model.setViewName("hello");
        model.addObject("msg", "hello springmvc");
        return model;
    }
}

5、编写一个hello.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
	    ${msg}
    </body>
</html>

6、测试

    配置好后,就可以通过浏览器访问(根路径取决于项目配置) 
    http://127.0.0.1/springmvc/hello
    如果访问成功,控制台后打印 hello world,jsp页面也会显示hello springmvc

springmvc的执行流程

  • 第一步:发起请求到前端控制器(DispatcherServlet)
  • 第二步:前端控制器请求处理器映射器(HandlerMapping)查找 Handler(可以根据xml配置、注解进行查找)
  • 第三步:处理器映射器(HandlerMapping)向前端控制器返回Handler
  • 第四步:前端控制器调用处理器适配器去执行Handler
  • 第五步:处理器适配器去执行Handler
  • 第六步:Handler执行完成给处理器适配器返回ModelAndView
  • 第七步:处理器适配器向前端控制器返回ModelAndView(ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
  • 第八步:前端控制器请求视图解析器去进行视图解析(根据逻辑视图名解析成真正的视图(jsp))
  • 第九步:视图解析器向前端控制器返回View
  • 第十步:前端控制器进行视图渲染(将模型数据(在ModelAndView对象中)填充到request域)
  • 第十一步:前端控制器向用户响应结果

重要的五个组件
> 前端控制器(DispatcherServlet)
> 处理器映射器(HandlerMapper)
> 处理器适配器(HandlerAdapter)
> 处理器(Handler)
> 视图解析器(ViewResolver)

springmvc常用的注解

  • RequestMapping:映射请求地址,可用于类或方法上
  • PathVariable:获取占位符中的参数
  • RequestParam:用于设置请求参数,有三个参数
    • value:参数名
    • required:是否必须
    • defaultValue:默认参数值。如果设置了该参数会自动将required设置为false
  • ResponseBody:这个类的所有方法返回的数据直接写给浏览器(如果是对象转为json数据)
  • RequestBody:将请求的json字符串转化为pojo对象

springmvc与struts2的比较

  • springmvc的入口是servlet,而struts的入口是filter
  • springmvc是方法级别的拦截,一个方法对应一个request上下文,而一个方法同时又与一个url对应,所以从架构本身上springmvc更容易实现restful url,而struts2是类级别的拦截,不容易实现restful url,因为struts2中Action的一个方法可以对应一个url,但其类属性却被所有方法共享,无法用注解或其他方式标识其所属方法了
  • 由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。
  • SpringMVC开发效率和性能高于Struts2。
  • Spring MVC和Spring是无缝集成

重定向(redirect)

model.setViewName("redirect:/user.jsp");

转发(forward)

model.setViewName("forward:/hello");

springmvc文件上传

  • 导入需要依赖的jar包
org.apache.commons.fileupload
org.apache.commons.io
  • 配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设定默认编码 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 单个文件的大小2M 2*1024*1024 -->
        <property name="maxUploadSizePerFile" value="2097152"></property>
        <!-- 文件的总大小为10M 10*1024*1024 -->
        <property name="maxUploadSize" value="10485760"></property>
    </bean>
  • 搭建一个用于文件上传的页面
<form action="${pageContext.request.contextPath}/upload/upload" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="file"><br>
    <input type="submit" value="上传">
</form>
  • 写一个测试方法用于接收单个文件上传
@PostMapping("/upload")
    public String upload(MultipartFile file, HttpServletRequest request, Model model){
        try {
            ServletContext context=request.getSession().getServletContext();
            String path=context.getRealPath("/images");
            File uploadDirectory=new File(path);
            if(!uploadDirectory.exists()){
                uploadDirectory.mkdirs();
            }
            String filename=file.getOriginalFilename();
            file.transferTo(new File(uploadDirectory,filename));
            model.addAttribute("fileName",filename);
            return "upload/result";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

拦截器

自定义拦截器实现HandlerInterceptor接口,该接口定义了3个方法,需要我们实现

> preHandle在handler执行之前被触发的方法
> postHandle在handler执行之后被触发的方法
> afterCompletion在视图渲染完之后被触发的方法
  • 自定义拦截器
public class InterceptorTest1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("InterceptorTest1 preHandle");
        System.out.println(handler);
        //如果返回true表示将请求传给下一个拦截器
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("InterceptorTest1 postHandle");
        System.out.println(handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("InterceptorTest1 afterCompletion");
        System.out.println(handler);
    }
}
  • 在springmvc.xml中配置拦截器
<mvc:interceptors>
        <mvc:interceptor>
            <!-- 配置要拦截所有的路径 -->
            <mvc:mapping path="/**"/>
            <!-- 配置拦截器 -->
            <bean class="com.bjlemon.web.interceptor.InterceptorTest1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bjlemon.web.interceptor.InterceptorTest2"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <!--排除某个路径-->
            <!--<mvc:exclude-mapping path="/user/login"></mvc:exclude-mapping>-->
            <bean class="com.bjlemon.web.interceptor.InterceptorTest3"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

类型转换

  • 自定义一个类实现Converter接口
//自定义类型转换器,第一个参数是指输入的参数类型,第二个参数是指输出的参数类型
public class StringToDateConverter implements Converter<String, Date> {

    public static final String[]DEFAULT_PATTERNS=new String[]{"yyyy-MM-dd"};

    private String[]patterns=DEFAULT_PATTERNS;

    public void setPatterns(String[] patterns) {
        this.patterns = patterns;
    }

    @Override
    public Date convert(String s) {
        Date date=null;

        if(StringUtils.isBlank(s)){
            throw new IllegalArgumentException("");
        }

        try {
            date= DateUtils.parseDate(s,this.patterns);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return date;
    }
}
  • 在springmvc.xml配置文件中注册添加该转换器
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.bjlemon.web.converter.StringToDateConverter">
                    <property name="patterns">
                        <array>
                            <value>yyyy-MM-dd</value>
                            <value>yyyy/MM/dd</value>
                            <value>yyyy-MM-dd HH:mm:ss</value>
                        </array>
                    </property>
                </bean>
            </set>
        </property>
    </bean>

异常的处理

  • 自定义异常类
public class MyException extends RuntimeException {

    private String message;

    public MyException(String message) {
        super(message);
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
  • 自定义异常处理器,实现HandlerExceptionResolver接口
public class MyExceptionHandlerResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView=new ModelAndView();
        String message="程序出错了,请联系管理员";
        if(ex!=null){
            if(ex instanceof UnknownAccountException){
                UnknownAccountException unknownAccountException=(UnknownAccountException)ex;
                message=unknownAccountException.getMessage();
            }else if(ex instanceof IncorrectCredentialsException){
                IncorrectCredentialsException incorrectCredentialsException=(IncorrectCredentialsException)ex;
                message=incorrectCredentialsException.getMessage();
            }else if(ex instanceof UnauthorizedException){
                modelAndView.setViewName("redirect:/index/unauthorized");
                return modelAndView;
            }else if(ex instanceof MyException){
                MyException myException=(MyException)ex;
                message=myException.getMessage();
            }
        }
        modelAndView.addObject("errorMessage",message);
        modelAndView.setViewName("common/error");
        return modelAndView;
    }

}
  • 在springmvc.xml配置文件中配置定义好的异常解析器
<bean id="myExceptionHandlerResolver" class="com.bjlemon.exception.MyExceptionHandlerResolver"></bean>

请求参数乱码问题

    <filter>
        <filter-name>characterEncodingFilter</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>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>