Spring学习小记2—SSM—Spring-MVC

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Mackyhuang/article/details/82431870

Spring-MVC

@author:MackyHuang

一个mvc框架,用来简化基于mvc架构的web应用开发

五大组件

DispatcherServlet(前端控制器)

接受请求,依据HandlerMapping的配置调用相应的模型来处理

HandlerMapping

包含了请求路径和模型的对应关系

Controller(处理器)

负责处理业务逻辑

ModelAndView

封装了处理结果
注:处理结果除了数据之外,还可能有视图名

ViewResolver(视图解释器)

DispatcherServley依据ViewResolver的解析,
调用真正的视图对象来生成相应的页面

Spring-MVC五大组件关系图

Spring-MVC五大组件关系图

五大组件工作步骤:

  • DispatcherServlet收到请求之后,依据HandlerMapping的配置,调用相应的Contrller来处理
  • Controller将处理结果封装成ModelAndView对象,然后返回给
  • DispatcherServlet依据ViewResolver的解析调用相应的视图对象(比如某个jsp)来生成相应的页面

编程步骤

导包,springframe的包

添加spring配置文件

WEB-INF\web.xml中配置DispatcherServlet

            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- 
                DispatcherServlet的初始化方法中会获取初始化参数的值
                这些值可以让DispatcherServlet获取spring配置文件的位置
                然后启动spring容器
             -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
            </servlet>
            <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>*.do</url-pattern>
            </servlet-mapping>

此时的错误详细见Splendid Erroer.大概就是关于MAVEN的jar包的管理

Controller的创建

创建的时候,创建的类需要继承Controller的接口,实现里面的一个handleRequest的方法

        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception
        {
            //添加业务处理
            System.out.println("handleRequest()");
            /*
             * ModelAndView的俩个构造器
             * (1)ModelAndView(String viewName)
             * ViewName 为 视图名称
             * (2)ModelAndView(String viewName, Map map)
             * map用于返回处理的结果数据
             * */
            return new ModelAndView("hello");
        }

这里有一个问题:

无法解析org.springframework.web.servlet.mvc.Controller

原因未知,解决办法是,将mawen中的springframe的jar包删除,只是使用自己下载添加进去的springframe,就可以运行

springmvc.xml配置HandlerMapping

      <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello.do">helloController</prop>
            </props>
        </property>
      </bean>

key的是请求的网址,然后转跳到prop标签里面的那个id对应的处理器的bean
随后处理器返回视图名通过ModelAndView对象给ViewResolver

springmvc.xml配置Controller

      <bean id="helloController" class="controller.HelloController"></bean>

控制器的名字将会用来使用在handlemapping里面

springmvc.xml配置ViewResolver

      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/"></property>
        <property name="suffix" value=".jsp"></property>
      </bean>

Controller封装成ModelAndView对象传过来的视图名,会通过以下配置映射到/WEB-INF/hello.jsp

(小提示:如何正确保证自己在导入spring包的时候文件名的正确:
在库中找到相应的那个包,然后右击->复制限定名->粘贴->删去尾巴的.class)

重新梳理一次整个spring-mvc的运行过程:

  1. 用户在网页上输入http://localhost:8023/springmvc/hello.do发送请求
  2. 启动DispatcherServlet,此时,在web.xml文件中会获得spring配置文件的地址,然后启动spring容器
  3. spring容器一旦创建,就会创建好三个对象(默认为单例singleton),分别是HandlerMapping, Controller, ViewResolver
  4. DispatcherServlet依据HandlerMapping的配置,找到其prop标签内id名称对应的bean,调用这个bean的Controller对象
  5. Controller处理业务信息,返回一个ModelAndView对象给DispatcherServlet
  6. DispatcherServlet根据ModelAndView中的视图名,根据ViewResolver解析出一个视图对象(比如某个.jsp),响应给用户

回过头来,用简单的方法配置一次springmvc

(此处直接显示出不同之处)

        @Controller
        public class Login 
        {
            @RequestMapping("/login.do")
            public String login()
            {
                return "login";
            }
        }
  • 可以不使用Controller的接口直接创建多个方法,每个方法处理一种请求
  • 方法名不做要求,和前面不同的还有返回类型,之前是ModelAndView,现在返回字符串类型
  • 使用@Controller来代替配置文件的bean,直接纳入容器进行管理
  • (记得这个需要在配置文件组件扫描)
  • 使用@RequestMapping,告知DispatcherServlet请求的路径和
  • 处理器类种方法的对应关系(spring配置文件种不用配置HandlerMapping
  • 为了可以找的到@RequestMapping,需要mvc注解扫描
    <mvc:annotation-driven></mvc:annotation-driven>
    • springmvc.xml中只需要组件扫描,mvc注解扫描和ViewResolver配置
  • @RequestMapping也可以添加在类的前面,当作模块名
  • 未添加需要访问: http://ip:port/springmvc02/login.do
  • 添加需要访问:http://ip:port/springmvc02/demo/login.do

Controller类读取参数

jstl小记
<%=request.getParameter(“name”) %>等效于 p a r a m . n a m e < {name }

这是之前读取参数的办法,在jsp中读取表单或者servlet中传过来的值

第一种方式:

通过HttpServletRequest对象,使用request.getParameter("name")方法获取表单数据,因为DispatcherServlet在调用Controller的时候会传一个Request对象。

        @RequestMapping("/hello.do")
            public String hello(HttpServletRequest request)
            {
                String user = request.getParameter("name");
                String password = request.getParameter("password");
                return "hello";
            }

第二种方式:

通过注解来获取,值得一提的是,在创建一个函数的时候,如果传入的参数不是Request对象,而是一个个变量,如果这个变量的名字和表单中的参数的name的值一样,那么,不需要注解也完全可以让函数得到参数的值,如下:

        @RequestMapping("/hello.do")
        public String hello(String name, String password)
        {
            System.out.println("Hello," + name);
            System.out.println("确认您的密码," + password);
            return "hello";
        }

可是如果这个时候参数名字不一样,就无法的到值,这个时候就需要注解@RequestParam("")添加在相应的变量参数前面,引号中需要填写表单中的name,如下:

        @RequestMapping("/hello.do")
        public String hello(@RequestParam("name") String user, String password)
        {
            System.out.println("Hello," + user);
            System.out.println("确认您的密码," + password);
            return "hello";
        }

注:推荐即使参数名字和name一样,也加上@RequestParam的注解, 因为根据java的反射机制的一个缺点,有的时候在检查参数的时候无法读到参数名字,只能读到变量的类型,这个时候就会得到一个null。因为eclipse会自动添加所以才能不用这个注解直接访问,将来没有这个环境的时候,为了节省空间可能不会获得这个参数的具体名字

第三种方式:

通过javabean的封装来获取

        @RequestMapping("/hello.do")
        public String hello(User user)
        {
            System.out.println("Hello," + user.getName());
            System.out.println("确认您的密码," + user.getPassword());
            return "hello";
        }

编写一个javabean封装表单中所有的name,其中包括所有的name,并且需要set和get方法,然后在方法中直接传入一个对象。

Controller类给页面传值

第一种方式:

使用Request对象绑定对象然后转发给页面

        @RequestMapping("/hello.do")
        public String hello(User user, HttpServletRequest request)
        {
            System.out.println("Hello," + user.getName());
            System.out.println("确认您的密码," + user.getPassword());
            request.setAttribute("name", user.getName());
            request.setAttribute("password", user.getPassword());
            return "hello";
        }

这里的用法和servlet的用法一样,设置属性值然后转发给页面,值得注意的是,这里的默认就是转发,所以直接返回就好

第二种方式:

返回ModelAndView对象来传参数

        @RequestMapping("/hello.do")
        public ModelAndView hello(User user)
        {
            System.out.println("Hello," + user.getName());
            System.out.println("确认您的密码," + user.getPassword());
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("user", user);
            ModelAndView modelAndView = new ModelAndView("hello", map);
            return modelAndView;
        }

这里返回ModelAndView,其中有一个带数据结果的Map对象,在内部,map的键值对和request中setAttribute的键值对结果是一样的。key的值和对应绑定名

第三种方式:

使用ModelMap对象

        @RequestMapping("/hello.do")
        public String hello(User user, ModelMap modelMap)
        {
            System.out.println("Hello," + user.getName());
            System.out.println("确认您的密码," + user.getPassword());
            modelMap.addAttribute("name", user.getName());
            modelMap.addAttribute("password", user.getPassword());
            return "hello";
        }

在参数传入一个ModelMap对象,用法和request大同小异:modelMap.addAttribute("name", user.getName())

第四种方式:

使用HttpSession对象

@RequestMapping("/hello.do")
public String hello(User user, HttpSession session)
{
    System.out.println("Hello," + user.getName());
    System.out.println("确认您的密码," + user.getPassword());
    session.setAttribute("name", user.getName());
    session.setAttribute("password", user.getPassword());
    return "hello";
}

HttpServletRequest对象都可以传递,HttpSession对象肯定也是可以的,用法与request相同

重定向(默认转发)

第一种方式(只返回一个字符串的):

        @RequestMapping("/tohello.do")
        public String tohello()
        {
            System.out.println("tohello()");
            return "redirect:hello.do";
        }

return 语句的string中,加上redirect:然后加上请求的页面的对应的RequestMapping

第二种(返回一个ModelAndView):

        public ModelAndView tohello()
        {
            System.out.println("tohello()");
            RedirectView redirectView = new RedirectView("hello.do");
            ModelAndView modelAndView = new ModelAndView(redirectView);
            return modelAndView;
        }

系统分层

重温MVC

  • Model 控制逻辑
  • View 表示逻辑
  • Controller 封装业务逻辑

  • Model分为
    业务逻辑 + 数据访问

    也就是 Service(服务类) + DAO(持久化类)

如何分类

这里的分层通过mvc继续细化:

  • 表示层:数据展示[V]和控制逻辑[C](请求分发)
  • 业务层:业务逻辑的处理[M]
  • 持久层:数据库的访问[M]

#

  • 上一层通过接口调用下一层提供的服务,比如,业务层调用持久层提供的接口
  • 下一层发生改变,不影响上一层,方便代码的维护和工作的分配

处理字符

网页出现乱码的原因:
提交表单的时候,浏览器会对中文进行编码,会使用在打开表单所在页面的字符所用的字符集来编码,比如现在使用的utf-8,然而服务器默认会用iso-8859-1来解码

此时可以使用springmvc提供的一个字符过滤器CharacterEncodingFilter进行配置,也有一定的要求

过滤器的条件

  • 表单的提交方式是post
  • 页面的编码和过滤器的配置是一样的
  • 因为这个过滤器其实就是调用了request.setCharacterEncoding()这个方法,其条件就是这个

详细配置之如下(在web.xml文件中配置)

        <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>

拦截器(interceptor)

拦截器,其实就是DispatcherServlet收到请求之后,如果有拦截器,会先调用拦截器,然后再调用相应的controller

过滤器是属于servlet的规范,而拦截器是属于springmvc框架的组件
过滤器是请求到达容器的时候先工作的,而拦截器是要调用DispatcherServlet的时候先工作的

拦截器编写步骤

  • 写一个JAVA类,实现HandlerInterceptor接口
  • 实现具体的拦截逻辑处理
  • 配置拦截器(告诉servlet哪些需要拦截)

        public class MyInterceptor implements HandlerInterceptor
        {
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
            {
                System.out.println("preHandle()");
                HttpSession session = request.getSession();
                Object object = session.getAttribute("name");
                if(object == null)
                {
                    response.sendRedirect("login.do");
                    return false;
                }
                return true;
            }
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                    ModelAndView modelAndView) throws Exception
            {
                System.out.println("postHandle");
            }
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                    throws Exception
            {
                System.out.println("afterCompletion");
            }   
        }
    
  • preHandle()
    DispatcherServlet收到请求之后,会先调用preHandle方法
    如果这个方法的返回值是True就继续向后调用,如果返回False就
    不会往后调用,第三个参数描述的是Controller方法的一个对象*/

  • postHandle()
    处理器Controller的方法已经执行完毕之后。正准备
    将结果ModelAndView对象返回给DispatcherServlet之前
    执行这个postHandle方法,可以在这个方法中修改处理结果
  • postHandle
    这是最后执行的方法,只有在preHandle方法返回一个true的时候
    这个方法才会执行
    可以写一个拦截器,专门用来处理Controller抛出来的异常

配置文件

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/login.do"/>
            <mvc:exclude-mapping path="/tohello.do"/>
            <bean class="controller.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

- 先后顺序执行
- 配置的时候,如果是/*只会拦截/hello.do,不会拦截/demo/hello.do
- 这个时候配置/** 他会拦截所有请求
- mvc:exclude-mapping里面是允许访问的

异常处理

第一种方法(适合全局处理简单异常):

  • 直接配置简单异常处理器

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.NumberFormatException">error</prop>
            </props>
        </property>
    </bean>
    

    创建一个SimpleMappingExceptionResolver的bean,里面Key的值就是异常的全称,而prop里面的内容是一个视图名,可以通过这个转跳到一个提示页面

第二种方法(annotation):

  • 在controller中的一个方法前面注解@ExceptionHandler,然后在里面用instanceof来判定异常的类型,然后做出相关的业务处理,然后返回一个视图名(返回一个ModelAndView对象也是可以的)

        @ExceptionHandler
        public String myException(Exception exception, HttpServletRequest request)
        {
            if(exception instanceof NumberFormatException)
            {
                request.setAttribute("meg", "NumberFormatException");
                return "error";
            }
            else if(exception instanceof StringIndexOutOfBoundsException)
            {
                request.setAttribute("meg", "StringIndexOutOfBoundsException");
                return "error";
            }
            else 
            {
                request.setAttribute("meg", "system_error");
                return "error";
            }
        }
    

相比而言,第一种方法只能进行简单的异常获取后跳转到页面,而第二种方法可以再获取异常之后,进行一系列的操作然后转跳到页面

猜你喜欢

转载自blog.csdn.net/Mackyhuang/article/details/82431870