SpringMVC入门--点滴积累,点滴成长。

文章目录


因为是把之前分散写的博客组合,错漏指出敬请指出!

1Spring MVC核心组件:

(1) DispatcherServlet : 前端控制器,调度其他组件的执行,降低不同组件间的耦合性,为核心模块.

(2) Handler : 处理器,完成具体的业务逻辑,相当于Servlet或者Action.

(3) HandlerMapping : DispatcherServlet 通过HandlerMapping 将请求映射到对应的Handler.

(4) HandlerInterceptor : 处理器拦截器,如果需要进行拦截处理,可实现该接口.

(5) HandlerExecutionChain : 处理器执行链, 包括Handler和HandlerInterceptor (有一个默认的)

(6) HandlerAdapter : 处理器适配器,Handler执行业务方法前,需要进行一系列的操作,如表单数据验证、数据类型转换、将表单数据封装到pojo等.

(7) ModelAndView : 装载模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet.

(8) ViewResolver : 视图解析器,DispatcherServlet 通过该解析器将逻辑视图解析为物理视图,渲染给客户端.

2.SpringMVC 工作流程:

(1) 客户端请求被DispatcherServlet 接收.

(2) 通过 Handler Mapping 映射到 Handler.

(3) 生成 Handler 和 HandlerInterceptor .

(4) Hander 和HanderInterceptor 以 HandlerExecutionCahie 形式返回给DispatcherServlet.

(5) DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法进行业务逻辑处理.

(6) 返回一个ModelAndView 对象给DispatcherServlet

(7) DispatcherServlet 将获取到的 ModelAndView 对象传给ViewResolver 视图解析器,将逻辑视图转换成物理视图.

(8) ViewResolver 返回一个View给 DispatcherServlet.

(9) DispatcherServlet 根据View 进行视图渲染,也就是将模型数据填入到视图中.

(10) DispatcherServlet 将渲染后的视图响应给客户端.

3.maven快速创建简单SpringMVC:

pom.xml 中引入依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>

<!--springmvc核心依赖-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>

<!--servlet依赖-->
<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>
  <scope>provided</scope>
</dependency>

4.在handler包下创建

package com.redocloud.handler;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * ClassName : HelloHandler
 * package : com.redocloud.handler
 * 功能描述:
 *
 * @Date : 2020/3/16 0016 12:08
 * @Author : one world
 */
@Controller
public class HelloHandler {
    @RequestMapping("/index")
    public String index(){
        System.out.println("执行index代码");
        return "index";
    }
}

5.web.xml中配置

<web-app>
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

6.在springmvc.xml文件下配置:

<context:component-scan base-package="com.redocloud.handler"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean> 

7.配置tomcat运行测试

核心:

通过DispatcherServlet 调用其他组件,实现业务。主要有controller 调用业务方法Method,业务逻辑 和ViewResolver 视图解析器,将业务方法返回值解析为物理视图和模型数据,返回给客户端。

文章目录

1 创建 MyController 注解作用于类:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController{
    String value()  default "";
}

2 创建 MyRequestMapping 注解,作用于类和方法

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping{
    String value() default "";
}

3 创建 MyViewResolver视图解析器

package com.redocloud.test;

/**
 * ClassName : MyViewResolver
 * package : PACKAGE_NAME
 * 功能描述:
 *
 * @Date : 2020/3/16 0016 19:49
 * @Author : one world
 */
public class MyViewResolver {
    private String prefix;
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public String jspMapping(String value){
        return this.prefix+value+this.suffix;
    }
}

4创建MyDispatcherServlet ,核心控制器,init 完成初始化工作,doPost来处理HTTP请求。

public class MyDispatcherServlet extends HttpServlet {
    //模拟IOC 保存Controller的实例对象
    private Map<String,Object> iocContainer =  new HashMap<String,Object>();
    //保存handler映射
    private Map<String, Method> handlerMapping = new HashMap<String,Method>();
    //自定义视图解析器
    private MyViewResolver myViewResolver;

    @Override
    public void init(ServletConfig config) throws ServletException {
//扫描Controller ,创建实例对象,存入iocContainer
        System.out.println("servlet初始化");
        scanController(config);
//初始化handler映射
        initHandlerMapping();
//加载视图解析器
        loadViewResolver(config);
    }
    //扫描Controller
    public void scanController(ServletConfig config){
        SAXReader reader = new SAXReader();
        try{
            //解析springmvc.xml
            String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");
            Document document = reader.read(path);
            Element root = document.getRootElement();
            Iterator iter = root.elementIterator();
            while (iter.hasNext()){
                Element ele = (Element)iter.next();
                if(ele.getName().equals("component-scan")){
                    String packageName = ele.attributeValue("base-package");
                    //获取base-package包下的所有类名
                    List<String> list = getClassNames(packageName);
                    for(String str:list){
                        Class clazz = Class.forName(str);
                        //判断是否有MyController注解
                        if(clazz.isAnnotationPresent(MyController.class)){
                            //获取Controller中的MyRequsetMapping注解的Value
                            MyRequestMapping annotion = (MyRequestMapping)clazz.getAnnotation(MyRequestMapping.class);
                            String value = annotion.value().substring(1);
                            //controller实例对象存入到iocContainer
                            iocContainer.put(value,clazz.newInstance());

                        }
                    }
                }

            }
        }catch (Exception e){
           e.printStackTrace();
        }
    }
    /**
     * 获取包下的所有类名
     * @param packageName
     * @return
     */
    public List<String> getClassNames(String packageName){
        List<String> classNameList = new ArrayList<String>();
        String packagePath = packageName.replace(".", "/");
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URL url = loader.getResource(packagePath);
        if(url != null){
            File file = new File(url.getPath());
            File[] childFiles = file.listFiles();
            for(File childFile : childFiles){
                String className = packageName+"."+childFile.getName().replace(".class", "");
                classNameList.add(className);
            }
        }
        return classNameList;
    }
    /**
     * 初始化 handler 映射
     */
    public void initHandlerMapping(){
        for(String str:iocContainer.keySet()){
            Class clazz = iocContainer.get(str).getClass();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //判断方式是否添加 MyRequestMapping 注解
                if(method.isAnnotationPresent(MyRequestMapping.class)){
                    //获取 Method 中 MyRequestMapping 注解的 value
                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String value = annotation.value().substring(1);
                    //method 存入 methodMapping
                    handlerMapping.put(value, method);
                }
            }
        }
    }

    /**
     * 加载自定义视图解析器
     * @param config
     */
    public void loadViewResolver(ServletConfig config){
        SAXReader reader = new SAXReader();
        try {
            //解析 springmvc.xml
            String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");
            Document document = reader.read(path);
            Element root = document.getRootElement();
            Iterator iter = root.elementIterator();
            while(iter.hasNext()){
                Element ele = (Element) iter.next();
                if(ele.getName().equals("bean")){
                    String className = ele.attributeValue("class");
                    Class clazz = Class.forName(className);
                    Object obj = clazz.newInstance();
                    //获取 setter 方法
                    Method prefixMethod = clazz.getMethod("setPrefix", String.class);
                    Method suffixMethod = clazz.getMethod("setSuffix", String.class);
                    Iterator beanIter = ele.elementIterator();
                    //获取 property 值
                    Map<String,String> propertyMap = new HashMap<String,String>();
                    while(beanIter.hasNext()){
                        Element beanEle = (Element) beanIter.next();
                        String name = beanEle.attributeValue("name");
                        String value = beanEle.attributeValue("value");
                        propertyMap.put(name, value);
                    }
                    for(String str:propertyMap.keySet()){
                        //反射机制调用 setter 方法,完成赋值
                        if(str.equals("prefix")){
                            prefixMethod.invoke(obj, propertyMap.get(str));
                        }
                        if(str.equals("suffix")){
                            suffixMethod.invoke(obj, propertyMap.get(str));
                        }
                    }
                    myViewResolver = (MyViewResolver) obj;
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        //获取请求
        String handlerUri = req.getRequestURI().split("/")[2];
        //获取 Controller 实例
        Object obj = iocContainer.get(handlerUri);
        String methodUri = req.getRequestURI().split("/")[3];
        //获取业务方法
        Method method = handlerMapping.get(methodUri);
        try {
            //反射机制调用业务方法
            String value = (String) method.invoke(obj);
            //视图解析器将逻辑视图转换为物理视图
            String result = myViewResolver.jspMapping(value);
            //页面跳转
            req.getRequestDispatcher(result).forward(req, resp);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}



### 5编写测试类


@MyController
@MyRequestMapping
public class TestController {
    @MyRequestMapping(value = "/test")
    public String test(){
        System.out.println("执行test相关业务");
        return "test";
    }
}

6 修改web.xml 文件中的servlet 为自定义的MyDispactcherServlet和 spring.xml文件中的解析器为MyViewResolver,

spring.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/tool"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd">
<!--配置自动扫描-->
<!--    <context:component-scan base-package="com.redocloud.handler"></context:component-scan>-->
    <context:component-scan base-package="com.redocloud.test"></context:component-scan>
<!--配置视图解析器-->
<!--    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
    <!--配置自定义的视图解析器-->
    <bean class="com.redocloud.test.MyViewResolver">
    <!--前缀-->
    <property name="prefix" value="/"></property>
    <!--后缀-->
    <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

```yaml
web.xml
<?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_3_1.xsd"
         version="3.1">
<!--  <servlet>-->
<!--    <servlet-name>springmvc</servlet-name>-->
<!--    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>-->
<!--    <init-param>-->
<!--      <param-name>contextConfigLocation</param-name>-->
<!--      <param-value>classpath:springmvc.xml</param-value>-->
<!--    </init-param>-->
<!--  </servlet>-->
<!--  <servlet-mapping>-->
<!--    <servlet-name>springmvc</servlet-name>-->
<!--    <url-pattern>/index</url-pattern>-->
<!--  </servlet-mapping>-->
<!--  <servlet-mapping>-->
<!--    <servlet-name>springmvc</servlet-name>-->
<!--    <url-pattern>/addUser</url-pattern>-->
<!--  </servlet-mapping>-->

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>com.redocloud.test.MyDispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>springmvc.xml</param-value>
    </init-param>
  </servlet>
<!--  <servlet-mapping>-->
<!--    <servlet-name>springmvc</servlet-name>-->
<!--    <url-pattern>/index</url-pattern>-->
<!--  </servlet-mapping>-->
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <!--字符过滤器,解决中文乱码问题-->
  <filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

7 注意事项:

自定义注解时,spring.xml的路径会有些问题,我的解决方式是直接放在了webapp下面
启动tomcat 在浏览器输入:http://localhost:8080/mvc/controllerTest/test

在测试时,由于粗心除了以下错误:
错误
由于忘记了在TestController类中的路径值,导致输入路径错误,导致以上异常。

1 @requestMapping:将URL请求和业务方法映射,用于控制器处

value:
@controller
@RequestMapping("/helloHandler")//相当于多了一个helloHandler访问路径,括号中内容可替换为(value = "hello")
public class HelloHandler{
 @RequestMapping(value="hello"public String hello(){
    System.out.println("hello");
    return "hello";
}
}

其访问路径为:…/项目名/helloHandler/hello

method:指定方法类型
@RequestMapping(value="/testMethod",method=RequestMethod.POST) //指明方法为POST方法访问
params: 指定参数和参数类型
  @RequestMapping(value="test",params={"name","money=100"})//此时必须包含name和money两个参数时才能使用标注下的方法
参数绑定:
           @RequestMapping(value = “paramsBind”)
            //在业务方法定义时声明参数列表
         public String paramsBind(@RequestParam("name") String name, @RequestParam("id") int id){//将String 类型的id自动转为int 型 具体的数据类型转换是通过HandlerAdapter完成的
           System.out.print(name+id);
          return "test";
}
RESTful的URL参数获取:
  @RequestMapping(value="rest/{name}")
        public String restTest(@PathVariable("name") String name){
            System.out.print(name);
            return "index";
      }
      路径为:../项目名/控制器名/rest/chen
      chen为name参数
          
        **#映射cookie** 
        Spring mvc获取Cookie的值
    @RequestMapping("/cookieTest")
    public String getCookie(@CookieValue(value="JSESSIONID"),String sessionId){
       System.out.print(sessionId);
       return "index";
   }

2.使用POJO绑定参数

Spring MVC根据请求参数名和POJO属性名进行匹配,自动为该对象填充属性值,且支持属性级联

(1)创建实体Address 、 User 并进行级联设置

public class Address{
    private int id;
    private String name;
}
public class User{
    private int id;
    private String name;
    private Address address;
}
(2)创建addUser.jsp
    <form action="addUser" method="post">
    编号:<input type="text" name="id"/><br/>
    姓名:<input type="text" name="name"/><br/>
    地址:<input type="text" name="address.name"/><br/>
    <input type="submit" value="提交"/>
   </form>

(3)业务方法:

   @RequestMapping("/addUser")
   public String getPOJO(User user){
     System.out.print(user);
     retturn "index";
}

(4)运行tomcat,

这里建议创建的maven项目每次运行前clean 、package后再运行项目,否则可能会报以下错误:

org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for…
中文乱码问题的解决:filter
约束文件需要修改为

  <?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_3_1.xsd"
         version="3.1">
  <web-app>

字符过滤器:

  <filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

JSP页面的转发和重定向:

Spring MVC默认以转发的形式响应JSP也可以手动进行修改
@ResuestMapping(“名称”)
public String 方法名(){
return “forward:/index.jsp”;转发 浏览器地址不改变
或者
return “redirect:/index.jsp”;重定向 浏览器地址改变
}

#1.Spring MVC业务数据绑定,将数据绑定给pageContext、request、session和application:(常用的域对象是request 和session,pageContext 和 application通过获取原生Servlet的方式进行绑定)
首先添加业务数据,再由ViewResolver 来完成数据绑定,Spring MVC添加业务数据的方式如下:
Map:

//使用map进行数据绑定

@RequestMapping("/mapTest")
public String mapTest(Map<String, Object> map){
    User user = new User();
    user.setId(2);
    user.setName("张三");
    map.put("user",user);
    return "index";
}

Model:

  //使用Model进行数据绑定
    @RequestMapping("/modelTest")
    public String modelTest(Model model){
        User user = new User();
        user.setId(2);
        user.setName("李四");
        model.addAttribute("user",user);
        return "index";
    }

ModelAndView:

  //ModelAndView使用的8种方式
    @RequestMapping("modelAndViewTest")
    public ModelAndView modelAndViewTest(){
        ModelAndView modelAndView = new ModelAndView();
        User user = new User();
        user.setId(2);
        user.setName("李四");
        modelAndView.addObject("user",user);
        //方式1
        modelAndView.setViewName("index");

        //方式2
        View view = new InternalResourceView("/index.jsp");
        modelAndView.setView(view);
        return  modelAndView;
    }
    //方式3
    @RequestMapping("/modelAndViewTest1")
    public ModelAndView modelAndViewTest1(){
        ModelAndView modelAndView = new ModelAndView("index");
        User user = new User();
        user.setId(2);
        user.setName("李四");
        modelAndView.addObject("user",user);
        return  modelAndView;
    }

    //方式4
    @RequestMapping("/modelAndViewTest2")
    public ModelAndView modelAndViewTest4(){
        View view = new InternalResourceView("/index.jsp");
        ModelAndView modelAndView = new ModelAndView(view);
        User user = new User();
        user.setId(1);
        user.setName("张三");
        modelAndView.addObject("user", user);
        return modelAndView;
    }
   //方式5
    @RequestMapping("/modelAndViewTest3")
    public ModelAndView modelAndViewTest5(){
        Map<String,Object> map = new HashMap<String,Object>();
        User user = new User();
        user.setId(1);
        user.setName("张三");
        map.put("user", user);
        ModelAndView modelAndView = new ModelAndView("index", map);
        return modelAndView;
    }
//方式6
    @RequestMapping("/modelAndViewTest4")
    public ModelAndView modelAndViewTest6(){
        Map<String,Object> map = new HashMap<String,Object>();
        User user = new User();
        user.setId(1);
        user.setName("张三");
        map.put("user", user);
        View view = new InternalResourceView("/index.jsp");
        ModelAndView modelAndView = new ModelAndView(view, map);
        return modelAndView;
    }
//方式7
    @RequestMapping("/modelAndViewTest5")
    public ModelAndView modelAndViewTest7(){
        User user = new User();
        user.setId(1);
        user.setName("张三");
        ModelAndView modelAndView = new ModelAndView("index", "user", user);
        return modelAndView;
    }
//方式8
    @RequestMapping("/modelAndViewTest6")
    public ModelAndView modelAndViewTest8(){
        User user = new User();
        user.setId(1);
        user.setName("张三");
        View view = new InternalResourceView("/index.jsp");
        ModelAndView modelAndView = new ModelAndView(view, "user", user);
        return modelAndView;
    }
    

@SessionAttributes:

//将业务数据绑定到request对象
    @Controller
    @SessionAttributes(value = {"username","password"})
    //或者以(type={User.class,Address.class})这种方式
public class SessionHandler {
        //存入
  @RequestMapping("/testPut")
    public void testPut(Model model){
      model.addAttribute("username","张三");
      model.addAttribute("password","322728");
      System.out.println("保存到session");
  }
       //获取session内对象
    @RequestMapping("/testGet")
    public void testGet(ModelMap modelMap){
        System.out.println("获取用户名和密码");
        Object username = modelMap.get("username");
        System.out.println(username);
        Object password = modelMap.get("password");
        System.out.println(password);
    }
       //清空session
    @RequestMapping("/testClear")
    public void testClear(SessionStatus sessionStatus){
      sessionStatus.setComplete();//清空session
        System.out.println("清空session");
    }
}

在tomcat运行后,在浏览器中依次输入:
http://localhost:8080/mvc/testPut
http://localhost:8080/mvc/testGet
http://localhost:8080/mvc/testClear
http://localhost:8080/mvc/testGet
运行结果:
保存到session
获取用户名和密码
张三
322728
清空session
获取用户名和密码
null
null

和@ModelAttribute

//ModelAttribute 添加业务数据,执行本方法前,先执行@ModelAttribute注解的方法,该注解注解的方法,会在Spring MVC调用任意方法之前自动调用
    @RequestMapping("/modelAttributeTest")
    public String modelAttributeTest(){
        return "index";
    }

    @ModelAttribute
    public User getUser(){
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
    //域对象中的数据都是以键值对 (key-value) 的形式保存的,那么此时的 key 默认取业务数据对应类的首字母小写之后的类名,如 User 类首字母小写之后为 "user",因此 JSP 页面中,可以直接通过 "user" 取值。

    //若 getUser 没有返回值,则必须手动在该方法中填充业务数据,使用 Map 或者 Model 均可。

    @ModelAttribute
    public void getUser2(Map<String,Object> map){
        User user = new User();
        user.setId(1);
        user.setName("张三");
        map.put("user", user);
    }

#1 RESTful :互联网软件架构模型,完成不同终端的数据访问交互,四种常规请求类型:
1)GET :获取资源
2)POST:创建资源
3)PUT:修改资源
4)DELETE:删除资源
注意:传统Web开发,form表单只支持GET和POST请求,可以通过添加HiddenHttpMethodFilter过滤器,将请求类型转化成PUT和DELETE。
HiddenHttpMethodFilter实现:识别参数中是否含有_method,如有根据其值判断是哪种操作后完成请求类型转换。
1)在隐藏域中添加参数_method

<form action="httpPut" method="post">
      <input type="hidden" name="_method" value="PUT/DELETE"/>
      <input type="submit" value="修改"/>
</form>    

2)在web.xml中配置HiddenHttpMethodFilter(注意这里如果配置字符过滤器的话,需要将字符过滤器放在所有过滤器之前,如下)

<!--字符过滤器,解决中文乱码问题-->
  <filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

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

3)以简单商品管理模块演示

public class Goods{
 private int id;
 private String name;
 private Double price;
 public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

Dao存储数据和处理

@Repository
public class GoodsDao {
    private Map<Integer,Goods> goodses = new HashMap<Integer,Goods>();

    public void add(Goods goods){
        goodses.put(goods.getId(),goods);
    }

    public Collection<Goods> getAll(){
        return goodses.values();
    }

    public Goods getById(int id){
        return goodses.get(id);
    }

    public void update(Goods goods){
      goodses.put(goods.getId(),goods);
    }

    public void deleteById(int id){
        goodses.remove(id);
    }
}

创建GoodsController
@PostMapping、@GetMapping、@PutMapping、@DeleteMapping 分别用来映射 Post、Get、Put、Delete 请求。

@Controller
public class GoodsController {
    @Autowired
    private GoodsDao goodsDao;

    @PostMapping(value = "/add")
    public String add(Goods goods){
        goodsDao.add(goods);
        return "redirect:/getAll";
    }

    @GetMapping(value = "/getAll")
    public ModelAndView getAll(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("index");//跳转到index.jsp
        modelAndView.addObject("goodses",goodsDao.getAll());
        return modelAndView;
    }

    @GetMapping(value = "/getById/{id}")
    public ModelAndView getById(@PathVariable(value = "id") int id){
        ModelAndView modelAndView = new ModelAndView();
        //这里只能跳转jsp,InternalResourceViewResolver这个解析器只能解析jsp文件,而不能解析html,设置视图名就是视图名.jsp文件页面
        modelAndView.setViewName("edit");//跳转到edit.jsp页面
        modelAndView.addObject("goods",goodsDao.getById(id));
        return modelAndView;
    }

    @PutMapping(value = "/update")
    public String update(Goods goods){
        goodsDao.update(goods);
        return "redirect:/getAll";
    }

    @DeleteMapping(value = "/delete/{id}")
    public String delete(@PathVariable(value = "id")  int id){
        goodsDao.deleteById(id);
        return "redirect:/getAll";
    }

}

jsp页面
addGoods.jsp

<form action="add" method="post">
    请输入id:<input type="text" name="id"/><br/>
    请输入name:<input type="text" name="name"/><br/>
    请输入price:<input type="text" name="price"/><br/>
    <input type="submit" value="提交"/>
</form>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${goodses}
</body>
</html>

此处省略edit页面…

图片上传

1 引入依赖:

使用Apache fileupload 组件,在pom.xml中引入fileupload组件依赖:

 <!--上传组件-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>

引入JSTL依赖,用于展示上传的图片:

<!--jstl组件-->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

2 jsp页面:

input 的 type 设置为 file
form 表单的 method 设置为 post(get 请求只会将文件名传给后台)
form 表单的 enctype 设置为 multipart/form-data,以二进制的形式传输数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%
    String path = request.getContextPath();
    System.out.println("页面上的path"+path);
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    System.out.println("basePath:"+basePath);
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="img">
    <input type="submit" name="提交">
</form><br />
<c:if test="${filePath!=null }">
    <h1>上传的图片</h1><br />
    <h1>${filePath}</h1>
    <img width="300px" src="${basePath}${filePath}"/>
</c:if>
</body>
</html>

3 springmvc.xml文件

配置springmvc.xml中的CommonsMultipartResolver

<!--扫描这个包下-->
<context:component-scan base-package="com.redocloud.handler"></context:component-scan>
<!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--前缀-->
    <property name="prefix" value="/"></property>
    <!--后缀-->
    <property name="suffix" value=".jsp"></property>
    </bean>
<!-- 配置 CommonsMultipartResolver bean,id 必须是 multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 处理文件名中文乱码 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 设置多文件上传,总大小上限,不设置默认没有限制,单位为字节,1M=1*1024*1024 -->
        
        **<!--如果上传的文件大小大于以下设定的值,会出现不能上传的情况-->**
        <property name="maxUploadSize" value="10485760"/>
        <!-- 设置每个上传文件的大小上限 -->
        <property name="maxUploadSizePerFile" value="10485760"/>
    </bean>
    <!-- 设置异常解析器,当上传失败时,跳转到error.jsp -->
     <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="defaultErrorView" value="error.jsp"/>
</bean>
    <!--默认的mvc注解映射的支持-->
    <mvc:annotation-driven/>
    <!--这里的这个配置很关键,不加的话,上传的文件在显示时会读取不到-->
    <!--使用DispatcherServlet拦截了所有的请求,导致无法获取静态资源-->
    <!--SpringMvc默认资源处理:-->
    <!--1.通过在springmvc.xml文件中加入-->

  <mvc:default-servlet-handler/>
<!--表示将对静态资源的处理由Spring mvc框架交回Web应用处理-->
    <!-- 2.使用mvc:resources,配置映射路径如下-->
    <mvc:resources mapping="/file/**" location="/file/"/>

4 编写controller

业务方法,使用 MultipartFile 对象作为参数,接收前端发送过来的文件,并完成上传操作。

@RequestMapping(value="/upload", method = RequestMethod.POST)
public String upload(@RequestParam(value="img")MultipartFile img, HttpServletRequest request)
            throws Exception {
        //getSize() 方法获取文件的大小来判断是否有上传文件
        if (img.getSize() > 0) {
            System.out.println("要开始传了");
            //获取保存上传文件的 file 文件夹绝对路径,就是webapp下我创建的file文件夹位置
            String path = request.getSession().getServletContext().getRealPath("file");
            //获取上传文件名
            System.out.println("path:"+path);
            String fileName = img.getOriginalFilename();
            //没有该文件时,创建
            if(!new File(path).exists()){
                System.out.println("没有该文件,创建中...");
                new File(path).mkdir();
            }
            //获取文件类型
            String contentType = img.getContentType();
            //获取源文件的扩展名
            String fileType ="."+ contentType.substring(contentType.indexOf("/")+1);
            //创建修改文件名,防止文件重复时覆盖
            String newFilename = UUID.randomUUID().toString().replace("-","")+fileType;
            System.out.println("fileName:"+newFilename);
            //-----创建文件路径
            File file = new File(path, newFilename);
            System.out.println("----file:"+file);
            img.transferTo(file);
            //保存上传之后的文件路径,传给jsp
            request.setAttribute("filePath", "file/"+newFilename);
            request.setAttribute("filename",newFilename);
            return "upload";
        }
        return "error";
    }

5 文件结构

这里的在这里插入图片描述
这里的file将其作为静态资源加载的文件夹。
完成后使用mvn打包后运行
结果:
在这里插入图片描述
还是有关于项目下路径的很多疑惑!

图片下载:

1 jsp页面 这里简单的用a标签来请求

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020/4/7 0007
  Time: 13:22
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%
    String path = request.getContextPath();
    System.out.println("页面上的path"+path);
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    System.out.println("basePath:"+basePath);
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="img">
    <input type="submit" name="提交">
</form><br />
<c:if test="${filePath!=null }">
    <h1>上传的图片</h1><br />
    <h1>${filePath}</h1>
    <img width="300px" src="${basePath}${filePath}"/>
    <h1>${filename}</h1>
    <a href="download?filename=${filename}">下载</a>
</c:if>
</body>
</html>

为了简便,我就直接在刚刚上传图片的页面上,添加了

<a href="download?filename=${filename}">下载</a>

2 controller:

 /**
 使用这种方式传参时,会出现跳转到download.jsp 原因暂不清楚
 @RequestMapping("/download")
    public void test(@RequestParam(value="filename") String fileName){
        System.out.println(fileName);
    }
    但是如果使用request来获取a标签所传的值,则可以正常跳转到download,并获取文件地址
**/
 @RequestMapping(value = "/download")
    public void download(HttpServletRequest request,HttpServletResponse response){
        String fileName = request.getParameter("filename");
        System.out.println(fileName);
        if(fileName!=null){
            //获取 file 绝对路径
            String realPath = request.getSession().getServletContext().getRealPath("file/");
            System.out.println("realPath:"+realPath);
            File file = new File(realPath,fileName);
            OutputStream out = null;
            if(file.exists()){
                //设置下载完后不打开文件
                response.setContentType("application/force-download");
                //设置文件名
                response.setHeader("Content-Disposition", "attachment;filename="+fileName);
                try {
                    out = response.getOutputStream();
                    out.write(FileUtils.readFileToByteArray(file));
                    out.flush();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }finally{
                    if(out != null){
                        try {
                            out.close();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }

多文件上传

和单文件上传一样

1JSP

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020/4/9 0009
  Time: 0:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%
    String path = request.getContextPath();
    System.out.println("页面上的path"+path);
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    System.out.println("basePath:"+basePath);
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="uploads" method="post" enctype="multipart/form-data">
    file1:<input type="file" name="imgs"><br />
    file2:<input type="file" name="imgs"><br />
    file3:<input type="file" name="imgs"><br />
    <input type="submit" name="提交">
</form>
<c:if test="${filePaths!=null }">
    <h1>上传的图片</h1><br />
    <c:forEach items="${filePaths }" var="filePath">
        <img width="300px" src="<%=basePath %>${filePath}"/>
    </c:forEach>
</c:if>
</body>
</html>

2 编写controller

@RequestMapping(value="/uploads", method = RequestMethod.POST)
    public String uploads(@RequestParam MultipartFile[] imgs, HttpServletRequest request)
            throws Exception {
        //创建集合,保存上传后的文件路径
        List<String> filePaths = new ArrayList<String>();
        for (MultipartFile img : imgs) {
            if (img.getSize() > 0) {
                String path = request.getSession().getServletContext().getRealPath("file");
                String fileName = img.getOriginalFilename();
                if(!new File(path).exists()){
                    System.out.println("没有该文件,创建中...");
                    new File(path).mkdir();
                }
                //获取文件类型
                String contentType = img.getContentType();
                //获取文件扩展名
                String fileType = contentType.substring(contentType.indexOf("/")+1);
                //新文件名
                String newFilename = UUID.randomUUID().toString().replace("-","");
                File file = new File(path, newFilename);
                filePaths.add("file/"+newFilename);
                img.transferTo(file);
            }
        }
        request.setAttribute("filePaths", filePaths);
        return "uploads";
    }

问题

1 传参问题

`<a href="download?filename=${filename}">下载</a>`
 @RequestMapping("/download")
    public void test(@RequestParam(value="filename") String fileName){
        System.out.println(fileName);
    }

这样发送请求和传参时,有问题

2 在关闭tomcat后,所上传的文件全部消失

第二个问题原因:每次存的文件都是在项目下,重新部署tomcat时,会清楚非系统性或无关文件。
主要原因:每次重新打开tomcat相当于将原来代码重新部署,就会覆盖上传了照片的包。

解决方案:在看了多个博主的方案后:
在server.xml文件中添加:

<Context docBase="D:\file" path="/file" reloadable="true"/>

这个我没有试过…

1.背景:

HTTP请求传输的参数都是String类型,因此需要用到参数的类型转换,通过Spring MVC的HandlerAdapter组件在执行Handler的业务方法前,完成参数的绑定。

@RequestMapping(value="/getType")
@ResponseBody
public String getType(int id){
  return "id"+id;
}

当请求中不带参数时:500错误,id为int型不能为null;
当传入的参数为非数字符串时:400错误,类型String不能转换为int
当参数类型使用包装类时,可以为null,String 仍然不能转换成包装类型

2. 对参数列表添加@RequestParam 注解,对参数进行设置

  @RequestMapping(value="/getType")
  @ResponseBody
  public String getType(@RequestParam(value="id",required=false,defaultValue="1") Integer id){
 return "id"+id;
}

引入

http表单中的请求参数都是String类型,业务方法中的参数为String 或者int 类型时,HandlerAdapter可以自动完成数据转换,但是如果是其他类型,如Date,无法从String自动转换成Date类型,所以需要通过实现Converter接口来辅助Spring MVC定制数据类型转换。

1 自创建DateConverter类实现springframework下的Converter接口:

public class DateConverter implements Converter<String,Date>{

    private String pattern;

    public DateConverter(String pattern){
        this.pattern = pattern;
    }

    public Date convert(String source) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        try {
            return simpleDateFormat.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

}

2配置spring.xml文件

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
  <property name="converters">
    <list>
      <bean class="com.southwind.utils.DateConverter">
        <!-- 调用有参构造函数创建 bean -->
        <constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
      </bean>
    </list>
  </property>
</bean>

<mvc:annotation-driven conversion-service="conversionService"/>

3创建add.jsp

<form action="dateConverterTest" method="post">
    请输入日期:<input type="text" name="date"/><font style="font-size:13px">(yyyy-MM-dd)</font><br/>
    <input type="submit" value="提交"/>
</form>

4创建Controller

@RequestMapping(value="/dateConverterTest")
@ResponseBody
public String dateConverterTest(Date date){
    return date.toString();
}

绪论

数据校验保证数据安全,Spring MVC中主要有两种方式:

(1) Validator接口

具体的数据校验规则要开发者手动设置,相对复杂
以简单的学生登录为例:
Student类

public class Student {
    private String name;
    private String password;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

引入依赖:

    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.1.0.Final</version>
    </dependency>

实现Validaor接口:

/**
 * ClassName : StudentValidation
 * package : com.redocloud.validation
 * 功能描述:
 *
 * @Date : 2020/4/9 0009 16:01
 * @Author : one world
 */
public class StudentValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        // TODO Auto-generated method stub
        return Student.class.equals(clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        // TODO Auto-generated method stub
        ValidationUtils.rejectIfEmpty(errors, "name", null, "姓名不能为空");
        ValidationUtils.rejectIfEmpty(errors, "password", null, "密码不能为空");
    }
}

controller:

/**
 * ClassName : ValidationController
 * package : com.redocloud.handler
 * 功能描述:
 *
 * @Date : 2020/4/9 0009 13:52
 * @Author : one world
 */
@Controller
public class ValidationController {
    @InitBinder
    //必须初始化,因为我忘记了这个找了很久的原因
    public void initBinder(DataBinder binder){
        binder.setValidator(new StudentValidator());
    }
    @GetMapping(value = "/login")
    public String login(Model model){
        model.addAttribute(new Student());
        System.out.println("Model");
        return "login";
    }
    @PostMapping(value = "/login")
    public String login(@Validated Student student,
                        BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "login";
        }
        return "success";
    }
}

jsp文件

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020/4/9 0009
  Time: 13:59
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<jsp:useBean id="student" class="com.redocloud.entity.Student" scope="request"/>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>学生登录</h1>
<form:form modelAttribute="student" action="login" method="post">
    学生姓名:<form:input path="name" /><form:errors path="name"/><br/>
    学生密码:<form:password path="password" /><form:errors path="password"/><br/>
    <input type="submit" value="提交"/>
</form:form>
</body>
</html>
(2) Annotation JSR-303 标准:不需要编写验证逻辑,直接通过注解的形式给每一条数据添加验证规则(直接在实体类的属性中添加验证规则)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SinceThenLater

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值