[java手把手教程][第二季]java后端博客系统文章系统——No7

转眼间又到了三月花开的季节,没一起感觉开篇都在说一些废话,这一期一样不例外。

项目github地址:https://github.com/pc859107393/SpringMvcMybatis

实时项目同步的地址是国内的码云:https://git.oschina.net/859107393/mmBlog-ser

我的简书首页是://www.greatytc.com/users/86b79c50cfb3/latest_articles

上一期是:[手把手教程][第二季]java 后端博客系统文章系统——No6

行走的java全栈
行走的java全栈

工具

  • IDE为idea16
  • JDK环境为1.8
  • gradle构建,版本:2.14.1
  • Mysql版本为5.5.27
  • Tomcat版本为7.0.52
  • 流程图绘制(xmind)
  • 建模分析软件PowerDesigner16.5
  • 数据库工具MySQLWorkBench,版本:6.3.7build

本期目标

  • 回顾SpringMvc
  • 总结提高

闲聊

最近看了很多技术相关的文档感觉受益良多,我也会不定时的在群里分享一些技术文档,虽然很多都是别人分享给我的。

世上无难事,只要肯攀登。(肯攀登是谁呢?)

虽然说我们是单独做技术的人,但是牛奶和面包总是不能少的。一味画饼充饥的创业都是耍流氓。

最近碰到几个想找我一起创业的人,不签合同,不给现金,都是吹逼项目牛逼,然后想我白干。 可惜我已经过了几句煽情的话就能打动的年纪。

无论谁找你创业,不给真金白银都是耍流氓。

SpringMvc

在前面我们已经做过很多关于SpringMvc的应用,可能大家很多日常的基本操作都有了概念,但是这个操作叫做什么名字呢?怎样更加合理的运用呢?Let's go!

1. 配置SpringMvc

我们需要创建Spring-Web.xml,代表它是我们的Spring依赖的注册文件(context)。

<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/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

上面演示的是我们注册的时候需要的Spring相关的申明。紧接着,我们需要开始一步步的注册web相关的资源:

<!-- 配置SpringMVC -->
    <!-- 1.开启SpringMVC注解模式 -->
    <!-- 简化配置:
        (1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter
        (2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持
    -->
    <mvc:annotation-driven/>

当我们引入了上面的代码的时候,我们已经开启了SpringMvc的注解映射(同理我们可以使用非注解映射!)

  • 简单的说明下非注解映射就是在Spring-web.xml中直接配置url。

当我们配置了url的访问注解后,按照道理来说只要我们开始配置了,那么我们的地址就是可以开始访问的,但是我们不可能所有的请求都需要框架来处理然后转发吧?so,我们需要配置一些特殊资源的访问路径,比如说静态的js、css、img等等,所以有了如下的配置:

    <!--配置静态资源的url映射-->
    <mvc:resources mapping="/css/**" location="/static/css/"/>
    <mvc:resources mapping="/images/**" location="/static/images/"/>
    <mvc:resources mapping="/view/**" location="/static/view/"/>
    <mvc:resources mapping="/js/**" location="/static/js/"/>
    <mvc:resources mapping="/fonts/**" location="/static/fonts/"/>
    <!--配置默认的前端控制器-->
    <mvc:default-servlet-handler/>

当然,我们上面得只是简单的配置了一些文件的url目录,同时我们可以部署我们的线上资源的访问权限,如果是非法用户那么就需要把他赶出服务器,所以我们需要请求拦截,并插入一些代码检查之类的操作,所以先配置拦截器如下:

    <!-- 访问拦截  -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**/**"/>
            <bean class="cn.acheng1314.intercepter.LoginHandlerInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

具体的拦截器代码如下:


/**
 * Created by mac on 2017/1/27.
 */
public class LoginHandlerInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String path = request.getServletPath();
        String userId;  //登录成功后写入session的用户id
        User user;  //通过用户ID查询到的用户信息
        /*
        我们拦截的网址是需要最少是作者权限才能进行编辑的,所以这里我们需要限制访问。
        <br/> 同时我们可以看到的是 登录页面必须是所有人都可以访问的,但是如果已经登录成功了,session在有效期内,我们的登录界面就不应该再展示给用户
        */

        if (!path.matches(".*/((endSupport)|(commit*)).*")) {
            if (path.contains("/main/login")) { //已经登录且身份信息且没有过期,我们直接跳转到后端主页去
                try {
                    userId = request.getSession().getAttribute("userId").toString();
                    user = userService.findOneById(userId);
                    if (request.getRequestedSessionId().equals(user.getUserSessionId())) {  //前面用户登录后会存入请求的sessionId和当前的sessionId对比
                        response.sendRedirect(request.getContextPath() + "/endSupport/index");
                        return false;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return true;
                }

            }
            return true;
        } else {

            try {
                userId = request.getSession().getAttribute("userId").toString();

                user = userService.findOneById(userId);
                if (!request.getRequestedSessionId().equals(user.getUserSessionId())) {  //前面用户登录后会存入请求的sessionId和当前的sessionId对比
                    throw new Exception("用户信息错误!");
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                response.sendRedirect(request.getContextPath() + "/main/login");
                return false;
            }
        }
    }
}

看到上面拦截器相关的代码,大家都会明白,在我的url中只要匹配了endSupport和commit相关字段的,我们都需要用户登录,其实说严格一点按照http请求的几种方式和restful来说没我们需要匹配更多的规则才行。重点思考:我们既然前面看到了拦截器那里仅仅配置了一个拦截器,那么我们能不能配置多个拦截器呢?答案是可以的,interceptors:指定拦截器链,拦截器的执行顺序就是此处添加拦截器的顺序。

通过上面的配置,我们已经可以实现html页面的展示,而且依赖js脚本刷新的动态页面也是基本可以显示了,但是,不是所有的后端工程师都精通web页面的开发,更多时候,可能我们单纯的更喜欢jsp这种方式的页面,所以我们需要添加jsp页面的解析,代码如下:

    <!-- 3.配置jsp 显示ViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

配置完成到这里,我们需要的就只剩下文件上传的数据模型和web接口的扫描了。为什么要扫描web接口呢?我们前面开启了注解映射,那么我们避免和项目其他的冲突,我们直接指定web接口的文件存放目录,让程序完成自动扫描,那么我们就只需要关心我们的业务开发了,是不是很爽?

    <!--上传文件的处理模型-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10000000"/>
    </bean>
    <!-- 4.扫描web相关的bean -->
    <context:component-scan base-package="cn.acheng1314.controller">
        <!-- 制定扫包规则 ,只扫描使用@Controller注解的JAVA类 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

小结

SpringMvc的配置顺序如下:

创建配置文件→加入依赖申明→开启注解式映射(非注解也行)→加入特殊资源访问映射→设置访问拦截→配置动态视图解析→配置web接口扫描规则和文件上传的处理模型。

既然已经配置好了注解开发设置,那么我们需要实际操作体会一下SpringMvc的开发,先看看代码如下:

@Controller
@RequestMapping("/front")
public class FrontWebController {
    /**
     * 返回主页面
     *
     * @return
     */
    @RequestMapping(value = "/main", method = RequestMethod.GET)
    public ModelAndView frontMain(HttpServletRequest request) throws Exception {
        ModelAndView view = new ModelAndView("frontMain");
        view.addObject("framJson", getFramJson());
        view.addObject("postsJson", findPublishPost(request, 1, 10));
        return view;
    }

在上面得代码中我们是返回一个web页面,这个web页面匹配到的url就是:xxxhost.xxx/front/main。主要规则说明如下:

  1. 类用Controller的注解:@Controller,说明这个类是web接口的注册类

  2. RequestMapping的意思是说明这个地址是:“/RequestMapping”

  3. http请求的方式有很多,可以在RequestMapping中限制具体的请求方式是什么?

    • 具体的形式有:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
    • 我们在RequestMapping中发现有更多的说明,如:name、value、path、method、params、headers、consumes、produces,具体的其他用途我们可以详细的查看文档。
  4. ModelAndView主要是用来说明这个请求地址是个web视图。

  5. view.addObject() 是用来将我们希望传输到web页面的数据插入到请求中

  6. 在web页面中,我们使用${objName}来获取数据。

通过上面得代码和归纳,我们大概明白了怎么去创建一个web页面需要怎么样的设置,但是我们如果要获取到json数据呢?具体如下:

@RequestMapping(value = "/findPublishPost"
            , produces = {APPLICATION_JSON_UTF8_VALUE}
            , method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public Object findPublishPost(HttpServletRequest request,
                                   @RequestParam(value = "pageNum", required = false)
                                          Integer pageNum,
                                   @RequestParam(value = "pageSize", required = false)
                                          Integer pageSize) throws Exception {
                                              return "这是返回的json数据";
                                          }

上面得代码我们可以得到json数据,为什么呢?

  1. 首先我们可以看到我们的RequestMapping多了produces和method。produces是对这个方法的综述,method是这个请求的访问方法,重要的是他们都是数组,也就是可以支持多种形式。
  2. ResponseBody这个注解是专门用来修饰某个方法,说明这个方法只返回ResponseBody(响应体)。
  3. RequestParam标记这个请求的参数,默认值是TRUE,除非单独表明required = false

这就完了吗?等等呢,还没完。为啥呢?我们想象一下,当我们要去找人的时候,是不是直接叫xx出来呢?既然这样,我现在需要从一个列表中拿到数据,是不是也应该这样呢?

    /**
     * RESTful风格的文章页面
     *
     * @param postId 文章ID
     * @return 返回文章页面
     */
    @RequestMapping(path = "/post/{postId}", method = RequestMethod.GET)
    public ModelAndView getPostView(@PathVariable int postId) {
        ModelAndView resultView = new ModelAndView("frontPost");
        resultView.addObject("framJson", getFramJson());
        resultView.addObject("postJson", getPostById(postId));
        return resultView;
    }

上线这段代码,我们最终可以看到一个文章详情页,而且重点的是“post/”后面的postId发生了变化,其他的都是对应着变化的。

  1. 首先在方法上面注解RequestMapping说明请求地址。
  2. path = "/post/{postId}" 说明url的形式是:/post/xxx。
  3. @PathVariable int postId说明这里参数postId和上面的{postId}的值相同。

说实话,到了上面这样子,其实大概东西都差不多了,不过需要注意的重点就是:web接口中的参数永远你都是不知道存不存在的,所以使用基本数据类型的参数都是不安全的,所以我们需要使用包装类型。

总结

Spring+SpringMvc类型的框架中,SpringMvc提供了web试图的填充和解析以及http请求的接收、处理和响应,所以我们需要先配置web相关设置,后面才能进行web相关的开发。


最后扯犊子一下,关于中国人、乐天、萨德和韩国我觉得尊重中国人,保护私有财产,韩国棒子滚一边去。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容