• 从头开始基于Maven搭建SpringMVC+Mybatis项目(4)


    接上文内容,上一节中的示例中完成了支持分页的商品列表查询功能,不过我们的目标是打造一个商品管理后台,本节中还需要补充添加、修改、删除商品的功能,这些功能依靠Mybatis操作数据库,并通过SpringMVC的数据验证功能检查数据合法性。既然是后台,那么肯定还需要验证和登录,这部分使用拦截器(interceptor)来实现。此外,我们还需要解决诸如中文处理、静态资源过滤等经常会造成麻烦的小问题。

    从头阅读传送门

    来看一下完成的效果,点击原商品列表功能/product/list,首先提示登录

    如果登录出错,做相应的提示

    登录成功后进入商品列表页面

    选择任意商品,点击修改,打开修改商品页面。名称和价格两个属性输入框存储原值以方便修改

    修改内容不符合规范则触发SpringMVC的验证提示

    点击退出则重回登录页。

    接下来叙述实现的主要环节,先回到petstore-persist模块,为商品管理增加插入(insert)、更新(update)、删除(delete)三个方法。

    ProductMapper.Java

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. void addProduct(Product product);  
    2.   
    3. void updateProduct(Product product);  
    4.   
    5. void deleteProduct(int id);  

    在Product.xml中增加匹配三个方法的SQL映射:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <insert id="addProduct" parameterType="com.example.petstore.persist.model.Product"  
    2.     useGeneratedKeys="true" keyProperty="id">  
    3.     insert into t_product(p_name,p_price) values(#{name},#{price})  
    4. </insert>  
    5.   
    6. <update id="updateProduct" parameterType="com.example.petstore.persist.model.Product">  
    7.     update t_product set  
    8.     p_name=#{name},p_price=#{price} where p_id=#{id}  
    9. </update>  
    10.   
    11. <delete id="deleteProduct" parameterType="int">  
    12.     delete from t_product where p_id=#{id}  
    13. </delete>  

    下面切换到petstore-web模块,先添加一个拦截器检查用户是否登录。SpringMVC的拦截器可以看作是Controller的守门警卫,其定义的preHandle方法和postHandle方法分别守在Controller的进出口,可以拦截住进出的客人(Request)做诸如登记、检查、对输出信息再处理等操作。我们这里的拦截器非常简单,在preHandle中检查用户的Session中是否携带登录信息,检查通过则进入目标页面,否则重新分派到登录页面。

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. public class AuthorityInterceptor implements HandlerInterceptor {  
    2.   
    3.     @Override  
    4.     public void afterCompletion(HttpServletRequest request,  
    5.             HttpServletResponse response, Object handler, Exception ex) {  
    6.     }  
    7.   
    8.     @Override  
    9.     public void postHandle(HttpServletRequest request, HttpServletResponse response,  
    10.             Object handler, ModelAndView modelAndView) {  
    11.     }  
    12.   
    13.     @Override  
    14.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response,  
    15.             Object handler) throws Exception {  
    16.         String backUrl = request.getServletPath().toString();  
    17.         String sessionName = "adminUser";  
    18.         String currentUser = (String)request.getSession(true).getAttribute(sessionName);  
    19.         if(currentUser != null && currentUser.equals("admin")) {  
    20.             return true;  
    21.         }  
    22.         response.sendRedirect(request.getContextPath() + "/admin/toLogin?backUrl=" + backUrl);  
    23.         return false;  
    24.     }  
    25. }  

    在配置文件中启用拦截器,本例中是spring-mvc.xml

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <mvc:interceptors>  
    2.     <mvc:interceptor>  
    3.         <mvc:mapping path="/**" />  
    4.         <mvc:exclude-mapping path="/admin/**" />  
    5.         <mvc:exclude-mapping path="/css/**" />  
    6.         <mvc:exclude-mapping path="/js/**" />  
    7.         <bean class="com.example.petstore.web.interceptor.AuthorityInterceptor "/>  
    8.     </mvc:interceptor>  
    9. </mvc:interceptors>  

    配置文件中除了指定拦截器的完整类名外,还设置了拦截规则,这里设置为拦截所有请求,但设置了三个例外。

    /admin/目录下的请求为登录相关处理,例如登录页面,这些需要能被未登录的用户访问。/css/和/js/目录下为静态文件,不应该被拦截。而且,静态文件也不应该交给SpringMVC的前置控制器DispatcherServlet处理,这样会造成不必要的性能损耗。所以还应该在spring-mvc.xml中加入:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <mvc:resources mapping="/css/**" location="/css/"/>  
    2. <mvc:resources mapping="/js/**" location="/js/"/>  

    这是告知SpringMVC,不要处理对这两个目录的请求。

    (注:处理静态文件的访问应该交给Nginx或Apache等Web服务器,如果仅使用Tomcat等Java容器更好的方式是通过在web.xml中设置servlet-mapping来处理。这里只是演示SpringMVC功能)

    被拦截的请求交给登录控制器AdminController处理

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @Controller  
    2. @SessionAttributes("adminUser")  
    3. @RequestMapping("/admin")  
    4. public class AdminController {  
    5.   
    6.     @RequestMapping(value="/toLogin")  
    7.     public String toLogin(@ModelAttribute("adminModel") AdminModel adminModel) {  
    8.         return "authority/login";  
    9.     }  
    10.       
    11.     @RequestMapping(value="/login", method = {RequestMethod.GET, RequestMethod.POST})  
    12.     public String login(Model model, @ModelAttribute("adminModel") AdminModel adminModel, @RequestParam("backUrl") String backUrl) throws IOException {  
    13.           
    14.         boolean valid = true;  
    15.         String message = null;  
    16.         if(adminModel == null) {  
    17.             message = "非法操作";  
    18.             valid = false;  
    19.         } else if (!adminModel.getUsername().equals("admin")) {  
    20.             message = "用户名不存在";  
    21.             valid = false;  
    22.         } else if (!adminModel.getPassword().equals("123456")) {  
    23.             message = "密码不正确";  
    24.             valid = false;  
    25.         }  
    26.           
    27.         if(!valid) {  
    28.             ErrorModel errorModel = new ErrorModel();  
    29.             errorModel.setMessage(message);  
    30.             errorModel.setPage("返回上一页", "javascript:history.back();");  
    31.             model.addAttribute("errorModel", errorModel);  
    32.             return "comm/error";  
    33.         } else {  
    34.             model.addAttribute("adminUser", adminModel.getUsername());  
    35.             if(StringUtils.isBlank(backUrl)) {  
    36.                 return "redirect:/product/list";  
    37.             } else {  
    38.                 return "redirect:" + backUrl;  
    39.             }  
    40.         }  
    41.     }  
    42.       
    43.     @RequestMapping(value="/logout")  
    44.     public String logout(ModelMap modelMap, SessionStatus sessionStatus, @ModelAttribute("adminModel") AdminModel adminModel) throws IOException {  
    45.         sessionStatus.setComplete();  
    46.         return "authority/login";  
    47.     }  
    48. }  

    AdminController定义了三个方法,toLogin直接返回登录视图,即登录页。logout先删除Session中的登录信息,再返回登录视图,这时用户重归未登录状态。login方法处理登录页中提交的登录请求,登录和授权不在本文介绍范围,这里只演示性的检查了用户名和密码为指定值则通过登录。注意这个方法中并没有直接操作Session,帮我们完成工作的是类名上的@SessionAttributes注解,对于它声明的属性名,在发生向ModelMap中写入时会转存到Session中,并一直有效直到调用SessionStatue.setComplete。

    登录成功后就可以执行增加、删除和修改的操作了。先看删除,通过ajax方式调用Controller接口处理:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <td><a href="javascript:void(0);" onclick="javascript:delProduct(${item.id});">删除</a></td>  
    [javascript] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <script type="text/javascript">  
    2. function delProduct(id) {  
    3.     if(!window.confirm("确定要删除吗?")) {  
    4.         return false;  
    5.     }  
    6.       
    7.     $.ajax({  
    8.         data:"id=" + id,  
    9.         type:"GET",  
    10.         dataType: 'json',  
    11.         url:"<c:url value='/product/delete'/>",  
    12.         error:function(data){  
    13.             alert("删除失败");  
    14.         },  
    15.         success:function(data){  
    16.             if(data.code > 0) {  
    17.                 alert("删除成功");  
    18.                 document.forms[0].submit();  
    19.             } else {  
    20.                 alert("删除失败");  
    21.             }  
    22.         }  
    23.     });  
    24. }  
    25. </script>  
    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @RequestMapping(value="/delete", method = {RequestMethod.GET})  
    2. @ResponseBody  
    3. public Map<String, String> delete(@RequestParam(value="id") int id) throws IOException {  
    4.     this.productService.deleteProduct(id);  
    5.     Map<String, String> map = new HashMap<String, String>(1);  
    6.     map.put("code", "1");  
    7.     return map;  
    8. }  

    添加和删除操作导向同一个JSP处理,通过是否携带id参数区分

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <a href="<c:url value='/product/toAddOrUpdate'/>">添加新商品</a>  
    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <a href="<c:url value='/product/toAddOrUpdate'/>?id=${item.id}">修改</a>  
    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @RequestMapping(value="/toAddOrUpdate", method = {RequestMethod.GET})  
    2. public String toAddOrUpdate(Model model, @RequestParam(value="id", defaultValue="0") int id) {  
    3.       
    4.     if(id > 0) {  
    5.         Product product = this.productService.selectById(id);  
    6.         if(product != null) {  
    7.             model.addAttribute("productModel", product);  
    8.         } else {  
    9.             ErrorModel errorModel = new ErrorModel();  
    10.             errorModel.setMessage("商品不存在或已下架");  
    11.             Map<String, String> pages = new HashMap<String, String>();  
    12.             pages.put("返回上一页", "javascript:history.back();");  
    13.             errorModel.setPages(pages);  
    14.             model.addAttribute("errorModel", errorModel);  
    15.             return "comm/error";  
    16.         }  
    17.     } else {  
    18.         model.addAttribute("productModel", new Product());  
    19.     }  
    20.     return "product/addOrUpdate";  
    21. }  

    如果请求中带有id参数,则预读出商品信息并存入ModelMap,在修改页面中显示这些信息方便用户浏览和修改。

    添加/修改商品页面:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <form:form action="${pageContext.request.contextPath}/product/addOrUpdate" method="POST" modelAttribute="productModel">  
    2.     <c:if test="${productModel.id==0}">  
    3.     添加新商品  
    4.     </c:if>  
    5.     <c:if test="${productModel.id!=0}">  
    6.     修改商品信息, 商品ID: ${productModel.id}  
    7.     </c:if>  
    8.     <div></div>  
    9.     <form:hidden path="id" />  
    10.     <div>商品名称: <form:input path="name" autocomplete="off" placeholder="商品名称" /><form:errors path="name" cssClass="error" /></div>  
    11.     <div>商品价格: <form:input path="price" autocomplete="off" placeholder="商品价格" /><form:errors path="price" cssClass="error" /></div>  
    12.     <div><button type="submit">提交</button></div>  
    13. </form:form>  

    在用户提交商品信息时,通常我们希望做一下检查以避免用户提交了不符合规定的内容。SpringMVC对服务器端验证提供了优秀的支持方案。我们来看对商品名称的检查:

    第一步,在addOrUpdate.jsp中添加<form:errors path="name" cssClass="error" />用于显示错误提示信息。

    第二步,修改Model类Product.java,在name属性上添加注解@Pattern(regexp="[\u4e00-\u9fa5]{4,30}"),即限定为4至30个中文字符。

    第三步,在Controller中检查BindingResult的实例是否包含错误:

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @RequestMapping(value="/addOrUpdate", method = {RequestMethod.POST})  
    2. public String addOrUpdate(Model model, @Valid @ModelAttribute("productModel") Product productModel, BindingResult bindingResult) {  
    3.       
    4.     if(bindingResult.hasErrors()) {  
    5.         return "product/addOrUpdate";  
    6.     }  
    7.       
    8.     int id = productModel.getId();  
    9.     if(id > 0) {  
    10.         this.productService.updateProduct(productModel);  
    11.     } else {  
    12.         this.productService.addProduct(productModel);  
    13.     }  
    14.     return list(model, new SearchModel(), PagingList.DEFAULT_PAGE_INDEX, PagingList.DEFAULT_PAGE_SIZE);  
    15. }  

    做了这些还不够,还需要hibernate的帮助,增加一个依赖

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <dependency>  
    2.     <groupId>org.hibernate</groupId>  
    3.     <artifactId>hibernate-validator</artifactId>  
    4.     <version>5.2.4.Final</version>  
    5. </dependency>  

    到这里主要的代码就完成了,不过还要处理一下烦人的中文字符乱码问题,修改web.xml,添加:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <filter>  
    2.     <filter-name>encodingFilter</filter-name>  
    3.     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
    4.     <init-param>  
    5.         <param-name>encoding</param-name>  
    6.         <param-value>UTF-8</param-value>  
    7.     </init-param>  
    8. </filter>  
    9. <filter-mapping>  
    10.     <filter-name>encodingFilter</filter-name>  
    11.     <url-pattern>/*</url-pattern>  
    12. </filter-mapping>  

    最后,介绍一下使用Maven的jetty-maven-plugin插件来测试项目,为此,还要稍稍改造一下petstore-persist模块。把src/main/java/com/example/petstore/persist/model/Product.xml移动到资源目录,即 src/main/resources/com/example/petstore/persist/model/Product.xml

    修改Maven配置文件,如D:Mavenconfsettings.xml,添加

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <pluginGroups>  
    2.   <pluginGroup>org.mortbay.jetty</pluginGroup>  
    3. </pluginGroups>  

    修改petstore-parent下的pom.xml,添加

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <build>  
    2.     <plugins>  
    3.         <plugin>  
    4.             <groupId>org.apache.maven.plugins</groupId>  
    5.             <artifactId>maven-compiler-plugin</artifactId>  
    6.             <version>2.3.2</version>  
    7.             <configuration>  
    8.                 <source>1.8</source>  
    9.                 <target>1.8</target>  
    10.                 <encoding>utf8</encoding>  
    11.             </configuration>  
    12.         </plugin>  
    13.     </plugins>  
    14. </build>  

    修改petstore-web下的pom.xml,添加

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. <build>  
    2.     <finalName>petstore-web</finalName>  
    3.     <plugins>  
    4.         <plugin>  
    5.             <groupId>org.mortbay.jetty</groupId>  
    6.             <artifactId>jetty-maven-plugin</artifactId>  
    7.             <version>8.1.16.v20140903</version>  
    8.             <configuration>  
    9.                 <scanIntervalSeconds>10</scanIntervalSeconds>  
    10.                 <webAppConfig>  
    11.                     <contextPath>/petstore</contextPath>  
    12.                 </webAppConfig>  
    13.             </configuration>  
    14.         </plugin>  
    15.     </plugins>  
    16. </build>  

    打开CMD命令窗口,切换工作目录都petstore-parent源码目录下,执行

    mvn clean install

    把petstore-persist模块安装到本地Maven仓库中,然后切换到petstore-web目录下,执行

    mvn jetty:run

    该命令启动jetty服务器并默认绑定8080端口,如果8080已被占用,可以指定其他端口

    mvn jetty:run -Djetty.port=9999

    启动成功后,打开浏览器输入 http://localhost:8080/petstore/index.jsp

    那么,你是否顺利看到站点首页了呢?

    本节源码下载

    (完)

  • 相关阅读:
    口语详解|为什么“how to say”是错的?
    9 tips to improve spoken english
    splash 安装
    ubuntu 安装NVIDIA驱动过程
    【Python数据分析】时间模块datetime
    【Python数据分析】Pandas模块下的Series与DataFrame
    【Python】文件
    博客园Markdown样式美化
    【Python】异常处理
    【Python】eval函数
  • 原文地址:https://www.cnblogs.com/sa-dan/p/6836978.html
Copyright © 2020-2023  润新知