美文网首页JAVA学习记录Java 杂谈Java
《Spring实战》-第五章:Web中的Spring(Sprin

《Spring实战》-第五章:Web中的Spring(Sprin

作者: 廖小明的赖胖子 | 来源:发表于2019-03-22 23:53 被阅读1次

    慢慢来比较快,虚心学技术

    前言:前面我们学习了关于Spring核心的IOC和AOP知识,除此之外,以此为基础,Spring的MVC框架还经常被用于Web开发(SpringMVC)

    一、什么是SpringMVC框架?

    在了解SpringMVC之前,我们先回顾一下Spring基础架构:

    Spring MVC 是Spring的一部分,基于模型 - 视图 - 控制器( Model-View-Controller , MVC )模式实现,它能够帮你构建像 Spring 框架那样灵活和松耦合的 Web 应用程序。在实际开发中,接收浏览器的请求响应,对数据进行处理,然后返回页面进行显示。

    二、SpringMVC组成以及运行原理

    Ⅰ、SpringMVC的组成

    1. DispatcherServlet:前端控制器 (SpringMVC的核心)-----相当于MVC中的C,作为中心调用其他组件,降低其他组件之间的耦合性
    2. HandlerMapping:处理器映射器 ----------------------------------根据用户请求找到对应路径的处理器(相当于处理器的名单)
    3. HandlAdapter:处理器适配器 --------------------------------------调用执行处理器方法(适配器模式的应用)
    4. Handler:处理器 --------------------------------------------------------处理用户请求的类,相当于传统意义上的Servlet
    5. ViewResolver:视图解析器 ------------------------------------------处理返回结果,将处理器适配器返回的数据模型转换成具体视图,并进行渲染输出(实际上就是将处理器返回的名称补充成具体的路径也就是一个视图,同时从数据模型中提取数据进行填充)
    6. View:视图 ---------------------------------------------------------------视图是数据最终需要展现给客户的地方,Spring支持多种类型的视图:jstlVies,freemarkerView等,最常用的是JSP和使用模板实现的html等

    Ⅱ、SpringMVC请求响应流程

    用户发起请求,携带请求信息到前端控制器进行调度

    前端控制器(DispatcherServlet)调用处理器映射器,根据请求信息从处理器映射器中找到访问路径的目标处理器

    前端控制器(DispatcherServlet)根据得到的目标处理器映射,调用处理器适配器方法(处理器适配器将处理器方法包装成适配器模式)

    处理器适配器(HandlerAdapter)调用处理器(Handler)相应功能方法,并将结果返回给前端控制器

    前端控制器(DispatcherServlet)根据得到的数据结果和目标视图名称,调用视图解析器(ViewResolver)返回目标视图完整路径

    前端控制器(DispatcherServlet)根据得到的视图路径,对目标视图(view)进行渲染(数据填充等),得到目标视图

    前端控制器(DispatcherServlet)将目标视图展现给用户

    从上述流程可以看到,SpringMVC的功能流转是围绕前端控制器(DispatcherServlet)实现的,这样的好处是使得各个组件之间的耦合性大大降低,各个组件只做自己应该做的事情。其实这是大部分框架想要实现的目标。

    分析DispatcherServlet,从Spring官网查看到的结构图如下:

    从结构图可以看到,DispatcherServlet包含了两个Web应用上下文,用于独立控制,其中:

    • Servlet WebApplicationContext:管理用于网络请求的处理器适配器,视图解析器,处理器映射器和处理器
    • Root WebApplicationContext:管理基本的数据库操作类,业务逻辑类等Bean

    通过两个应用上下文管理基本Bean和网络Bean,互不干扰,但是其中管理的Bean之间可以互相使用

    三、SpringMVC的简单使用(注解方式)

    首先我们应该先引入Spring对WebMvc的支持,maven引入如下:

    <!--引入网络Servlet支持-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!--引入SpringMVC-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>
    

    ①创建DispatcherServlet类,继承并重载AbstractAnnotationConfigDispatcherServletInitializer类的三个方法:

    //定义DispatcherServlet类名为WebAppInitializer
    public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        /**
         * 指定DispatcherServlet的基本Bean应用上下文配置类
         * @return
         */
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{RootConfig.class};
        }
    
        /**
         * 指定DispatcherServlet的网络类应用上下文配置类
         * @return
         */
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{WebConfig.class};
        }
    
        /**
         * 将DispatcherServlet映射到“/”,即应用内所有访问都会经过DispatcherServlet的处理
         * @return
         */
        @Override
        protected String[] getServletMappings() {
            logger.debug("DispatcherServlet获取匹配的前端控制器。。。。。。");
            return new String[]{"/"};
        }
    }
    

    ②创建上述代码的两个配置类:RootConfig.javaWebConfig.java,其中,RootConfig只扫描除了WebConfig扫描范围外的基本类,而WebConfig只扫描基本的网络类,同时配置视图解析器和处理器映射器,并开启mvc配置

    //定义WebConfig配置类
    @Configuration
    @ComponentScan(basePackages = {"com.my.spring.controller"})//WebConfig扫描包的范围
    @EnableWebMvc /*<mvc:annotation-driven> 开启mvc配置*/
    public class WebConfig extends WebMvcConfigurationSupport {
    
        /**
         * 定义一个视图解析器
         *
         * @return org.springframework.web.servlet.ViewResolver
         *
         * @author xxx 2019/3/5
         * @version 1.0
         **/
        @Bean
        public ViewResolver viewResolver(){
            //基本的视图解析器
            InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();
            //视图前缀,指向WEB-INF目录下的view目录,意思是所有的视图名称进入视图解析器的时候都会被加上前缀
            resourceViewResolver.setPrefix("/WEB-INF/view/");
             //视图后缀,此处指定后缀为jsp,意思是所有的视图名称进入视图解析器的时候都会被加上后缀,前缀+view名+后缀得到完整路径
            resourceViewResolver.setSuffix(".jsp");
             //可以在JSP页面中通过${}访问beans
            resourceViewResolver.setExposeContextBeansAsAttributes(true);
            return resourceViewResolver;
        }
    
        /**
         * 配置一个默认的处理器,实现父类接口,自动处理静态资源的映射
         * @param configurer
         */
        @Override
        protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }
    
    //定义RootConfig配置类
    @Configuration
    //指定扫描范围,排除过滤掉使用了@EnableWebMvc注解扫描范围的bean,不进行扫描
    @ComponentScan(basePackages ={"com.my.spring"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {EnableWebMvc.class})})
    public class RootConfig {
    }
    

    ③编写基本的Bean类

    @Data//lombok的注解,编译添加setter和getter方法
    public class BaseBean {
    
        private Integer id;
    
        private String name;
    
    }
    

    ④编写逻辑操作类(暂时没有用到数据库,所以只是模拟)

    //定义基本Dao接口
    public interface BaseRepository {
        /**
         * 根据id获取BaseBean
         * @param id 目标id
         * @return
         */
        BaseBean findOne(Integer id);
    }
    
    //定义基本Dao实现类,@Repository注解使用了@Component,可以被当作组件装配
    @Repository
    public class BaseRepositoryImpl implements BaseRepository {
    
        @Override
        public BaseBean findOne(Integer id) {
            if(id!=0){
                return null;
            }
            BaseBean baseBean = new BaseBean();
            baseBean.setId(0);
            baseBean.setName("测试bean");
            return baseBean;
        }
    }
    
    //定义基本Service接口
    public interface BaseService {
         /**
         * 根据id获取BaseBean
         * @param id 目标id
         * @return
         */
        BaseBean findBean(Integer id);
    }
    
    //定义基本操作实现类,使用@Service注解,标明该类是一个service,该注解使用了@Comonnet注解,所以可被作为组件进行装配
    @Service
    public class BaseServiceImpl implements BaseService {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        //注入基本操作dao
        @Autowired
        private BaseRepository baseRepository;
    
        @Override
        public BaseBean findBean(Integer id) {
            return this.baseRepository.findOne(id);
        }
    }
    

    ⑤创建处理类HomeController,使用@Controller注解标明当前类为一个处理类,同样使用@Component注解,可被装配

    @Controller
    public class HomeController {
    
        @Autowired
        private BaseService baseService;
    
        /**
         *使用@RequestMapping注解,将当前方法作为可访问路径,value值指定了访问路径,而method值指定了访问方式
         */
        @RequestMapping(method = RequestMethod.GET,value = "/home")
        public String home(){
            //返回试图名为home的视图
            return "home";
        }
    }
    

    看到上述代码,我们首先做最简单的测试,通过访问/home路径,访问具体的静态资源:

    根据WebConfig中的视图解析器,我们在相应路径下创建:/WEB-INF/view/home.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Home Page</title>
    </head>
    <body>
        <h1>Hello World</h1>
    </body>
    </html>
    

    路径如下:

    启动项目,页面访问如下:本项目名为SpringAction05

    从访问结果可以看到,访问被转向了home.jsp,而我们在controller方法中只是return了一个home,也就是说,视图解析器为我们补充了完整的路径并将视图返回给浏览器

    注:

    AbstractAnnotationConfigDispatcherServletInitializer 会同时创建 DispatcherServletContextLoaderListener

    GetServlet-ConfigClasses() 方法返回的带有 @Configuration 注解的类将会用来定义 DispatcherServlet 应用上下文中的 bean 。

    getRootConfigClasses() 方法返回的带有 @Configuration 注解的类将会用来配置 ContextLoaderListener 创建的应用上下文中的 bean 。

    四、信息交互

    Ⅰ、传递模型数据到视图中

    有时候我们并不只是需要对访问进行转发,同时可能需要携带一些信息给浏览器端,SpringMVC提供了Model类对返回信息进行封装返回:

    下面我们在上述controller方法home()返回之前封装信息返回给视图,并从视图中获取到对应的数据:

    @Controller
    public class HomeController {
    
        @Autowired
        private BaseService baseService;
    
        @RequestMapping(method = RequestMethod.GET,value = "/home")
        public String home(Model model){
    
            //往model中放置信息(key,value)
            model.addAttribute("Message","I am HomePage!!!");
    
            //返回试图名为home的视图
            return "home";
        }
    }
    

    修改home.jsp获取目标数据,此处我们使用JSTL标签库获取,所以需要先引入JSTL标签库的jar包:

    <!-- jstl -->
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    

    home.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!--引入JSTL标签库,并以c为标签前缀-->
    <%@ page isELIgnored="false" %><!--禁用tomcat自带的EL表达式,否则无法获取对应的数据-->
    <html>
    <head>
        <title>Home Page</title>
    </head>
    <body>
        <h1>Hello World</h1>
        <!--通过EL表达式获取key为Message的属性值-->
        <c:out value="${Message}"></c:out>
    </body>
    </html>
    

    测试结果如下:

    Ⅱ、接受请求的输入

    Spring MVC 允许以多种方式将客户端中的数据传送到控制器的处理器方法中,包括:

    • 查询参数( Query Parameter )。
    • 路径变量( Path Variable )
    • 表单参数( Form Parameter )。

    ①查询参数形式

    我们在controller中编写一个getBean方法,要求接收一个参数,参数名为id,通过拿到的id进行查询,将查询到的基本bean封装到数据模型并返回给视图

    @RequestMapping(method = RequestMethod.GET,value = "/getBean")
    public String getBeanByParam(@RequestParam("id")Integer beanId, Model model){
    
        BaseBean bean = this.baseService.findBean(beanId);
        //以bean为key将目标对象封装到数据模型
        model.addAttribute("bean",bean);
    
        //返回试图名为home的视图
        return "showMessage";
    }
    

    然后创建一个showMessage.jsp作为目标视图

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page isELIgnored="false" %>
    <html>
    <head>
        <title>信息主页</title>
    </head>
    <body>
        <!--判空-->
        <c:if test="${bean==null}">
            <li>bean不存在</li>
        </c:if>
        <c:out value="${bean.id}"></c:out><!--提取目标对象的信息-->
        <c:out value="${bean.name}"></c:out>
    </body>
    </html>
    

    浏览器访问路径如下:http://locahost:8080/SpringAction05/getBean?id=0

    注:其实getBeanByParam(@RequestParam("id")Integer id, Model model)方法中的@RequestParam("id")可以省略不写,如果不写的话,那么访问参数必须与参数位的名称一致,即:http://locahost:8080/SpringAction05/getBean?beanId=0

    ②表单参数形式

    将查询参数通过form表单提交到前端控制器,此时,Spring允许通过对象接收查询参数,要求form表单提交的字段与对象属性字段对应

    首先创建一个form表单,表单提交到/getBean

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <body>
    <h2>Hello World!</h2>
    <form action="./getBean" method="post">
        提交id:<input name="id" type="text"/><br>
        <button type="submit">提交</button>
    </form>
    </body>
    </html>
    

    Contorller接收参数(以对象形式):

    @RequestMapping(method = RequestMethod.POST,value = "/getBean")
    public String getBeanByForm(BaseBean baseBean, Model model){
    
        BaseBean bean = this.baseService.findBean(baseBean.getId());
    
        model.addAttribute("bean",bean);
    
        //返回试图名为home的视图
        return "showMessage";
    }
    

    浏览器访问结果如下:


    ③路径变量方式

    SpringMVC可以通过@PathVariable从路径中提取参数变量:

    @RequestMapping(method = RequestMethod.GET,value = "/getBean/{id}")//指定getBean/后的参数位id参数占位
    public String getBeanByPath(@PathVariable("id") Integer id, Model model){//通过@PathVariable注解提取变量
        BaseBean bean = this.baseService.findBean(id);
    
        model.addAttribute("bean",bean);
    
        //返回试图名为home的视图
        return "showMessage";
    }
    

    访问结果如下:

    相关文章

      网友评论

        本文标题:《Spring实战》-第五章:Web中的Spring(Sprin

        本文链接:https://www.haomeiwen.com/subject/augdvqtx.html