SpringMVC

SpringMVC

SSM:spring + springMVC + mybatis

mvc:模型(dao,service)识图(jsp) 控制器(Servlet)

1、搭建环境

  1. 新建普通maven项目,删除src使它变成父项目。导入依赖

        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.1.9.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.2</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
        </dependencies>
    
  2. 在父项目下新建普通maven项目,然后右键模块名,添加框架支持:javaee,webapplication。变成web项目

2、接口实现

​ 相较于传统的 用户->web->service->dao,由web传回用户数据,决定转发和重定向。

​ springmvc 在用户和web层中间有一层控制器(DispatcherServlet),由控制器统一决定

  1. 在web.xml配置springmvc的核心 DispatcherServlet

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--关联一个springmvc的配置文件:【servlet名字-servlet.xml】-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
        </servlet>
    
        <!--/:匹配所有请求(不包括.jsp)-->
        <!--/*:匹配所有请求(包括.jsp)-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  2. 编写springmvc的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
        <!--视图解析器:ModelAndView-->
        <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <bean id="/hello" class="com.ndkj.controller.HelloController"/>
    </beans>
    
  3. 写controller

    public class HelloController implements Controller {
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //ModelAndView
            ModelAndView mv =new ModelAndView();
    
            //把模型和识图封装到mv对象中
            mv.addObject("msg","HelloSpringMVC");
            mv.setViewName("hello");    //视图:hello和springmvc的配置文件中的视图解析器的前缀后缀拼接成完整的路径
            return mv;
        }
    }
    

2.1、实现原理(重要)

DispatcherServlet:这是核心,也叫控制器。

  1. 用户发出请求,经过servlet-mapping,被核心所拦截处理请求;

  2. 控制器中设置了springmvc的配置文件,控制器调用HandlerMapping,然后HandlerExecution将Handler信息传递给控制器;

    • HandlerMapping根据请求的URL查找Handler
  3. 得到Handler信息后,控制器调用HandlerAdapter,去找和Handler匹配的类;

  4. 类(这个类继承了Controller接口)执行:创建ModelAndView对象,并在其内部封装对象(属性、内容)和视图(页面);

  5. Controller将ModelAndView返回给HandlerAdapter,再返回给控制器

  6. 控制器调用视图解析器(ViewResolver)解析ModelAndView,将视图名称传给控制器

    • ViewResolver获取了数据

    • 获取了视图名称

    • 拼接名称,找到对应视图并渲染

  7. 控制器根据视图解析器结果调用具体的视图显示


用户 -> 控制器DispatcherServlet -> HandlerMapping -> HandlerExecution -> 控制器收到Handler ->HandlerAdapter ->具体的Controller实现类 -> 返回对象给HandlerAdapter -> 控制器 -> 视图解析器找具体视图 ->控制器调用视图

3、注解实现

springmvc配置改成:

<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--自动扫描包,让注解生效-->
    <context:component-scan base-package="com.ndkj.controller"/>
    <!--让springmvc不处理静态资源:.css .js .mp3 .mp4-->
    <mvc:default-servlet-handler/>
    <!--相当于之前的 HandlerMapping 和 HandlerAdapter-->
    <mvc:annotation-driven/>


    <!--视图解析器:ModelAndView-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

controller改成:

@Controller
public class HelloController {

    @RequestMapping ("/hello") //就是之前的HandlerMapping实例,根据url查找controller
    public String hello(Model model){
        //封装数据
        model.addAttribute("msg","hello,SpringMVCAnnotation");

        return "hello"; //被视图解析器处理,拼接成视图名称
    }
}

注解实现相较于接口实现

  • springmvc配置文件中只需要创建视图解析器实例,而HandlerMapping和Adapter ,一个annotation-driven搞定。
  • 而且接口实现的话,一个控制器只能写一个方法
  • @Controller注解类里面的方法,如果返回值是String(默认是转发,重定向是:“redirect:viewName”),如果有具体页面可以跳转,那么就会被视图解析器解析。
    • 类上面@RestController,则一个类的方法返回值都不会结果视图解析器,直接以json返回到前端
    • 或者是在方法上写@ResponseBody,返回值就不会被视图解析器解析

4、Restful风格

资源定位及资源操作的风格。使设计的软件更简洁,更有层次,更易于实现缓存等机制。

通过不同的请求方式来实现不同的效果:GET、POST、DELETE、PUT

	//传统风格:http://localhost:8888/springmvc_04_controller/add?a=1&b=2
    //restful风格:http://localhost:8888/springmvc_04_controller/add/1/2 

	@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)//限定方法,等价于@GetMapping
    public String test(@PathVariable int a,@PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg",res);
        return "test";
    }
  • @PathVariable绑定参数到URL的变量上
  • 通过method属性约束请求类型,如果用户请求类型不一致,就会提示405错误
  • @RequestMapping( method=xx) 等价于 @XxMapping

地址栏一样,可以通过不同的请求方法,执行不同的操作;地址栏隐藏了参数名,更加安全

5、前端参数传递

public String test1(@RequestParam("name") String name, Model model){
    //1.接受前端参数
    System.out.println(name);
    //2.返回参数到前端
    model.addAttribute("msg",name);
    //3.跳转视图
    return "test";
}
  • @RequestParam 设置参数名
  • 如果参数是一个对象,会将地址栏中的参数与对象的属性挨个匹配,自动生成对象

6、过滤器解决乱码

6.1、手动自定义过滤器

  1. 过滤器实现类:
public class EncodingFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("UTF-8");
        servletResponse.setCharacterEncoding("UTF-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    public void destroy() {

    }
}
  1. web.xml中配置过滤器的接受和映射
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>com.ndkj.filter.EncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern><!--这里 /* 是为了接收jsp页面-->
    </filter-mapping>

6.2、springmvc自动配置的乱码过滤器

直接在web.xml中配置

	<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><!--这里 /* 是为了接收jsp页面的请求 一般只用在过滤器上-->
    </filter-mapping>

7、JSON(重点)

一种数据交换的文本格式,全称:JS对象标记

  • JSON字符串转化为JS对象:

    var obj = JSON.parse('{"a":"hello","b":"world"}');
    //结果是{a:'hello',b:'world'}
    
  • JS对象转为JSON字符串

    var json = JSON.stringify({a:'hello',b:'world'});
    //'{"a":"hello","b":"world"}'
    

7.1、Jackson

Jackson是JSON解析工具(还有阿里的fastjson)

  1. 导入依赖

            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.10.0</version>
            </dependency>
    

2.编写Controller

@RequestMapping(value = "/j1")
@ResponseBody//不走视图解析器,直接返回一个字符串写入 HTTP 响应正文
public String json1() throws JsonProcessingException {
    User user = new User("jason",18,"男");
    //jackson
    ObjectMapper mapper = new ObjectMapper();
    String value = mapper.writeValueAsString(user);
    return value;
}

json乱码问题:

    <!--json乱码问题-->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

或者Controller中设置

@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")

用jackson以JSON返回当前时间:

    @RequestMapping(value = "/j3")
    @ResponseBody//不走视图解析器,直接返回一个字符串
    public String json3() throws JsonProcessingException {
        Date date = new Date();
        //jackson
        ObjectMapper mapper = new ObjectMapper();
        //取消时间戳写法
        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
        //自定义日期写法
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        String value = mapper.writeValueAsString(date);
        return value;
    }

7.2、fastjson

  1. 导入依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.60</version>
    </dependency>
    
    
  2. fastjson有三个主要的类:

    • JSONObject:json对象
    • JSONArray:json对象数组
    • JSON:JSONObject和JSONArray之间的转化
    @RequestMapping(value = "/j4")
    @ResponseBody//不走视图解析器,直接返回一个字符串
    public String json4() throws JsonProcessingException {
        User user1 = new User("jason",18,"男");
        User user2 = new User("mike",19,"男");
        User user3 = new User("jordan",20,"男");
        List<User> list=new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        return JSON.toJSONString(list);
    }

8、Ajax

异步无刷新请求

ajax核心:XMLHttpRequest对象(XHR)。

使用jQuery Ajax,其本质就是XHR,只是对它进行了封装,方便调用

	$.post({
                url:"${pageContext.request.contextPath}/a1",//请求地址
                data:{"name":$("#name").val()},	//键值对,传入数据
                success:function (data) {	//callback(success或error):function,参数data是由后端提供(json)
                    alert(data);
                }
            });
  • 后端只是返回了一个data,并没有控制视图的跳转

  • 后端仅返回数据,主动权交给了前端

9、拦截器

  • 只会拦截访问控制器的方法,而不会拦截静态资源(比过滤器更高效一点)
  • 拦截器是AOP思想的具体实现

实现步骤:

  1. 新建一个类,实现HandlerInterceptor接口

    public class MyInterceptor implements HandlerInterceptor {
        //return true:放行,执行下一个拦截;false则不执行请求内的方法,也就不执行下一个拦截
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("==========处理前==============");
            return true;
        }
    
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("==========处理后==============");
        }
    
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("=============清理==============");
        }
    }
    
    • 只有preHandler有返回值,一般就是在这个方法实现拦截;
    • 后面两个方法一般就是输出日志
  2. 在spring配置中配置拦截器

        <!--配置拦截器-->
        <mvc:interceptors>
            <mvc:interceptor>
                <!--/admin/**:拦截这个请求下的所有请求,/admin/a1.....-->
                <mvc:mapping path="/**"/>
                <!--指定哪个类拦截-->
                <bean class="com.ndkj.config.MyInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    

通过判断有无登陆记录拦截访问页面请求

public class LoginInterceptor implements HandlerInterceptor {
    //拦截在index页面未登录就访问首页的情况
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        //index -> 登陆页面 直接放行
        if(request.getRequestURI().contains("toLogin")){
            return true;
        }
        //登陆 -> 首页默认登陆成功 直接放行
        if(request.getRequestURI().contains("login")){
            return true;
        }
        //在index有登录记录,放行
        if (session.getAttribute("userLoginInfo")!=null){
            return true;
        }
        //在index没有登陆想访问首页,被拦截直接跳转登录页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }
}

10、上传和下载文件

10.1、上传文件

  1. 导入依赖

        <dependencies>
            <!--文件上传-->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.3</version>
            </dependency>
            <!--servletapi导入高版本-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
            </dependency>
        </dependencies>
    
  2. 前端form表单,enctype为multipart/form-data,是让文件以二进制流上传

    <form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
        <input type="file" name="file"/>
        <input type="submit" value="upload"/>
    </form>
    
  3. springmvc为文件上传提供了直接支持,MultipleResolver实现

        <!--文件上传配置-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!--必须和pageEncoding一致-->
            <property name="defaultEncoding" value="utf-8"/>
            <!--上传文件大小,单位是字节-->
            <property name="maxUploadSize" value="10485760"/>
            <property name="maxInMemorySize" value="40960"/>
        </bean>
    
  4. 服务器端处理文件

    1. 判断文件名是否为空、设置保存文件的路径

       		//获取文件名
              String filename = file.getOriginalFilename();
      
              //如果文件名为空,返回首页
              if ("".equals(filename)) {
                  return "redirect:/index.jsp";
              }
              System.out.println("上传文件名:" + filename);
      
              //设置文件保存路径  当前绝对路径/upload (idea的out文件夹下)
              String path = request.getServletContext().getRealPath("/upload");
              //如果没有该路径则创建一个
              File realPath = new File(path);
              if (!realPath.exists()) {
                  realPath.mkdir();
              }
      
    2. 保存文件

      • 第一种方法

        		InputStream inputStream = file.getInputStream();//输入流获取文件
                OutputStream outputStream = new FileOutputStream(new File(realPath, filename));//输出流保存文件
        
                //读取写出
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = inputStream.read(buffer)) != -1){
                    outputStream.write(buffer,0,len);
                    outputStream.flush();
                }
                outputStream.close();
                inputStream.close();
        
      • 第二种方法

        /*
            * 采用file.transferTo 保存上传的文件
            * */       
        //通过CommonsMultipartFile的方法直接写文件
                file.transferTo(new File(realPath+"/"+file.getOriginalFilename()));
        

10.2、下载文件

public String download(HttpServletResponse response,HttpServletRequest request) throws IOException {
        //要下载的图片地址(这里是写成固定的)
        String realPath = request.getServletContext().getRealPath("/upload");
        String fileName = "wallpaper.png";

        //1.设置response响应头
        response.reset();//设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8");
        response.setContentType("multipart/form-data");//二进制流传输数据
        response.setHeader("Content-Dispostion",
                "attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));

        File file = new File(realPath,fileName);
        //2.读取文件--输入流
        InputStream inputStream = new FileInputStream(file);
        //3.写出文件--输出流
        OutputStream outputStream = response.getOutputStream();
        byte[] buffer = new byte[1024];
        int index=0;
        while((index=inputStream.read(buffer))!=-1){
            outputStream.write(buffer,0,index);
            outputStream.flush();
        }
    	//4.关闭流
        outputStream.close();
        inputStream.close();
        return  null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值