spring 3以及之后版本的异步方法

这里我们来看看spring 3.0 以及以后版本中支持的@Async (方法异步)

其实在之前的程序中也没看到过有使用@Async 的,最近才接触到,想着如果使用异步缓存是不是响应速度会大幅提升那,就比如你去查询,发现缓存中没有数据,你要从数据库中获取数据,然后要把数据放到缓存中然后才能将数据展示到前台,其中将数据放到缓存的这个步骤占用了一部分时间,这样的话前台展示就比较慢了,所以如果保存到缓存这步使用异步处理方式那就可以节省这部分时间,响应速度相对会快一点,哈哈哈 个人理解如果有不对的地方还请指教……

下面来看看小demo 学学怎么使用

一、在web项目中将service 层的某个方法定义为异步方法

这里吧这个工程代码都贴出来吧:

1、web.xml 配置


  
  
  1. <?xml version=“1.0” encoding=“UTF-8”?>
  2. <web-app xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
  3. xmlns= “http://java.sun.com/xml/ns/javaee”
  4. xsi:schemaLocation= “http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd”
  5. id= “WebApp_ID” version= “3.0”>
  6. <display-name>website2 </display-name>
  7. <!– 加载spring容器配置 –>
  8. <listener>
  9. <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class>
  10. </listener>
  11. <!– 设置Spring容器加载配置文件路径 –>
  12. <context-param>
  13. <param-name>contextConfigLocation </param-name>
  14. <param-value>
  15. classpath:config/springmvc-servlet.xml,
  16. classpath:config/ApplicationContext.xml
  17. </param-value>
  18. </context-param>
  19. <!– 字符编码过滤器 –>
  20. <filter>
  21. <filter-name>encodingFilter </filter-name>
  22. <filter-class>org.springframework.web.filter.CharacterEncodingFilter </filter-class>
  23. <init-param>
  24. <param-name>encoding </param-name>
  25. <param-value>utf-8 </param-value>
  26. </init-param>
  27. <init-param>
  28. <param-name>forceEncoding </param-name>
  29. <param-value>true </param-value>
  30. </init-param>
  31. </filter>
  32. <filter-mapping>
  33. <filter-name>encodingFilter </filter-name>
  34. <url-pattern>*.do </url-pattern>
  35. </filter-mapping>
  36. <!– 前端控制器 –>
  37. <servlet>
  38. <servlet-name>springmvc </servlet-name>
  39. <servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class>
  40. <init-param>
  41. <param-name>contextConfigLocation </param-name>
  42. <param-value>classpath:config/springmvc-servlet.xml </param-value>
  43. </init-param>
  44. <!– 这个配置文件在容器启动的时候 就加载 –>
  45. <load-on-startup>1 </load-on-startup>
  46. </servlet>
  47. <servlet-mapping>
  48. <servlet-name>springmvc </servlet-name>
  49. <!– 拦截请求 –>
  50. <url-pattern>*.do </url-pattern>
  51. </servlet-mapping>
  52. <welcome-file-list>
  53. <welcome-file>index.html </welcome-file>
  54. <welcome-file>index.htm </welcome-file>
  55. <welcome-file>index.jsp </welcome-file>
  56. <welcome-file>default.html </welcome-file>
  57. <welcome-file>default.htm </welcome-file>
  58. <welcome-file>default.jsp </welcome-file>
  59. </welcome-file-list>
  60. </web-app>


2、ApplicationContext.xml 配置文件


  
  
  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"
  4. xmlns:tx= "http://www.springframework.org/schema/tx"
  5. xmlns:context= "http://www.springframework.org/schema/context"
  6. xmlns:aop= "http://www.springframework.org/schema/aop"
  7. xmlns:task= "http://www.springframework.org/schema/task"
  8. xsi:schemaLocation= "http://www.springframework.org/schema/beans
  9. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  10. http://www.springframework.org/schema/context
  11. http://www.springframework.org/schema/context/spring-context-3.2.xsd
  12. http://www.springframework.org/schema/aop
  13. http://www.springframework.org/schema/aop/spring-aop.xsd
  14. http://www.springframework.org/schema/task
  15. http://www.springframework.org/schema/task/spring-task-3.2.xsd
  16. http://www.springframework.org/schema/tx
  17. http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
  18. <!-- 加载配置JDBC文件 -->
  19. <context:property-placeholder location="classpath:db.properties" />
  20. <!-- 数据源 -->
  21. <bean id="dataSource"
  22. class= "org.springframework.jdbc.datasource.DriverManagerDataSource">
  23. <property name="driverClassName">
  24. <value>${jdbc.driverClassName}<span class="hljs-tag">&lt;/<span class="hljs-name">value</span>&gt;</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="26"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag">&lt;/<span class="hljs-name">property</span>&gt;</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="27"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"url"</span>&gt;</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="28"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag">&lt;<span class="hljs-name">value</span>&gt;</span>${jdbc.url} </value>
  25. </property>
  26. <property name="username">
  27. <value>${jdbc.username}<span class="hljs-tag">&lt;/<span class="hljs-name">value</span>&gt;</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="32"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag">&lt;/<span class="hljs-name">property</span>&gt;</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="33"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span>&gt;</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="34"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag">&lt;<span class="hljs-name">value</span>&gt;</span>${jdbc.password} </value>
  28. </property>
  29. </bean>
  30. <!--使用自动注入的时候要 添加他来扫描bean之后才能在使用的时候 -->
  31. <context:component-scan base-package="com.website.service ,com.website.dao" />
  32. <!-- 在使用mybatis时 spring使用sqlsessionFactoryBean 来管理mybatis的sqlsessionFactory -->
  33. <!-- 而像这种使用接口实现的方式 是使用sqlsessionTemplate来进行操作的,他提供了一些方法 -->
  34. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  35. <property name="dataSource" ref="dataSource" />
  36. <!-- mybatis配置文件路径 -->
  37. <property name="configLocation" value="" />
  38. <!-- 实体类映射文件路径,在开发中映射文件肯定是多个所以使用mybatis/*.xml来替代 -->
  39. <property name="mapperLocations" value="classpath:mybatis/UserMapping.xml" />
  40. </bean>
  41. <!--其实这里类的实例就是mybatis中SQLSession -->
  42. <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  43. <constructor-arg index="0">
  44. <ref bean="sqlSessionFactory" />
  45. </constructor-arg>
  46. </bean>
  47. <!--使用spring的异步@Async简单定义方式 -->
  48. <!-- <task:annotation-driven /> -->
  49. <!--异步定义推荐方式 -->
  50. <task:executor id="executor" pool-size="15" />
  51. <task:scheduler id="scheduler" pool-size="30" />
  52. <task:annotation-driven executor="executor" scheduler="scheduler" />
  53. <!-- 定义事务管理器 -->
  54. <bean id="transactionManager"
  55. class= "org.springframework.jdbc.datasource.DataSourceTransactionManager">
  56. <property name="dataSource" ref="dataSource" />
  57. </bean>
  58. <!-- 下面使用aop切面的方式来实现 -->
  59. <tx:advice id="TestAdvice" transaction-manager="transactionManager">
  60. <!--配置事务传播性,隔离级别以及超时回滚等问题 -->
  61. <tx:attributes>
  62. <tx:method name="save*" propagation="REQUIRED" />
  63. <tx:method name="del*" propagation="REQUIRED" />
  64. <tx:method name="update*" propagation="REQUIRED" />
  65. <tx:method name="add*" propagation="REQUIRED" />
  66. <tx:method name="*" rollback-for="Exception" />
  67. </tx:attributes>
  68. </tx:advice>
  69. <aop:config>
  70. <!--配置事务切点 -->
  71. <aop:pointcut id="services"
  72. expression= "execution(* com.website.service.*.*(..))" />
  73. <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
  74. </aop:config>
  75. </beans>

这里注意,在使用spring的异步的时候要有:

xmlns:task="http://www.springframework.org/schema/task"
  
  

  
  
  1. http://www.springframework.org/schema/task
  2. http://www.springframework.org/schema/task/spring-task-3.2.xsd

这三个是必须要有的,而且要配置下面两种中的一种,来定义@Async 异步方法


  
  
  1. <!--使用spring的异步@Async简单定义方式 -->
  2. <!-- <task:annotation-driven /> -->
  3. <!--异步定义推荐方式 -->
  4. <task:executor id="executor" pool-size="15" />
  5. <task:scheduler id="scheduler" pool-size="30" />
  6. <task:annotation-driven executor="executor" scheduler="scheduler" />
ok 除了这两个其他的都和其他的spring一般配置是一样的没有什么不同。


3、springmvc 配置:


  
  
  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:aop= "http://www.springframework.org/schema/aop"
  4. xmlns:context= "http://www.springframework.org/schema/context"
  5. xmlns:mvc= "http://www.springframework.org/schema/mvc" xmlns:util= "http://www.springframework.org/schema/util"
  6. xmlns:tx= "http://www.springframework.org/schema/tx" xmlns:jdbc= "http://www.springframework.org/schema/jdbc"
  7. xmlns:cache= "http://www.springframework.org/schema/cache"
  8. xsi:schemaLocation= "
  9. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  10. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
  11. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
  12. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
  13. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
  14. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
  15. http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
  16. http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">
  17. <!-- 注解驱动 -->
  18. <mvc:annotation-driven />
  19. <!-- 扫描 -->
  20. <context:component-scan base-package="com.website.controller"> </context:component-scan>
  21. <!-- 视图解析器 -->
  22. <bean id="viewResolver"
  23. class= "org.springframework.web.servlet.view.InternalResourceViewResolver">
  24. <property name="prefix" value="/WEB-INF/view/">
  25. </property>
  26. <property name="suffix" value=".jsp"> </property>
  27. </bean>
  28. </beans>

4、mybatis 的映射文件配置


  
  
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <!--这个namespace + 下面的id 就是一个完整的路径,在dao层我们写了完整的路径之后mybatis就是映射这个文件中的相关sql语句 -->
  5. <mapper namespace="com.website.userMapper">
  6. <!-- parameterType就是你接受的参数的类型, -->
  7. <!-- 添加用户信息 -->
  8. <insert id="insertUser" parameterType="java.util.Map">
  9. insert into user(id,name,password) values(#{id},#{name},#{password})
  10. </insert>
  11. </mapper>

5、db.properties 配置


  
  
  1. jdbc.driverClassName=com.mysql.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf8
  3. jdbc.username=root
  4. jdbc.password=admin


ok 到这里所有的配置文件都全了,你可能也发现了,其实和我们平时配置的很像,除了applicationContext.xml 中的那两处不同之外其他的都是一样的。


下面我们来看看代码,看看和不是异步的有什么不同之处:

6、前端请求代码:


  
  
  1. function profilep() {
  2. // 组装json格式数据
  3. var mydata = '{"name":"' + $(<span class="hljs-string">'#name'</span>).val() + <span class="hljs-string">'","id":"'</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> + $( '#id').val() + '","password":"' + $(<span class="hljs-string">'#password'</span>).val()</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> + <span class="hljs-string">'"}'</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $.ajaxSetup({
  4. contentType : 'application/json'
  5. });
  6. $.post( 'http://localhost:18080/website2/user/save2.do', mydata,
  7. function(data) {
  8. alert( "id: " + data.id + "\nname: " + data.name
  9. + "\password: " + data.password);
  10. }, 'json');
  11. }

7、Controller (控制层)


  
  
  1. package com.website.controller;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Controller;
  8. import org.springframework.web.bind.annotation.RequestBody;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RequestMethod;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. import com.website.po.User;
  13. import com.website.service.UserService;
  14. /**
  15. * @author WHD data 2016年6月5日
  16. */
  17. @Controller
  18. @RequestMapping(value = "/user")
  19. public class UserController {
  20. @Autowired
  21. private UserService userService;
  22. @RequestMapping(value = "/list.do")
  23. public String list() {
  24. return "info";
  25. }
  26. @ResponseBody
  27. @RequestMapping(value = "/save2.do" ,method = RequestMethod.POST)
  28. // 知己接收对象,因@RequestBody spring 帮我们处理了 协议到对象的这个过程
  29. public User info2(@RequestBody User user) {
  30. String id = user.getId();
  31. String name = user.getName();
  32. String password = user.getPassword();
  33. Map<String, String> map = new HashMap<String, String>();
  34. map.put( "id", id);
  35. map.put( "name", name);
  36. map.put( "password", password);
  37. try {
  38. //异步方法,先执行的是异步方法,但是异步方法执行时间比较长,所以在异步方法执行期间同步方法在执行
  39. userService.async();
  40. //同步方法
  41. userService.saveUser(map);
  42. } catch (Exception e) {
  43. // TODO Auto-generated catch block
  44. e.printStackTrace();
  45. }
  46. User user2= new User(id,name,password);
  47. // 直接返回对象,因@ResponseBody spring 会帮我们处理对象和协议之间的转化
  48. return user2;
  49. }
  50. }

当前端请求到来时执行info2 这个方法,其中就调用了两个service 层的方法即一个是异步方法一个是同步方法,下面看看service层的代码。

8、service 层


  
  
  1. package com.website.service;
  2. import java.util.Map;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.scheduling.annotation.Async;
  5. import org.springframework.stereotype.Service;
  6. import com.website.dao.UserDao;
  7. /**
  8. * @author WHD data 2016年6月5日
  9. */
  10. @Service( "userService")
  11. public class UserService {
  12. @Autowired
  13. private UserDao userDao;
  14. public void saveUser(Map<String, String> map) throws Exception {
  15. userDao.saveUser(map);
  16. }
  17. @Async
  18. public void async(){
  19. System.out.println( "异步开始-----------》》》");
  20. try {
  21. Thread.sleep( 10* 1000);
  22. } catch (InterruptedException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26. System.out.println( "异步结束-----------》》》");
  27. }
  28. }
你可能发现了,service 层和以往有所不同,那就是在异步方法上有@Async 而同步方法上就没有了,所以方法是否别定义为异步方法,处理在ApplicationContext.xml 配置文件中添加了task 以及task注解之外就是异步方法上的@Async 这个注解了,其他的都一样。

9、dao 层


  
  
  1. package com.website.dao;
  2. import java.util.Map;
  3. import org.mybatis.spring.SqlSessionTemplate;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Repository;
  6. /**
  7. * @author WHD data 2016年6月5日
  8. */
  9. @Repository( "userDao")
  10. public class UserDao {
  11. @Autowired
  12. private SqlSessionTemplate sqlSession;
  13. public void saveUser(Map<String, String> map) {
  14. int end = sqlSession.insert( "com.website.userMapper.insertUser", map);
  15. System.out.println( "end" + end);
  16. }
  17. }

ok到这里所以的代码就完了,下面我们看看代码执行时打印的结果:


  
  
  1. 异步开始-----------》》》
  2. end1
  3. 异步结束-----------》》》

ok 看到这个方法就知道了,这个异步方法执行了,首先开始执行异步方法,因为他是异步方法所以同时开始执行了同步方法,但是异步方法执行时间比较长,所以同步方法结束后很久异步方法才结束,这样就是一个异步执行的效果,这也是我们想要的结果!


二、spring3.0之后的定时任务中使用@Async

上面的代码是在springmvc+spring +mybatis 的web项目中使用了异步方法,而在spring3.0以及之后的版本中使用异步方法,下面我们来看看小demo

这里的定时任务和mybatis 或hibernate 没有进行整合,只是spring 简单来看看,因为整合不影响异步执行,只是多了上面说的那三个东西,即配置文件中的task 以及注解就可以了,所以下面就看看简单spring的定时任务的异步方法。

1、spring 的配置文件ApplicationContext.xml


  
  
  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"
  4. xmlns:jee= "http://www.springframework.org/schema/jee"
  5. xmlns:tx= "http://www.springframework.org/schema/tx"
  6. xmlns:task= "http://www.springframework.org/schema/task"
  7. xmlns:context= "http://www.springframework.org/schema/context"
  8. xsi:schemaLocation= "http://www.springframework.org/schema/beans
  9. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  10. http://www.springframework.org/schema/tx
  11. http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  12. http://www.springframework.org/schema/jee
  13. http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
  14. http://www.springframework.org/schema/task
  15. http://www.springframework.org/schema/task/spring-task-3.0.xsd
  16. http://www.springframework.org/schema/context
  17. http://www.springframework.org/schema/context/spring-context-3.0.xsd"
  18. default-lazy-init= "true">
  19. <description>Spring </description>
  20. <context:component-scan base-package="com.service" />
  21. <!-- 异步配置 -->
  22. <task:executor id="executor" pool-size="50" />
  23. <task:scheduler id="scheduler" pool-size="100" />
  24. <task:annotation-driven executor="executor"
  25. scheduler= "scheduler" />
  26. <!-- 配置自定义任务 -->
  27. <bean name="scrapydTask" lazy-init="false" class="com.controller.Test"> </bean>
  28. </beans>

这个是spring 配置文件,其中的和之前一样,要引入task 并且要有task注解。

因为这里是spring定时任务,所以要有定时任务的配置文件 

ok 因为只是一个简单的spring定时任务所以也就只有一个spring配置文件,下面我们看看代码:


2、controller 控制层:


  
  
  1. package com.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.scheduling.annotation.Async;
  5. import org.springframework.scheduling.annotation.EnableAsync;
  6. import org.springframework.scheduling.annotation.Scheduled;
  7. import org.springframework.stereotype.Component;
  8. import com.service.Test2;
  9. @Component
  10. public class Test {
  11. @Autowired
  12. private Test2 test2;
  13. @Scheduled(cron = "0 0/1 * * * *")
  14. void schedule(){
  15. test2.asyncFun();
  16. test2.other();
  17. }
  18. }

我们看到这里有两个方法一个是异步方法,一个是同步方法,我们首先调用的是异步方法,之后调用了同步方法,所以我们想要的结果就是首先开始执行异步方法,当开始执行后接着执行同步方法,这样就不用等着异步方法执行完了在执行,如果添加缓存什么的我们就不用管了,所以这样的方法还是比较好的。当然我们也看到了那个定时任务的注解。

3、service层


  
  
  1. package com.service;
  2. import org.springframework.scheduling.annotation.Async;
  3. import org.springframework.stereotype.Service;
  4. @Service( "test2")
  5. public class Test2 {
  6. @Async
  7. public void asyncFun(){
  8. System.out.println( "------异步方法开始");
  9. try {
  10. Thread.sleep( 15* 1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println( "------异步方法结束");
  15. }
  16. public void other(){
  17. try {
  18. Thread.sleep( 3* 1000);
  19. } catch (InterruptedException e) {
  20. // TODO Auto-generated catch block
  21. e.printStackTrace();
  22. }
  23. System.out.println( "同步方法");
  24. }
  25. }

看到这个方法中我们的异步方法有个@Async 这样的注解,这个注解就是使这个方法定义为一个异步方法。

4、执行结果:


这就是执行的结果,这是不是想要的那,是的这就是我们想要的异步执行!

5、ok到这里定时任务的异步就完成了,我们简单的总结一下,首先我们要在spring的配置文件中添加定时注解,异步注解,并且要在定义为异步方法的地方添加异步注解。

具体的配置可以参考上面的小demo 

ok  到此就结束了,代码写的比较简单,如有错误还请指教 谢谢……




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值