SpringBoot快速入门

目录:

    SpringBoot简介

    springBoot接口返回json

    SpringBoot的热部署

    SpringBoot资源文件管理配置

    SpringBoot配置全局的异常捕获

 

 


 

SpringBoot简介

  微框架,与Spring4一起诞生,比如@RestController

  可以快速上手,整合了一些子项目(开源框架或者第三方开源库)

  可以依赖很少的配置就可以十分快速的搭建并允许项目

 

SpringBoot特点

  基于Spring作的开发,使开发者快速入门,门槛很低。(Spring全家桶)

  Springboot可以创建独立运行的应用而不依赖于容器

  不需要打包成war包,可以放入tomcat中直接运行

  提供maven极简配置,缺点是会引入很多你不需要的包

  根据项目来依赖,从而配置Spring,需要什么配什么

  提供可视化的相关功能,方便监控,比如性能,应用的健康程度等

  简化配置,不用再看过多的xml

  为微服务SpringCloud铺路,SpringBoot可以整合很多各式各样的框架来构建微服务,比如dubbo,thrift等等

 

SpringBoot使用场景:

  有Spring的地方都行

  J2EE/web项目

  微服务

 

 


 

 

 

springBoot接口返回json

首先我们要介绍一个注解@RestController(@RestController = @Controller + @ResponseBody)

该注解就好比@Controller的功能加上@ResponseBody的作用

用法:(下面的代码在访问后返回的就是json)

//@Controller
@RestController //@RestController = @Controller + @ResponseBody
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getUser")
    //@ResponseBody
    public User getUser() {
        User u = new User();
        u.setName("kerry");
        u.setAge(18);
        u.setBirthday(new Date());
        u.setPassword("1977");
        return u;
    }
}

Json的使用小知识:

   下面代码中有三个较为常用的注解:

     @JsonIgnore:使返回Json的时候不会返回该成员变量的信息

    @JsonInclude(Include.NON_NULL):当成员变量的值为空的时候不会返回该信息

    @JsonFormat():将Date类型的成员变量按照模板返回处理

public class User {
    
    private String name;
    @JsonIgnore
    private String password;
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss a", locale = "zh" ,timezone = "GMT+8")
    private Date birthday;
    @JsonInclude(Include.NON_NULL)
    private String desc;
}    

 

 

 


 

 

SpringBoot的热部署

在我们项目的开发期间经常遇到说修改一点代码之后要重启项目重新部署。在我们使用SpringBoot开发的时候也是需要重启部署项目,

这样的话对于我们项目的开发是复杂的。所以Spring为此提供了(devtools)进行SpringBoot的热部署。

 

首先我们要在pom.xml文件中引入:

 

然后要在src/main/resources包下操作application.properties文件

 

为该文件设置上相关配置

 

 

 

 

 


 

SpringBoot资源文件管理配置

  在SpringMVC中也会有一些properties配置文件,当我们的Controller和Service想要使用配置文件里的内容时,只需要使用@Value即可导入进来。在SpringBoot也有这种功能.

  

首先我们在pom.xml文件中导入依赖的包:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
</dependency>

然后在src/main/resources包下创建一个resource.properties

    =====      

 

 

 

 

 然后创建Resource类,然后为其配置上相对应的注解

  @Configuration  :用于定义配置类,可替换xml配置文件

  @ConfigurationProperties(prefix = "com.imooc.opensource")  :设置上属性值前缀

  @PropertySource(value = "classpath:resource.properties")  :设置属性源 :设置为classpath路径下 (src/main/resources包下的文件在打包的时候会存放在classpath根下)

@Configuration
@ConfigurationProperties(prefix = "com.imooc.opensource")
@PropertySource(value = "classpath:resource.properties")
public class Resource {
    private String name;
    private String website;
    private String language;
  //此处省略成员变量的Getter / Setter 方法
}

 

配置完上面的代码,此时我们在加载到这个Bean的时候,就会将resource.properties配置文件中的配置注入到Resource对应的实例Bean中提供使用

例如在Controller中使用:

@RestController
public class Controller {
    @Autowired
    private Resource resource;  //在这从容器中获取到Resource的Bean实例,此时Bean的属性值已经从配置文件中注入了

    @RequestMapping("/getResource")
    public MyJsonResult getResource() {
        Resource bean = new Resource();//新创建一个Resource对象 
        BeanUtils.copyProperties(resource, bean);//将实例Bean的属性Copy给新创建的对象(执行完这一步,新创建的对象也就拥有了相对应的属性值)
        return MyJsonResult.ok(bean);//将新创建的包装对象返回。
    }

}

访问 http://localhost:8080/getResource 链接之后,结果如下

 

 SpringBoot资源文件配置server(服务器)

  在我们使用SpringBoot的时候会使用其默认的Tomcat、要想在资源文件中配置Tomcat相关属性

 

首先要在src/main/resources包下操作application.properties文件

 

为该文件设置上相关配置

############################################################
#
# Server 服务端相关配置
#
############################################################
# 配置api端口号  #默认8080
#server.port=8088
# 配置context-path, 一般来说这个配置在正式发布的时候不配置
#server.context-path=/IMooc
# 错误页,指定发生错误时,跳转的URL --> BasicErrorController
#server.error.path=/error
# session最大超时时间(分钟),默认为30分钟
server.session-timeout=60
# 该服务绑定IP地址,启动服务器时如本机不是该IP地址则抛出异常启动失败, 
# 只有特殊需求的情况下才配置, 具体根据各自的业务来设置
#server.address=192.168.1.2

############################################################
# Server - tomcat 相关常用配置
############################################################
# tomcat最大线程数, 默认为200
#server.tomcat.max-threads=250
# tomcat的URI编码
server.tomcat.uri-encoding=UTF-8
# 存放Tomcat的日志、Dump等文件的临时文件夹,默认为系统的tmp文件夹
#(如:C:%users\Shanhy\AppData\Local\Temp)
#server.tomcat.basedir=H:/springboot-tomcat-tmp
# 打开Tomcat的Access日志,并可以设置日志格式的方法:
#server.tomcat.access-log-enabled=true
#server.tomcat.access-log-pattern=
# accesslog目录,默认在basedir/logs
#server.tomcat.accesslog.directory=
# 日志文件目录
#logging.path=H:/springboot-tomcat-tmp
# 日志文件名称,默认为spring.log
#logging.file=myapp.log

 


 

SpringBoot配置全局的异常捕获

 

WEB页面跳转:普通异常:

(一)配置一个抛出异常的Controller(控制器),这样当我们访问http://localhost:8080/ec/error的时候就会抛出异常

@Controller
@RequestMapping("/ec")
public class ErrorController {
    @RequestMapping("/error")
    public String error() {
        int i = 5/0;
        return "tymeleaf/error";
    }
}

(二)配置上异常处理器,当我们在执行方法抛出异常的时候会进行拦截后处理(由于上面我们配置了thymeleaf 静态资源配置,所以当我们直接在ViewName上设置了“error”的时候会返回到error.html页面上)

@ControllerAdvice //Advice控制器
public class MyExceptionHandler {//异常执行器。
    public static final String IMOOC_ERROR_VIEW = "error";
    @ExceptionHandler(value = Exception.class)//对异常进行捕获
    public Object errorHandler(HttpServletRequest request,HttpServletResponse response,Exception e)throws Exception{
        e.printStackTrace();
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception",e);
        mav.addObject("url",request.getRequestURL());
        mav.setViewName(IMOOC_ERROR_VIEW);
        return mav;
    }
}

(三)当我们访问http://localhost:8080/ec/error使其抛出异常的时候就被呗异常处理器拦截处理到error.html(下面是error.html和结果返回)

  ===   

 

 

AJAX异常拦截处理:

(一)配置一个抛出异常的Controller(控制器),这样当我们访问http://localhost:8080/ec/ajaxerror的时候就会去访问ajaxerror.html页面

@Controller
@RequestMapping("/ec")
public class ErrorController {
    @RequestMapping("/ajaxerror")
    public String ajaxerror() { 
        return "thymeleaf/ajaxerror";
    }
    @RequestMapping("/GetAjaxerror")
    public MyJsonResult getAjaxerror() {
        System.out.println(1);
        int a= 1/0;
        return MyJsonResult.ok();
    }
}

(二)配置ajaxerror.html页面(由于我们是模拟ajax异常,首先导入jquery.js文件,后在<body>标签中去加载ajaxerror.js文件)

<html>
    <head lang="en">
        <meta charset="UTF-8" />
        <title></title>
        <script th:src="@{/static/js/jquery.js}"></script>
    </head>
    <body>
        <h1 >测试ajax错误异常</h1>
        <script th:src="@{/static/js/ajaxerror.js}"></script>
    </body>
</html>

(三)配置ajaxerror.js文件(该文件会去访问用ajax的方式去访问http://localhost:8080/ec/GetAjaxerror链接,也就是上面Controller配置好的,然后会抛出异常)

$.ajax({
    url:"/ec/GetAjaxerror",
    type:"POST",
    async:false,
    success:function(data){
        if(data.status == 200 && data.mgs == "OK"){
            alert("success");
        }else{
            alert("发生异常:"+data.msg);
        },
        error:function(response,ajaxOptions,thrownError){
            alert("error");
        }
    }
});

 

(四)配置Ajax异常处理器(拦截Exception异常后,将其转成json形式返回回去)

@RestControllerAdvice
public class MyAjaxExceptionHandler {
    
    @ExceptionHandler(value = Exception.class)
    public MyJsonResult defaultErrorHandler(HttpServletRequest req,Exception e)throws Exception{
        e.printStackTrace();
        return MyJsonResult.errorException(e.getMessage());
    }
    

}

 

统一返回异常的形式:

在上面我们有普通访问Web抛出的异常,也有Ajax方式访问抛出的异常。这两种异常呢一个是返回异常视图(ModelAndView)一个是返回Json对象。那么当我们抛出异常的时候怎么能确定拦截返回的是哪个呢。

那我们只能对它们两进行整合,然后区分出返回的对象。

 

(一)配置异常处理器(我们只需要在异常处理的时候对Request(请求)进行参数上的判断,然后区分出是不是Ajax请求就行了)

@ControllerAdvice //Advice控制器
public class MyExceptionHandler {//异常执行器。
    public static final String IMOOC_ERROR_VIEW = "error";

@ExceptionHandler(value = Exception.class)//对异常进行捕获 public Object errorHandler(HttpServletRequest request,HttpServletResponse response,Exception e)throws Exception{ e.printStackTrace(); if(isAjax(request)) { return MyJsonResult.errorException(e.getMessage()); }else { ModelAndView mav = new ModelAndView(); mav.addObject("exception",e); mav.addObject("url",request.getRequestURL()); mav.setViewName(IMOOC_ERROR_VIEW); return mav; } } private boolean isAjax(HttpServletRequest request) { return (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With").toString())); } }

 

 


 

SpringBoot整合redis

(一)pom.xml中需要引入相关依赖

<!-- 引入redis依赖 -->
 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

 

(二)资源文件中对redis进行配置

  在src/main/resources包下操作application.properties文件

############################################################
# REDIS 配置
############################################################
# Redis数据库索引(默认为0)
spring.redis.database=1
# Redis服务器地址
spring.redis.host=192.168.29.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=1000
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=2
# 连接超时时间(毫秒)
spring.redis.timeout=0

(三)引入redis工具类

package com.imooc.utils;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisKeyValueTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * 
 * @Title: RedisOperator.java
 * @Package com.itzixi.web.component
 * @Description: 使用redisTemplate的操作实现类 Copyright: Copyright (c) 2016
 *               Company:FURUIBOKE.SCIENCE.AND.TECHNOLOGY
 * 
 * @author leechenxiang
 * @date 2017年9月29日 下午2:25:03
 * @version V1.0
 */
@Component
public class RedisOperator {
    
//    @Autowired
//    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // Key(键),简单的key-value操作

    /**
     * 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。
     * 
     * @param key
     * @return
     */
    public long ttl(String key) {
        return redisTemplate.getExpire(key);
    }
    
    /**
     * 实现命令:expire 设置过期时间,单位秒
     * 
     * @param key
     * @return
     */
    public void expire(String key, long timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
    
    /**
     * 实现命令:INCR key,增加key一次
     * 
     * @param key
     * @return
     */
    public long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 实现命令:DEL key,删除一个key
     * 
     * @param key
     */
    public void del(String key) {
        redisTemplate.delete(key);
    }

    // String(字符串)

    /**
     * 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key)
     * 
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 实现命令:SET key value EX seconds,设置key-value和超时时间(秒)
     * 
     * @param key
     * @param value
     * @param timeout
     *            (以秒为单位)
     */
    public void set(String key, String value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令:GET key,返回 key所关联的字符串值。
     * 
     * @param key
     * @return value
     */
    public String get(String key) {
        return (String)redisTemplate.opsForValue().get(key);
    }

    // Hash(哈希表)

    /**
     * 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value
     * 
     * @param key
     * @param field
     * @param value
     */
    public void hset(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 实现命令:HGET key field,返回哈希表 key中给定域 field的值
     * 
     * @param key
     * @param field
     * @return
     */
    public String hget(String key, String field) {
        return (String) redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
     * 
     * @param key
     * @param fields
     */
    public void hdel(String key, Object... fields) {
        redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。
     * 
     * @param key
     * @return
     */
    public Map<Object, Object> hgetall(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // List(列表)

    /**
     * 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头
     * 
     * @param key
     * @param value
     * @return 执行 LPUSH命令后,列表的长度。
     */
    public long lpush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 实现命令:LPOP key,移除并返回列表 key的头元素。
     * 
     * @param key
     * @return 列表key的头元素。
     */
    public String lpop(String key) {
        return (String)redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。
     * 
     * @param key
     * @param value
     * @return 执行 LPUSH命令后,列表的长度。
     */
    public long rpush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

}
redis工具类

(四)在Controller中使用

package com.imooc.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.imooc.pojo.MyJsonResult;
import com.imooc.pojo.SysUser;
import com.imooc.pojo.User;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;

@RestController
@RequestMapping("redis")
public class RedisController {
    
    @Autowired
    private StringRedisTemplate strRedis;
    
    @Autowired
    private RedisOperator redis;
    
    @RequestMapping("/test")
    public MyJsonResult test() {
        
        strRedis.opsForValue().set("imooc-cache", "hello 慕课网~~~~~~");
        
        SysUser user = new SysUser();
        user.setId("100111");
        user.setUsername("imooc");
        user.setPassword("abc123");
        user.setIsDelete(0);
        user.setRegistTime(new Date());
        strRedis.opsForValue().set("json:user", JsonUtils.objectToJson(user));
        
        SysUser jsonUser = JsonUtils.jsonToPojo(strRedis.opsForValue().get("json:user"), SysUser.class);
        
        return MyJsonResult.ok(jsonUser);
    }
    
    @RequestMapping("/getJsonList")
    public MyJsonResult getJsonList() {
        
        User user = new User();
        user.setAge(18);
        user.setName("慕课网");
        user.setPassword("123456");
        user.setBirthday(new Date());
        
        User u1 = new User();
        u1.setAge(19);
        u1.setName("imooc");
        u1.setPassword("123456");
        u1.setBirthday(new Date());
        
        User u2 = new User();
        u2.setAge(17);
        u2.setName("hello imooc");
        u2.setPassword("123456");
        u2.setBirthday(new Date());
        
        List<User> userList = new ArrayList<>();
        userList.add(user);
        userList.add(u1);
        userList.add(u2);
        
        redis.set("json:info:userlist", JsonUtils.objectToJson(userList), 2000);
        
        String userListJson = redis.get("json:info:userlist");
        List<User> userListBorn = JsonUtils.jsonToList(userListJson, User.class);
        
        return MyJsonResult.ok(userListBorn);
    }
}
Controller代码

 


 

SpringBoot 整合定时任务task

使用注解@EnableScheduling开启定时任务,会自动扫描

定义@Component作为组件被容器扫描

@Scheduled支持cron表达式,表达式生成地址:http://cron.qqe2.com

 

(一)在类上添加上@EnableScheduling标签

@SpringBootApplication
//@MapperScan(basePackages = "com.imooc.mapper")//扫描mybatis mapper包路径
@ComponentScan(basePackages = {"com.imooc","org.n3r.idworker"})//扫描所以需要的包,包含一些自用的工具包  所在的路径
@EnableScheduling//开启定时任务
public class ImoocApplication {
    public static void main(String[] args) {
        SpringApplication.run(ImoocApplication.class, args);
    }
}

(二)配置上定时器执行任务

@Component
public class TestTask {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    //定义每过3秒执行任务
    @Scheduled(fixedRate = 3000)
    public void reportCurrentTime() {
        System.out.println("现在时间:"+dateFormat.format(new Date()));
    }
}

 

 


 

SpringBoot异步执行程序

使用注解@EnableAsnc开启异步,会自动扫描

定义@Componet @Async 作为组件被容器扫描执行

 

(一)在Application类上开启异步

@SpringBootApplication
//@MapperScan(basePackages = "com.imooc.mapper")//扫描mybatis mapper包路径
@ComponentScan(basePackages = {"com.imooc","org.n3r.idworker"})//扫描所以需要的包,包含一些自用的工具包  所在的路径
@EnableAsync//开启异步调用方法
public class ImoocApplication {
    public static void main(String[] args) {
        SpringApplication.run(ImoocApplication.class, args);
    }
}

(二)定义异步执行方法(如果不加上@Async那会是同步的,会耽误很多时间)

@Component
public class AsyncTask {
    @Async
    public Future<Boolean> doTask11() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("任务1耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>(true);
    }
    @Async
    public Future<Boolean> doTask22() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(700);
        long end = System.currentTimeMillis();
        System.out.println("任务2耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>(true);
    }
    @Async
    public Future<Boolean> doTask33() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(600);
        long end = System.currentTimeMillis();
        System.out.println("任务3耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>(true); 
    }
}

(三)此时创建类调用

@RestController
@RequestMapping("tasks")
public class DoTask {
    @Autowired
    private AsyncTask asyncTask;
    @RequestMapping("test1")
    public String test1() throws Exception {
        
        long start = System.currentTimeMillis();
        Future<Boolean> a = asyncTask.doTask11();
        Future<Boolean> b = asyncTask.doTask22();
        Future<Boolean> c = asyncTask.doTask33(); 
        while (!a.isDone() || !b.isDone() || !c.isDone()) {
            if (a.isDone() && b.isDone() && c.isDone()) {
                break;
            }
        }
        long end = System.currentTimeMillis();
        String times = "任务全部完成,总耗时:" + (end - start) + "毫秒";
        System.out.println(times);
        return times;
    }
}

 

SpringBoot异步执行使用场景

发生信息,发生邮件,App消息推送,节省运维凌晨发布任务时间提供效率

 


 

 

SpringBoot拦截器的使用

(一)使用注解@Configuration配置拦截器  +  继承WebMvcConfigurerAdapter  +  重写addInterceptors添加需要的拦截器地址

 

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        / /拦截器按照顺序执行
        registry.addInterceptor(new TwoInterceptor()).addPathPatterns("/two/**").addPathPatterns("/one/**");
        registry.addInterceptor(new OneInterceptor()).addPathPatterns("/one/**");
    }
}

(二)配置拦截器执行类

public class OneInterceptor implements HandlerInterceptor  {

    // 在请求处理之前进行调用(Controller方法调用之前)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
            Object object) throws Exception {
        
        System.out.println("被one拦截,放行...");
        return true;
        
        /*if (true) {
            returnErrorResponse(response, IMoocJSONResult.errorMsg("被one拦截..."));
        }
        
        return false;*/
    }
    
    //请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,  Object object, ModelAndView mv)throws Exception {
    }
    
    //在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception ex)throws Exception {
    }
    
  //拦截方法
public void returnErrorResponse(HttpServletResponse response, MyJsonResult result) throws IOException, UnsupportedEncodingException { OutputStream out=null; try{ response.setCharacterEncoding("utf-8"); response.setContentType("text/json"); out = response.getOutputStream(); out.write(JsonUtils.objectToJson(result).getBytes("utf-8")); out.flush(); } finally{ if(out!=null){ out.close(); } } } }

 

posted @ 2021-03-08 20:22  _kerry  阅读(128)  评论(0编辑  收藏  举报