zoukankan      html  css  js  c++  java
  • 【SpringBoot】SpringBoot配置与单元测试(二)

      SpringBoot项目创建参考【SpringBoot】SpringBoot快速入门(一)

      本文介绍SpringBoot项目的POM文件、配置与单元测试

    POM文件

      1、SpringBoot的pom文件中,看到都依赖一个springboot的父项目,如下

    1 <parent>
    2     <groupId>org.springframework.boot</groupId>
    3     <artifactId>spring‐boot‐starter‐parent</artifactId>
    4     <version>2.1.8.RELEASE</version>
    5 </parent>

        而springboot的父项目又有一个父项目,此项目才是真正管理Spring Boot应用里面的所有依赖版本

    1 <parent>
    2     <groupId>org.springframework.boot</groupId>
    3     <artifactId>spring-boot-dependencies</artifactId>
    4     <version>2.1.8.RELEASE</version>
    5     <relativePath>../../spring-boot-dependencies</relativePath>
    6 </parent>

        以后导入依赖默认是不需要写版本(没有在dependencies里面管理的依赖自然需要声明版本号)

        依赖关系如下:

        

      2、SpringBoot的启动器

        spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件

    1 <dependencies>
    2     <!-- 不需要写版本号,版本号依赖父项目(spring-boot-starter-parent)管理 -->
    3     <!-- SpringBoot 将所有的功能场景抽取出来,做成一个个starter(启动器),
    4         只需要在项目中引入这些starter相关场景的所有依赖都会导入进来,要用什么功能就导入什么启动器-->
    5     <dependency>
    6         <groupId>org.springframework.boot</groupId>
    7         <artifactId>spring-boot-starter-web</artifactId>
    8     </dependency>
    9 </dependencies>

    主程序类,主入口类

      1、主程序类,入口类

     1 /**
     2  * @SpringBootApplication 用来标注一个主程序,说明这是一个Spring Boot应用
     3  */
     4 @SpringBootApplication
     5 public class Application {
     6 
     7     public static void main(String[] args) {
     8 
     9         // Spring应用启动
    10         SpringApplication.run(Application.class, args);
    11     }
    12 }    

      2、SpringBoot注解

         a、@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,

        SpringBoot 就应该运行这个类的main方法来启动SpringBoot应用;  

    1 @Target(ElementType.TYPE)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4 @Inherited
    5 @SpringBootConfiguration
    6 @EnableAutoConfiguration
    7 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    8         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    9 public @interface SpringBootApplication {

        b、@SpringBootApplication > @SpringBootConfiguration: Spring Boot的配置类;标注在某个类上,表示这是一个Spring Boot的配置类; 

        c、@SpringBootApplication > @SpringBootConfiguration > @Configuration: 配置类上来标注这个注解; 配置类 ----- 配置文件; 配置类也是容器中的一个组件;@Component

        d、@SpringBootApplication > @EnableAutoConfiguration: 开启自动配置功能; Spring配置的东西,SpringBoot开启自动配置功能;这样自动配置才能生效;

    1 @Target(ElementType.TYPE)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4 @Inherited
    5 @AutoConfigurationPackage
    6 @Import(AutoConfigurationImportSelector.class)
    7 public @interface EnableAutoConfiguration {

        e、@SpringBootApplication > @EnableAutoConfiguration > @AutoConfigurationPackage: 自动配置包

        f、@SpringBootApplication > @EnableAutoConfiguration > @AutoConfigurationPackage > @Import(AutoConfigurationPackages.Registrar.class): Spring的底层注解@Import,给容器中导入一个组件;导入的组件由 AutoConfigurationPackages.Registrar.class;

          将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;         

        g、@SpringBootApplication > @EnableAutoConfiguration > @Import(EnableAutoConfigurationImportSelector.class); 选择要导入容器的组件,  

          EnableAutoConfigurationImportSelector: 导入哪些组件的选择器; 将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;

          会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件, 并配置好这些组件;

          

          有了自动配置类,免去了手动编写配置注入功能组件等的工作;SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);

          Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

          J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-2.1.8.RELEASE.jar;

    SpringBoot自动配置原理

      1、SpringBoot启动的时候加载主配置类,在主配置类@SpringBootApplication注解中,

        开启了自动配置功能 @EnableAutoConfiguration

      2、@EnableAutoConfiguration的作用

        a、利用EnableAutoConfigurationImportSelector给容器中导入一些组件

        b、EnableAutoConfigurationImportSelector类中,可以查看getAutoConfigurationEntry()方法的内容,代码逻辑如下

     1 // 获取候选的配置
     2 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
     3 
     4 
     5 // getCandidateConfigurations此方法中调用了
     6 SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
     7                 getBeanClassLoader());
     8 
     9 // loadFactoryNames方法就会返回所有配置类名称
    10 // 扫描所有jar包类路径下 META‐INF/spring.factories
    11 // 把扫描到的这些文件的内容包装成properties对象
    12 // 从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中

        c、将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中; spring.factories内容如下:

      1 # Auto Configure
      2 org.springframework.boot.autoconfigure.EnableAutoConfiguration=
      3 org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
      4 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
      5 org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
      6 org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
      7 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
      8 org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
      9 org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,
     10 org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
     11 org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,
     12 org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,
     13 org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,
     14 org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,
     15 org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,
     16 org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,
     17 org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,
     18 org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,
     19 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,
     20 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,
     21 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,
     22 org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,
     23 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,
     24 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,
     25 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,
     26 org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,
     27 org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,
     28 org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,
     29 org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,
     30 org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,
     31 org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,
     32 org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,
     33 org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,
     34 org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,
     35 org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,
     36 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
     37 org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,
     38 org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,
     39 org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,
     40 org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,
     41 org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,
     42 org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,
     43 org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,
     44 org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,
     45 org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,
     46 org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,
     47 org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,
     48 org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,
     49 org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,
     50 org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,
     51 org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,
     52 org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,
     53 org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,
     54 org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,
     55 org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,
     56 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,
     57 org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,
     58 org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,
     59 org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,
     60 org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,
     61 org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,
     62 org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,
     63 org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,
     64 org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,
     65 org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,
     66 org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,
     67 org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,
     68 org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,
     69 org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,
     70 org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,
     71 org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,
     72 org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,
     73 org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,
     74 org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,
     75 org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,
     76 org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,
     77 org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,
     78 org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,
     79 org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,
     80 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,
     81 org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,
     82 org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,
     83 org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,
     84 org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,
     85 org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,
     86 org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,
     87 org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,
     88 org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,
     89 org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,
     90 org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,
     91 org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,
     92 org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,
     93 org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,
     94 org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,
     95 org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,
     96 org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,
     97 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,
     98 org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,
     99 org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,
    100 org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,
    101 org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,
    102 org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,
    103 org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,
    104 org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,
    105 org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,
    106 org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,
    107 org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,
    108 org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,
    109 org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,
    110 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,
    111 org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,
    112 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,
    113 org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,
    114 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,
    115 org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,
    116 org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,
    117 org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,
    118 org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,
    119 org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

      3、每一个自动配置类进行自动配置功能,每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中; 用他们来做自动配置; 

      4、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;

     1 @Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
     2 @EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的
     3 ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把
     4 HttpEncodingProperties加入到ioc容器中
     5 
     6 
     7 //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
     8 @Configuration
     9 //启动指定属性类的,相当于将HttpProperties注入容器
    10 @EnableConfigurationProperties(HttpProperties.class)
    11 
    12 // Spring底层@Conditional注解,根据不同的条件,如果 满足指定的条件,整个配置类里面的配置就会生效; 
    13 // 判断当前应用是否是web应用,如果是,当前配置类生效
    14 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    15 
    16 // 判断当前项目有没有这个类
    17 // CharacterEncodingFilter; SpringMVC中进行乱码解决的过滤器;
    18 @ConditionalOnClass(CharacterEncodingFilter.class)
    19 
    20 // 判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
    21 // 即使配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
    22 @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
    23 public class HttpEncodingAutoConfiguration {
    24 
    25     // 类已经和SpringBoot的配置文件映射了
    26     private final HttpProperties.Encoding properties;
    27 
    28     // 只有一个有参构造器的情况下,参数的值就会从容器中拿
    29     public HttpEncodingAutoConfiguration(HttpProperties properties) {
    30         this.properties = properties.getEncoding();
    31     }
    32 
    33     // 给容器中添加一个组件,这个组件的某些值需要从properties中获取
    34     @Bean
    35     //判断容器没有这个组件? 没有则注入
    36     @ConditionalOnMissingBean
    37     public CharacterEncodingFilter characterEncodingFilter() {
    38         CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    39         filter.setEncoding(this.properties.getCharset().name());
    40         filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
    41         filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
    42         return filter;
    43     }
    44 
    45     @Bean
    46     public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
    47         return new LocaleCharsetMappingsCustomizer(this.properties);
    48     }
    49 
    50     private static class LocaleCharsetMappingsCustomizer
    51             implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
    52 
    53         private final HttpProperties.Encoding properties;
    54 
    55         LocaleCharsetMappingsCustomizer(HttpProperties.Encoding properties) {
    56             this.properties = properties;
    57         }
    58 
    59         @Override
    60         public void customize(ConfigurableServletWebServerFactory factory) {
    61             if (this.properties.getMapping() != null) {
    62                 factory.setLocaleCharsetMappings(this.properties.getMapping());
    63             }
    64         }
    65 
    66         @Override
    67         public int getOrder() {
    68             return 0;
    69         }
    70 
    71     }
    72 
    73 }

        根据当前不同的条件判断,决定这个配置类是否生效?

        一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取 的,这些类里面的每一个属性又是和配置文件绑定的;

      5、所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;配置文件能配置什么就可以参照某个功 能对应的这个属性类

    1 // 从配置文件中获取指定的值和bean的属 性进行绑定
    2 @ConfigurationProperties(prefix = "spring.http")
    3 public class HttpProperties {

      6、在配置文件中设置 debug = true,可以在控制台查看springboot自动配置的服务,启动了那些自动配置类。

      精髓

        1)、SpringBoot启动会加载大量的自动配置类

        2)、使用时,查看需要的功能有没有SpringBoot默认写好的自动配置类;

        3)、再来看这个自动配置类中到底配置了哪些组件; (只要我们要用的组件有,我们就不需要再来配置了)

        4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;

          xxxxAutoConfigurartion:自动配置类; 给容器中添加组件

          xxxxProperties:封装配置文件中相关属性;
    
    

    SpringBoot单元测试

      1、引入SpringBoot测试依赖

    1 <dependency>
    2     <groupId>org.springframework.boot</groupId>
    3     <artifactId>spring-boot-starter-test</artifactId>
    4     <scope>test</scope>
    5 </dependency>

      2、编辑测试类。TestSpringbootApplicationTests.java

     1 package com.test.springboot;
     2 
     3 import org.junit.Test;
     4 import org.junit.runner.RunWith;
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.boot.test.context.SpringBootTest;
     7 import org.springframework.context.ApplicationContext;
     8 import org.springframework.test.context.junit4.SpringRunner;
     9 
    10 /**
    11  * SpringBoot单元测试
    12  *
    13  * 可以在测试期间很方便的类似编码一样的自动注入
    14  */
    15 @RunWith(SpringRunner.class)
    16 @SpringBootTest
    17 public class TestSpringbootApplicationTests {
    18 
    19     @Autowired
    20     ApplicationContext context;
    21 
    22     @Test
    23     public void contextLoads(){
    24         boolean b = context.containsBean("helloController");
    25         System.out.println(b);
    26     }
    27 
    28 
    29 }

      3、运行测试类测试方法,运行结果如下:

        

      

  • 相关阅读:
    分享一下前一段时间的开发总结
    循环,梦
    从C#程序中调用非受管DLLs
    大学生零工资就业,谁之过?
    国外宽带用户的上网速度能达到多少呢?
    天沉沉,来个好天气吧
    虚伪,不只是形容一个人
    回头思考关于xml的使用
    从毕业生当中看人与人的差距
    C# 编码规则
  • 原文地址:https://www.cnblogs.com/h--d/p/11986555.html
Copyright © 2011-2022 走看看