手把手教会你如何玩转SpringMVC

一:SpringMVC处理流程



二:SpringMVC开发步骤

当然,首先都要记得导包哦!!!!!!!!

(1)SpringMVC中得前端配置器的配置

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">  
  3.   <display-name>SpringMvc</display-name>  
  4.   <welcome-file-list>  
  5.     <welcome-file>index.html</welcome-file>  
  6.     <welcome-file>index.htm</welcome-file>  
  7.     <welcome-file>index.jsp</welcome-file>  
  8.     <welcome-file>default.html</welcome-file>  
  9.     <welcome-file>default.htm</welcome-file>  
  10.     <welcome-file>default.jsp</welcome-file>  
  11.   </welcome-file-list>  
  12.     
  13.   <!-- 前端配置器 -->  
  14.   <servlet>  
  15.         <servlet-name>springmvc</servlet-name>  
  16.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  17.         <!-- 默认找 /WEB-INF/[servlet的名称]-servlet.xml -->  
  18.         <init-param>  
  19.             <param-name>contextConfigLocation</param-name>  
  20.             <param-value>classpath:springmvc.xml</param-value>  
  21.         </init-param>  
  22.   </servlet>  
  23.     
  24.   <servlet-mapping>  
  25.   <servlet-name>springmvc</servlet-name>  
  26.     <!--   
  27.         1. /*  拦截所有   jsp  js png .css  真的全拦截   建议不使用  
  28.         2. *.action *.do 拦截以do action 结尾的请求     肯定能使用   ERP    
  29.         3. /  拦截所有 (不包括jsp) (包含.js .png.css)  强烈建议使用     前台 面向消费者  www.jd.com/search   /对静态资源放行  
  30.      -->  
  31.     <url-pattern>*.action</url-pattern>  
  32.   </servlet-mapping>  
  33.     
  34. </web-app>  

(2)配置springmvc.xml

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xmlns:context="http://www.springframework.org/schema/context"  
  5.     xmlns:mvc="http://www.springframework.org/schema/mvc"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
  7.         http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd  
  8.         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">  
  9.    
  10.  <!-- 扫描@Controler  @Service-->  
  11.  <context:component-scan base-package="hnu.scw"></context:component-scan>  
  12.    
  13.    
  14.  </beans>  

(3)编写Controler类,进行页面控制

[html] view plain copy
  1. package hnu.scw.controler;  
  2.   
  3. import org.springframework.stereotype.Controller;  
  4. import org.springframework.web.bind.annotation.RequestMapping;  
  5. import org.springframework.web.servlet.ModelAndView;  
  6.   
  7. @Controller  
  8. public class SpringMvcControler {  
  9.   
  10.     @RequestMapping(value="/test/study1.action")  
  11.     public ModelAndView testControler1(){  
  12.         ModelAndView modelAndView = new ModelAndView();  
  13.         modelAndView.setViewName("/WEB-INF/jsp/itemList.jsp");  
  14.         return modelAndView;  
  15.     }  
  16. }  

这上面的步骤就是一个基本的springmvc开发模式了。

三:SpringMVC架构分析图



四:SpringMVC配置三大组件

[html] view plain copy
  1. <!-- 处理器映射器 ,其实有三种这里写了一种,其实后面都被注解被替换,所以了解就好了,知道就行-->  
  2. lt;!--         <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> -->  
  3.        <!-- 处理器适配器 -->  
  4. lt;!--         <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> -->  
  5.        <!-- 注解驱动 -->  
  6.        <mvc:annotation-driven/>  
  7.          
  8.        <!-- 视图解释器 -->  
  9.        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  10.         <property name="prefix" value="/WEB-INF/jsp/"/>  
  11.         <property name="suffix" value=".jsp"/>  
  12.        </bean>  

PS:(1)如果不进行配置也可以,也可以,因为SpringMVC架构有默认配置,但是只从3.1版之后,默认的就废弃了,所以配置一下最好。

(2)如果配置了注解驱动,那么处理器映射器和处理器适配器就可以不用配置

(3)视图解释器主要是为了节省在Controler的页面跳转,将相同的代码进行省略。比如有很多个都是要跳转到/WEB-INF/jsp/下面,那么就可以将这个部分提取出来。

五:SpringMVC整合Mybatis

其实这个很简单,如果看了我之前关于Mybatis文章的话,那么就很明白了。

这里所以我就不多说了,大体讲解一下步骤。

(1)将spring与Mybatis整合-----------在我的Mybatis的干货知识文章有已经讲解非常非常清楚了

(2)将SpringMVC整合到(1)中的框架中。--------------这个内容就是上面讲解的整合方式了。

六:Controler层参数绑定

(1)原始参数绑定

[html] view plain copy
  1. //去修改页面 入参 id  
  2.     @RequestMapping(value = "/itemEdit.action")  
  3. //  public ModelAndView toEdit(@RequestParam(value = "id",required = false,defaultValue = "1") Integer idaaq,  
  4.     public ModelAndView toEdit(HttpServletRequest request,HttpServletResponse response  
  5.             ,HttpSession session,Model model){  
  6.           
  7.         //Servlet时代开发  
  8.         String id = request.getParameter("id");       
  9.         //查询一个商品  
  10.         Items items = itemService.selectItemsById(Integer.parseInt(id));  
  11.         ModelAndView mav = new ModelAndView();  
  12.         //数据  
  13.         mav.addObject("item", items);  
  14.         mav.setViewName("editItem");  
  15.         return mav;  
  16.           
  17. }  

(2)绑定之简单类型

[html] view plain copy
  1. //去修改页面 入参 id  
  2.     @RequestMapping(value = "/itemEdit.action")  
  3. //  public ModelAndView toEdit(@RequestParam(value = "id",required = false,defaultValue = "1") Integer idaaq,  
  4.     public ModelAndView toEdit(Integer id ){  
  5.           
  6.         //查询一个商品  
  7.         Items items = itemService.selectItemsById(id);  
  8.         ModelAndView mav = new ModelAndView();  
  9.         //数据  
  10.         mav.addObject("item", items);  
  11.         mav.setViewName("editItem");  
  12.         return mav;  
  13.           
  14.     }  
PS:1)这个等价于上面最原始的方法绑定,但是这个就有一个要求,就是在形参中的名字必须和要接受的参数名字一样,这样才能够绑定成功。

    2)存在开发人员中,因为形参的名字和要接受的参数名字不一样,比如传过来的名字是id,但是形参又是设置为了idaao,所以这样的情况又怎么处理呢?其实也有处理的办法,就是使用@RequestParm来进行处理。这个情况在上面这个例子也已经进行说明了,其中还有required这个属性的意思就是,这个是否必须有。defaultValue属性的含义就是设置一个默认值。

注:这个实际都不是这样用,因为麻烦,花那么大的功夫就是为了接受参数,而还不如直接将形参的名字改成和传送的参数一样,这不是多么简单的处理方式嘛。所以这种不建议使用哦。。。

(3)绑定之实体类对象JavaBean

[html] view plain copy
  1. //提交修改页面 入参  为 Items对象    
  2.     @RequestMapping(value = "/updateitem.action")  
  3.     public ModelAndView updateitem(Items items){  
  4.         //修改  
  5.         itemService.updateItemsById(vo.getItems());  
  6.         ModelAndView mav = new ModelAndView();  
  7.         mav.setViewName("success");  
  8.         return mav;  
  9.           
  10.     }  

PS:必须保证实体类对象中的属性和传送过来要接受的参数名一样。。。。切记切记!!!!!!!!!!!!!!!

(4)绑定之包装JavaBean类对象

[html] view plain copy
  1. //提交修改页面 入参  为 Items对象    
  2.     @RequestMapping(value = "/updateitem.action")  
  3.     public ModelAndView updateitem(QueryVo vo){  
  4.   
  5.         //修改  
  6.         itemService.updateItemsById(vo.getItems());  
  7.           
  8.         ModelAndView mav = new ModelAndView();  
  9.         mav.setViewName("success");  
  10.         return mav;  
  11.           
  12.     }  
其中的QueryVo对象代码如下所示:
[html] view plain copy
  1. package com.itheima.springmvc.pojo;  
  2.   
  3. public class QueryVo {  
  4.   
  5.       
  6.     //商品  
  7.     private Items items;  
  8.   
  9.     public Items getItems() {  
  10.         return items;  
  11.     }  
  12.   
  13.     public void setItems(Items items) {  
  14.         this.items = items;  
  15.     }  
  16.       
  17. }  

(5)绑定参数之数组------适用于比如在页面中的checkbox组件中

[html] view plain copy
  1. //删除多个  
  2.     @RequestMapping(value = "/deletes.action")  
  3.     public ModelAndView deletes(Integer[] ids){  
  4.               
  5.         ModelAndView mav = new ModelAndView();  
  6.         mav.setViewName("success");  
  7.         return mav;  
  8.     }  

(6)绑定参数之List集合-----------适用于一次性修改多个相同类型格式的数据,比如更新多个片段

包装类代码:

[html] view plain copy
  1. package com.itheima.springmvc.pojo;  
  2.   
  3. import java.util.List;  
  4.   
  5. public class QueryVo {  
  6.       
  7.     private List<Items> itemsList;  
  8.       
  9.     public List<Items> getItemsList() {  
  10.         return itemsList;  
  11.     }  
  12.     public void setItemsList(List<Items> itemsList) {  
  13.         this.itemsList = itemsList;  
  14.     }     
  15. }  

controller层代码:

[html] view plain copy
  1. //修改  
  2.     @RequestMapping(value = "/updates.action",method = {RequestMethod.POST,RequestMethod.GET})  
  3.     public ModelAndView updates(QueryVo vo){  
  4.           
  5.           
  6.         ModelAndView mav = new ModelAndView();  
  7.         mav.setViewName("success");  
  8.         return mav;  
  9.     }  
PS:(1)对于List参数只能用包装类去接受,而不能想当然的在形参中用List<Items> itemlist,这样取接受,这个SpringMVC是不认识的。

(2)只是注意一点,在用的时候,就需要设置itemlist的下标,即itemlist[0]........,如下所示

[html] view plain copy
  1. <c:forEach items="${itemList }" var="item" varStatus="s">  
  2. <tr>  
  3.     <td><input type="checkbox" name="ids" value="${item.id }"></td>  
  4.     <td><input type="text" name="itemsList[${s.index}].name" value="${item.name }"></td>  
  5.     <td><input type="text" name="itemsList[${s.index }].price" value="${item.price }"></td>  
  6.     <td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>  
  7.     <td>${item.detail }</td>  
  8.       
  9.     <td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>  
  10.   
  11. </tr>  
  12. </c:forEach>  

(7)自定义参数转换

例子:1)在开发过程中,比如填写时间的格式的时候,本来是2017-09-11,然而填写成了2017:09:11,这样的话,在本身为Date类型的话,这样就会报错,所以为了处理这样的需求,就可以采取参数转换处理方法。

2)或者碰到某些固定的组件只能产生某种格式,但是这种格式又无法满足需求,这时候就可以通过参数转换方法将需要改变的格式进行修改,达到符合需求的作用。

步骤:(1)编写转换方法----也就是想进行参数转换的方式,即由什么格式转为什么格式的方法,

下面就以由yyyy:MM-dd HH_mm-ss的格式转为正常的Date的格式的方法

[html] view plain copy
  1. package com.itheima.springmvc.conversion;  
  2.   
  3. import java.text.DateFormat;  
  4. import java.text.SimpleDateFormat;  
  5. import java.util.Date;  
  6.   
  7. import org.springframework.core.convert.converter.Converter;  
  8.   
  9. /**  
  10.  * 转换日期类型的数据  
  11.  * S : 页面传递过来的类型  
  12.  * T : 转换后的类型  
  13.  * @author lx  
  14.  *  
  15.  */  
  16. public class DateConveter implements Converter<String, Date>{  
  17.   
  18.     public Date convert(String source) {  
  19.         // TODO Auto-generated method stub  
  20.         try {  
  21.             if(null != source){//2016:11-05 11_43-50  
  22.                 DateFormat df = new SimpleDateFormat("yyyy:MM-dd HH_mm-ss");  
  23.                 return df.parse(source);  
  24.             }  
  25.         } catch (Exception e) {  
  26.             // TODO: handle exception  
  27.         }  
  28.         return null;  
  29.     }  
  30.   
  31. }  

(2)配置spring.xml

[html] view plain copy
  1. <!-- 注解驱动 -->  
  2.         <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>  
  3.           
  4.         <!-- 配置Conveter转换器  转换工厂 (日期、去掉前后空格)。。 -->  
  5.         <bean id="conversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">  
  6.             <!-- 配置 多个转换器-->  
  7.             <property name="converters">  
  8.                 <list>  
  9.                     <bean class="com.itheima.springmvc.conversion.DateConveter"/>  
  10.                 </list>  
  11.             </property>  
  12.         </bean>  

七:SpringMVC与Struts2的区别

1、 springmvc的入口是一个servlet即前端控制器,而struts2入口是一个filter过滤器。

2、 springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例)struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。

3、 Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面。Jsp视图解析器默认使用jstl

八:配置web.xml设置post提交的乱码处理

[html] view plain copy
  1. <!-- 处理POST提交乱码问题 -->  
  2.   <filter>  
  3.     <filter-name>encoding</filter-name>  
  4.     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  5.     <init-param>  
  6.         <param-name>encoding</param-name>  
  7.         <param-value>UTF-8</param-value>  
  8.     </init-param>  
  9.   </filter>  
  10.     
  11.   <filter-mapping>  
  12.     <filter-name>encoding</filter-name>  
  13.     <url-pattern>*.action</url-pattern>  
  14.   </filter-mapping>  

九:@ResuletMapping的适用

(1) 限定GET方法

@RequestMapping(method = RequestMethod.GET)

如果通过POST访问则报错:

HTTP Status 405 - Request method 'POST' not supported

例如:

@RequestMapping(value ="itemList",method = RequestMethod.POST)

(2) 限定POST方法

@RequestMapping(method = RequestMethod.POST)

如果通过GET访问则报错:

HTTP Status 405 - Request method 'GET' not supported

(3) GETPOST都可以

@RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})

(4)限定Controller中的URL访问方法必须以某字符串开头

class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头

(5)为方法配置多个访问URL路径

value的值是数组,可以将多个url映射到同一个方法,比如:

[html] view plain copy
  1. @RequestMapping(value = {"/item/scw.action","/item/haha.action"})  

十:Controller层的返回值

(1)ModelAndView-----controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。

(2)String

效果一:逻辑视图名---------controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

[html] view plain copy
  1. //指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/itemList.jsp  
  2. return "itemList";  

效果二:Redirect重定向---Contrller方法返回字符串可以重定向到一个url地址

如下商品修改提交后重定向到商品编辑页面。

[html] view plain copy
  1. /**  
  2.  * 更新商品  
  3.  *   
  4.  * @param item  
  5.  * @return  
  6.  */  
  7. @RequestMapping("updateItem")  
  8. public String updateItemById(Item item) {  
  9.     // 更新商品  
  10.     this.itemService.updateItemById(item);  
  11.   
  12.     // 修改商品成功后,重定向到商品编辑页面  
  13.     // 重定向后浏览器地址栏变更为重定向的地址,  
  14.     // 重定向相当于执行了新的request和response,所以之前的请求参数都会丢失  
  15.     // 如果要指定请求参数,需要在重定向的url后面添加 ?itemId=1 这样的请求参数  
  16.     return "redirect:/itemEdit.action?itemId=" + item.getId();  
  17. }  

效果三:forward转发-------Controller方法执行后继续执行另一个Controller方法

如下商品修改提交后转向到商品修改页面,修改商品的id参数可以带到商品修改方法中。

[html] view plain copy
  1. /**  
  2.  * 更新商品  
  3.  *   
  4.  * @param item  
  5.  * @return  
  6.  */  
  7. @RequestMapping("updateItem")  
  8. public String updateItemById(Item item) {  
  9.     // 更新商品  
  10.     this.itemService.updateItemById(item);  
  11.   
  12.     // 修改商品成功后,重定向到商品编辑页面  
  13.     // 重定向后浏览器地址栏变更为重定向的地址,  
  14.     // 重定向相当于执行了新的request和response,所以之前的请求参数都会丢失  
  15.     // 如果要指定请求参数,需要在重定向的url后面添加 ?itemId=1 这样的请求参数  
  16.     // return "redirect:/itemEdit.action?itemId=" + item.getId();  
  17.   
  18.     // 修改商品成功后,继续执行另一个方法  
  19.     // 使用转发的方式实现。转发后浏览器地址栏还是原来的请求地址,  
  20.     // 转发并没有执行新的request和response,所以之前的请求参数都存在  
  21.     return "forward:/itemEdit.action";  
  22.   
  23. }  
  24. //结果转发到editItem.action,request可以带过去  
  25. return "forward: /itemEdit.action";  

(3)void

Controller方法形参上可以定义requestresponse,使用requestresponse指定响应结果:

1、使用request转发页面,如下:

request.getRequestDispatcher("页面路径").forward(request, response);

request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request,response);

2、可以通过response页面重定向:

response.sendRedirect("url")

response.sendRedirect("/springmvc-web2/itemEdit.action");

3、可以通过response指定响应结果,例如响应json数据如下:

response.getWriter().print("{\"abc\":123}");

代码测试:
[html] view plain copy
  1. /**  
  2.  * 返回void测试  
  3.  *   
  4.  * @param request  
  5.  * @param response  
  6.  * @throws Exception  
  7.  */  
  8. @RequestMapping("queryItem")  
  9. public void queryItem(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  10.     // 1 使用request进行转发  
  11.     // request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request,  
  12.     // response);  
  13.   
  14.     // 2 使用response进行重定向到编辑页面  
  15.     // response.sendRedirect("/springmvc-web2/itemEdit.action");  
  16.   
  17.     // 3 使用response直接显示  
  18.     response.getWriter().print("{\"abc\":123}");  
  19. }  

总结:针对三种不同的返回值,适用在不同的情况中:

对于ModelAndeView类型,是最无敌的,因为可以很方便的即返回数据,又返回视图。。

对于String类型,是企业和官方最推荐的,其主要是返回视图,model带数据,并且实现解耦形式。

对于void类型,其不返回视图,那么就很明显的适用于Ajax进行请求,返回Jsao格式数据足够达到了效果。

十一:异常处理器

处理流程:


编写异常处理器类,即继承HandlerExceptionResolver类。

[html] view plain copy
  1. package com.itheima.springmvc.exception;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. import org.springframework.web.servlet.HandlerExceptionResolver;  
  7. import org.springframework.web.servlet.ModelAndView;  
  8.   
  9. /**  
  10.  * 异常处理器的自定义的实现类  
  11.  * @author lx  
  12.  *  
  13.  */  
  14. public class CustomExceptionResolver implements HandlerExceptionResolver{  
  15.   
  16.     public ModelAndView resolveException(HttpServletRequest request,   
  17.             HttpServletResponse response, Object obj,  
  18.             Exception e) {  
  19.         // TODO Auto-generated method stub  发生异常的地方 Serivce层  方法  包名+类名+方法名(形参) 字符串  
  20.         //日志    1.发布 tomcat war  Eclipse  2.发布Tomcat  服务器上  Linux  Log4j  
  21.           
  22.         ModelAndView mav = new ModelAndView();        
  23.         mav.addObject("error", "未知异常");  
  24.         mav.setViewName("error");  
  25.         return mav;  
  26.     }  
  27. }  
将异常处理配置到springmvc.xml中
[html] view plain copy
  1. <!-- Springmvc的异常处理器 -->  
  2.          <bean class="com.itheima.springmvc.exception.CustomExceptionResolver"/>   
另外,当发生某种自己能够预期的异常的话,那么就需要更友好的提示了,所以还可以自定义异常。

自定义异常代码:(例子)

[html] view plain copy
  1. package com.itheima.springmvc.exception;  
  2.   
  3. public class MessageException extends Exception{      
  4.     private String msg;  
  5.     public MessageException(String msg) {  
  6.         super();  
  7.         this.msg = msg;  
  8.     }  
  9.   
  10.     public String getMsg() {  
  11.         return msg;  
  12.     }  
  13.   
  14.     public void setMsg(String msg) {  
  15.         this.msg = msg;  
  16.     }     
  17. }  
所以对上面的程序改进就是,可以变成如下的形式,即可以判断是属于预见异常还是运行时异常了,达到更好的一种异常处理。
[html] view plain copy
  1. package com.itheima.springmvc.exception;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. import org.springframework.web.servlet.HandlerExceptionResolver;  
  7. import org.springframework.web.servlet.ModelAndView;  
  8.   
  9. /**  
  10.  * 异常处理器的自定义的实现类  
  11.  * @author lx  
  12.  *  
  13.  */  
  14. public class CustomExceptionResolver implements HandlerExceptionResolver{  
  15.   
  16.     public ModelAndView resolveException(HttpServletRequest request,   
  17.             HttpServletResponse response, Object obj,  
  18.             Exception e) {  
  19.         // TODO Auto-generated method stub  发生异常的地方 Serivce层  方法  包名+类名+方法名(形参) 字符串  
  20.         //日志    1.发布 tomcat war  Eclipse  2.发布Tomcat  服务器上  Linux  Log4j  
  21.           
  22.         ModelAndView mav = new ModelAndView();  
  23.         //判断异常为类型  
  24.         if(e instanceof MessageException){  
  25.             //预期异常  
  26.             MessageException me = (MessageException)e;  
  27.             mav.addObject("error", me.getMsg());  
  28.         }else{  
  29.             mav.addObject("error", "未知异常");  
  30.         }  
  31.         mav.setViewName("error");  
  32.         return mav;  
  33.     }  
  34.   
  35. }  

十二:springmvc与json数据交互

这里要提到的一个知识点就是,对json字符串的接受与发送。。我想这个是很常见的一个开发吧。比如在ajax中发送一个json字符串过来,那么到Controller层就需要对json串进行自动解析并且封装到实体类对象类。。既然用了springmvc当然是已经帮我们实现好了的。

首先,先讲解一下什么叫json字符串,注意是字符串。。

[html] view plain copy
  1. {  
  2. "id": 1,  
  3. "name": "测试商品",  
  4. "price": 99.9,  
  5. "detail": "测试商品描述",  
  6. "pic": "123456.jpg"  
  7. }  
这个是json格式的数据,然而并不是json字符串,请特别的注意。。。特别的注意。。。

而上面的json格式对应的json字符串应该是这样的,
var params = '{"id": 1,"name": "测试商品","price": 99.9,"detail": "测试商品描述","pic": "123456.jpg"}';

对了,他们之间的差别就是外层多了两个单引号,所以这就是为什么我说的是字符串!!!!!!!!!!!!

进入正题:

首先要记住导包


比如有个ajax放松json字符串给Controller层,

[html] view plain copy
  1. $.ajax({  
  2.         url : "${pageContext.request.contextPath }/json.action",  
  3.         data : params,  
  4.         contentType : "application/json;charset=UTF-8",//发送数据的格式  
  5.         type : "post",  
  6.         dataType : "json",//回调  
  7.         success : function(data){  
  8.             alert(data.name);  
  9.         }  
  10.           
  11.     });  

PS:如果ajax要发送json字符串,那么就必须知名contentType属性

Controller层代码:

[html] view plain copy
  1. //json数据交互  
  2.     @RequestMapping(value = "/json.action")  
  3.         @ResponseBody  
  4.     public Items json(@RequestBody Items items){  
  5.           
  6.         System.out.println(items);  
  7.           
  8.         return items;  
  9.     }  
PS:(1)是的,就是这么简单,如果要接受json字符串将其转为实体类,那么用@RequestBody放在形参前面就可以让springmvc自动处理了哦!
(2)如果要发送的也是json格式的字符串,那么就用@ResponseBody在方法前面进行注解就可以了。。。

框架毕竟是框架,省去了很多事情,要不然还要自己进行拼接,麻烦很多。。。

十三:springmvc架构上传图片或者文件(用案例来分析)

首先看看jsp的代码(非常简单的就写了点):

[html] view plain copy
  1. <!-- 上传图片是需要指定属性 enctype="multipart/form-data" -->  
  2.     <!-- <form id="itemForm" action="" method="post" enctype="multipart/form-data"> -->  
  3.     <form id="itemForm"  action="${pageContext.request.contextPath }/updateitem.action" method="post" enctype="multipart/form-data">  
  4.         <input type="hidden" name="items.id" value="${item.id }" /> 修改商品信息:  
  5.         <table width="100%" border=1>           
  6.             <tr>  
  7.                 <td>商品图片</td>  
  8.                 <td>  
  9.                     <c:if test="${item.pic !=null}">  
  10.                         <img src="/pic/${item.pic}" width=100 height=100/>  
  11.                         <br/>  
  12.                     </c:if>  
  13.                     <input type="file"  name="pictureFile"/>   
  14.                 </td>  
  15.             </tr>  
  16.                 <td colspan="2" align="center"><input type="submit" value="提交" />  
  17.                 </td>  
  18.             </tr>  
  19.         </table>  
  20.     </form>  
接下来重中之中,就是Controller层的代码了:(关于如何接受图片或者文件都一样)
[html] view plain copy
  1. //提交修改页面 入参  为 Items对象    
  2.     @RequestMapping(value = "/updateitem.action")  
  3.     public String updateitem(QueryVo vo,MultipartFile pictureFile) throws Exception{  
  4.         //生成唯一内容的名字,因为可能用户上传的图片或者文件的名字相同,那么就会发生覆盖,这样是不符合的   
  5.         String name = UUID.randomUUID().toString().replaceAll("-", "");  
  6.         //获取上传内容的后缀,比如图片就是jpg,png,文件就可能是txt,表格就可以是et,xls  
  7.         String ext = FilenameUtils.getExtension(pictureFile.getOriginalFilename());  
  8.         //进行保存  
  9.         pictureFile.transferTo(new File("D:\\upload\\" + name + "." + ext));  
  10.         //封装类中设置图片的名字  
  11.         vo.getItems().setPic(name + "." + ext);  
  12.         //修改  
  13.         itemService.updateItemsById(vo.getItems());  
  14.   
  15.         return "redirect:/itemEdit.action?id=" + vo.getItems().getId();       
  16.     }  
PS: 这里关键点在于(1)参数接受中,一定要用MultiparFile 接口类型,然后名字和jsp中的file的名字一样,当然如果不一样,那么就用@MappingParem进行修改,之前说过的。

(2)在jsp中,对于只要存在 文件,那么就需要在form中写入entype=”multipart/form-data“
还有就是要在springmvc.xml中进行配置这个属性内容:

[html] view plain copy
  1. <!-- 上传图片配置实现类 -->  
  2.        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
  3.         <!-- 上传图片的大小   B   5M  1*1024*1024*5-->  
  4.         <property name="maxUploadSize" value="5000000"/>  
  5.        </bean>  

切记:这个id和class是不可以更改的,都必须按照上面进行配置

(3)上面的方法还可以使用如下的代码也可以实现上传:

[java] view plain copy
  1. <span style="font-family:SimSun;"><span style="color:#ff0000;"> </span>//提交修改页面 入参  为 Items对象    
  2.     @RequestMapping(value = "/updateitem.action")  
  3.     public String updateitem(QueryVo vo,MultipartFile pictureFile) throws Exception{  
  4.         //生成唯一内容的名字,因为可能用户上传的图片或者文件的名字相同,那么就会发生覆盖,这样是不符合的   
  5.         String name = UUID.randomUUID().toString().replaceAll("-""");  
  6.         //获取上传内容的后缀,比如图片就是jpg,png,文件就可能是txt,表格就可以是et,xls  
  7.         String ext = FilenameUtils.getExtension(pictureFile.getOriginalFilename());  
  8.         //进行保存  
  9.         FileUtils.writeByteArrayToFile(new File("D:\\upload\\" + name + "." + ext) , pictureFile.getBytes());<span style="color:#ff0000;"> //不同上面方法的地方</span>  
  10.         //封装类中设置图片的名字  
  11.         vo.getItems().setPic(name + "." + ext);  
  12.         //修改  
  13.         itemService.updateItemsById(vo.getItems());  
  14.   
  15.         return "redirect:/itemEdit.action?id=" + vo.getItems().getId();       
  16.     }</span>  

十四:RestFul风格(了解就可以)

适用情况:比如又URL------localhost:8080/hnucw/update.action?id=2,即进行对id=2的内容进行修改

想换成localhost:8080/hnucw/update/2.action这样的访问地址。

这样的好处主要是外国都是这样的形式,并且京东,淘宝,csdn都有这样的用户,这样的好处在于,看地址的话好看些,不用带太长的地址

开发方法:(就是用@pathVariable进行接受就可以了,还有就是url对应的内容要用{}进行括号起来,其他都一样)

[html] view plain copy
  1. //RestFul风格的开发  
  2.     @RequestMapping(value = "/itemEdit/{id}.action")  
  3.     public ModelAndView toEdit1(@PathVariable Integer id){  
  4.         Items items = itemService.selectItemsById(id);  
  5.         ModelAndView mav = new ModelAndView();  
  6.         //数据  
  7.         mav.addObject("item", items);  
  8.         mav.setViewName("editItem");  
  9.         return mav;  
  10.           
  11.     }  

十五:拦截器

步骤:(1)springmvc中配置拦截器

[html] view plain copy
  1. <!-- SPringmvc的拦截器  可以配置多个-->  
  2.         <mvc:interceptors>  
  3.             <!-- 多个拦截器 -->  
  4.             <mvc:interceptor>  
  5.                 <mvc:mapping path="/**"/>  
  6.                 <!-- 自定义的拦截器类 -->  
  7.                 <bean class="自定义拦截器类"/>  
  8.             </mvc:interceptor>  
  9.             <mvc:interceptor>  
  10.                 <mvc:mapping path="/**"/>  
  11.                 自定义的拦截器类  
  12.                 <bean class="自定义拦截器类"/>  
  13.             </mvc:interceptor>   
  14.         </mvc:interceptors>  

(2)编写拦截器

[html] view plain copy
  1. package com.itheima.springmvc.interceptor;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. import org.springframework.web.servlet.HandlerInterceptor;  
  7. import org.springframework.web.servlet.ModelAndView;  
  8.   
  9. public class Interceptor1 implements HandlerInterceptor{  
  10.   
  11.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {  
  12.         system。out.println("方法前")  
  13.         return true;  
  14.     }  
  15.     public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)  
  16.             throws Exception {  
  17.         // TODO Auto-generated method stub  
  18.         System.out.println("方法后 1");  
  19.           
  20.     }  
  21.     public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)  
  22.             throws Exception {  
  23.         // TODO Auto-generated method stub  
  24.         System.out.println("页面渲染后 1");        
  25.     }  
  26. }  
PS:拦截器分三个方法,拦截器方法处理前,拦截器方法后,页面渲染后方法。

如果配置了多个拦截器,那么就有以下的执行顺序。。

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用

afterCompletion只有preHandle返回true才调用

拦截器的实例应用:(拦截未登录的用户,只有当用户是登录的时候,才能访问其他页面)

jsp页面:

[html] view plain copy
  1. <form action="${pageContext.request.contextPath }/login.action" method="post">  
  2.     用户名:<input type="text" name="username" value="safdsdafas">  
  3.     <input type="submit" value="提交">  
  4. </form>  
编写拦截器:
[html] view plain copy
  1. package com.itheima.springmvc.interceptor;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. import org.springframework.web.servlet.HandlerInterceptor;  
  7. import org.springframework.web.servlet.ModelAndView;  
  8.   
  9. public class Interceptor1 implements HandlerInterceptor{  
  10.   
  11.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {  
  12.         // TODO Auto-generated method stub  
  13.         System.out.println("方法前 1");  
  14.         //判断用户是否登陆  如果没有登陆  重定向到登陆页面   不放行   如果登陆了  就放行了  
  15.         // URL  http://localhost:8080/springmvc-mybatis/login.action  
  16.         //URI /login.action  
  17.         String requestURI = request.getRequestURI();  
  18.         if(!requestURI.contains("/login")){  
  19.             String username = (String) request.getSession().getAttribute("USER_SESSION");  
  20.             if(null == username){  
  21.                 response.sendRedirect(request.getContextPath() + "/login.action");  
  22.                 return false;  
  23.             }  
  24.         }  
  25.         return true;  
  26.     }  
  27.     public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)  
  28.             throws Exception {  
  29.         // TODO Auto-generated method stub  
  30.         System.out.println("方法后 1");  
  31.           
  32.     }  
  33.     public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)  
  34.             throws Exception {  
  35.         // TODO Auto-generated method stub  
  36.         System.out.println("页面渲染后 1");  
  37.           
  38.     }  
  39.   
  40. }  

编写拦截器配置信息springmvc:

[html] view plain copy
  1. <!-- SPringmvc的拦截器 -->  
  2.         <mvc:interceptors>  
  3.             <!-- 多个拦截器 -->  
  4.             <mvc:interceptor>  
  5.                 <mvc:mapping path="/**"/>  
  6.                 <!-- 自定义的拦截器类 -->  
  7.                 <bean class="com.itheima.springmvc.interceptor.Interceptor1"/>  
  8.             </mvc:interceptor>  
  9.         </mvc:interceptors>  
编写Ctroller层:
[html] view plain copy
  1. //去登陆的页面  
  2.     @RequestMapping(value = "/login.action",method = RequestMethod.GET)  
  3.     public String login(){  
  4.         return "login";  
  5.     }  
  6.     @RequestMapping(value = "/login.action",method = RequestMethod.POST)  
  7.     public String login(String username  
  8.             ,HttpSession httpSession){  
  9.         httpSession.setAttribute("USER_SESSION", username);  
  10.         return "redirect:/item/itemlist.action";  
  11.     }  

OK,这样就是整个的拦截的处理过程了哦。。这样的话就能实现如果没有登录的话,那么就需要返回到登录页面,当登录之后,才能返回其他的页面。这个例子虽然小,但是在很多的系统都有这样的处理的哦。。。而且更主要的是掌握拦截器的用法。

十六:获取一个唯一的32位的标识方法(这个在很多地方都有使用,所以这里提一下)

这里通过UUID的方法(这个是Java  JDK中自带的一个方法,但是很少人会用)

[html] view plain copy
  1. UUID.randomUUID().toString().replace("-", "")  

十七:自定义一个万能的controller接受JSP传过来的对象(这样的好处在于,不用对于不同JSP传送过来的参数进行封装多个POJO对象了哦!!是不是很方便)

(1)自定义封装对象

[html] view plain copy
  1. package com.mbfw.util;  
  2.   
  3. import java.util.Collection;  
  4. import java.util.HashMap;  
  5. import java.util.Iterator;  
  6. import java.util.Map;  
  7. import java.util.Set;  
  8.   
  9. import javax.servlet.http.HttpServletRequest;  
  10.   
  11. public class PageData extends HashMap implements Map {  
  12.   
  13.     private static final long serialVersionUID = 1L;  
  14.   
  15.     Map map = null;  
  16.     HttpServletRequest request;  
  17.   
  18.     public PageData(HttpServletRequest request) {  
  19.         this.request = request;  
  20.         Map properties = request.getParameterMap();  
  21.         Map returnMap = new HashMap();  
  22.         Iterator entries = properties.entrySet().iterator();  
  23.         Map.Entry entry;  
  24.         String name = "";  
  25.         String value = "";  
  26.         while (entries.hasNext()) {  
  27.             entry = (Map.Entry) entries.next();  
  28.             name = (String) entry.getKey();  
  29.             Object valueObj = entry.getValue();  
  30.             if (null == valueObj) {  
  31.                 value = "";  
  32.             } else if (valueObj instanceof String[]) {  
  33.                 String[] values = (String[]) valueObj;  
  34.                 for (int i = 0; i < values.length; i++) {  
  35.                     value = values[i] + ",";  
  36.                 }  
  37.                 value = value.substring(0, value.length() - 1);  
  38.             } else {  
  39.                 value = valueObj.toString();  
  40.             }  
  41.             returnMap.put(name, value);  
  42.         }  
  43.         map = returnMap;  
  44.     }  
  45.   
  46.     public PageData() {  
  47.         map = new HashMap();  
  48.     }  
  49.   
  50.     @Override  
  51.     public Object get(Object key) {  
  52.         Object obj = null;  
  53.         if (map.get(key) instanceof Object[]) {  
  54.             Object[] arr = (Object[]) map.get(key);  
  55.             obj = request == null ? arr : (request.getParameter((String) key) == null ? arr : arr[0]);  
  56.         } else {  
  57.             obj = map.get(key);  
  58.         }  
  59.         return obj;  
  60.     }  
  61.   
  62.     public String getString(Object key) {  
  63.         return (String) get(key);  
  64.     }  
  65.   
  66.     @SuppressWarnings("unchecked")  
  67.     @Override  
  68.     public Object put(Object key, Object value) {  
  69.         return map.put(key, value);  
  70.     }  
  71.   
  72.     @Override  
  73.     public Object remove(Object key) {  
  74.         return map.remove(key);  
  75.     }  
  76.   
  77.     public void clear() {  
  78.         map.clear();  
  79.     }  
  80.   
  81.     public boolean containsKey(Object key) {  
  82.         // TODO Auto-generated method stub  
  83.         return map.containsKey(key);  
  84.     }  
  85.   
  86.     public boolean containsValue(Object value) {  
  87.         // TODO Auto-generated method stub  
  88.         return map.containsValue(value);  
  89.     }  
  90.   
  91.     public Set entrySet() {  
  92.         // TODO Auto-generated method stub  
  93.         return map.entrySet();  
  94.     }  
  95.   
  96.     public boolean isEmpty() {  
  97.         // TODO Auto-generated method stub  
  98.         return map.isEmpty();  
  99.     }  
  100.   
  101.     public Set keySet() {  
  102.         // TODO Auto-generated method stub  
  103.         return map.keySet();  
  104.     }  
  105.   
  106.     @SuppressWarnings("unchecked")  
  107.     public void putAll(Map t) {  
  108.         // TODO Auto-generated method stub  
  109.         map.putAll(t);  
  110.     }  
  111.   
  112.     public int size() {  
  113.         // TODO Auto-generated method stub  
  114.         return map.size();  
  115.     }  
  116.   
  117.     public Collection values() {  
  118.         // TODO Auto-generated method stub  
  119.         return map.values();  
  120.     }  
  121.   
  122. }  

(2)自定义一个controller类,用于继承使用

[html] view plain copy
  1. package com.mbfw.controller.base;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4.   
  5. import org.springframework.web.context.request.RequestContextHolder;  
  6. import org.springframework.web.context.request.ServletRequestAttributes;  
  7. import org.springframework.web.servlet.ModelAndView;  
  8.   
  9. import com.mbfw.entity.Page;  
  10. import com.mbfw.util.Const;  
  11. import com.mbfw.util.Logger;  
  12. import com.mbfw.util.PageData;  
  13. import com.mbfw.util.Tools;  
  14. import com.mbfw.util.UuidUtil;  
  15.   
  16. public class BaseController {  
  17.     private static final long serialVersionUID = 6357869213649815390L;  
  18.   
  19.     /**  
  20.      * 得到PageData  
  21.      */  
  22.     public PageData getPageData() {  
  23.         return new PageData(this.getRequest());  
  24.     }  
  25.   
  26.     /**  
  27.      * 得到ModelAndView  
  28.      */  
  29.     public ModelAndView getModelAndView() {  
  30.         return new ModelAndView();  
  31.     }  
  32.   
  33.     /**  
  34.      * 得到request对象  
  35.      */  
  36.     public HttpServletRequest getRequest() {  
  37.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  38.   
  39.         return request;  
  40.     }  
  41.   
  42. }  

(3)使用自定义对象

[html] view plain copy
  1. @Controller  
  2. @RequestMapping(value = "/asset")  
  3. public class AssetShenpiController extends BaseController {  
  4.         @RequestMapping(value = "/atp_approvalprojectlist")  
  5.     public ModelAndView shenpiList() throws Exception {  
  6.         PageData pd = this.getPageData();  
  7.        }  
  8.   
  9. }  

备注:通过上面的方法,这样获取到的pd对象就能够获取到从JSP页面中传送过来的参数了(比如从form表单中提交过来的多个input内容),其中都是Map中的Key和Value的形式。。。这样是不是很简单,省了很多写不同接受对象的方式呢

十八:下载文件(比如doc,excl,txt等等)

方法一:

[html] view plain copy
  1. @RequestMapping(value = "/atp_downloadfile")  
  2.     public ResponseEntity<byte[]> download() throws IOException {      
  3.         PageData pd = this.getPageData();  
  4.         //获取文件路径  
  5.         String urlFile = URLDecoder.decode(pd.getString("fileurl"), "utf-8");    
  6.         File file=new File(urlFile);    
  7.         HttpHeaders headers = new HttpHeaders();  
  8.         //获取下载名(因为之前通过了@进行分割)  
  9.         String fileName = urlFile.split("@")[1];  
  10.         fileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1");//为了解决中文名称乱码问题    
  11.         headers.setContentDispositionFormData("attachment", fileName);     
  12.         headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);     
  13.         return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),      
  14.                                           headers, HttpStatus.CREATED);      
  15.     }  
方法二:(但是有可能文件名为中文会乱码,但是上面的不会)
[html] view plain copy
  1.       @RequestMapping(value = "/atp_downloadfile")  
  2. ublic void downLoadAlreadyFile(HttpServletRequest request ,HttpServletResponse response) throws Exception{  
  3. PageData pd = this.getPageData();  
  4. //获取文件路径  
  5. String urlFile = URLDecoder.decode(pd.getString("fileurl"), "utf-8");  
  6.  //获取输入流    
  7.       InputStream bis = new BufferedInputStream(new FileInputStream(new File(urlFile)));    
  8.       //假如以中文名下载的话    
  9.       String filename = "哈哈";    
  10.       //转码,免得文件名中文乱码    
  11.       filename = URLEncoder.encode(filename,"GBK");    
  12.       //设置文件下载头    
  13.       response.addHeader("Content-Disposition", "attachment;filename=" + filename);      
  14.       //1.设置文件ContentType类型,这样设置,会自动判断下载文件类型      
  15.       response.setContentType("multipart/form-data");     
  16.       BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());    
  17.       int len = 0;    
  18.       while((len = bis.read()) != -1){    
  19.           out.write(len);    
  20.           out.flush();    
  21.       }    
  22.       out.close();  
  23.       pd.put("result", "success");  
  24.       return pd;  
  25.     }  

方法三:

编写文件下载类:

[html] view plain copy
  1. package com.mbfw.util;  
  2. import java.io.BufferedOutputStream;  
  3. import java.io.OutputStream;  
  4. import java.net.URLEncoder;  
  5. import javax.servlet.http.HttpServletResponse;  
  6. public class FileDownload {  
  7.     /**  
  8.      * @param response  
  9.      * @param filePath //文件完整路径(包括文件名和扩展名)  
  10.      * @param fileName //下载后看到的文件名  
  11.      * @return 文件名  
  12.      */  
  13.     public static void fileDownload(final HttpServletResponse response, String filePath, String fileName) throws Exception {  
  14.         byte[] data = FileUtil.toByteArray2(filePath);  
  15.         fileName = URLEncoder.encode(fileName, "UTF-8");  
  16.         response.reset();  
  17.         response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");  
  18.         response.addHeader("Content-Length", "" + data.length);  
  19.         response.setContentType("application/octet-stream;charset=UTF-8");  
  20.         OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());  
  21.         outputStream.write(data);  
  22.         outputStream.flush();  
  23.         outputStream.close();  
  24.         response.flushBuffer();  
  25.     }  
  26. }  

编写文件下载工具类:

[html] view plain copy
  1. package com.mbfw.util;  
  2. import java.io.BufferedInputStream;  
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.File;  
  5. import java.io.FileInputStream;  
  6. import java.io.FileNotFoundException;  
  7. import java.io.IOException;  
  8. import java.io.RandomAccessFile;  
  9. import java.nio.ByteBuffer;  
  10. import java.nio.MappedByteBuffer;  
  11. import java.nio.channels.FileChannel;  
  12. import java.nio.channels.FileChannel.MapMode;  
  13.   
  14. public class FileUtil {  
  15.   
  16.     public static void main(String[] args) {  
  17.         String dirName = "d:/mbfw/topic/";// 创建目录  
  18.         FileUtil.createDir(dirName);  
  19.     }  
  20.   
  21.     /**  
  22.      * 创建目录  
  23.      *   
  24.      * @param destDirName 目标目录名  
  25.      * @return 目录创建成功返回true,否则返回false  
  26.      */  
  27.     public static boolean createDir(String destDirName) {  
  28.         File dir = new File(destDirName);  
  29.         if (dir.exists()) {  
  30.             return false;  
  31.         }  
  32.         if (!destDirName.endsWith(File.separator)) {  
  33.             destDirName = destDirName + File.separator;  
  34.         }  
  35.         // 创建单个目录  
  36.         if (dir.mkdirs()) {  
  37.             return true;  
  38.         } else {  
  39.             return false;  
  40.         }  
  41.     }  
  42.   
  43.     /**  
  44.      * 删除文件  
  45.      *   
  46.      * @param filePathAndName String 文件路径及名称 如c:/fqf.txt  
  47.      * @param fileContent String  
  48.      * @return boolean  
  49.      */  
  50.     public static void delFile(String filePathAndName) {  
  51.         try {  
  52.             String filePath = filePathAndName;  
  53.             filePath = filePath.toString();  
  54.             java.io.File myDelFile = new java.io.File(filePath);  
  55.             myDelFile.delete();  
  56.   
  57.         } catch (Exception e) {  
  58.             System.out.println("删除文件操作出错");  
  59.             e.printStackTrace();  
  60.   
  61.         }  
  62.   
  63.     }  
  64.   
  65.     /**  
  66.      * 读取到字节数组0  
  67.      *   
  68.      * @param filePath //路径  
  69.      * @throws IOException  
  70.      */  
  71.     public static byte[] getContent(String filePath) throws IOException {  
  72.         File file = new File(filePath);  
  73.         long fileSize = file.length();  
  74.         if (fileSize > Integer.MAX_VALUE) {  
  75.             System.out.println("file too big...");  
  76.             return null;  
  77.         }  
  78.         FileInputStream fi = new FileInputStream(file);  
  79.         byte[] buffer = new byte[(int) fileSize];  
  80.         int offset = 0;  
  81.         int numRead = 0;  
  82.         while (offset < buffer.length && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {  
  83.             offset += numRead;  
  84.         }  
  85.         // 确保所有数据均被读取  
  86.         if (offset != buffer.length) {  
  87.             throw new IOException("Could not completely read file " + file.getName());  
  88.         }  
  89.         fi.close();  
  90.         return buffer;  
  91.     }  
  92.   
  93.     /**  
  94.      * 读取到字节数组1  
  95.      *   
  96.      * @param filePath  
  97.      * @return  
  98.      * @throws IOException  
  99.      */  
  100.     public static byte[] toByteArray(String filePath) throws IOException {  
  101.   
  102.         File f = new File(filePath);  
  103.         if (!f.exists()) {  
  104.             throw new FileNotFoundException(filePath);  
  105.         }  
  106.         ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());  
  107.         BufferedInputStream in = null;  
  108.         try {  
  109.             in = new BufferedInputStream(new FileInputStream(f));  
  110.             int buf_size = 1024;  
  111.             byte[] buffer = new byte[buf_size];  
  112.             int len = 0;  
  113.             while (-1 != (len = in.read(buffer, 0, buf_size))) {  
  114.                 bos.write(buffer, 0, len);  
  115.             }  
  116.             return bos.toByteArray();  
  117.         } catch (IOException e) {  
  118.             e.printStackTrace();  
  119.             throw e;  
  120.         } finally {  
  121.             try {  
  122.                 in.close();  
  123.             } catch (IOException e) {  
  124.                 e.printStackTrace();  
  125.             }  
  126.             bos.close();  
  127.         }  
  128.     }  
  129.   
  130.     /**  
  131.      * 读取到字节数组2  
  132.      *   
  133.      * @param filePath  
  134.      * @return  
  135.      * @throws IOException  
  136.      */  
  137.     public static byte[] toByteArray2(String filePath) throws IOException {  
  138.   
  139.         File f = new File(filePath);  
  140.         if (!f.exists()) {  
  141.             throw new FileNotFoundException(filePath);  
  142.         }  
  143.   
  144.         FileChannel channel = null;  
  145.         FileInputStream fs = null;  
  146.         try {  
  147.             fs = new FileInputStream(f);  
  148.             channel = fs.getChannel();  
  149.             ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());  
  150.             while ((channel.read(byteBuffer)) > 0) {  
  151.                 // do nothing  
  152.                 // System.out.println("reading");  
  153.             }  
  154.             return byteBuffer.array();  
  155.         } catch (IOException e) {  
  156.             e.printStackTrace();  
  157.             throw e;  
  158.         } finally {  
  159.             try {  
  160.                 channel.close();  
  161.             } catch (IOException e) {  
  162.                 e.printStackTrace();  
  163.             }  
  164.             try {  
  165.                 fs.close();  
  166.             } catch (IOException e) {  
  167.                 e.printStackTrace();  
  168.             }  
  169.         }  
  170.     }  
  171.   
  172.     /**  
  173.      * Mapped File way MappedByteBuffer 可以在处理大文件时,提升性能  
  174.      *   
  175.      * @param filename  
  176.      * @return  
  177.      * @throws IOException  
  178.      */  
  179.     public static byte[] toByteArray3(String filePath) throws IOException {  
  180.   
  181.         FileChannel fc = null;  
  182.         RandomAccessFile rf = null;  
  183.         try {  
  184.             rf = new RandomAccessFile(filePath, "r");  
  185.             fc = rf.getChannel();  
  186.             MappedByteBuffer byteBuffer = fc.map(MapMode.READ_ONLY, 0, fc.size()).load();  
  187.             // System.out.println(byteBuffer.isLoaded());  
  188.             byte[] result = new byte[(int) fc.size()];  
  189.             if (byteBuffer.remaining() > 0) {  
  190.                 // System.out.println("remain");  
  191.                 byteBuffer.get(result, 0, byteBuffer.remaining());  
  192.             }  
  193.             return result;  
  194.         } catch (IOException e) {  
  195.             e.printStackTrace();  
  196.             throw e;  
  197.         } finally {  
  198.             try {  
  199.                 rf.close();  
  200.                 fc.close();  
  201.             } catch (IOException e) {  
  202.                 e.printStackTrace();  
  203.             }  
  204.         }  
  205.     }  
  206.   
  207. }  

编写调用方法:

[html] view plain copy
  1. @RequestMapping(value = "/downExcel")  
  2.     public void downExcel(HttpServletResponse response) throws Exception {  
  3.         FileDownload.fileDownload(response, PathUtil.getClasspath() + Const.FILEPATHFILE + "Users.xls", "Users.xls");  
  4.   
  5.     }  

十九:JSP导入Excl表格到数据库

比如格式如下的一个Excel表格,并用一个实例来进行分析:::


JSP代码:(有用了jQuery的tip插件,当然这只是一个例子,根据需要来写就是了)

[html] view plain copy
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>  
  3. <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>  
  4. <%  
  5.     String path = request.getContextPath();  
  6.     String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
  7. %>  
  8. <!DOCTYPE html>  
  9. <html lang="en">  
  10.     <head>  
  11.         <base href="<%=basePath%>">  
  12.         <meta charset="utf-8" />  
  13.         <title></title>  
  14.         <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
  15.         <link href="static/css/bootstrap.min.css" rel="stylesheet" />  
  16.         <link rel="stylesheet" href="static/css/ace.min.css" />  
  17.         <link rel="stylesheet" href="static/css/ace-skins.min.css" />  
  18.         <link rel="stylesheet" href="static/assets/css/font-awesome.css" />  
  19.         <!-- ace styles -->  
  20.         <link rel="stylesheet" href="static/assets/css/ace.css" class="ace-main-stylesheet" id="main-ace-style" />  
  21.           
  22.         <script type="text/javascript">  
  23.               
  24.             //保存  
  25.             function save(){  
  26.                 if($("#excel").val()=="" || document.getElementById("excel").files[0] =='请选择xls格式的文件'){  
  27.                       
  28.                     $("#excel").tips({  
  29.                         side:3,  
  30.                         msg:'请选择文件',  
  31.                         bg:'#AE81FF',  
  32.                         time:3  
  33.                     });  
  34.                     return false;  
  35.                 }  
  36.                   
  37.                 $("#Form").submit();  
  38.                 $("#zhongxin").hide();  
  39.                 $("#zhongxin2").show();  
  40.             }  
  41.               
  42.             function fileType(obj){  
  43.                 var fileType=obj.value.substr(obj.value.lastIndexOf(".")).toLowerCase();//获得文件后缀名  
  44.                 if(fileType != '.xls'){  
  45.                     $("#excel").tips({  
  46.                         side:3,  
  47.                         msg:'请上传xls格式的文件',  
  48.                         bg:'#AE81FF',  
  49.                         time:3  
  50.                     });  
  51.                     $("#excel").val('');  
  52.                     document.getElementById("excel").files[0] = '请选择xls格式的文件';  
  53.                 }  
  54.             }  
  55.         </script>  
  56.     </head>  
  57. <body>  
  58.     <form action="user/readExcel.do" name="Form" id="Form" method="post" enctype="multipart/form-data">  
  59.         <div id="zhongxin">  
  60.         <table style="width:95%;" >  
  61.             <tr>  
  62.                 <td style="padding-top: 20px;"><input type="file" id="excel" name="excel" style="width:50px;" onchange="fileType(this)" /></td>  
  63.             </tr>  
  64.             <tr>  
  65.                 <td style="text-align: center;">  
  66.                     <a class="btn btn-mini btn-primary" onclick="save();">导入</a>  
  67.                     <a class="btn btn-mini btn-danger" onclick="top.Dialog.close();">取消</a>.  
  68.                     <a style="float: right;"  class="btn btn-mini btn-success" onclick="window.location.href='<%=basePath%>/user/downExcel.do'">下载模版</a>                    
  69.                 </td>  
  70.             </tr>  
  71.         </table>  
  72.         </div>  
  73.           
  74.         <div id="zhongxin2" class="center" style="display:none"><br/><img src="static/images/jzx.gif" /><br/><h4 class="lighter block green"></h4></div>  
  75.           
  76.     </form>  
  77.           
  78.         <!-- 引入 -->  
  79.         <!--[if !IE]> -->  
  80.         <script type="text/javascript">  
  81.             window.jQuery || document.write("<script src='static/assets/js/jquery.js'>"+"<"+"/script>");  
  82.         </script>  
  83.         <!-- <![endif]-->  
  84.         <!--[if IE]>  
  85.         <script type="text/javascript">  
  86.             window.jQuery || document.write("<script src='static/assets/js/jquery1x.js'>"+"<"+"/script>");  
  87.         </script>  
  88.         <![endif]-->  
  89.         <script src="static/js/bootstrap.min.js"></script>  
  90.         <!-- ace scripts -->  
  91.         <script src="static/assets/js/ace/elements.fileinput.js"></script>  
  92.         <script src="static/assets/js/ace/ace.js"></script>  
  93.         <!--提示框-->  
  94.         <script type="text/javascript" src="static/js/jquery.tips.js"></script>  
  95.         <script type="text/javascript">  
  96.         $(top.hangge());  
  97.         $(function() {  
  98.             //上传  
  99.             $('#excel').ace_file_input({  
  100.                 no_file:'请选择EXCEL ...',  
  101.                 btn_choose:'选择',  
  102.                 btn_change:'更改',  
  103.                 droppable:false,  
  104.                 onchange:null,  
  105.                 thumbnail:false, //| true | large  
  106.                 whitelist:'xls|xls',  
  107.                 blacklist:'gif|png|jpg|jpeg'  
  108.                 //onchange:''  
  109.                 //  
  110.             });  
  111.               
  112.         });  
  113.           
  114.         </script>  
  115.       
  116. </body>  
  117. </html>  

controller层代码:

[html] view plain copy
  1. /**  
  2.      * 从EXCEL导入到数据库  
  3.      */  
  4.     @RequestMapping(value = "/readExcel")  
  5.     public ModelAndView readExcel(@RequestParam(value = "excel"required = false) MultipartFile file) throws Exception {  
  6.         ModelAndView mv = this.getModelAndView();  
  7.         PageData pd = new PageData();  
  8.         if (null != file && !file.isEmpty()) {  
  9.             String filePath = PathUtil.getClasspath() + Const.FILEPATHFILE; // 文件上传路径  
  10.             String fileName = FileUpload.fileUp(file, filePath, "userexcel"); // 执行上传  
  11.   
  12.             List<PageData> listPd = (List) ObjectExcelRead.readExcel(filePath, fileName, 2, 0, 0); // 执行读EXCEL操作,读出的数据导入List 2:从第3行开始;0:从第A列开始;0:第0个sheet  
  13.   
  14.             /* 存入数据库操作====================================== */  
  15.             pd.put("RIGHTS", ""); // 权限  
  16.             pd.put("LAST_LOGIN", ""); // 最后登录时间  
  17.             pd.put("IP", ""); // IP  
  18.             pd.put("STATUS", "0"); // 状态  
  19.             pd.put("SKIN", "default"); // 默认皮肤  
  20.   
  21.             List<Role> roleList = roleService.listAllERRoles(); // 列出所有二级角色  
  22.   
  23.             pd.put("ROLE_ID", roleList.get(0).getROLE_ID()); // 设置角色ID为随便第一个  
  24.             /**  
  25.              * var0 :编号 var1 :姓名 var2 :手机 var3 :邮箱 var4 :备注......等等  
  26.              * 这个var就是readExcel方法返回对象中在方法中进行封装好的信息,也就是对应的信息  
  27.              */  
  28.             for (int i = 0; i < listPd.size(); i++) {  
  29.                 pd.put("USER_ID", this.get32UUID()); // 产生唯一的ID,这个在上面的知识点中有提到了,可以参考  
  30.                 pd.put("NAME", listPd.get(i).getString("var1")); // 姓名  
  31.                   
  32.                 String USERNAME = GetPinyin.getPingYin(listPd.get(i).getString("var1")); // 根据姓名汉字生成全拼  
  33.                 pd.put("USERNAME", USERNAME);  
  34.                 if (userService.findByUId(pd) != null) { // 判断用户名是否重复,如果重复了就再后面添加随机生成的六位数  
  35.                     USERNAME = GetPinyin.getPingYin(listPd.get(i).getString("var1")) + Tools.getRandomNum();  
  36.                     pd.put("USERNAME", USERNAME);  
  37.                 }  
  38.                 pd.put("BZ", listPd.get(i).getString("var4")); // 备注  
  39.                 if (Tools.checkEmail(listPd.get(i).getString("var3"))) { // 邮箱格式不对就跳过  
  40.                     pd.put("EMAIL", listPd.get(i).getString("var3"));  
  41.                     if (userService.findByUE(pd) != null) { // 邮箱已存在就跳过  
  42.                         continue;  
  43.                     }  
  44.                 } else {  
  45.                     continue;  
  46.                 }  
  47.   
  48.                 pd.put("NUMBER", listPd.get(i).getString("var0")); // 编号已存在就跳过  
  49.                 pd.put("PHONE", listPd.get(i).getString("var2")); // 手机号  
  50.   
  51.                 pd.put("PASSWORD", new SimpleHash("SHA-1", USERNAME, "123").toString()); // 设置初始默认密码为123,并且将这个密码加密到数据库中  
  52.                 if (userService.findByUN(pd) != null) {  
  53.                     continue;  
  54.                 }  
  55.                 userService.saveU(pd);//保存数据到数据库中  
  56.             }  
  57.             /* 存入数据库操作====================================== */  
  58.             mv.addObject("msg", "success");  
  59.         }  
  60.         mv.setViewName("save_result");  
  61.         return mv;  
  62.     }  
controller层中的PathUtil类代码:(都封装了很多方法,可以根据需要使用)
[html] view plain copy
  1. package com.mbfw.util;  
  2.   
  3. import java.io.File;  
  4.   
  5. import javax.servlet.http.HttpServletRequest;  
  6.   
  7. import org.springframework.web.context.request.RequestContextHolder;  
  8. import org.springframework.web.context.request.ServletRequestAttributes;  
  9.   
  10. /**  
  11.  * 路径工具类  
  12.  *   
  13.  * @author  
  14.  */  
  15. public class PathUtil {  
  16.   
  17.     /**  
  18.      * 图片访问路径  
  19.      *   
  20.      * @param pathType 图片类型 visit-访问;save-保存  
  21.      * @param pathCategory 图片类别,如:话题图片-topic、话题回复图片-reply、商家图片  
  22.      * @return  
  23.      */  
  24.     public static String getPicturePath(String pathType, String pathCategory) {  
  25.         String strResult = "";  
  26.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  27.         StringBuffer strBuf = new StringBuffer();  
  28.         if ("visit".equals(pathType)) {  
  29.         } else if ("save".equals(pathType)) {  
  30.             String projectPath = PublicUtil.getPorjectPath().replaceAll("\\\\", "/");  
  31.             projectPath = splitString(projectPath, "bin/");  
  32.   
  33.             strBuf.append(projectPath);  
  34.             strBuf.append("webapps/ROOT/");  
  35.         }  
  36.   
  37.         strResult = strBuf.toString();  
  38.   
  39.         return strResult;  
  40.     }  
  41.   
  42.     private static String splitString(String str, String param) {  
  43.         String result = str;  
  44.   
  45.         if (str.contains(param)) {  
  46.             int start = str.indexOf(param);  
  47.             result = str.substring(0, start);  
  48.         }  
  49.   
  50.         return result;  
  51.     }  
  52.   
  53.     /*  
  54.      * 获取classpath1  
  55.      */  
  56.     public static String getClasspath() {  
  57.         String path = (String.valueOf(Thread.currentThread().getContextClassLoader().getResource("")) + "../../").replaceAll("file:/", "").replaceAll("%20", " ").trim();  
  58.         if (path.indexOf(":") != 1) {  
  59.             path = File.separator + path;  
  60.         }  
  61.         return path;  
  62.     }  
  63.   
  64.     /*  
  65.      * 获取classpath2  
  66.      */  
  67.     public static String getClassResources() {  
  68.         String path = (String.valueOf(Thread.currentThread().getContextClassLoader().getResource(""))).replaceAll("file:/", "").replaceAll("%20", " ").trim();  
  69.         if (path.indexOf(":") != 1) {  
  70.             path = File.separator + path;  
  71.         }  
  72.         return path;  
  73.     }  
  74.   
  75.     public static String PathAddress() {  
  76.         String strResult = "";  
  77.   
  78.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  79.   
  80.         StringBuffer strBuf = new StringBuffer();  
  81.   
  82.         strBuf.append(request.getScheme() + "://");  
  83.         strBuf.append(request.getServerName() + ":");  
  84.         strBuf.append(request.getServerPort() + "");  
  85.   
  86.         strBuf.append(request.getContextPath() + "/");  
  87.   
  88.         strResult = strBuf.toString();// +"ss/";//加入项目的名称  
  89.   
  90.         return strResult;  
  91.     }  
  92.   
  93. }  
Controller层中的FileUpload类:(都封装了很多方法,可以根据需要使用)
[html] view plain copy
  1. package com.mbfw.util;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6.   
  7. import org.apache.commons.io.FileUtils;  
  8. import org.springframework.web.multipart.MultipartFile;  
  9.   
  10. /**  
  11.  * 上传文件 创建人:研发中心 创建时间:2014年12月23日  
  12.  *   
  13.  * @version  
  14.  */  
  15. public class FileUpload {  
  16.   
  17.     /**  
  18.      * @param file //文件对象  
  19.      * @param filePath //上传路径  
  20.      * @param fileName //文件名  
  21.      * @return 文件名  
  22.      */  
  23.     public static String fileUp(MultipartFile file, String filePath, String fileName) {  
  24.         String extName = ""; // 扩展名格式:  
  25.         try {  
  26.             if (file.getOriginalFilename().lastIndexOf(".") >= 0) {  
  27.                 extName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));  
  28.             }  
  29.             copyFile(file.getInputStream(), filePath, fileName + extName).replaceAll("-", "");  
  30.         } catch (IOException e) {  
  31.             System.out.println(e);  
  32.         }  
  33.         return fileName + extName;  
  34.     }  
  35.   
  36.     /**  
  37.      * 写文件到当前目录的upload目录中  
  38.      *   
  39.      * @param in  
  40.      * @param fileName  
  41.      * @throws IOException  
  42.      */  
  43.     private static String copyFile(InputStream in, String dir, String realName) throws IOException {  
  44.         File file = new File(dir, realName);  
  45.         if (!file.exists()) {  
  46.             if (!file.getParentFile().exists()) {  
  47.                 file.getParentFile().mkdirs();  
  48.             }  
  49.             file.createNewFile();  
  50.         }  
  51.         FileUtils.copyInputStreamToFile(in, file);  
  52.         return realName;  
  53.     }  
  54. }  
controller层中的读取Excl行列的ObjectExcelRead类:
[html] view plain copy
  1. package com.mbfw.util;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.util.ArrayList;  
  6. import java.util.List;  
  7.   
  8. import org.apache.poi.hssf.usermodel.HSSFCell;  
  9. import org.apache.poi.hssf.usermodel.HSSFRow;  
  10. import org.apache.poi.hssf.usermodel.HSSFSheet;  
  11. import org.apache.poi.hssf.usermodel.HSSFWorkbook;  
  12. public class ObjectExcelRead {  
  13.   
  14.     /**  
  15.      * @param filepath //文件路径  
  16.      * @param filename //文件名  
  17.      * @param startrow //开始行号:默认开始传入的时候设置为2,这个根据Excl表格形式来  
  18.      * @param startcol //开始列号:默认为0开始  
  19.      * @param sheetnum //sheet  
  20.      * @return list  
  21.      */  
  22.     public static List<Object> readExcel(String filepath, String filename, int startrow, int startcol, int sheetnum) {  
  23.         List<Object> varList = new ArrayList<Object>();  
  24.   
  25.         try {  
  26.             File target = new File(filepath, filename);  
  27.             FileInputStream fi = new FileInputStream(target);  
  28.             HSSFWorkbook wb = new HSSFWorkbook(fi); // 创建一个Excel文件  
  29.             HSSFSheet sheet = wb.getSheetAt(sheetnum); // sheet 从0开始  
  30.             int rowNum = sheet.getLastRowNum() + 1; // 取得最后一行的行号  
  31.   
  32.             for (int i = startrow; i < rowNum; i++) { // 行循环开始  
  33.   
  34.                 PageData varpd = new PageData();//这里用的是自己封装的对象,这里只需要用你需要读取Excl数据封装成的对象即可  
  35.                 HSSFRow row = sheet.getRow(i); // 获取对应行  
  36.                 int cellNum = row.getLastCellNum(); // 每行的最后一个单元格位置  
  37.   
  38.                 for (int j = startcol; j < cellNum; j++) { // 列循环开始  
  39.   
  40.                     HSSFCell cell = row.getCell(Short.parseShort(j + ""));//获取对应列  
  41.                     String cellValue = null;  
  42.                     if (null != cell) {  
  43.                         switch (cell.getCellType()) { // 判断excel单元格每列的内容的格式,并对其进行转换,以便插入数据库  
  44.                             case 0: //获取的类型是数字HSSFCell.CELL_TYPE_NUMERIC  
  45.                                 cellValue = String.valueOf((int) cell.getNumericCellValue());  
  46.                                 break;  
  47.                             case 1: //获取的类型就是字符串HSSFCell.CELL_TYPE_STRING  
  48.                                 cellValue = cell.getStringCellValue();  
  49.                                 break;  
  50.                             case 2://获取的类型是时间  
  51.                                 cellValue = cell.getNumericCellValue() + "";  
  52.                                 // cellValue = String.valueOf(cell.getDateCellValue());  
  53.                                 break;  
  54.                             case 3://获取的是空值,HSSFCell.CELL_TYPE_BLANK  
  55.                                 cellValue = "";  
  56.                                 break;  
  57.                             case 4://获取的是Boolean,HSSFCell.CELL_TYPE_BOOLEAN  
  58.                                 cellValue = String.valueOf(cell.getBooleanCellValue());  
  59.                                 break;  
  60.                             case 5://获取的是非法字符,HSSFCell.CELL_TYPE_ERROR  
  61.                                 cellValue = String.valueOf(cell.getErrorCellValue());  
  62.                                 break;  
  63.                         }  
  64.                     } else {  
  65.                         cellValue = "";  
  66.                     }  
  67.   
  68.                     varpd.put("var" + j, cellValue); //在行中,添加对应的列的内容  
  69.                 }  
  70.                 varList.add(varpd);//添加每一行的内容  
  71.             }  
  72.         } catch (Exception e) {  
  73.             System.out.println(e);  
  74.         }  
  75.         return varList; //将封装好的数据对象,返回从Excel表中读取的内容  
  76.     }  
  77. }  
controller层中将中文转为拼音的方法类GetPinYin代码:
[html] view plain copy
  1. public class GetPinyin {  
  2.   
  3.     /**  
  4.      * 得到 全拼  
  5.      *   
  6.      * @param src  
  7.      * @return  
  8.      */  
  9.     public static String getPingYin(String src) {  
  10.         char[] t1 = null;  
  11.         t1 = src.toCharArray();  
  12.         String[] t2 = new String[t1.length];  
  13.         HanyuPinyinOutputFormat t3 = new HanyuPinyinOutputFormat();  
  14.         t3.setCaseType(HanyuPinyinCaseType.LOWERCASE);  
  15.         t3.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  
  16.         t3.setVCharType(HanyuPinyinVCharType.WITH_V);  
  17.         String t4 = "";  
  18.         int t0 = t1.length;  
  19.         try {  
  20.             for (int i = 0; i < t0; i++) {  
  21.                 // 判断是否为汉字字符  
  22.                 if (java.lang.Character.toString(t1[i]).matches("[\\u4E00-\\u9FA5]+")) {  
  23.                     t2 = PinyinHelper.toHanyuPinyinStringArray(t1[i], t3);  
  24.                     t4 += t2[0];  
  25.                 } else {  
  26.                     t4 += java.lang.Character.toString(t1[i]);  
  27.                 }  
  28.             }  
  29.             return t4;  
  30.         } catch (BadHanyuPinyinOutputFormatCombination e1) {  
  31.             e1.printStackTrace();  
  32.         }  
  33.         return t4;  
  34.     }  

controller层中随机生成一个六位数的类Tools:(这个很简单的)

[html] view plain copy
  1. public class Tools {  
  2.   
  3.     /**  
  4.      * 随机生成六位数验证码  
  5.      *   
  6.      * @return  
  7.      */  
  8.     public static int getRandomNum() {  
  9.         Random r = new Random();  
  10.         return r.nextInt(900000) + 100000;// (Math.random()*(999999-100000)+100000)  
  11.     }  

二十:导出数据库中的内容到Excl表格

JSP代码:

[html] view plain copy
  1. <!-- 检索  -->  
  2.             <form action="user/listUsers.do" method="post" name="userForm" id="userForm">  
  3.             <table>  
  4.                 <tr>  
  5.                     <td style="vertical-align:top;">  
  6.                         <span class="input-icon">  
  7.                             <input autocomplete="off" id="nav-search-input" type="text" name="USERNAME" value="${pd.USERNAME }" placeholder="这里输入检索关键词" title="检索范围列表:从用户名,用户姓名,邮箱,部门,备注列表中进行搜索" />  
  8.                             <i id="nav-search-icon" class="icon-search"></i>  
  9.                         </span>  
  10.                     </td>               
  11.                     <td style="vertical-align:top;">   
  12.                         <select class="chzn-select" name="ROLE_ID" id="role_id" data-placeholder="请选择系统角色" style="vertical-align:top;">  
  13.                         <option value=""></option>  
  14.                         <option value="">全部</option>  
  15.                         <c:forEach items="${roleList}" var="role">  
  16.                             <option value="${role.ROLE_ID }" <c:if test="${pd.ROLE_ID==role.ROLE_ID}">selected</c:if>>${role.ROLE_NAME }</option>  
  17.                         </c:forEach>  
  18.                         </select>  
  19.                     </td>  
  20.                     <!-- 用户权限搜索 -->  
  21.                     <td style="vertical-align:top;">  
  22.                         <select class="chzn-select" name="user_Permission" id="user_Permission" data-placeholder="请选择用户部门权限" style="vertical-align:top;">  
  23.                         <option value=""></option>  
  24.                         <option value="">全部</option>  
  25.                         <c:forEach items="${userDepartmentAuthoritys}" var="department">  
  26.                             <option value="${department.authority_Code}" <c:if test="${department.authority_Code==pd.user_Permission}">selected</c:if>>                         
  27.                             ${department.authority_Name}                              
  28.                             </option>  
  29.                         </c:forEach>  
  30.                         </select>  
  31.                     </td>  
  32.                     <td><input class="span10 date-picker" name="creatuser_Time" id="creatuser_Time"  value="${pd.creatuser_Time}" type="text" data-date-format="yyyy-mm-dd" readonly="readonly"  placeholder="用户创建开始日期搜索" title="用户创建开始日期" style="width: 155px;"/></td>  
  33.                     <td><input class="span10 date-picker" name="creatuser_endTime" id="creatuser_endTime"  value="${pd.creatuser_endTime}" type="text" data-date-format="yyyy-mm-dd" readonly="readonly"  placeholder="用户创建截止日期搜索" title="用户创建截止日期" style="width: 155px;"/></td>   
  34.                     <c:if test="${QX.cha == 1 }">  
  35.                     <td style="vertical-align:top;"><button class="btn btn-mini btn-light" onclick="search();" title="检索"><i id="nav-search-icon" class="icon-search"></i></button></td>  
  36.                     <%-- <!--这个功能现在不要 -->  
  37.                     <td style="vertical-align:top;"><a class="btn btn-mini btn-light" onclick="window.location.href='<%=basePath%>/user/listtabUsers.do';" title="切换模式"><i id="nav-search-icon" class="icon-exchange"></i></a></td>  
  38.                     --%>  
  39.                     <td style="vertical-align:top;"><a class="btn btn-mini btn-light" onclick="toExcel();" title="导出到EXCEL"><i id="nav-search-icon" class="icon-download-alt"></i></a></td>  
  40.                     <c:if test="${QX.edit == 1 }"><td style="vertical-align:top;"><a class="btn btn-mini btn-light" onclick="fromExcel();" title="从EXCEL导入"><i id="nav-search-icon" class="icon-cloud-upload"></i></a></td></c:if>  
  41.                     </c:if>  
  42.                 </tr>  
  43.             </table>  

备注:上面的有几个都是关于检索内容的控制,如果不写也是可以的,因为这个功能一般都还有限制信息的情况下,也是一样,所以说,如果想导出所有的数据,那么不选择上面的限制列表的内容就可以了哈。。。这个应该都能明白含义的。(上面那些列表的数据,我是从其他页面跳转带过来的,所以你们测试的时候可以直接写死都没关系啦)

JS代码:

[html] view plain copy
  1. //导出excel  
  2.         function toExcel(){  
  3.             var checkContent = $("#nav-search-input").val();//获取关键子搜索的内容  
  4.             var creatuser_Time = $("#creatuser_Time").val(); //获取用户创建检索内容  
  5.             var creatuser_endTime = $("#creatuser_endTime").val(); //获取用户创建截止时间检索内容  
  6.             var ROLE_ID = $("#role_id").val(); // 获取角色管理检索内容  
  7.             var department = $("#user_Permission").val(); //获取用户部门检索内容  
  8.             window.location.href='<%=basePath%>user/excel.do?checkContent='+checkContent+'&creatuser_Time='+creatuser_Time+'&creatuser_endTime='+creatuser_endTime+'&ROLE_ID='+ROLE_ID+'&department'+department;  
  9.         }  

controller层代码:

[html] view plain copy
  1. /*  
  2.      * 导出用户信息到EXCEL  
  3.      * @return  
  4.      */  
  5.     @RequestMapping(value = "/excel")  
  6.     public ModelAndView exportExcel() {  
  7.         ModelAndView mv = this.getModelAndView();  
  8.         PageData pd = new PageData();  
  9.         pd = this.getPageData();  
  10.         try {  
  11.             if (Jurisdiction.buttonJurisdiction(menuUrl, "cha")) {  
  12.                 // 检索条件===  
  13.                 String checkContent = pd.getString("checkContent");  
  14.                 if (null != checkContent && !"".equals(checkContent)) {//存在检索内容  
  15.                     checkContent = checkContent.trim();   //去掉末尾空字符  
  16.                     pd.put("checkContent", checkContent);  
  17.                 }  
  18.                 String creatuser_Time = pd.getString("creatuser_Time");//获取检索用户创建时间  
  19.                 String creatuser_endTime = pd.getString("creatuser_endTime"); //获取检索用户截止时间  
  20.                 if (creatuser_Time != null && !"".equals(creatuser_Time)) {  
  21.                     creatuser_Time = creatuser_Time + " 00:00:00";  
  22.                     pd.put("creatuser_Time", creatuser_Time);  
  23.                 }  
  24.                 if (creatuser_endTime != null && !"".equals(creatuser_endTime)) {  
  25.                     creatuser_endTime = creatuser_endTime + " 00:00:00";  
  26.                     pd.put("creatuser_endTime", creatuser_endTime);  
  27.                 }  
  28.                 String roleContent = pd.getString("ROLE_ID"); //检索的系统角色  
  29.                 if (roleContent != null && !"".equals(roleContent)) {  
  30.                     pd.put("roleContent", roleContent);  
  31.                 }  
  32.                 String department = pd.getString("department"); //检索的用户权限内容  
  33.                 if (department != null && !"".equals(department)) {  
  34.                     pd.put("department", department);  
  35.                 }  
  36.                 // 检索条件===  
  37.   
  38.                 Map<String, Object> dataMap = new HashMap<String, Object>();  
  39.                 List<String> titles = new ArrayList<String>();  
  40.                   
  41.                 //设置Excel标题显示格式  
  42.                 titles.add("编号"); // 1  
  43.                 titles.add("用户名"); // 2  
  44.                 titles.add("姓名"); // 3  
  45.                 titles.add("系统角色"); // 4                  
  46.                 titles.add("邮箱"); // 5  
  47.                 titles.add("上一级部门"); // 6  
  48.                 titles.add("所属部门"); // 7  
  49.                 titles.add("部门权限"); // 8  
  50.                 titles.add("用户创建时间"); // 9  
  51.                 titles.add("备注"); // 10  
  52.   
  53.                 dataMap.put("titles", titles);  
  54.   
  55.                 List<PageData> userList = userService.listAllUser(pd);//获取符合条件数据信息  
  56.                 List<PageData> varList = new ArrayList<PageData>();  
  57.                 for (int i = 0; i < userList.size(); i++) {  //添加相对应的获取到的数据到每一列中存储(这里就是10列数据)  
  58.                     PageData vpd = new PageData();  
  59.                     vpd.put("var1", userList.get(i).getString("NUMBER")); // 1  
  60.                     vpd.put("var2", userList.get(i).getString("USERNAME")); // 2  
  61.                     vpd.put("var3", userList.get(i).getString("NAME")); // 3  
  62.                     vpd.put("var4", userList.get(i).getString("ROLE_NAME")); // 4  
  63.                     vpd.put("var5", userList.get(i).getString("EMAIL")); // 5  
  64.                     vpd.put("var6", userList.get(i).getString("superior_organization_name")); // 6  
  65.                     vpd.put("var7", userList.get(i).getString("organization_name")); // 7  
  66.                     //对部门权限的显示这里要处理一下  
  67.                     if((Integer)(userList.get(i).get("user_Permission")) == 1){  
  68.                         vpd.put("var8", "总行管理员"); // 8    
  69.                     }else if((Integer)(userList.get(i).get("user_Permission")) == 2){  
  70.                         vpd.put("var8", "支行管理员"); // 8  
  71.                     }else if((Integer)(userList.get(i).get("user_Permission")) == 3){  
  72.                         vpd.put("var8", "普通员工"); // 8  
  73.                     }                                 
  74.                     vpd.put("var9", userList.get(i).getString("creatuser_Time")); // 9  
  75.                     vpd.put("var10", userList.get(i).getString("BZ")); // 10  
  76.                     varList.add(vpd);  
  77.                 }  
  78.                 dataMap.put("varList", varList);  
  79.                 ObjectExcelView erv = new ObjectExcelView(); // 执行excel操作  
  80.                 mv = new ModelAndView(erv, dataMap);  
  81.             }  
  82.         } catch (Exception e) {  
  83.             logger.error(e.toString(), e);  
  84.         }  
  85.         return mv;  
  86.     }  

controller层中使用到的工具类ObjectExcelView:

[html] view plain copy
  1. package com.mbfw.util;  
  2. import java.util.Date;  
  3. import java.util.List;  
  4. import java.util.Map;  
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpServletResponse;  
  7. import org.apache.poi.hssf.usermodel.HSSFCell;  
  8. import org.apache.poi.hssf.usermodel.HSSFCellStyle;  
  9. import org.apache.poi.hssf.usermodel.HSSFFont;  
  10. import org.apache.poi.hssf.usermodel.HSSFSheet;  
  11. import org.apache.poi.hssf.usermodel.HSSFWorkbook;  
  12. import org.springframework.web.servlet.view.document.AbstractExcelView;  
  13. public class ObjectExcelView extends AbstractExcelView {  
  14.   
  15.     @Override  
  16.     protected void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  17.         // TODO Auto-generated method stub  
  18.         Date date = new Date();  
  19.         String filename = Tools.date2Str(date, "yyyyMMddHHmmss");//设置下Excl的名称  
  20.         HSSFSheet sheet;  
  21.         HSSFCell cell;  
  22.         response.setContentType("application/octet-stream");  //设置下载的方式和格式  
  23.         response.setHeader("Content-Disposition", "attachment;filename=" + filename + ".xls");  
  24.         sheet = workbook.createSheet("sheet1");//创建表格  
  25.   
  26.         List<String> titles = (List<String>) model.get("titles"); //获取之前设置好的标题的样式(在controller层)  
  27.         int len = titles.size();  
  28.         HSSFCellStyle headerStyle = workbook.createCellStyle(); // 标题样式  
  29.         headerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);  
  30.         headerStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);  
  31.         HSSFFont headerFont = workbook.createFont(); // 标题字体  
  32.         headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);  
  33.         headerFont.setFontHeightInPoints((short) 11);  
  34.         headerStyle.setFont(headerFont);  
  35.         short width = 20height = 25 * 20;  
  36.         sheet.setDefaultColumnWidth(width);  
  37.         for (int i = 0; i < len; i++) { // 设置标题  
  38.             String title = titles.get(i);  
  39.             cell = getCell(sheet, 0, i);  
  40.             cell.setCellStyle(headerStyle);  
  41.             setText(cell, title);  
  42.         }  
  43.         sheet.getRow(0).setHeight(height);  
  44.   
  45.         HSSFCellStyle contentStyle = workbook.createCellStyle(); // 内容样式  
  46.         contentStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);  
  47.         List<PageData> varList = (List<PageData>) model.get("varList");  
  48.         int varCount = varList.size();  
  49.         for (int i = 0; i < varCount; i++) {  
  50.             PageData vpd = varList.get(i);  
  51.             for (int j = 0; j < len; j++) {  
  52.                 String varstr = vpd.getString("var" + (j + 1)) != null ? vpd.getString("var" + (j + 1)) : "";  
  53.                 cell = getCell(sheet, i + 1, j);  
  54.                 cell.setCellStyle(contentStyle);  
  55.                 setText(cell, varstr);  
  56.             }  
  57.   
  58.         }  
  59.   
  60.     }  
  61.   
  62. }  

ObjectExcelView类中使用到的方法:

1:Tools类中的date2Str

[html] view plain copy
  1. /**  
  2.      * 按照参数format的格式,日期转字符串  
  3.      *   
  4.      * @param date  
  5.      * @param format  
  6.      * @return  
  7.      */  
  8.     public static String date2Str(Date date, String format) {  
  9.         if (date != null) {  
  10.             SimpleDateFormat sdf = new SimpleDateFormat(format);  
  11.             return sdf.format(date);  
  12.         } else {  
  13.             return "";  
  14.         }  
  15.     }  

效果图:(不是很好截图,大概明白就好,关键代码,望理解哈~!!!!!)


备注:上面这几个步骤就是大概的过程了,主要是能读懂里面的代码含义,其实很简单的,而且里面进行的数据库的数据的获取,这个自己去编写啦,我就写了个service告诉大家(我用的是Mybatis框架,关于这个可以参考我其他的文章,都进行了很详细很详细的介绍的哦!!!!!)

二十一:上传文件(再多加几种实现方法)

JSP代码:

[java] view plain copy
  1. <form action="${pageContext.request.contextPath}/upload/singleUpload.action" enctype="multipart/form-data" method="post">    
  2.         <input type="file" name=singleFile>    
  3.         <input type="submit" value="submit"/>    
  4.     </form>  
Controller代码:

方法一:

[java] view plain copy
  1. @RequestMapping(value = "/upload/singleUpload.action", method=RequestMethod.POST)    
  2.         public String singleUpload(@RequestParam("singleFile")  MultipartFile file, HttpServletRequest request) throws IOException{                   
  3. <span style="white-space:pre">  </span>if (!file.isEmpty()) {  
  4.               String type = file.getOriginalFilename().substring(  
  5.                       file.getOriginalFilename().indexOf("."));// 取文件格式后缀名  
  6.               String filename = System.currentTimeMillis() + type;// 取当前时间戳作为文件名  
  7.               String path = request.getSession().getServletContext()  
  8.                      .getRealPath("/upload/" + filename);// 存放位置  
  9.               File destFile = new File(path);  
  10.               try {  
  11.                   // FileUtils.copyInputStreamToFile()这个方法里对IO进行了自动操作,不需要额外的再去关闭IO流  
  12.                  FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);// 复制临时文件到指定目录下  
  13.               } catch (IOException e) {  
  14.                   e.printStackTrace();  
  15.              }  
  16.               return "success";  
  17.           } else {  
  18.               return "fail";  
  19.           }        
  20.     }   
方法二:
[java] view plain copy
  1. @RequestMapping(value = "/upload/singleUpload.action", method=RequestMethod.POST)    
  2.         public String singleUpload(@RequestParam("singleFile")  MultipartFile singleFile, HttpServletRequest request) throws IOException{    
  3.            String savePath = request.getSession().getServletContext().getRealPath("upload/temp");    
  4.             if(singleFile != null && !singleFile.isEmpty()){     
  5.                 String fileName = singleFile.getOriginalFilename();   
  6.                 //采用UUID生成随机文件名    
  7.                 fileName = UUID.randomUUID().toString().replace("-""") + fileName.substring(fileName.lastIndexOf("."));    
  8.                 File targetFile = new File(savePath, fileName);    
  9.                 if(!targetFile.exists()){    
  10.                     targetFile.mkdir();    
  11.                 }    
  12.                 singleFile.transferTo(targetFile);    
  13.             return "success";     
  14.             }     
  15.             return "fail';   
  16.     }   

二十二:服务器校验(用的很少,因为比较麻烦,这个校验一般都是在前端就进行了校验,通过才进行到后台,所以了解就好了这个)

功能:当JSP提交的内容字段,不符合实体类中定义的标准的时候,就会发生校验错误,然后就可以设置回到提交页面当中,从而达到一种校验功能。

具体的步骤:

(1)设置实体校验内容

[java] view plain copy
  1. public class Person {  
  2.     private Integer id;  
  3.     @Size(max=10,min=5,message="用户名称必须是5到10个字符组成!")      //关键点  
  4.     private String name;  
  5.       
  6.     @NotNull(message="年龄不能为空!")       //关键点  
  7.     private Integer age;  
  8.       
  9.     private Date joinDate;  
  10.     public Integer getId() {  
  11.         return id;  
  12.     }  
  13.     public void setId(Integer id) {  
  14.         this.id = id;  
  15.     }  
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.     public void setName(String name) {  
  20.         this.name = name;  
  21.     }  
  22.     public Integer getAge() {  
  23.         return age;  
  24.     }  
  25.     public void setAge(Integer age) {  
  26.         this.age = age;  
  27.     }  
  28.     public Date getJoinDate() {  
  29.         return joinDate;  
  30.     }  
  31.     public void setJoinDate(Date joinDate) {  
  32.         this.joinDate = joinDate;  
  33.     }  
  34. }  

(2)添加校验jar包-----就是Spring校验包和Hibernate校验包


(3)Springmvc.xml配置文件中,添加注解驱动属性

[java] view plain copy
  1. <!--注解驱动-->  
  2. <mvc:annotation-driven />  

(4)Controller类中进行控制错误反馈情况----------(用修改内容的功能来做一个演示)

[java] view plain copy
  1.          //修改保存 ,注意参数中的BindingResult这个。。这是一个关键  
  2. @RequestMapping("/person/update.action")  
  3. public String update(@Valid Person p,   
  4.         BindingResult br,  
  5.         @RequestParam(required=true)MultipartFile uploadfile) throws IOException{  
  6.       
  7.     if(br.hasErrors()){     //如果实体类校验失败  
  8.         return "person/jPersonUpdate";  
  9.     }else{  
  10.         //上传文件写磁盘  
  11.         FileUtils.writeByteArrayToFile(new File("c:\\girl.jpg"), uploadfile.getBytes());  
  12.         personService.update(p);  
  13.         return "redirect:/person/listAll.action";  
  14.     }  
  15. }  

(5)JSP显示错误内容-----------------注意这个只能使用springmvc中的标签才可以实现显示效果,所以这个很麻烦

[java] view plain copy
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>  
  3.   
  4. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  5. <html>  
  6.   <head>  
  7.   </head>  
  8.     
  9.   <body>  
  10. <!--如果想一次性显示所有的错误内容,直接下面代码-->  
  11. <sf:errors path="*" />  
  12. <sf:form action="${pageContext.request.contextPath}/person/update.action"   
  13.     method="post"   
  14.     modelAttribute="person"  
  15.     enctype="multipart/form-data"  
  16.     >  
  17.     <sf:hidden path="id"/>  
  18.     
  19.     修改人员信息 <br>  
  20.       
  21. <table>  
  22. <tr>  
  23.     <td>姓名</td>  
  24.     <td><sf:input path="name"/><sf:errors path="name" /></td>  
  25. </tr>  
  26. <tr>  
  27.     <td>年龄</td>  
  28.     <td><sf:input path="age"/><sf:errors path="age" /></td>  
  29. </tr>  
  30. <tr>  
  31.     <td>图片</td>  
  32.     <td><input type="file" name="uploadfile"/></td>  
  33. </tr>  
  34. <tr>  
  35.     <td><input type="submit" name="btnSubmit" value="保存"/></td>  
  36.     <td></td>  
  37. </tr>  
  38. </table>      
  39.   
  40. </sf:form>     
  41.   </body>  
  42. </html>  

二十三:SpringMVC中针对前端日期提交的控制

功能:当前端进行提交日期,比如2017-12-12的时候,而实体中的类型是Date类型,这样不进行转换就会出现问题,所以就需要针对性的有时候进行处理,只需要在Controller类中添加如下的方法就可以了:

[java] view plain copy
  1. @InitBinder  
  2.     //此方法用于日期的转换,如果未加,当页面日期格式转换错误,将报400错误,实际是因为此方法  
  3.     public void initBinder(WebDataBinder binder) {  
  4.         DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");  
  5.         dateFormat.setLenient(true);  
  6.         binder.registerCustomEditor(Date.classnew CustomDateEditor(dateFormat, true));  
  7.     }  


好了,当掌握这些知识点后,对于用springmvc的知识点还有利用springmvc+spring+mybatis进行开发,那是足够的了。如果还想学习Hibernate或者struts2的知识,都可以看我前面的文章的哦。。。。。总之,对于企业级的这些框架ssh+ssh,都进行了非常详细的讲解了。还会持续进行更新的哦!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值