04SpringBoot 启动类配置及自动配置原理

目录

1.SpringBoot启动类配置

2.启动类注解@SpringBootApplication

3.注解@CmponentScan(了解)

4.注解@SpringBootConfiguration

4.1注解 @Configuration

5.自动配置原理-注解 @EnableAutoConfiguration(重头戏)

5.1注解@EnableAutoConfiguration

5.1.1注解@AutoConfigurationPackage

5.1.2注解@Import({AutoConfigurationImportSelector.class})

6. @ImportResource导入spring的配置类

7. @Conditional注解 


       上一章中我们提到,springboot通过父项目依赖和starter的场景启动器来管理和启动相关组件,以 spring-boot-starter-web 场景管理器为例,它能够为提供 Web 开发场景所需要的几乎所有依赖,因此在使用 Spring Boot 开发 Web 项目时,只需要引入该 Starter 即可,而不需要额外导入 Web 服务器和其他的 Web 依赖。 

       但我们在启动springboot项目时候,springboot是如何按需加载所需要的自动配置配置项?引入哪些场景,这些场景的自动配置才会开始呢?下面以springboot启动类注解来说明。

1.SpringBoot启动类配置

       默认情况下,Spring Boot 项目会创建一个名为 ***Application 的主程序启动类 ,该类中使用了 @SpringBootApplication来开启 Spring Boot 的自动配置,另外该启动类中包含一个 main() 方法,用来启动该项目。代码如下:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
        //2、查看容器中的组件
        String[] names =run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

       其中 @SpringBootApplication可以使用其核心的三个注解替换,但要指定xxxApplication所在的包路径,代码如下:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.example.demo")//注意,这里要指定DemoApplication所在的包
public class DemoApplication {

    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
        //2、查看容器中的组件
        String[] names =run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

 

        通过上述结果可以看到springboot为我们添加好了所有开发中常用的组件,包括自动配置SpringMvC的全套组件如dispatcherServlet、字符编码过滤器characterEncodingFilter、web常用场景组件如beanNameViewResolver等,如下:

自动配置SpringMvC

  •  引入SpringMVC全套组件
  •  自动配置好SpringMlVC常用组件(功能)

自动配好wWeb常见功能,如:字符编码问题

  •  SpringBoot帮我们配置好了所有web开发的常见场景

默认的包结构

  •  主程序所在目录的包及所有子包里面的组件都会被默认扫描进来。无需以前的包扫描配置
  •  要改变扫描路径使用@SpringBootApplication(scanBasePackages="xxx.xxx") 或者@ComponentScan("xxx.xxx")指定扫描路径

2.启动类注解@SpringBootApplication

       上述中springboot为我们添加好了所有开发中常用的组件实现主要取决于@SpringBootApplication注解。@SpringBootApplication注解的作用介绍如下:

  @SpringBootApplication 注解是一个组合注解,包含三个核心注解:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

  @SpringBootApplication 注解标注的类 表明 该类 是 springBoot的 主配置类;

  @SpringBootApplication 注解标注的类 中的 main() 方法是 Spring Boo t应用的启动入口;

  @SpringBootApplication 能够扫描 Spring组件 并 自动配置Spring Boot。

  @SpringBootApplication 注解代码如下:

@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 {...}

3.注解@CmponentScan(了解)

       @CmponentScan标注用于指定要扫描的包,将哪些包下面的带有实例化注解的类进行扫描,生成组件,交给SpringIOC容器管理。参数是包路径。

       如果用在主启动类中,包路径必须是主启动类所在的包,否则扫描不到主启动类,无法启动报错。

4.注解@SpringBootConfiguration

       @Configuration是Spring底层的一个注解,使用@Configuration标注的类,说明该类是配置类。

       前面内容中提到 @SpringBootApplication 注解标注的类 表明该类 是 springBoot的 主配置类,原因在于 @SpringBootApplication 的三大核心注解之一@SpringBootConfiguration。

@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 {...}

       通过观察发现 @SpringBootConfiguration 是标注在 @SpringBootApplication上面,@SpringBootConfiguration所标注的类说明该类是配置类。因此说明@SpringBootApplication也是配置类。

       进入@SpringBootConfiguration注解的中查看源码,如下:     

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {....}

       通过观察源码,可以发现 @Configuration标注在 @SpringBootConfiguration类上,说明该类SpringBootConfiguration也是配置类,是springBoot的一个配置类;

    因此@Configuration标注的类@SpringBootConfiguration是配置类,@SpringBootConfiguration标注的类@SpringBootApplication是配置类,@SpringBootApplication标注的类如:DemoApplication.主程序类也是配置类。说明@SpringBootApplication标注的类可以作为配置类springBoot的一个配置类。

4.1注解 @Configuration

      @Configuration底层代码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}

      @Configuration是配置类注解,是spring的底层注解;从@Component可以看出配置类也是一个组件;这个注解@Configuration用在类上,表示告诉spring这个类是配置类===配置文件

@Configuration // 告诉spring这是一个配置类 ==  配置文件
public class DemoConfig {
    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public User user(){
        return new User();
    }
}

上述代码等价与在xml中配置bean标签

<bean id="user" class="com.example.demo.bean.User"></bean>

      @Configuration修饰的类,默认带有参数proxyBeanMethods,参数proxyBeanMethods默认值是true。

      proxyBeanMethods=true:如果为true值,springboot总会检查Bean这个组件在容器中是否存在,如果存在则不会新创建对象。配置类对象Configuration是被cglib代理类增强的代理对象。调用配置类中的注册方法如user()得到的都是同一个bean对象。即@Configuration默认创建的bean对象是单例的。

@Configuration //默认使用属性@Configuration(proxyBeanMethods=true)
public class DemoConfig {
    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public User user(){
        return new User();
    }
}

测试类


@SpringBootApplication
public class DemoApplication {
 
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
        //2、查看容器中的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
 
        //配置类的测试使用
        //从容器中获取组件

        //从容器中获取配置类组件,配置类demoConfig组件 是一个代理对象
        DemoConfig demoConfig = run.getBean(DemoConfig.class);
        System.out.println(demoConfig);

        //在 proxyBeanMethods=true 情况下,spring 会检查容器是否存在
        //在 proxyBeanMethods=true 情况下, 不存在则创建新对象
        User user0 =  demoConfig.user(); / run.getBean("user",User.class);

        //在 proxyBeanMethods=true 情况下, 存在则 不 创建新对象
        User user1 =  demoConfig.user(); / run.getBean("user",User.class);
       
        // 结果为 true 说明二者是同一个对象 是单例对象
        System.out.println(user0==user1);
    }
}

      proxyBeanMethods=false:如果为fasle值,则springboot不会检查Bean这个组件在容器中是否存在,每次都会创建一个对象,配置类对象不是被cglib代理类增强的代理对象,调用配置类中的注册方法都是得到的是不同的bean对象。

 @Configuration (proxyBeanMethods=false)
public class DemoConfig {
    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public User user(){
        return new User();
    }
}

测试类


@SpringBootApplication
public class DemoApplication {
 
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);

        //配置类的测试使用
        //从容器中获取组件

        //从容器中获取配置类组件,配置类demoConfig组件 是一个代理对象
        DemoConfig demoConfig = run.getBean(DemoConfig.class);
        System.out.println(demoConfig);

        //在 proxyBeanMethods=false 情况下,spring 不会检查容器是否存在,直接创建新对象
        User user0 =  demoConfig.user(); / run.getBean("user",User.class);

        //在 proxyBeanMethods=false 情况下,spring 不会检查容器是否存在,直接创建新对象
        User user1 =  demoConfig.user(); / run.getBean("user",User.class);
       
        // 结果为 false 说明二者 不是同一个对象 是多例对象
        System.out.println(user0==user1);
    }
}

小结:proxyBeanMethods=true 和false的区别;

          full:全配置、lite:轻量级配置

          proxyBeanMethods=true 每次都会到容器中检查是否存在这个javaBean。启动比较慢。就是full:全配置。

          proxyBeanMethods=false每次都会跳过到容器中检查是否存在这个过程,启动比较快。就是lite:轻量级配置

         因此,如果给容器只是注册组件,这些组件又不被依赖,此时是用false,springboot启动比较快,加载也比较快。如果这些组件经常被依赖,此时使用true。

5.自动配置原理-注解 @EnableAutoConfiguration(重头戏)

       在讲 @EnableAutoConfiguration 之前,先回顾下 @Import注解,@Import是spring的底层注解,主要用于给容器中自动创建组件,例如:创建一个Book类,使用@Import(Book.class)进行导入。 

@SpringBootApplication
@Import(Book.class)
public class DemoApplication {
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
        //2、查看容器中的组件
        String[] names = run.getBeanNamesForType(Book.class);
        for (String name : names) {
            System.out.println(name);
        }

        Book book = run.getBean("cn.edu.tyut.springboot.entity.Book",Book.class);
        System.out.println(book);
    }
}

       通过上述代码观察到:@Import注解作用是给容器中自动创建组件,例如后面要讲到的,导入自动配置选择器组件:@Import({AutoConfigurationImportSelector.class}) ,也可以导入多个组件,组件之间用逗号隔开。导入组件在IOC容器中的名字默认是类的全限定名,例如:Book book = run.getBean("cn.edu.tyut.springboot.entity.Book",Book.class);

5.1注解@EnableAutoConfiguration

       前面内容中提到:@SpringBootApplication 能够扫描 Spring组件 并 自动配置Spring Boot。主要取决于 @SpringBootApplication 的​​​​​​ 核心注解 @EnableAutoConfiguration。

       @EnableAutoConfiguration:开启自动配置功能; 以前需要配置的东西,现在springBoot帮我们自动配置,通过这个注解开启自动配置;@EnableAutoConfiguration注解代码如下:

@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类中使用了两个注解,分别是@AutoConfigurationPackage与@Import({AutoConfigurationImportSelector.class})。 

5.1.1注解@AutoConfigurationPackage

  • 作用:自动配置包,将主配置类(@SpringBootApplication标注的类)的所在包下的所有子包内的所有组件全部加入到spring容器中;
  • @AutoConfigurationPackage能够自动配置的原因是注解类中有注解@Import(AutoConfigurationPackages.Registrar.class)。
  • @Import(AutoConfigurationPackages.Registrar.class) 给容器导入一个组件Registrar;再由组件 Registrar 导入一系列其他的组件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {......}

  • 组件 Registrar 中 register方法进行批量导入指定路径下的组件

       register方法进行批量导入指定路径下的组件具体执行流程:

  1. 首先得到元数据metedata 即 com.example.demo.DemoAppliaction
  2. 在new PackageImports(metadata) 中 通过 AutoConfigurationPackage.class.getName() 类名 org.springframework.boot.autoconfigure.AutoConfigurationPackage
  3. metedata元数据根据AutoConfigurationPackage类名获取注解标签basePackageClasses和basePackages。
  4. 根据注解标签 basePackages 获取所在包名,如果为空,则使用ClassUtils.getPackageName()获取。
  5. 最终得到主程序所在的包 com.example.demo
  6. 注册 主程序 DemoAppliaction 所在的包路径 com.example.demo下所有的组件。

       因此我们说以上流程就是 @AutoConfigurationPackage自动配置包,将主配置类(@SpringBootApplication标注的类)的所在包下的所有子包内的所有组件全部加入到spring容器中的过程和原因。

5.1.2注解@Import({AutoConfigurationImportSelector.class})

  • spring底层注解@import,给容器导入一个组件;导入的组件由AutoConfigurationImportSelector.class提供;AutoConfigurationImportSelector批量导入组件,它决定导入哪些组件,过程如下图源码:

通过源码发现:

  • 通过getAutoConfigurationEntry(annotationMetadata)方法导入一些组件。
  • 通过List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)方法获取所有要导入的组件。
  • 通过List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
  • 通过Map<String, List<String>> loadSpringFactories(ClassLoader classLoader)
  • 从META-INF/spring.factories位置加载一个文件,就是默认扫描当前系统所有META-INF/spring.factories位置的文件。
  • 重点是spring-boot-autoconfigure-2.6.7.jar包下的META-INF/spring.factories文件,里面配置好了要自动加载的组件有哪些。如下图:

6. @ImportResource导入spring的配置类

@ImportResource用来导入spring的配置文件,例如@ImportResource(“classpath:springbean.xml”)

步骤1:service类

public class MyServicve {
}

步骤2:创建配置文件

在resources目录下新建一个名为beans.xml的XML自定义配置文件,在该配置文件中通过配置向Spring容器中添加MyConfigServicve类对象。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id=" myConfigServicve " 
          class="com.example.demo.services.MyServicve" />

</beans>

步骤3:在启动类上引入XML文件

@SpringBootApplication
@ImportResource("classpath:beans.xml")
public class DemoApplication {
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = 
                     SpringApplication.run(DemoApplication.class, args);
        //2、查看容器中的组件
        MyServicve  servicve = run.getBean(MyServicve.class);
        System.out.println(servicve);
    }
}

7. @Conditional注解 

   此注解是满足某些条件时候按条件进行组件注入。其有很多衍生注解,主要用于条件判断,例如

@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnClass、@ConditionalOnMissingClass、、@ConditionalOnJava等,可以用在类上,也可以用在方法上。用在类上表示如果容器中存在其注解指定的bean组件或class时,类中所有的组件才能进行注入到 IOC 容器中。或容器中没有其注解中指定的bean组件或者class时,类中所有组件才能进行注入到IOC 容器中。用在方法上,只对当前的方法起作用。

例如:如下面案例,如果注解 @ConditionalOnBean(name="book")用在加类上,如果容器中存在有book组件,类中的所有组件(例如组件user、book、dept)才会注入到容器中。如果注解 @ConditionalOnBean(name="book")用到方法user上,如果有book组件时候,组件user、才会注入到容器中。

 public class User {
    public User() {
        System.out.println("创建user bean");
    }
 }

 public class Book {
    public Book(){
        System.out.println("创建book bean");
    }
 }

 public class Dept {
    public Dept(){
        System.out.println("创建dept bean");
    }
}

类加上注解 @ConditionalOnBean(name="book"),如果有book组件,类中的所有组件(例如组件user)才生效,这些组件才会注入到容器中。
但在进行注册时候,容器中没有book组件,因此不会注册组件 book、user、dept到容器中,见下面测试。

@Configuration ()

@ConditionalOnBean(name="book")
public class DemoConfig {
    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public User user(){

         User user =new User();

         user.setBook(book);
         return new User();
    }

    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public Dept dept(){

         Dept dept=new Dept ();   
         return  dept;
    }

    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public Book book(){

         Book book=new Book ();   
         return  book;
    }

}

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication .class, args);
        /**
         * 2、判断容器中是否存在 这些组件。
         *    如果@ConditionalOnBean(name="book")指定name="book"的组件不在容器中存在,
         *    则@ConditionalOnBean(name="book")所标识的类中的组件user、dept、book不会注入到容器。
         */
        Boolean  isContainUser = run.containsBean("user");
        Boolean  isContainBook = run.containsBean("book");
        Boolean  isContainDept = run.containsBean("dept");
        System.out.println(isContainUser);// 结果为 false
        System.out.println(isContainBook);// 结果为 false
        System.out.println(isContainDept);// 结果为 false
    }
}

如果注解 @ConditionalOnBean(name="book")用到user方法上,实际运行过程中开始的时候容器中是没有book组件的,因此组件user不会注入到容器中,但不会影响dept和book组件的注入,因为book 和 dept 组件并不受条件限制。

@Configuration (

public class DemoConfig {

    @ConditionalOnBean(name="book")
    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public User user(){

         User user =new User();

         user.setBook(book);
         return new User();
    }

    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public Dept dept(){

         Dept dept=new Dept ();   
         return  dept;
    }

    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public Book book(){

         Book book=new Book ();   
         return  book;
    }

}

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication .class, args);
        /**
         * 2、判断容器中是否存在 这些组件。
         *    如果@ConditionalOnBean(name="book")指定name="book"的组件不在容器中存在,
         *    则@ConditionalOnBean(name="book")所标识的方组件user不会注入到容器。但未
         *    标识的dept、book组件会注入到容器中。
         */
        Boolean  isContainUser = run.containsBean("user");
        Boolean  isContainBook = run.containsBean("book");
        Boolean  isContainDept = run.containsBean("dept");
        System.out.println(isContainUser);// 结果为 false
        System.out.println(isContainBook);// 结果为 true
        System.out.println(isContainDept);// 结果为 true
    }
}

另外注意特殊情况:如果注解 @ConditionalOnBean(name="book")用到user方法上,但组件book在 组件 user 之前,则运行时候,先注册组件book,在检查user方法时候,此时容器中已经有组件book,因此组件user会注入到容器中。

@Configuration (

public class DemoConfig {

    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public Book book(){

         Book book=new Book ();   
         return  book;
    }

    @ConditionalOnBean(name="book")
    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public User user(){

         User user =new User();

         user.setBook(book);
         return new User();
    }

    @Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
    public Dept dept(){

         Dept dept=new Dept ();   
         return  dept;
    }

}

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication .class, args);
        /**
         * 2、判断容器中是否存在 这些组件。
         *    如果@ConditionalOnBean(name="book")指定name="book"的组件不在容器中存在,
         *    则@ConditionalOnBean(name="book")所标识的方组件user不会注入到容器。但未
         *    标识的dept、book组件会注入到容器中。
         */
        Boolean  isContainUser = run.containsBean("user");
        Boolean  isContainBook = run.containsBean("book");
        Boolean  isContainDept = run.containsBean("dept");
        System.out.println(isContainUser);// 结果为 true
        System.out.println(isContainBook);// 结果为 true
        System.out.println(isContainDept);// 结果为 true
    }
}

05全局配置文件application.properties详解

https://blog.csdn.net/qq_41946216/article/details/124769491?spm=1001.2014.3001.5501

好的,对于使用Druid连接池的Spring Boot应用程序,您需要在启动中进行以下配置: 1.引入Druid依赖 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> ``` 2.配置数据源 在application.properties或application.yml中添加以下配置: ```yaml spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver ``` 3.配置Druid连接池 在启动中添加以下代码: ```java @Configuration public class DruidConfig { @Bean public ServletRegistrationBean druidServlet() { return new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); } @Bean public FilterRegistrationBean druidFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return filterRegistrationBean; } @Bean(initMethod = "init", destroyMethod = "close") public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mydb"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setMaxActive(20); dataSource.setInitialSize(1); return dataSource; } } ``` 以上代码中,我们配置了Druid的Servlet和Filter,并且创建了一个Druid连接池的DataSource。 注意:以上代码中的url、username和password需要根据您的实际情况进行修改。 希望这能对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值