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

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

项目github地址:github.com/pc859107393…

实时项目同步的地址是国内的码云:git.oschina.net/859107393/m…

我的简书首页是:www.jianshu.com/users/86b79…

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

行走的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相关的开发。


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值