无风无影

   ::  :: 新随笔  ::  ::  :: 管理

SpringBoot常见解答

一、什么是 Spring Boot? Spring Boot、Spring MVC 和 Spring 有什么区别?

  Spring Boot 并不是一个框架,而是Spring组件一站式解决方案,主要是简化了使用Spring的难度;它是一种创建独立应用程序的更简单方法,只需要很少或没有配置,主要通过自动配置和启动项来解决Spring繁重的配置。
  Spring最重要的特征是依赖注入。所有 SpringModules 不是依赖注入就是 IOC 控制反转。恰当的使用 DI 或者是 IOC,可以实现松耦合的应用。
  SpringMvc:Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet,MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单。

  问:springboot与spring的区别。
  答案:1)Java在集成spring框架时需要配置大量的配置文件,开发效率低。
           2)spring boot优于spring,配置简单,而且可以集成spring框架的项目。

 

二、Spring Boot优点?为什么要用springboot?

  Spring Framework旨在简化J2EE企业应用程序开发。Spring Boot Framework旨在简化Spring开发。

  主要优点如下:

  1)更少的xml,更少的配置和注释

  2)简化maven配置和jar包冲突

  3)开发或工程时间明显减少,通常会提高整体生产力

  4)很容易地与Spring生态系统集成

  5)很容易地与Spring生态系统集成

  6)独立运行Spring项目 Spring boot 可以以jar包形式独立运行,嵌入式HTTP服务器,如Tomcat和Jetty,可以轻松地开发和测试web应用程序

  7)Spring Boot提供命令行接口(CLI)工具,用于开发和测试Spring Boot应用程序,如Java或Groovy。

  问:你觉得 Spring Boot 最大的优势是什么呢?
  答:Spring Boot 的最大的优势是“约定优于配置“。“约定优于配置“是一种软件设计范式,开发人员按照约定的方式来进行编程,可以减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。

  问:Spring Boot 中 “约定优于配置“的具体产品体现在哪里。
  答:Spring Boot Starter、Spring Boot Jpa 都是“约定优于配置“的一种体现。都是通过“约定优于配置“的设计思路来设计的,Spring Boot Starter 在启动的过程中会根据约定的信息对资源进行初始化;Spring Boot Jpa 通过约定的方式来自动生成 Sql ,避免大量无效代码编写。

  问:sprinboot的核心功能和使用优点。
  答:核心功能:内嵌servlet容器(tomcat,jetty) 提供了start的pom配置,简化了maven的配置 自动配置spring的bean,如果不满足开发需求,可自定义bean的自动化配置。
    使用优点:快速搭建项目,与主流框架集成无需配置,部署简单

、Spring Boot 的核心配置文件有哪几个?它们的区别是什么?

   主要包含:application 和 bootstrap 配置文件
  application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。
  bootstrap 配置文件有以下几个应用场景。

  •   使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
  •   一些固定的不能被覆盖的属性;
  •   一些加密/解密的场景;

四、Spring Boot 的配置文件有哪几种格式?它们有什么区别?
  主要包含:.properties 和 .yml,它们的区别主要是书写格式不同。

  
1).properties
    app.user.name = javastack

2).yml
    app:
    user:
    name: javastack
    注意:.yml 格式不支持 @PropertySource 注解导入配置。
View Code
  •    bootstrap.properties/bootstrap.yml 用于配置无需重写的系统常量,例如springcloud项目用到的config配置中心的连接属性等.加载优先级高于application.properties.
  •   application.properties/application.yml 用于配置重写springboot项目的默认框架属性,例如:重写tomcat,springmvc,日志框架等默认属性.主要提供给spring框架加载使用.

注: properties后缀名与yml后缀名配置文件二选一即可. 两种不同格式的配置文件而已.

五、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
  启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,它主要组合包含了以下 3 个注解:

  @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

  @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = {                DataSourceAutoConfiguration.class })。

  @ComponentScan:Spring组件扫描。


六、开启 Spring Boot 特性有哪几种方式?

  • 1)继承spring-boot-starter-parent项目
  • 2)导入spring-boot-dependencies项目依赖

七、、运行 Spring Boot 有哪几种方式?

  • 1)打包用命令或者放到容器中运行
  • 2)用 Maven/ Gradle 插件运行
  • 3)直接执行 main 方法运行

八、Spring Boot 自动配置原理是什么或什么是自动配置?

  这个是因为@SpringBootApplication 注解的原因,我们知道 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。

  @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制

  @ComponentScan: 扫描被@Component (@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类。

  @Configuration:允许在上下文中注册额外的bean或导入其他配置类

  @EnableAutoConfiguration是启动自动配置的关键,源码如下(建议自己打断点调试,走一遍基本的流程):

  @ConditionalOnClass

 

  
 import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.context.annotation.Import;

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

        Class<?>[] exclude() default {};

        String[] excludeName() default {};
    }
    @EnableAutoConfiguration 注解通过Spring 提供的 @Import 注解导入了AutoConfigurationImportSelector类(@Import 注解可以导入配置类或者Bean到当前类中)。

    ``AutoConfigurationImportSelector类中getCandidateConfigurations方法会将所有自动配置类的信息以 List 的形式返回。这些配置信息会被 Spring 容器作 bean 来管理。

        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                    getBeanClassLoader());
            Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
            return configurations;
        }
    自动配置信息有了,那么自动配置还差什么呢?

    @Conditional 注解。@ConditionalOnClass(指定的类必须存在于类路径下),@ConditionalOnBean(容器中是否有指定的Bean)等等都是对@Conditional注解的扩展。拿 Spring Security 的自动配置举个例子:

    SecurityAutoConfiguration中导入了WebSecurityEnablerConfiguration类,WebSecurityEnablerConfiguration源代码如下:

    @Configuration
    @ConditionalOnBean(WebSecurityConfigurerAdapter.class)
    @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    @EnableWebSecurity
    public class WebSecurityEnablerConfiguration {

    }
    WebSecurityEnablerConfiguration类中使用@ConditionalOnBean指定了容器中必须还有WebSecurityConfigurerAdapter 类或其实现类。所以,一般情况下 Spring Security 配置类都会去实现 WebSecurityConfigurerAdapter,这样自动将配置就完成了。
View Code

  

具体步骤描述:
  1、SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration;@EnableAutoConfiguration的作用是利用AutoConfigurationImportSelector给容器中导入一些组件;
  2、Spring Boot 在启动时会去依赖的 Starter 包中寻找 resources/META-INF/spring.factories 文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的 Jar 包。

  可以查看public String[] selectImports(AnnotationMetadata annotationMetadata)方法的内容。通过protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)获取候选的配置,这个是扫描所有的ar包类路径下"META-INF/spring.factories";

  3、 根据 spring.factories 配置加载 AutoConfigure 类把扫描到的这些文件包装成Properties对象。从properties中获取到EnableAutoConfiguration.class类名对应的值,然后把他们添加在容器中。
  4、根据 @Conditional 注解的条件,进行自动配置并将 Bean 注入 Spring Context。整个过程就是将类路径下"META-INF/spring.factories"里面配置的所有EnableAutoConfiguration的值加入到容器中。每一个这样XXAutoConfiguration类都是容器中的一个组件都加入到容器中,用他们来做自动配置。

  总结一下,其实就是 Spring Boot 在启动的时候,按照约定去读取 Spring Boot Starter 的配置信息,再根据配置信息对资源进行初始化,并注入到 Spring 容器中。这样 Spring Boot 启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应 Bean 资源即可。


  问:如何禁用特定的配置?
  答:1、以使用@EnableAutoConfiguration批注的exclude属性来指示它或者@SpringBootApplication批注启用自动配置- 它具有@EnableAutoConfiguration作为元注释 - 我们可以使用相同名称的属性禁用自动配置:
    2、spring.autoconfigure.exclude环境属性禁用自动配置。application.properties文件中的此设置与以前相同:
    spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

  问:Spring Boot 工厂模式的加载
  答:Spring Framework 内部使用一种工厂加载机制(Factory Loading Mechanism)。这种机制使用 SpringFactoriesLoader 完成,SpringFactoriesLoader 使用 loadFactories 方法加载并实例化从 META-INF 目录里的 spring.factories 文件出来的工厂,这些 spring.factories 文件都是从 classpath 里的 jar 包里找出来的。

 

九、你如何理解 Spring Boot 中的 Starter


  主要是管理依赖包,并且以一致的方式传递和管理其他所需的依赖关系。
  Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。你可以一站式集成Spring及其他技术,而不需要到处找示例代码和依赖包。如你想使用Spring JPA访问数据库,只要加入spring-boot-starter-data-jpa启动器依赖就能使用了。

  Spring Boot 也提供了其它的启动器项目包括,包括用于开发特定类型应用程序的典型依赖项:

  •   spring-boot-starter-web-services - SOAP Web Services
  •   spring-boot-starter-web - Web 和 RESTful 应用程序
  •   spring-boot-starter-test - 单元测试和集成测试
  •   spring-boot-starter-jdbc - 传统的 JDBC
  •   spring-boot-starter-hateoas - 为服务添加 HATEOAS 功能
  •   spring-boot-starter-security - 使用 SpringSecurity 进行身份验证和授权
  •   spring-boot-starter-data-jpa - 带有 Hibeernate 的 Spring Data JPA
  •   spring-boot-starter-data-rest - 使用 Spring Data REST 公布简单的 REST 服务

 

十、如何在 Spring Boot 启动的时候运行一些特定的代码?
  可以实现接口 ApplicationRunner 或者 CommandLineRunner,这两个接口实现方式一样,它们都只提供了一个 run 方法


十一、Spring Boot 有哪几种读取配置的方式?
  Spring Boot 可以通过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量,


十二、Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
  Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作为日志框架,如果你使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架


十三、SpringBoot 实现热部署有哪几种方式?

  • Spring Loaded
  • Spring-boot-devtools

十四、 Spring Boot 配置加载方式?

  • 1)properties文件;
  • 2)YAML文件;
  • 3)系统环境变量;
  • 4)命令行参数;

十五、保护 Spring Boot 应用有哪些方法?

  • 在生产中使用HTTPS
  • 使用Snyk检查你的依赖关系
  • 升级到最新版本
  • 启用CSRF保护
  • 使用内容安全策略防止XSS攻击

十六、Spring Boot 2.X 有什么新特性?与 1.X 有什么区别?

  • 配置变更
  • JDK 版本升级
  • 第三方类库升级
  • 响应式 Spring 编程支持
  • HTTP/2 支持
  • 配置属性绑定

  参考:https://mp.weixin.qq.com/s/-WWBvWpD0Prib02XoU1sjw


十七、创建一个 Spring Boot Project 的最简单的方法是什么?

  Spring Initializr是启动 Spring Boot Projects 的一个很好的工具。此外还可以通过maven来创建。


十八、为什么我们需要 spring-boot-maven-plugin?

  • spring-boot-maven-plugin 提供了一些像 jar 一样打包或者运行应用程序的命令:
  • spring-boot:run 运行你的 SpringBooty 应用程序。
  • spring-boot:repackage 重新打包你的 jar 包或者是 war 包使其可执行
  • spring-boot:start 和 spring-boot:stop 管理 Spring Boot 应用程序的生命周期(也可以说是为了集成测试)。
  • spring-boot:build-info 生成执行器可以使用的构造信息。

十九、如何在 Spring Boot 中添加通用的 JS 代码?
  在源文件夹下,创建一个名为 static 的文件夹。然后,你可以把你的静态的内容放在这里面。
  然后通过两种方法使用js:方法 1:关闭安全验证application.properties文件中设置 management.security.enabled:FALSE
              方法2:在日志中搜索密码并传递至请求标头中

二十、什么是嵌入式服务器?我们为什么要使用嵌入式服务器呢?为何在Spring Boot应用程序中使用Jetty而不是Tomcat?
  嵌入式服务器就是我们的可执行单元包含服务器的二进制文件(例如,tomcat.jar)。轻量、方便,无需安装服务器。
  Spring Boot Web starter使用Tomcat作为默认的嵌入式servlet容器, 如果你想使用 Jetty 的话只需要修改pom.xml(Maven)或者build.gradle(Gradle)就可以了。
  

  
Maven:
<!--从Web启动器依赖中排除Tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加Jetty依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
View Code

 

二十一、Spring Cache 三种常用的缓存注解和意义?

  •   @Cacheable ,用来声明方法是可缓存,将结果存储到缓存中以便后续使用相同参数调用时不需执行实际的方法,直接从缓存中取值。
  •   @CachePut,使用 @CachePut 标注的方法在执行前,不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
  •   @CacheEvict,是用来标注在需要清除缓存元素的方法或类上的,当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。

二十二、Spring Boot 如何设置支持跨域请求?


  一般前端的解决方案有:

  • ① 使用 JSONP 来支持跨域的请求,JSONP 实现跨域请求的原理简单的说,就是动态创建<script>标签,然后利用<script>的 SRC 不受同源策略约束来跨域获取数据。缺点是需要后端配合输出特定的返回信息。
  • ② 利用反向代理的机制来解决跨域的问题,前端请求的时候先将请求发送到同源地址的后端,通过后端请求转发来避免跨域的访问。

         SpringBoot后端的解决方案是:后端设置支持非同源的请求,Spring Boot 设置支持非同源的请求有两种方式。

   

第一,配置 CorsFilter。
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.addExposedHeader("*");

UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);

return new CorsFilter(configSource);
}
}
第二,在启动类上添加:
public class Application extends WebMvcConfigurerAdapter {

@Override 
public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/**") 
.allowCredentials(true) 
.allowedHeaders("*") 
.allowedOrigins("*") 
.allowedMethods("*");

} 
}
View Code

 

二十二、什么是Spring Boot Actuator
  能够在生产中运行时监控和管理应用程序。Spring Boot Actuator可以使用HTTP或JMX端点公开操作信息。
  但是,大多数应用程序都使用HTTP,其中端点的标识和/执行器前缀形成URL路径。

二十三、Spring DevTools
  Spring Boot 还专门提供了一个组件包:Spring DevTools, DevTools 包括一组额外的工具,可以使应用程序开发体验更加愉快。
  spring-boot-devtools 为应用提供一些开发时特性,包括默认值设置,自动重启,livereload 等。

二十四、Spring Boot支持轻松绑定是什么意思?
  Spring Boot中的轻松绑定适用于配置属性的类型安全绑定。
  使用宽松绑定时,环境属性的键不需要与属性名称完全匹配。这样的环境属性可以用驼峰camelCase,kebab-case,snake_case或大写字母写成,单词用下划线分隔。
  例如,如果具有@ConfigurationProperties批注的bean类中的属性名为myProp,则可以将其绑定到以下任何环境属性:myProp,my-prop,my_prop或MY_PROP。

二十五、 如何在Spring Boot启动的时候运行一些逻辑
  可以实现接口 ApplicationRunner 或者者 CommandLineRunner,这两个接口实现方式一样,它们都只提供了一个 run 方法。

二十六、SpringBoot几个常用的注解

  • (1)@RestController和@Controller指定一个类,作为控制器的注解
  • (2)@RequestMapping方法级别的映射注解,这一个用过Spring MVC的小伙伴相信都很熟悉
  • (3)@EnableAutoConfiguration和@SpringBootApplication是类级别的注解,根据maven依赖的jar来自动猜测完成正确的spring的对应配置,只要引入了spring-boot-starter-web的依赖,默认会自动配置Spring MVC和tomcat容器
  • (4)@Configuration类级别的注解,一般这个注解,我们用来标识main方法所在的类,完成元数据bean的初始化。
  • (5)@ComponentScan类级别的注解,自动扫描加载所有的Spring组件包括Bean注入,一般用在main方法所在的类上
  • (6)@ImportResource类级别注解,当我们必须使用一个xml的配置时,使用@ImportResource和@Configuration来标识这个文件资源的类。
  • (7)@Autowired注解,一般结合@ComponentScan注解,来自动注入一个Service或Dao级别的Bean
  • (8)@Component类级别注解,用来标识一个组件,比如我自定了一个filter,则需要此注解标识之后,Spring Boot才会正确识别。

二十七、springboot核心启动函数main有哪些作用,用到的核心注解有什么作用。
    main: 主要作用启动spring boot框架,加载容器和诸多默认组件。
  核心注解:springbootApplication

二十八、springboot框架的项目需要兼容老项目(spring框架),该如何实现。
  集成老项目spring框架所需要的配置文件即可,也可添加所需的资源,@ImportResource({"classpath:spring1.xml" , "classpath:spring2.xml"})

二十九、需要加载外部配置文件的属性,该如何配置。
  

1)自定义所需的配置文件。
#自定义配置其他属性:
user.username=zhangsan
user.age=20
2)将配置文件引入到程序中:@PropertySource,@ConfigrationProperties
@PropertySource(value ="classpath:user.properties")
@ConfigurationProperties(prefix = "user")br/>@Component
public class User {
private String username;
private Integer age;
get/set封装省略....
}
3)在main启动函数中加入注解激活配置:
@EnableConfigrationProperties.
View Code

 

三十、介绍一下@SpringBootApplication注解

  

package org.springframework.boot.autoconfigure;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
package org.springframework.boot;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}
View Code

 

  可以看出大概可以把 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。根据 SpringBoot官网,这三个注解的作用分别是:

  @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  @ComponentScan: 扫描被@Component (@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类。
  @Configuration:允许在上下文中注册额外的bean或导入其他配置类、

三十一、Spring Boot支持哪些嵌入式web容器?
  Spring Boot支持以下嵌入式servlet容器:

       

 

 

 

三十二、什么是Spring Security
  Spring Security 实际上起源于 Acegi Security,这个框架能为基于 Spring 的企业应用提供强大而灵活安全访问控制解决方案,并且框架这个充分利用 Spring 的 IoC 和 AOP 功能,提供声明式安全访问控制的功能。后面,随着这个项目发展, Acegi Security 成为了Spring官方子项目,后来被命名为 “Spring Security”。
定义:**Spring Security 是一个功能强大且高度可以定制的框架,侧重于为Java 应用程序提供身份验证和授权。


三十三、什么是 JavaConfig?
Spring JavaConfig 是 Spring 社区的产品,它提供了配置 Spring IoC 容器的纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于:

  • (1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。
  • (2)减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。
  • (3)类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。

三十四、如何使用 Spring Boot 实现异常处理?
  Spring 提供了一种使用 ControllerAdvice 处理异常的非常有用的方法。 我们通过实现一个 ControlerAdvice 类,来处理控制器类抛出的所有异常。

三十五、依赖只引入了1个起步包,内置依赖了很多其他的包,为什么?.每个包之间没有冲突,怎么做到的呢?

  • Maven的依赖具有传递性,起步包依赖其他的包,所以当前工程会传递依赖其他包。
  • 因为当前SpringBoot工程引入的依赖包的坐标全部被父工程管理了。



 

posted on 2019-12-03 15:14  NWNS-无风无影  阅读(278)  评论(0)    收藏  举报