0%

8.1 配置文件的优先级

server.servlet.context-path=/app
server.port=8888
spring.config.location=xxx/xxx/application.properties

下面本文中说到"级别高"意思就是会替换其他的, 以它为首选, 它最终会起作用, 其他的会被覆盖;

本节要回答的问题:springboot应用部署后, 配置文件以哪个为准? 假如应用打包成可执行app.jar后有以下这几个配置文件:

  1. jar同目录的 application.properties; ->官网称此为: file:./config/
  2. jar同目录的 config/application.properties; ->官网称此为: file:./config/
  3. jar内部jar包中的 classes/application.properties; ->官网称此为: classpath:/
  4. jar内部jar包中的 classes/config/application.properties; ->官网称此为: classpath:/config/
  5. 启动参数时直接覆盖某个单项配置, 比如改端口号: java -jar app.jar --server.port=9999来指定;
  6. 随便指定一个地址放置 application.properties , 使用 启动参数spring.config.location=xxx来指定; (注意: 这个配置实测会互补失效–即: 只有它自己起作用)

–let’s guess:

我们先来做一个猜测, 如果是让我们自己设计, 想象实际上线的场景, 我们会期望哪个优先级最高(最终会覆盖其他配置的)?

如果我在线上部署了一个app.jar文件, 执行java -jar app.jar 启动应用; 启动之后线上如果有问题了, 比如端口被占用, 比如数据库配置修改, 我需要马上调整一个参数, 最快的方式是什么?

  1. 首先: 直接重启, java -jar app.jar --some.prop=new_value 肯定是这个最快, 或者干脆自己指定一个配置文件的地址, –参数加命令行后面; (上面5/6)

  2. 除了执行带参数, 方便修改的肯定是 jar平级目录下了, 那么, 应该轮到 app.jar平级目录下的配置了:

    是设置哪个级别高呢? 直接读取application.properties? 还是 config/application.properties?

    有的人认为 config很直观, 就把它下面的设为高级别—没错—springboot的设计者就是这类的(上面2);

  3. 当然 紧接着就是平级的了(上面1)

  4. 所以jar包里面的两处, 应该是低级别的了, 按照第2点的逻辑, 应该是 classpath:/config/application.properties 级别高于 classpath:application.properties;

所以我们就大概出来一个思路, 配置起作用优先级别依次是:

  1. 启动时指定某些参数

    java -jar app.jar --server.port=9999

  2. 启动时指定配置文件(有坑~)

    java -jar app.jar -spring.config.location=/mydir/application.properties

  3. jar同目录的config/application.properties

    app.jar 同级别目录下的 ./config/application.properties

  4. jar同目录的application.properties

    app.jar同级别目录下的 application.properties

  5. jar内部 classpath目录下的 ./config/application.properties

    app.jar/BOOT-INF/classes/config/application.properties

  6. jar内部classpath目录下的 application.properties

    app.jar/BOOT-INF/classes/application.properties

注意: 2处之所以说有坑是因为:

  1. 其他的配置文件只是优先级不同, 所有的配置文件还都是可以加载互补的, 只是优先级更高的会覆盖优先级低的
  2. 但是2处这个怂配置则不然: 假如你指定了 -spring.config.location=xxx , 你要小心了, 这相当于是个渣男: 他只顾自己, 不和其他的互补, 别的都被忽略了!!!

8.2 配置文件的优先级-代码证明

8.2.1 代码核心文件

  1. idea里新建一个springboot的module: springboot-02-config, 创建了4个配置文件, 配置对应关系如图:

image.png

  1. pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.niewj</groupId>
        <artifactId>springboot-02-config</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springboot-02-config</name>
        <description>springboot-2-config</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.8.6</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.10</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>app</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
  2. person.java

    package com.niewj.springboot.model;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * Created by niewj on 2020/8/5 23:20
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
    
        private Integer age;
        private String lastName;
        private boolean student;
        private String location;
    
        private List<String> hobbies;
    }
  3. controller:

    package com.niewj.springboot.controller;
    
    import com.niewj.springboot.model.Person;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * Created by niewj on 2020/8/5 23:17
     */
    @RestController
    public class HelloController {
    
        @Autowired
        private Person person;
    
        @RequestMapping("/hello")
        public Object hello(){
            return person;
        }
    }

8.2.2 执行流程

  1. 编译打包

    mvn clean install -Dmaven.test.skip=true

  2. 打包后生成一个 app.jar可执行文件

  3. show in explore 进入 app.jar所在目录, 我们选择把jar包复制到其他目录, 比如: E:/deploy/app.jar

  4. 目录下shift+右键->在此处打开命令行窗口

    config(目录下有application.properties)
    app.jar
    application.properties

  5. 在命令行java -jar app.jar

    PS E:\deploy> java -jar .\app.jar
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.2.RELEASE)
    
    2020-08-06 13:23:53.011  INFO 20304 --- [           main] com.niewj.springboot.ConfigApplication   : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 20304 (E:\deploy\app.jar started by niewj in E:\deploy)
    2020-08-06 13:23:53.015  INFO 20304 --- [           main] com.niewj.springboot.ConfigApplication   : No active profile set, falling back to default profiles: default
    2020-08-06 13:23:56.206  INFO 20304 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8004 (http)
    2020-08-06 13:23:56.225  INFO 20304 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-08-06 13:23:56.226  INFO 20304 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
    2020-08-06 13:23:56.343  INFO 20304 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 13:23:56.343  INFO 20304 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3275 ms
    2020-08-06 13:23:56.512  INFO 20304 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 13:23:56.721  INFO 20304 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8004 (http) with context path ''
    2020-08-06 13:23:56.724  INFO 20304 --- [           main] com.niewj.springboot.ConfigApplication   : Started ConfigApplication in 4.112 seconds (JVM running for 4.652)

    可以看到, 使用的是端口: 8004 , 再通过 http://localhost:8004/hello 访问, 显示:

    {"age":30,"lastName":"wj","student":false,"location":"file/config/application.properties","hobbies":["8004","running","coding","cooking"]}

    –> 可见起作用的是: app.jar同目录下的 config/application.properties

  6. 我们删掉这个目录再试一次:

    PS E:\deploy> java -jar .\app.jar
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.2.RELEASE)
    
    2020-08-06 13:27:45.312  INFO 7716 --- [           main] com.niewj.springboot.ConfigApplication   : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 7716 (E:\deploy\app.jar started by niewj in E:\deploy)
    2020-08-06 13:27:45.315  INFO 7716 --- [           main] com.niewj.springboot.ConfigApplication   : No active profile set, falling back to default profiles: default
    2020-08-06 13:27:48.470  INFO 7716 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8003 (http)
    2020-08-06 13:27:48.482  INFO 7716 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-08-06 13:27:48.483  INFO 7716 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
    2020-08-06 13:27:48.591  INFO 7716 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 13:27:48.592  INFO 7716 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3227 ms
    2020-08-06 13:27:48.754  INFO 7716 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 13:27:48.954  INFO 7716 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8003 (http) with context path ''
    2020-08-06 13:27:48.957  INFO 7716 --- [           main] com.niewj.springboot.ConfigApplication   : Started ConfigApplication in 4.081 seconds (JVM running for 4.606)

    可以看到, 使用的是端口: 8003, 再通过 http://localhost:8003/hello 访问, 显示:

    {"age":30,"lastName":"wj","student":false,"location":"file/application.properties","hobbies":["8003","running","coding","cooking"]}

    –> 可见起作用的是: app.jar同目录下的 ./application.properties

    1. 删掉./application.properties再试:
    PS E:\deploy> java -jar .\app.jar
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.2.RELEASE)
    
    2020-08-06 13:31:42.852  INFO 3700 --- [           main] com.niewj.springboot.ConfigApplication   : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 3700 (E:\deploy\app.jar started by niewj in E:\deploy)
    2020-08-06 13:31:42.855  INFO 3700 --- [           main] com.niewj.springboot.ConfigApplication   : No active profile set, falling back to default profiles: default
    2020-08-06 13:31:46.018  INFO 3700 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8002 (http)
    2020-08-06 13:31:46.032  INFO 3700 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-08-06 13:31:46.033  INFO 3700 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
    2020-08-06 13:31:46.144  INFO 3700 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 13:31:46.145  INFO 3700 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3239 ms
    2020-08-06 13:31:46.306  INFO 3700 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 13:31:46.509  INFO 3700 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8002 (http) with context path ''
    2020-08-06 13:31:46.514  INFO 3700 --- [           main] com.niewj.springboot.ConfigApplication   : Started ConfigApplication in 4.051 seconds (JVM running for 4.579)

    可以看到, 使用的是端口: 8002, 再通过 http://localhost:8002/hello 访问, 显示:

    {"age":30,"lastName":"wj","student":false,"location":"resource/config/application.properties","hobbies":["8002","running","coding","cooking"]}

    –> 可见起作用的是: app.jar包内的classpath目录下的: classpath:config/application.properties

    1. 到此就不用再试了, 如果没有config目录, 肯定读取的是classpath: application.properties了, 这个我们平时用的就是它;

    正好证实了上面的结论.

    1. 我们再试试 spring.config.location命令行指定配置文件的执行方式: 在桌面上放了一个 application.properties, 内容如下:

      server.port=8333
      
      person.hobbies=8333,running,coding,cooking
      person.location=spring.config.location/application.properties
      
    2. java -jar app.jar --spring.config.location=C:\Users\weiju\Desktop\application.properties

      PS E:\deploy> java -jar app.jar --spring.config.location=C:\Users\weiju\Desktop\application.properties
      
        .   ____          _            __ _ _
       /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
      ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
       \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
        '  |____| .__|_| |_|_| |_\__, | / / / /
       =========|_|==============|___/=/_/_/_/
       :: Spring Boot ::        (v2.2.2.RELEASE)
      
      2020-08-06 13:39:40.251  INFO 20548 --- [           main] com.niewj.springboot.ConfigApplication   : Starting ConfigApplication v0.0.1-SNAPSHOT on LAPTOP-7EINAF4M with PID 20548 (E:\deploy\app.jar started by niewj in E:\deploy)
      2020-08-06 13:39:40.254  INFO 20548 --- [           main] com.niewj.springboot.ConfigApplication   : No active profile set, falling back to default profiles: default
      2020-08-06 13:39:43.357  INFO 20548 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8333 (http)
      2020-08-06 13:39:43.368  INFO 20548 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
      2020-08-06 13:39:43.368  INFO 20548 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
      2020-08-06 13:39:43.452  INFO 20548 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
      2020-08-06 13:39:43.453  INFO 20548 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3142 ms
      2020-08-06 13:39:43.615  INFO 20548 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
      2020-08-06 13:39:43.805  INFO 20548 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8333 (http) with context path ''
      2020-08-06 13:39:43.809  INFO 20548 --- [           main] com.niewj.springboot.ConfigApplication   : Started ConfigApplication in 3.997 seconds (JVM running for 4.531)

      可以看到, 使用的是端口: 8333, 再通过 http://localhost:8333/hello 访问, 显示:

      {"age":null,"lastName":null,"student":false,"location":"spring.config.location/application.properties","hobbies":["8333","running","coding","cooking"]}

      –> 同样: 证明了渣男属性 spring.config.location : 只顾自己, 别人都抛弃了, 没有了互补功能;

    需要注意的几点:

    1. 我们可以看到他们之间除了优先级, 其他的是都加载的, 是互补的关系(命令行带参spring.config.properties除外)
    2. 上面的测试, 除了删除之外, 改名也生效, 名字不是 application.properties/application.yml都不会认的;
    3. 我们没有试命令行指定某个参数, 比如 java -jar app.jar --server.port=8080 这个; 它是第一优先级!

8.3 官网说明

4.2.3. Application Property Files


SpringApplication loads properties from application.properties files in the following locations and adds them to the Spring Environment:

  1. A /config subdirectory of the current directory
  2. The current directory
  3. A classpath /config package
  4. The classpath root

The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).

……

Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/. The resulting search order is the following:

  1. file:./config/
  2. file:./config/*/
  3. file:./
  4. classpath:/config/
  5. classpath:/

8.4 加载优先级总结(按高到底:)

优先级高的会覆盖低的, 也可以理解他们的加载时机是顺序相反的!

  1. 启动时指定某些参数 > java -jar app.jar --server.port=9999

  2. 启动时指定配置文件(有坑:互补失效!)

java -jar app.jar -spring.config.location=/mydir/application.properties

  1. jar同目录的config/application.properties

app.jar 同级别目录下的 ./config/application.properties

  1. jar同目录的application.properties

app.jar同级别目录下的 application.properties

  1. jar内部 classpath目录下的 ./config/application.properties

app.jar/BOOT-INF/classes/config/application.properties

  1. jar内部classpath目录下的 application.properties

    app.jar/BOOT-INF/classes/application.properties

springboot自动装配原理和两个注解类的关系是深有千千结: @EnableAutoConfiguration和@Conditinal

我们从springboot的启动类上的注解看下这千丝万缕: 张宇的《一言难尽》不是有这么几句:

从哪里开始 从哪里失去
我一言难尽 忍不住伤心
衡量不出爱或不爱之间的距离
隐隐约约中 明白你的决定
不敢勉强你 只好为难自己
我为难我自己…

9.1 从哪里开始: @SpringBootApplication

package com.niewj.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConfigApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }

}

@SpringBootApplication:

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

我们来说下这两个注解的功能 :

// 1. 主要是讲此类标识为: @Configuration, 作为配置类, 让spring容器启动识别;

// 2. @EnableAutoConfiguration主要是自动装配; 下面细说 它;

9.2 自动装配去: @EnableAutoConfiguration:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 3
@Import(AutoConfigurationImportSelector.class) // 4
public @interface EnableAutoConfiguration 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) // 5
public @interface AutoConfigurationPackage {

}

// 3. @AutoConfigurationPackage 主要做了什么? 看注解链:

@SpringBootApplication -> @EnableAutoConfiguration -> @AutoConfigurationPackage -> @Import(AutoConfigurationPackages.Registrar.class)

好了, 我们就主要看看 AutoConfigurationPackages.Registrar.class做了什么我们见不得的事情:

参见下面的 #9.2.1 然后小结:

把入口类的package名, 注册到容器的Bean定义中, 后续扫描注解用(@Component/@Service/@Controller/@Repository等)

// 4. @Import(AutoConfigurationImportSelector.class) 我们也另立门户:

参见下面的 #9.2.2 然后小结:

做了啥啥啥

9.2.1 AutoConfigurationPackages.Registrar.class

/**
 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
 * configuration.
 */
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImport(metadata).getPackageName());
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImport(metadata));
    }

}

实现了 ImportBeanDefinitionRegistrar接口, 也就是说, 是向容器中注册Bean的(不明白的可以参考:spring注解驱动开发-(5) 向Spring容器中注册组件的方法), 注册什么Bean呢?

register(registry, new PackageImport(metadata).getPackageName());

打个断点debug看看: 相当于向容器中注册了当前入口类的包名, 后续扫描包下注解的时候就可以用到了!

image.png

原来如此, 小结: AutoConfigurationPackages.Registrar.class的功能如其名: 把入口类的package名注册到容器的Bean定义中以供后续加载和扫描注解用; 比如 @Component/@Service/@Controller/@Repository等这些;

9.2.2 @Import(AutoConfigurationImportSelector.class)

@Import就不用介绍了(可以参考文末的链接): 向容器中注册一个普通Bean的; 那么AutoConfigurationImportSelector是一个什么样的普通Bean呢?

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    // ==>关键在这里: getAutoConfigurationEntry()
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                                                                              annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param autoConfigurationMetadata the auto-configuration metadata
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                           AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // ==>关键在这里:getCandidateConfigurations()
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

/**
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {@link SpringFactoriesLoader} with
 * {@link #getSpringFactoriesLoaderFactoryClass()}.
 * @param metadata the source metadata
 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 * attributes}
 * @return a list of candidate configurations
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {    
    // ==>关键在这里: SpringFactoriesLoader.loadFactoryNames()
    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;
}

那么核心问题来了, SpringFactoriesLoader的loadFactoryNames()是作甚的?

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
      getBeanClassLoader());

/**
 * Load the fully qualified class names of factory implementations of the
 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
 * class loader.
 * @param factoryType the interface or abstract class representing the factory
 * @param classLoader the ClassLoader to use for loading resources; can be
 * {@code null} to use the default
 * @throws IllegalArgumentException if an error occurs while loading factory names
 * @see #loadFactories
 */
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

Load the fully qualified class names of factory implementations of the given type from “META-INF/spring.factories”, using the given class loader.

工具人翻译:

从”META-INF/spring.factories”配置文件里, 加载给定类型的工厂factoryType实现的全限定类名, 使用给定的类加载器。

例如 org.springframework.boot.autoconfigure包下的 spring-boot-autoconfigure-2.2.2.RELEASE.jar中的”META-INF/spring.factories”

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

到这里是不是感觉快迷失了? 回忆下上面那句歌词:从哪里开始 从哪里失去 感觉是不是很恐怖? 好了, 我们收住!

小结一下: @Import(AutoConfigurationImportSelector.class)主要做的事:

  1. 由于 ImportSelector 接口就是返回一个 类名的字符串数组String[], 然后交给spring容器让它来把这些字符串形式的类初始化了, 然后都注册到容器中;
  2. 这里就是, 使用 @EnableAutoConfiguration 的全限定名去它所在的jar包spring-boot-autoconfigure.jar下去找 META-INF/spring.factories, 然后去读取那124个类, 转换成 String[](当然不是全部, 下面的@Conditinal就是限定满足条件的才会放进去! 如下图的debug状态, 经过filter后, 只剩下了24个)
  3. 那么这24个类都做了什么呢? 其实就类似于我们自己写一个配置类(标注@Configuration), 然后提供初始化Bean的方法(标注@Bean), 让它注册到容器中被容器管理; 只不过是这样的类多了, 难免混乱, 这种管理方式只是为了统一, 简洁!

也就是说, 如果加载类是: org.springframework.boot.autoconfigure.EnableAutoConfiguration就能读取到这么多全限定类名, 没错, 我数了下, 一共 124 个类

filter图:

image-20200806192214724

9.3 @Conditional和xxxAutoConfiguration类

紧接着上面的124个类–>那么问题来了:

  1. 这些类是干嘛的?

    向容器中提供注册Bean的, 各种功能的Bean, 类似于我们自己的@Configuration + @Bean;

  2. 这些类124个都加载注册到容器中吗?

    不是的, 是有条件的:@Conditinal

  3. 这是什么操作? 为什么有 SpringFactoriesLoader ? 为什么有META-INF/spring.factories? 这都是什么鬼?

    SPI 机制(Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制), 很多地方有用到:

    1. JDBC加载不同类型数据库的驱动
    2. 日志门面类和具体的日志实现
    3. Dubbo

    参考: 高级开发必须理解的Java中SPI机制

9.3.1 Conditional相关注解一览

springboot中常见的 Conditinal以及其用意:

@Conditional
@ConditionalOnJava // 系统的java版本是否符合要求
@ConditionalOnBean // 容器中存在指定的Bean
@ConditionalOnClass // 系统中有指定的类
@ConditionalOnProperty // 系统中指定的属性是否有指定的值
@ConditionalOnWebApplication // 当前是Web环境
@ConditionalOnNotWebApplication // 当前不是web环境
@ConditionalOnResource // 类路径下是否存在指定的资源文件

@ConditionalOnMissingBean // 容器中不存在指定的Bean
@ConditionalOnMissingClass // 系统中不存在指定类
@ConditionalOnSingleCandidate // 容器中只有一个指定Bean, 或者这个Bean是首选Bean(primary的)

9.3.2 几个示例

下面我们找几个springboot中的XXXAutoConfiguration的示例:

示例1: AOP自动装配类

AopAutoConfiguration源码(注释删去了)

package org.springframework.boot.autoconfigure.aop;

import org.aspectj.weaver.Advice;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) // ===> #1.
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)  // ==> #2
	static class AspectJAutoProxyingConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {

		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.aspectj.weaver.Advice") // ==> #3
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	static class ClassProxyingConfiguration {

		ClassProxyingConfiguration(BeanFactory beanFactory) {
			if (beanFactory instanceof BeanDefinitionRegistry) {
				BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
				AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
		}

	}

}

// #1. @ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true)

类上注解: 是说只有在配置文件里能找到: spring.aop.auto=true 此类才注册到容器; matchIfMissing限定是否一定要匹配为true, 这里说明 不为true 也注册;

// #2. @ConditionalOnClass(Advice.class)

类上注解: 只有在系统中能找到类文件 Advice.class 时 才注册AspectJAutoProxyingConfiguration

示例2: DispatcherServlet自动装配类

DispatcherServletAutoConfiguration源码局部(删去部分注释)

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
// #1 只在web环境下才能装配, 且是servlet的环境下;
@ConditionalOnWebApplication(type = Type.SERVLET) 
// #2 只有系统中有类 DispatcherServlet 存在才能装配;
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

	/*
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	/*
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

	@Configuration(proxyBeanMethods = false)
     // #3 括号中的类DefaultDispatcherServletCondition定义了条件
	@Conditional(DefaultDispatcherServletCondition.class)
	// #4 下面所有都是系统有ServletRegistration类才装配
    @ConditionalOnClass(ServletRegistration.class) 
	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
	protected static class DispatcherServletConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
			return dispatcherServlet;
		}

		@Bean
		@ConditionalOnBean(MultipartResolver.class)	// ==>#5
		@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) // ==>#6
		public MultipartResolver multipartResolver(MultipartResolver resolver) {
			// Detect if the user has created a MultipartResolver but named it incorrectly
			return resolver;
		}

	}

	......省略后面部分......

}

说明见类注释中;

另外说明:

我们在基于注解的spring开发中没有配置 DispatcherServlet, 就是因为这个DispatcherServlet自动装配类在起作用, 这里向容器中注册了一个Bean:public DispatcherServlet dispatcherServlet()这块! 相关的配置信息都在参数类: WebMvcProperties中(自然它也是容器中已注册的.)

示例3: WebMvc自动装配类

@Configuration(proxyBeanMethods = false)
// 1. 要求servlet的web环境;
@ConditionalOnWebApplication(type = Type.SERVLET) 
// 2. 要求能找到Servlet/DispatcherServlet/WebMvcConfigure这几个类才装配
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) 
// 3. 要求容器中没有已注册的Bean WebMvcConfigurationSupport的示例才会装配;
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {}

参考文档:


  1. 【Spring Boot源码分析】@EnableAutoConfiguration注解(一)@AutoConfigurationImportSelector注解的处理

  2. 高级开发必须理解的Java中SPI机制

大致整理下面这些类的作用和关系:

BeanPostProcessor Bean初始化的后置处理器;
BeanFactoryPostProcessor BeanFactory的后置处理器;
BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子接口, 新增了一个方法!

1. BeanPostProcessor

Bean初始化的后置处理器
postProcessBeforeInitialization() 在bean初始化前执行;
postProcessAfterInitialization() 在bean初始化完成后执行;

BeanPostProcessor 默认是会对整个Spring容器中所有的bean进行处理; 可以根据参数 bean和 beanName做一些选择和拦截操作, 比如为某个bean设一个值;

package com.niewj.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * 通过 BeanPostProcessor 接口
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        System.out.println("MyBeanPostProcessor-初始化!");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 可以通过此方法, 找到某一个目标 bean, 做一些操作: 如给容器中的 Service1 依赖的空对象赋值
        // 注意: 这里的 Service1.setService2(new Service2()) 中的 Service2 不会注册到容器中!
        // 但是 在Service1中使用 Service2的方法时, 是有我们赋值的对象的;
        System.out.println("###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[" + beanName + "]###-> begin");
        if (bean instanceof Service1) {
            Service1 service1 = (Service1) bean;
            System.out.println("bean=" + bean + "; bean依赖为: " + service1.getService2());
            // 注入依赖(自己new的)
            service1.setService2(new Service2());
        }
        System.out.println("###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[" + beanName + "]###-> end");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 由于上面已经赋值, 这里可以拿到
        System.out.println("###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[" + beanName + "]###-> begin");
        if (bean instanceof Service1) {
            Service1 service1 = (Service1) bean;
            System.out.println("bean=" + bean + "; bean依赖为: " + service1.getService2());
        }
        System.out.println("###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[" + beanName + "]###-> end");
        return bean;
    }
}

2. BeanFactoryPostProcessor

相比较 BeanPostProcessor, 它是针对 BeanFactory的;

BeanFactoryPostProcessor的执行时机是: 在Bean定义保存加载完成, bean尚未初始化前执行;

要先于 BeanPostProcessor的两个方法;

但是要晚于 BeanDefinitionRegistryPostProcessor 的另一个方法: postProcessBeanDefinitionRegistry()

具体参见 BeanDefinitionRegistryPostProcessor

package com.niewj.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("===> MyBeanFactoryPostProcessor#postProcessBeanFactory-->可以获取容器内bean定义的数量->getBeanDefinitionCount=> " + beanFactory.getBeanDefinitionCount());
        System.out.println("===> MyBeanFactoryPostProcessor#postProcessBeanFactory-->可以打印容器中的所有 定义的BeanDefinition->getBeanDefinitionNames:");
        System.out.println(Arrays.asList(beanFactory.getBeanDefinitionNames()));
    }
}

3. BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 是上面 BeanFactoryPostProcessor 的子接口;

它除了有方法 postProcessBeanFactory()之外, 自己还新增了一个方法 postProcessBeanDefinitionRegistry

他们的执行时机:

postProcessBeanDefinitionRegistry()方法的执行时机是: 容器中所有的bean定义将要被加载前! 可以获取到 bean定义的注册器, 可用于注册bean;

postProcessBeanFactory() 方法的执行时机是: 容器中所有的bean已经保存加载, 但bean还未创建! 可以用来从beanFactory中拿出 bean;

package com.niewj.util;

import com.niewj.bean.Pojo4;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("===> MyBeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 调用!!!");
        System.out.println("registry instanceof BeanFactory: " + (registry instanceof BeanFactory));

        System.out.println("###->MyBeanDefinitionRegistryPostProcessor注册bean:[Service2.class]###-> begin");
        // 注册一个bean: Service2(并没有注解 @Component)
        AbstractBeanDefinition myBean = BeanDefinitionBuilder.rootBeanDefinition(Service2.class).getBeanDefinition();
        registry.registerBeanDefinition("service2", myBean);
        System.out.println("###->MyBeanDefinitionRegistryPostProcessor注册bean:[Service2.class]###-> over");

        System.out.println("###->MyBeanDefinitionRegistryPostProcessor注册bean:[Pojo4.class]###-> begin");
        // 注册一个bean: Pojo4
        AbstractBeanDefinition myPojo4 = BeanDefinitionBuilder.rootBeanDefinition(Pojo4.class).getBeanDefinition();
        registry.registerBeanDefinition("myPojo4", myPojo4);
        System.out.println("###->MyBeanDefinitionRegistryPostProcessor注册bean:[Pojo4.class]###-> over");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 1. 可以再次修改容器内一些bean的特征: 如设置 Pojo4类延迟初始化!
        BeanDefinition myPojo4 = beanFactory.getBeanDefinition("myPojo4");
        myPojo4.setLazyInit(true);

        // 2. 可以获取容器内bean定义的数量
        System.out.println("===> MyBeanDefinitionRegistryPostProcessor#postProcessBeanFactory-->可以获取容器内bean定义的数量->getBeanDefinitionCount=> " + beanFactory.getBeanDefinitionCount());

        // 3. 可以打印容器中的所有 定义的BeanDefinition
        System.out.println("===> MyBeanDefinitionRegistryPostProcessor#postProcessBeanFactory-->可以打印容器中的所有 定义的BeanDefinition->getBeanDefinitionNames:\t");
        System.out.println(Arrays.asList(beanFactory.getBeanDefinitionNames()));
    }
}

像本例中 Test2Service 并未注解 @Component/@Service等; 通过注册器可以将普通bean注册到容器:

BeanDefinitionBuilder.rootBeanDefinition(Test2Service.class).getBeanDefinition();
registry.registerBeanDefinition(“test2Service”, myBean);

其他相关的类代码:

  • Service1.java
package com.niewj.util;

import org.springframework.stereotype.Component;

@Component
public class Service1 {

    public Service2 getService2() {
        return service2;
    }

    public void setService2(Service2 service2) {
        this.service2 = service2;
    }

    private Service2 service2;

    public void doBusiness(){
        System.out.println("Service1.doBusiness");
        service2.doSth();
    }

}
  • Service2.java
package com.niewj.util;

public class Service2 {

    public void doSth(){
        System.out.println("Service2.doSth: 执行了!");
    }
}
  • Pojo4.java
package com.niewj.bean;

public class Pojo4 {
    public Pojo4(){
        System.out.println("Pojo4[无注解java类] 初始化~~~");
    }
}
  • User.java
package com.niewj.bean;

import lombok.Data;

@Data
public class User {
    private String name;
    private String passwd;
    private boolean online ;

    public User(String name, String passwd, boolean online){
        System.out.println("User-初始化!");
        this.name = name;
        this.passwd = passwd;
        this.online = online;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", passwd='" + passwd + '\'' +
                ", online=" + online +
                '}';
    }
}
  • 配置类: TestBeanCreationConfig.java
package com.niewj.config;

import com.niewj.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.niewj.util")
public class TestBeanCreationConfig {

    @Bean
    public User user() {
        return new User("u", "122", false);
    }
}
  • 测试用例: BeanCreationTest.java
package com.niewj;

import com.niewj.config.TestBeanCreationConfig;
import com.niewj.util.Service1;
import com.niewj.util.Service2;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.stream.Stream;

/**
 * spring bean创建和初始化测试:
 *  BeanFactoryPostProcessor
 *  BeamDefinitionRegistryPostProcessor
 */
public class BeanCreationTest {

    @Test
    public void testBeanCreation() {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(TestBeanCreationConfig.class);
        ctx.refresh();

        // 打印spring容器中的 BeanDefinition
        Stream.of(ctx.getBeanDefinitionNames()).forEach(e -> System.out.println(e));
        System.out.println("============Service1#doBusiness()=================");

        Service1 service1 = ctx.getBean(Service1.class);
        service1.doBusiness();
        System.out.println("======Service2#doSth()=====");
        Service2 service2 = ctx.getBean(Service2.class);
        service2.doSth();

        ctx.close();
    }
}
  • output: 控制台输出:
===> MyBeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 调用!!!
registry instanceof BeanFactory: true
###->MyBeanDefinitionRegistryPostProcessor注册bean:[Service2.class]###-> begin
###->MyBeanDefinitionRegistryPostProcessor注册bean:[Service2.class]###-> over
###->MyBeanDefinitionRegistryPostProcessor注册bean:[Pojo4.class]###-> begin
###->MyBeanDefinitionRegistryPostProcessor注册bean:[Pojo4.class]###-> over
===> MyBeanDefinitionRegistryPostProcessor#postProcessBeanFactory-->可以获取容器内bean定义的数量->getBeanDefinitionCount=> 13
===> MyBeanDefinitionRegistryPostProcessor#postProcessBeanFactory-->可以打印容器中的所有 定义的BeanDefinition->getBeanDefinitionNames:	
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, testBeanCreationConfig, myBeanDefinitionRegistryPostProcessor, myBeanFactoryPostProcessor, myBeanPostProcessor, service1, user, service2, myPojo4]
===> MyBeanFactoryPostProcessor#postProcessBeanFactory-->可以获取容器内bean定义的数量->getBeanDefinitionCount=> 13
===> MyBeanFactoryPostProcessor#postProcessBeanFactory-->可以打印容器中的所有 定义的BeanDefinition->getBeanDefinitionNames:
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, testBeanCreationConfig, myBeanDefinitionRegistryPostProcessor, myBeanFactoryPostProcessor, myBeanPostProcessor, service1, user, service2, myPojo4]
MyBeanPostProcessor-初始化!
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[testBeanCreationConfig]###-> begin
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[testBeanCreationConfig]###-> end
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[testBeanCreationConfig]###-> begin
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[testBeanCreationConfig]###-> end
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[service1]###-> begin
bean=com.niewj.util.Service1@77167fb7; bean依赖为: null
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[service1]###-> end
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[service1]###-> begin
bean=com.niewj.util.Service1@77167fb7; bean依赖为: com.niewj.util.Service2@1fe20588
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[service1]###-> end
User-初始化!
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[user]###-> begin
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[user]###-> end
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[user]###-> begin
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[user]###-> end
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[service2]###-> begin
###->MyBeanPostProcessor#postProcessBeforeInitialization->beanName=[service2]###-> end
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[service2]###-> begin
###->MyBeanPostProcessor#postProcessAfterInitialization->beanName=[service2]###-> end
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testBeanCreationConfig
myBeanDefinitionRegistryPostProcessor
myBeanFactoryPostProcessor
myBeanPostProcessor
service1
user
service2
myPojo4
============Service1#doBusiness()=================
Service1.doBusiness
Service2.doSth: 执行了!
======Service2#doSth()=====
Service2.doSth: 执行了!

4. 执行顺序解析

  1. BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 可以拿到 BeanDefinitionRegistry, 可用来注册bean; 这里注册了2个bean这里 Service2.classPojo4.class 1-6行

  2. BeanDefinitionRegistryPostProcessor#postProcessBeanFactory 可以拿到 BeanFactory ; 这里打印里已有的bean定义数量 和 BeanFactory里所有的bean定义内容; 7-9行

  3. BeanFactoryPostProcessor#postProcessBeanFactory 可以拿到 BeanFactory; 这里同样打印了bean定义数量和BeanFactory里所有的bean定义内容; 10-12行

    – 上面两个PostProcessor是BeanFactory相关的, 只会执行一次; 下面的 BPP(BeanPostProcessor)则不同, BPP是每个Bean都要过一遍; 可以用来对Bean进行过滤做一些拦截处理:

  4. 遍历: BeanPostProcessor#postProcessBeforeInitialization 这里输出了每个bean的 beanName; 并过滤了一个bean:Service1, 如果是它, 就给手动注入了一个Service2对象; 12-32行

  5. 遍历: BeanPostProcessor#postProcessAfterInitialization 这里输出了每个bean的 beanName; 并过滤了一个bean:Service1, 获取到它的Service2对象, 可以看到非null了; 12-32行

  6. 上面流程就是3个PostProcessor的顺序关系; 后面的 33-45行就是 Stream.of(ctx.getBeanDefinitionNames()).forEach(e -> System.out.println(e));打印的内容了;

5. 小结

3个PostProcessor的执行顺序:

(1). BeanDefinitionRegistryPostProcessor# postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) ;

(2). BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);

(3). BeanPostProcessor# postProcessBeforeInitialization(Object bean, String beanName)

(4). BeanPostProcessor# postProcessAfterInitialization(Object bean, String beanName)

(1). Bean的创建

bean的创建实际上就是指 构造方法的调用;

singleton(单例)bean -容器初始化时会预先调用, 除非标注了 @Lazy 注解指定懒加载(延迟创建);

prototype(原型)bean -当第一次调用 getBean方法时, 才会调用构造方法

(2). Bean的初始化和销毁-4种方法

bean的初始化, 指的是在构造方法调用之后, 对象的一些初始化操作;

bean的销毁, 指的是在spring容器关闭前, 对bean对象做的一些后续处理操作的调用;

Bean的初始化和销毁有一下几种方法:

(2.1) @Bean注解指定initMethod/destroyMethod;

bean: 普通java类, 其中定义了 方法名: init close

package com.niewj.bean;

public class LifeTestBean1 {
    private String value;

    public LifeTestBean1(String value){
        System.out.println("LifeTestBean1-初始化!");
        this.value = value;
    }

    public void init(){
        System.out.println("LifeTestBean1#init-调用!");
    }

    public void close(){
        System.out.println("LifeTestBean1#close-调用!");
    }
}

Life1Config配置类中: 通过 @Bean 的属性 指定了初始化和销毁方法:

  • initMethod=”init”
  • destroyMethod=”close”
package com.niewj.config;

import com.niewj.bean.LifeTestBean1;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Life1Config {

    @Bean(value = "lifeBean1", initMethod = "init", destroyMethod = "close")
    public LifeTestBean1 lifeTestBean(){
        return  new LifeTestBean1("bean1");
    }
}

测试用例: testLifecycle

package com.niewj;

import com.niewj.bean.LifeTestBean1;
import com.niewj.config.Life1Config;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.stream.Stream;

/**
 * spring生命周期.
 */
public class LifecycleTest {

    @Test
    public void testLifecycle() {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Life1Config.class);

        // 打印spring容器中的 BeanDefinition
        Stream.of(ctx.getBeanDefinitionNames()).forEach(e-> System.out.println(e));
        System.out.println("=============================");
        LifeTestBean1 bean = ctx.getBean(LifeTestBean1.class);

        System.out.println(bean);

        ctx.close();
    }
}

​ 可以看到:

  • 构造方法会预先调用(单例bean);
  • 然后init方法被调用;
  • 最后上下文关闭时, close方法也被调用!
LifeTestBean1-初始化!
LifeTestBean1#init-调用!
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
life1Config
lifeBean1
=============================
com.niewj.bean.LifeTestBean1@7d3a22a9
LifeTestBean1#close-调用!

(2.2) InitializingBean/DisposableBean 接口;

bean: LifeTestBean2 implements InitializingBean, DisposableBean

package com.niewj.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * 实现 InitializingBean && DisposableBean
 */
public class LifeTestBean2 implements InitializingBean, DisposableBean {
    private String value;

    public LifeTestBean2(String value){
        System.out.println("LifeTestBean2-初始化!");
        this.value = value;
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("LifeTestBean2#DisposableBean#destroy-调用!");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("LifeTestBean2#InitializingBean#afterPropertiesSet-调用!");
    }
}

配置类: Life2Config:

package com.niewj.config;

import com.niewj.bean.LifeTestBean2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Life2Config {

    @Bean
    public LifeTestBean2 lifeTestBean(){
        return  new LifeTestBean2("bean2");
    }
}

测试用例:

@Test
public void testLifecycle2() {
    // 1. 通过bean实现 InitializingBean和DisposableBean接口
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Life2Config.class);

    // 打印spring容器中的 BeanDefinition
    Stream.of(ctx.getBeanDefinitionNames()).forEach(e-> System.out.println(e));
    System.out.println("=============================");
    LifeTestBean2 bean = ctx.getBean(LifeTestBean2.class);

    System.out.println(bean);

    ctx.close();
}

output:

  • 构造方法调用(singleton 容器预先初始化);
  • InitializingBean#afterPropertiesSet调用;
  • 工厂关闭前: DisposableBean#destroy调用;
LifeTestBean2-初始化!
LifeTestBean2#InitializingBean#afterPropertiesSet-调用!
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
life2Config
lifeTestBean
=============================
com.niewj.bean.LifeTestBean2@7d3a22a9
LifeTestBean2#DisposableBean#destroy-调用!

(2.3) @PostConstruct/@PreDestroy 注解(JSR250);

bean: LifeTestBean3 注解: @PreDestroy PostConstruct

package com.niewj.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * 通过 JSR250注解 @PostConstruct && @PreDestroy
 */
public class LifeTestBean3 {
    private String value;

    public LifeTestBean3(String value) {
        System.out.println("LifeTestBean3-初始化!");
        this.value = value;
    }

    @PreDestroy
    public void doDestroy() {
        System.out.println("LifeTestBean3#doDestroy-调用!");
    }

    @PostConstruct
    public void doInit() {
        System.out.println("LifeTestBean3#doInit-调用!");
    }
}

配置类:

package com.niewj.config;

import com.niewj.bean.LifeTestBean3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Life3Config {

    @Bean
    public LifeTestBean3 lifeTestBean() {
        return new LifeTestBean3("bean3");
    }
}

测试用例:

@Test
public void testLifecycle3() {
    // 1. 通过 JSR250注解 @PostConstruct && @PreDestroy
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Life3Config.class);

    // 打印spring容器中的 BeanDefinition
    Stream.of(ctx.getBeanDefinitionNames()).forEach(e-> System.out.println(e));
    System.out.println("=============================");
    LifeTestBean3 bean = ctx.getBean(LifeTestBean3.class);

    System.out.println(bean);

    ctx.close();
}

output:

  • 构造方法调用;
  • @PostConstruct标注的方法调用;
  • 上下文关闭时; @PreDestroy标注的方法被调用;
LifeTestBean3-初始化!
LifeTestBean3#doInit-调用!
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
life3Config
lifeTestBean
=============================
com.niewj.bean.LifeTestBean3@2a32de6c
LifeTestBean3#doDestroy-调用!

(2.4) BeanPostProcessor接口(作用范围广);

此接口对所有扫描到的bean都起作用:

实现: BeanPostProcessor 接口:

  • before-init : postProcessBeforeInitialization(Object bean, String beanName)
  • after-init: postProcessAfterInitialization(Object bean, String beanName)
package com.niewj.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * 通过 BeanPostProcessor 接口
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        System.out.println("MyBeanPostProcessor-初始化!");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization-调用!->beanName=" + beanName + "; bean=" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor#postProcessAfterInitialization-调用!->beanName=" + beanName + "; bean=" + bean);
        return bean;
    }
}

com.niewj.bean包下一个普通类:

package com.niewj.bean;

import org.springframework.stereotype.Component;

/**
 * 普通bean+@Component
 */
@Component
public class LifeTestBean4 {

    public LifeTestBean4() {
        System.out.println("LifeTestBean4-初始化!");
    }
}

配置类: Life4Config:

扫描范围: @ComponentScan(“com.niewj.bean”)

包括: LifeTestBean4.java && MyBeanPostProcessor.java

package com.niewj.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.niewj.bean")
public class Life4Config {

}

用例: 注册配置类: Life4Config

@Test
public void testLifecycle4() {
    // 1. 通过 实现 BeanPostProcesser 接口
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Life4Config.class);

    // 打印spring容器中的 BeanDefinition
    Stream.of(ctx.getBeanDefinitionNames()).forEach(e-> System.out.println(e));
    System.out.println("=============================");

    ctx.close();
}

output: 观察控制台:

MyBeanPostProcessor: postProcessBeforeInitialization/postProcessAfterInitialization

LifeTestBean4: postProcessBeforeInitialization/postProcessAfterInitialization

MyBeanPostProcessor-初始化!
MyBeanPostProcessor#postProcessBeforeInitialization-调用!->beanName=life4Config; bean=com.niewj.config.Life4Config$$EnhancerBySpringCGLIB$$684530bf@27c86f2d
MyBeanPostProcessor#postProcessAfterInitialization-调用!->beanName=life4Config; bean=com.niewj.config.Life4Config$$EnhancerBySpringCGLIB$$684530bf@27c86f2d
LifeTestBean4-初始化!
MyBeanPostProcessor#postProcessBeforeInitialization-调用!->beanName=lifeTestBean4; bean=com.niewj.bean.LifeTestBean4@197d671
MyBeanPostProcessor#postProcessAfterInitialization-调用!->beanName=lifeTestBean4; bean=com.niewj.bean.LifeTestBean4@197d671
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
life4Config
lifeTestBean4
myBeanPostProcessor
=============================

(2.5) bean的初始化小结

初始化和销毁有四种方式:

1. @Bean注解指定属性 initMethod/destroyMethod;

2. 普通bean实现 InitializingBean/DisposableBean接口;

3. 普通bean方法上标注 @PostConstruct/@PreDestroy注解;

4. 通用方法: 实现接口 BeanPostProcessor;

(2.6) 问题来了: 4种方式的优先级顺序是如何的?

上实例: LifeTestBeanAll.java

其中包含:

1.@PostConstruct/@PreDestroy 注解;

2.InitializingBean, DisposableBean接口的实现;

3.@Bean 指定 initMethod/destroyMethod方法;

4.MyBeanPostProcessor实现(在下方);

package com.niewj.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * 普通bean+@Component
 */
@Component
public class LifeTestBeanAll implements InitializingBean, DisposableBean {

    public LifeTestBeanAll() {
        System.out.println("LifeTestBeanAll 构造方法调用!");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("LifeTestBeanAll#@PostConstruct 调用");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("LifeTestBeanAll#@PreDestroy 调用");
    }

    public void init(){
        System.out.println("LifeTestBeanAll#@Bean(initMethod) 调用");
    }

    public void close(){
        System.out.println("LifeTestBeanAll#@Bean(destroyMethod) 调用");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("LifeTestBeanAll#Disposable 调用~~");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("LifeTestBeanAll#InitializingBean#调用!~");
    }
}

MyBeanPostProcessor:

package com.niewj.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * 通过 BeanPostProcessor 接口
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        System.out.println("MyBeanPostProcessor-初始化!");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization-调用!->beanName=" + beanName + "; bean=" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor#postProcessAfterInitialization-调用!->beanName=" + beanName + "; bean=" + bean);
        return bean;
    }
}

配置类:

package com.niewj.config;

import com.niewj.bean.LifeTestBeanAll;
import com.niewj.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.niewj.bean")
public class LifeAllConfig {

    @Bean(initMethod = "init", destroyMethod = "close")
    public LifeTestBeanAll lifeTestBeanAll(){
        return new LifeTestBeanAll();
    }
}

测试用例:

@Test
public void testLifecycleAll() {
    // 注释掉 LifeTestBean4#@Component以防干扰
    // 1. 通过 实现 BeanPostProcesser 接口
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(LifeAllConfig.class);

    // 打印spring容器中的 BeanDefinition
    Stream.of(ctx.getBeanDefinitionNames()).forEach(e-> System.out.println(e));
    System.out.println("=============================");

    ctx.close();
}

output: 关键地方来了:

MyBeanPostProcessor-初始化!
MyBeanPostProcessor#postProcessBeforeInitialization-调用!->beanName=lifeAllConfig; bean=com.niewj.config.LifeAllConfig$$EnhancerBySpringCGLIB$$77052e36@3d121db3
MyBeanPostProcessor#postProcessAfterInitialization-调用!->beanName=lifeAllConfig; bean=com.niewj.config.LifeAllConfig$$EnhancerBySpringCGLIB$$77052e36@3d121db3
LifeTestBeanAll 构造方法调用!
MyBeanPostProcessor#postProcessBeforeInitialization-调用!->beanName=lifeTestBeanAll; bean=com.niewj.bean.LifeTestBeanAll@6b26e945
LifeTestBeanAll#@PostConstruct 调用
LifeTestBeanAll#InitializingBean#调用!~
LifeTestBeanAll#@Bean(initMethod) 调用
MyBeanPostProcessor#postProcessAfterInitialization-调用!->beanName=lifeTestBeanAll; bean=com.niewj.bean.LifeTestBeanAll@6b26e945
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
lifeAllConfig
lifeTestBeanAll
myBeanPostProcessor
=============================
LifeTestBeanAll#@PreDestroy 调用
LifeTestBeanAll#Disposable 调用~~
LifeTestBeanAll#@Bean(destroyMethod) 调用

分析整理: 只看 lifeTestBeanAll:

1.BeanPostProcessor#postProcessBeforeInitialization首先调用;

2.@PostConstruct;

3.InitializingBean接口实现;

4.@Bean(initMethod)

5.BeanPostProcessor#postProcessAfterInitialization调用;

6.@PreDestroy

7.Disposable接口实现

8.@Bean(destroyMethod)

初始化顺序: BeanPostProcessor的before-init –> @PostConstruct注解 –> InitializingBean接口 –> @Bean注解指定的initMethod方法 –>BeanPostProcessor的after-init;

destroy的顺序: @PreDestroy注解 –> DisposableBean接口 – > @Bean(destroyMethod)

(2.7) 源码分析:doCreateBean

源码位置:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
doCreateBean(){...
    try {
	    populateBean(beanName, mbd, instanceWrapper);
	    exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
...
}

1. populateBean: 为bean属性赋值等操作;

2. initializeBean: (下面时序图中的操作)

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
方法流程源码片段:
---方法名:
AbstractAutowireCapableBeanFactory#initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
(1). applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
	1. 遍历并调用BeanPostProcessor#postProcessBeforeInitialization()
(2). invokeInitMethods(beanName, wrappedBean, mbd): 
    1. 调用 InitializingBean接口的 afterPropertiesSet()方法;
    2. 调用 invokeCustomInitMethod() --> @Bean(initMethod) 执行!
(3). applyBeanPostProcessorsAfterInitialization
	1. 调用 postProcessAfterInitialization()
2.1 applyBeanPostProcessorsBeforeInitialization
2.2 invokeInitMethods
2.3. applyBeanPostProcessorsAfterInitialization

时序图:
image.png

(3). BeanPostProcessor扩展

@Autowired注解:

AutowiredAnnotationBeanPostProcessor类–>处理 @Autowired 注解

@PostConstruct && @PreDestroy的实现

InitDestroyAnnotationBeanPostProcessor类实现 @PostConstruct @PreDestroy注解

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
{
	metadata.invokeInitMethods(bean, beanName);
}

invokeInitMethods:
{
element.invoke(target);
}

根据以上知识点, 便可以知道为什么4个初始化和销毁的方法的优先级顺序是 BeanPostProcessor#before->@PostConstruct->InitializingBean->@Bean(initMethod)->BeanPostProcessor#after了;

openjdk8的sun.misc.Unsafe.java源码

2. Unsafe源码搬运展示

/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.misc;

import java.security.*;
import java.lang.reflect.*;

import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;


/**
 * A collection of methods for performing low-level, unsafe operations.
 * Although the class and all methods are public, use of this class is
 * limited because only trusted code can obtain instances of it.
 *
 * @author John R. Rose
 * @see #getUnsafe
 */

public final class Unsafe {

    private static native void registerNatives();
    static {
        registerNatives();
        sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
    }

    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();

    /**
     * Provides the caller with the capability of performing unsafe
     * operations.
     *
     * <p> The returned <code>Unsafe</code> object should be carefully guarded
     * by the caller, since it can be used to read and write data at arbitrary
     * memory addresses.  It must never be passed to untrusted code.
     *
     * <p> Most methods in this class are very low-level, and correspond to a
     * small number of hardware instructions (on typical machines).  Compilers
     * are encouraged to optimize these methods accordingly.
     *
     * <p> Here is a suggested idiom for using unsafe operations:
     *
     * <blockquote><pre>
     * class MyTrustedClass {
     *   private static final Unsafe unsafe = Unsafe.getUnsafe();
     *   ...
     *   private long myCountAddress = ...;
     *   public int getCount() { return unsafe.getByte(myCountAddress); }
     * }
     * </pre></blockquote>
     *
     * (It may assist compilers to make the local variable be
     * <code>final</code>.)
     *
     * @exception  SecurityException  if a security manager exists and its
     *             <code>checkPropertiesAccess</code> method doesn't allow
     *             access to the system properties.
     */
    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }

    /// peek and poke operations
    /// (compilers should optimize these to memory ops)

    // These work on object fields in the Java heap.
    // They will not work on elements of packed arrays.

    /**
     * Fetches a value from a given Java variable.
     * More specifically, fetches a field or array element within the given
     * object <code>o</code> at the given offset, or (if <code>o</code> is
     * null) from the memory address whose numerical value is the given
     * offset.
     * <p>
     * The results are undefined unless one of the following cases is true:
     * <ul>
     * <li>The offset was obtained from {@link #objectFieldOffset} on
     * the {@link java.lang.reflect.Field} of some Java field and the object
     * referred to by <code>o</code> is of a class compatible with that
     * field's class.
     *
     * <li>The offset and object reference <code>o</code> (either null or
     * non-null) were both obtained via {@link #staticFieldOffset}
     * and {@link #staticFieldBase} (respectively) from the
     * reflective {@link Field} representation of some Java field.
     *
     * <li>The object referred to by <code>o</code> is an array, and the offset
     * is an integer of the form <code>B+N*S</code>, where <code>N</code> is
     * a valid index into the array, and <code>B</code> and <code>S</code> are
     * the values obtained by {@link #arrayBaseOffset} and {@link
     * #arrayIndexScale} (respectively) from the array's class.  The value
     * referred to is the <code>N</code><em>th</em> element of the array.
     *
     * </ul>
     * <p>
     * If one of the above cases is true, the call references a specific Java
     * variable (field or array element).  However, the results are undefined
     * if that variable is not in fact of the type returned by this method.
     * <p>
     * This method refers to a variable by means of two parameters, and so
     * it provides (in effect) a <em>double-register</em> addressing mode
     * for Java variables.  When the object reference is null, this method
     * uses its offset as an absolute address.  This is similar in operation
     * to methods such as {@link #getInt(long)}, which provide (in effect) a
     * <em>single-register</em> addressing mode for non-Java variables.
     * However, because Java variables may have a different layout in memory
     * from non-Java variables, programmers should not assume that these
     * two addressing modes are ever equivalent.  Also, programmers should
     * remember that offsets from the double-register addressing mode cannot
     * be portably confused with longs used in the single-register addressing
     * mode.
     *
     * @param o Java heap object in which the variable resides, if any, else
     *        null
     * @param offset indication of where the variable resides in a Java heap
     *        object, if any, else a memory address locating the variable
     *        statically
     * @return the value fetched from the indicated Java variable
     * @throws RuntimeException No defined exceptions are thrown, not even
     *         {@link NullPointerException}
     */
    public native int getInt(Object o, long offset);

    /**
     * Stores a value into a given Java variable.
     * <p>
     * The first two parameters are interpreted exactly as with
     * {@link #getInt(Object, long)} to refer to a specific
     * Java variable (field or array element).  The given value
     * is stored into that variable.
     * <p>
     * The variable must be of the same type as the method
     * parameter <code>x</code>.
     *
     * @param o Java heap object in which the variable resides, if any, else
     *        null
     * @param offset indication of where the variable resides in a Java heap
     *        object, if any, else a memory address locating the variable
     *        statically
     * @param x the value to store into the indicated Java variable
     * @throws RuntimeException No defined exceptions are thrown, not even
     *         {@link NullPointerException}
     */
    public native void putInt(Object o, long offset, int x);

    /**
     * Fetches a reference value from a given Java variable.
     * @see #getInt(Object, long)
     */
    public native Object getObject(Object o, long offset);

    /**
     * Stores a reference value into a given Java variable.
     * <p>
     * Unless the reference <code>x</code> being stored is either null
     * or matches the field type, the results are undefined.
     * If the reference <code>o</code> is non-null, car marks or
     * other store barriers for that object (if the VM requires them)
     * are updated.
     * @see #putInt(Object, int, int)
     */
    public native void putObject(Object o, long offset, Object x);

    /** @see #getInt(Object, long) */
    public native boolean getBoolean(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putBoolean(Object o, long offset, boolean x);
    /** @see #getInt(Object, long) */
    public native byte    getByte(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putByte(Object o, long offset, byte x);
    /** @see #getInt(Object, long) */
    public native short   getShort(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putShort(Object o, long offset, short x);
    /** @see #getInt(Object, long) */
    public native char    getChar(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putChar(Object o, long offset, char x);
    /** @see #getInt(Object, long) */
    public native long    getLong(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putLong(Object o, long offset, long x);
    /** @see #getInt(Object, long) */
    public native float   getFloat(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putFloat(Object o, long offset, float x);
    /** @see #getInt(Object, long) */
    public native double  getDouble(Object o, long offset);
    /** @see #putInt(Object, int, int) */
    public native void    putDouble(Object o, long offset, double x);

    /**
     * This method, like all others with 32-bit offsets, was native
     * in a previous release but is now a wrapper which simply casts
     * the offset to a long value.  It provides backward compatibility
     * with bytecodes compiled against 1.4.
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public int getInt(Object o, int offset) {
        return getInt(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putInt(Object o, int offset, int x) {
        putInt(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public Object getObject(Object o, int offset) {
        return getObject(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putObject(Object o, int offset, Object x) {
        putObject(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public boolean getBoolean(Object o, int offset) {
        return getBoolean(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putBoolean(Object o, int offset, boolean x) {
        putBoolean(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public byte getByte(Object o, int offset) {
        return getByte(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putByte(Object o, int offset, byte x) {
        putByte(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public short getShort(Object o, int offset) {
        return getShort(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putShort(Object o, int offset, short x) {
        putShort(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public char getChar(Object o, int offset) {
        return getChar(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putChar(Object o, int offset, char x) {
        putChar(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public long getLong(Object o, int offset) {
        return getLong(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putLong(Object o, int offset, long x) {
        putLong(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public float getFloat(Object o, int offset) {
        return getFloat(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putFloat(Object o, int offset, float x) {
        putFloat(o, (long)offset, x);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public double getDouble(Object o, int offset) {
        return getDouble(o, (long)offset);
    }

    /**
     * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
     * See {@link #staticFieldOffset}.
     */
    @Deprecated
    public void putDouble(Object o, int offset, double x) {
        putDouble(o, (long)offset, x);
    }

    // These work on values in the C heap.

    /**
     * Fetches a value from a given memory address.  If the address is zero, or
     * does not point into a block obtained from {@link #allocateMemory}, the
     * results are undefined.
     *
     * @see #allocateMemory
     */
    public native byte    getByte(long address);

    /**
     * Stores a value into a given memory address.  If the address is zero, or
     * does not point into a block obtained from {@link #allocateMemory}, the
     * results are undefined.
     *
     * @see #getByte(long)
     */
    public native void    putByte(long address, byte x);

    /** @see #getByte(long) */
    public native short   getShort(long address);
    /** @see #putByte(long, byte) */
    public native void    putShort(long address, short x);
    /** @see #getByte(long) */
    public native char    getChar(long address);
    /** @see #putByte(long, byte) */
    public native void    putChar(long address, char x);
    /** @see #getByte(long) */
    public native int     getInt(long address);
    /** @see #putByte(long, byte) */
    public native void    putInt(long address, int x);
    /** @see #getByte(long) */
    public native long    getLong(long address);
    /** @see #putByte(long, byte) */
    public native void    putLong(long address, long x);
    /** @see #getByte(long) */
    public native float   getFloat(long address);
    /** @see #putByte(long, byte) */
    public native void    putFloat(long address, float x);
    /** @see #getByte(long) */
    public native double  getDouble(long address);
    /** @see #putByte(long, byte) */
    public native void    putDouble(long address, double x);

    /**
     * Fetches a native pointer from a given memory address.  If the address is
     * zero, or does not point into a block obtained from {@link
     * #allocateMemory}, the results are undefined.
     *
     * <p> If the native pointer is less than 64 bits wide, it is extended as
     * an unsigned number to a Java long.  The pointer may be indexed by any
     * given byte offset, simply by adding that offset (as a simple integer) to
     * the long representing the pointer.  The number of bytes actually read
     * from the target address maybe determined by consulting {@link
     * #addressSize}.
     *
     * @see #allocateMemory
     */
    public native long getAddress(long address);

    /**
     * Stores a native pointer into a given memory address.  If the address is
     * zero, or does not point into a block obtained from {@link
     * #allocateMemory}, the results are undefined.
     *
     * <p> The number of bytes actually written at the target address maybe
     * determined by consulting {@link #addressSize}.
     *
     * @see #getAddress(long)
     */
    public native void putAddress(long address, long x);

    /// wrappers for malloc, realloc, free:

    /**
     * Allocates a new block of native memory, of the given size in bytes.  The
     * contents of the memory are uninitialized; they will generally be
     * garbage.  The resulting native pointer will never be zero, and will be
     * aligned for all value types.  Dispose of this memory by calling {@link
     * #freeMemory}, or resize it with {@link #reallocateMemory}.
     *
     * @throws IllegalArgumentException if the size is negative or too large
     *         for the native size_t type
     *
     * @throws OutOfMemoryError if the allocation is refused by the system
     *
     * @see #getByte(long)
     * @see #putByte(long, byte)
     */
    public native long allocateMemory(long bytes);

    /**
     * Resizes a new block of native memory, to the given size in bytes.  The
     * contents of the new block past the size of the old block are
     * uninitialized; they will generally be garbage.  The resulting native
     * pointer will be zero if and only if the requested size is zero.  The
     * resulting native pointer will be aligned for all value types.  Dispose
     * of this memory by calling {@link #freeMemory}, or resize it with {@link
     * #reallocateMemory}.  The address passed to this method may be null, in
     * which case an allocation will be performed.
     *
     * @throws IllegalArgumentException if the size is negative or too large
     *         for the native size_t type
     *
     * @throws OutOfMemoryError if the allocation is refused by the system
     *
     * @see #allocateMemory
     */
    public native long reallocateMemory(long address, long bytes);

    /**
     * Sets all bytes in a given block of memory to a fixed value
     * (usually zero).
     *
     * <p>This method determines a block's base address by means of two parameters,
     * and so it provides (in effect) a <em>double-register</em> addressing mode,
     * as discussed in {@link #getInt(Object,long)}.  When the object reference is null,
     * the offset supplies an absolute base address.
     *
     * <p>The stores are in coherent (atomic) units of a size determined
     * by the address and length parameters.  If the effective address and
     * length are all even modulo 8, the stores take place in 'long' units.
     * If the effective address and length are (resp.) even modulo 4 or 2,
     * the stores take place in units of 'int' or 'short'.
     *
     * @since 1.7
     */
    public native void setMemory(Object o, long offset, long bytes, byte value);

    /**
     * Sets all bytes in a given block of memory to a fixed value
     * (usually zero).  This provides a <em>single-register</em> addressing mode,
     * as discussed in {@link #getInt(Object,long)}.
     *
     * <p>Equivalent to <code>setMemory(null, address, bytes, value)</code>.
     */
    public void setMemory(long address, long bytes, byte value) {
        setMemory(null, address, bytes, value);
    }

    /**
     * Sets all bytes in a given block of memory to a copy of another
     * block.
     *
     * <p>This method determines each block's base address by means of two parameters,
     * and so it provides (in effect) a <em>double-register</em> addressing mode,
     * as discussed in {@link #getInt(Object,long)}.  When the object reference is null,
     * the offset supplies an absolute base address.
     *
     * <p>The transfers are in coherent (atomic) units of a size determined
     * by the address and length parameters.  If the effective addresses and
     * length are all even modulo 8, the transfer takes place in 'long' units.
     * If the effective addresses and length are (resp.) even modulo 4 or 2,
     * the transfer takes place in units of 'int' or 'short'.
     *
     * @since 1.7
     */
    public native void copyMemory(Object srcBase, long srcOffset,
                                  Object destBase, long destOffset,
                                  long bytes);
    /**
     * Sets all bytes in a given block of memory to a copy of another
     * block.  This provides a <em>single-register</em> addressing mode,
     * as discussed in {@link #getInt(Object,long)}.
     *
     * Equivalent to <code>copyMemory(null, srcAddress, null, destAddress, bytes)</code>.
     */
    public void copyMemory(long srcAddress, long destAddress, long bytes) {
        copyMemory(null, srcAddress, null, destAddress, bytes);
    }

    /**
     * Disposes of a block of native memory, as obtained from {@link
     * #allocateMemory} or {@link #reallocateMemory}.  The address passed to
     * this method may be null, in which case no action is taken.
     *
     * @see #allocateMemory
     */
    public native void freeMemory(long address);

    /// random queries

    /**
     * This constant differs from all results that will ever be returned from
     * {@link #staticFieldOffset}, {@link #objectFieldOffset},
     * or {@link #arrayBaseOffset}.
     */
    public static final int INVALID_FIELD_OFFSET   = -1;

    /**
     * Returns the offset of a field, truncated to 32 bits.
     * This method is implemented as follows:
     * <blockquote><pre>
     * public int fieldOffset(Field f) {
     *     if (Modifier.isStatic(f.getModifiers()))
     *         return (int) staticFieldOffset(f);
     *     else
     *         return (int) objectFieldOffset(f);
     * }
     * </pre></blockquote>
     * @deprecated As of 1.4.1, use {@link #staticFieldOffset} for static
     * fields and {@link #objectFieldOffset} for non-static fields.
     */
    @Deprecated
    public int fieldOffset(Field f) {
        if (Modifier.isStatic(f.getModifiers()))
            return (int) staticFieldOffset(f);
        else
            return (int) objectFieldOffset(f);
    }

    /**
     * Returns the base address for accessing some static field
     * in the given class.  This method is implemented as follows:
     * <blockquote><pre>
     * public Object staticFieldBase(Class c) {
     *     Field[] fields = c.getDeclaredFields();
     *     for (int i = 0; i < fields.length; i++) {
     *         if (Modifier.isStatic(fields[i].getModifiers())) {
     *             return staticFieldBase(fields[i]);
     *         }
     *     }
     *     return null;
     * }
     * </pre></blockquote>
     * @deprecated As of 1.4.1, use {@link #staticFieldBase(Field)}
     * to obtain the base pertaining to a specific {@link Field}.
     * This method works only for JVMs which store all statics
     * for a given class in one place.
     */
    @Deprecated
    public Object staticFieldBase(Class<?> c) {
        Field[] fields = c.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (Modifier.isStatic(fields[i].getModifiers())) {
                return staticFieldBase(fields[i]);
            }
        }
        return null;
    }

    /**
     * Report the location of a given field in the storage allocation of its
     * class.  Do not expect to perform any sort of arithmetic on this offset;
     * it is just a cookie which is passed to the unsafe heap memory accessors.
     *
     * <p>Any given field will always have the same offset and base, and no
     * two distinct fields of the same class will ever have the same offset
     * and base.
     *
     * <p>As of 1.4.1, offsets for fields are represented as long values,
     * although the Sun JVM does not use the most significant 32 bits.
     * However, JVM implementations which store static fields at absolute
     * addresses can use long offsets and null base pointers to express
     * the field locations in a form usable by {@link #getInt(Object,long)}.
     * Therefore, code which will be ported to such JVMs on 64-bit platforms
     * must preserve all bits of static field offsets.
     * @see #getInt(Object, long)
     */
    public native long staticFieldOffset(Field f);

    /**
     * Report the location of a given static field, in conjunction with {@link
     * #staticFieldBase}.
     * <p>Do not expect to perform any sort of arithmetic on this offset;
     * it is just a cookie which is passed to the unsafe heap memory accessors.
     *
     * <p>Any given field will always have the same offset, and no two distinct
     * fields of the same class will ever have the same offset.
     *
     * <p>As of 1.4.1, offsets for fields are represented as long values,
     * although the Sun JVM does not use the most significant 32 bits.
     * It is hard to imagine a JVM technology which needs more than
     * a few bits to encode an offset within a non-array object,
     * However, for consistency with other methods in this class,
     * this method reports its result as a long value.
     * @see #getInt(Object, long)
     */
    public native long objectFieldOffset(Field f);

    /**
     * Report the location of a given static field, in conjunction with {@link
     * #staticFieldOffset}.
     * <p>Fetch the base "Object", if any, with which static fields of the
     * given class can be accessed via methods like {@link #getInt(Object,
     * long)}.  This value may be null.  This value may refer to an object
     * which is a "cookie", not guaranteed to be a real Object, and it should
     * not be used in any way except as argument to the get and put routines in
     * this class.
     */
    public native Object staticFieldBase(Field f);

    /**
     * Detect if the given class may need to be initialized. This is often
     * needed in conjunction with obtaining the static field base of a
     * class.
     * @return false only if a call to {@code ensureClassInitialized} would have no effect
     */
    public native boolean shouldBeInitialized(Class<?> c);

    /**
     * Ensure the given class has been initialized. This is often
     * needed in conjunction with obtaining the static field base of a
     * class.
     */
    public native void ensureClassInitialized(Class<?> c);

    /**
     * Report the offset of the first element in the storage allocation of a
     * given array class.  If {@link #arrayIndexScale} returns a non-zero value
     * for the same class, you may use that scale factor, together with this
     * base offset, to form new offsets to access elements of arrays of the
     * given class.
     *
     * @see #getInt(Object, long)
     * @see #putInt(Object, long, int)
     */
    public native int arrayBaseOffset(Class<?> arrayClass);

    /** The value of {@code arrayBaseOffset(boolean[].class)} */
    public static final int ARRAY_BOOLEAN_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(boolean[].class);

    /** The value of {@code arrayBaseOffset(byte[].class)} */
    public static final int ARRAY_BYTE_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(byte[].class);

    /** The value of {@code arrayBaseOffset(short[].class)} */
    public static final int ARRAY_SHORT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(short[].class);

    /** The value of {@code arrayBaseOffset(char[].class)} */
    public static final int ARRAY_CHAR_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(char[].class);

    /** The value of {@code arrayBaseOffset(int[].class)} */
    public static final int ARRAY_INT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(int[].class);

    /** The value of {@code arrayBaseOffset(long[].class)} */
    public static final int ARRAY_LONG_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(long[].class);

    /** The value of {@code arrayBaseOffset(float[].class)} */
    public static final int ARRAY_FLOAT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(float[].class);

    /** The value of {@code arrayBaseOffset(double[].class)} */
    public static final int ARRAY_DOUBLE_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(double[].class);

    /** The value of {@code arrayBaseOffset(Object[].class)} */
    public static final int ARRAY_OBJECT_BASE_OFFSET
            = theUnsafe.arrayBaseOffset(Object[].class);

    /**
     * Report the scale factor for addressing elements in the storage
     * allocation of a given array class.  However, arrays of "narrow" types
     * will generally not work properly with accessors like {@link
     * #getByte(Object, int)}, so the scale factor for such classes is reported
     * as zero.
     *
     * @see #arrayBaseOffset
     * @see #getInt(Object, long)
     * @see #putInt(Object, long, int)
     */
    public native int arrayIndexScale(Class<?> arrayClass);

    /** The value of {@code arrayIndexScale(boolean[].class)} */
    public static final int ARRAY_BOOLEAN_INDEX_SCALE
            = theUnsafe.arrayIndexScale(boolean[].class);

    /** The value of {@code arrayIndexScale(byte[].class)} */
    public static final int ARRAY_BYTE_INDEX_SCALE
            = theUnsafe.arrayIndexScale(byte[].class);

    /** The value of {@code arrayIndexScale(short[].class)} */
    public static final int ARRAY_SHORT_INDEX_SCALE
            = theUnsafe.arrayIndexScale(short[].class);

    /** The value of {@code arrayIndexScale(char[].class)} */
    public static final int ARRAY_CHAR_INDEX_SCALE
            = theUnsafe.arrayIndexScale(char[].class);

    /** The value of {@code arrayIndexScale(int[].class)} */
    public static final int ARRAY_INT_INDEX_SCALE
            = theUnsafe.arrayIndexScale(int[].class);

    /** The value of {@code arrayIndexScale(long[].class)} */
    public static final int ARRAY_LONG_INDEX_SCALE
            = theUnsafe.arrayIndexScale(long[].class);

    /** The value of {@code arrayIndexScale(float[].class)} */
    public static final int ARRAY_FLOAT_INDEX_SCALE
            = theUnsafe.arrayIndexScale(float[].class);

    /** The value of {@code arrayIndexScale(double[].class)} */
    public static final int ARRAY_DOUBLE_INDEX_SCALE
            = theUnsafe.arrayIndexScale(double[].class);

    /** The value of {@code arrayIndexScale(Object[].class)} */
    public static final int ARRAY_OBJECT_INDEX_SCALE
            = theUnsafe.arrayIndexScale(Object[].class);

    /**
     * Report the size in bytes of a native pointer, as stored via {@link
     * #putAddress}.  This value will be either 4 or 8.  Note that the sizes of
     * other primitive types (as stored in native memory blocks) is determined
     * fully by their information content.
     */
    public native int addressSize();

    /** The value of {@code addressSize()} */
    public static final int ADDRESS_SIZE = theUnsafe.addressSize();

    /**
     * Report the size in bytes of a native memory page (whatever that is).
     * This value will always be a power of two.
     */
    public native int pageSize();


    /// random trusted operations from JNI:

    /**
     * Tell the VM to define a class, without security checks.  By default, the
     * class loader and protection domain come from the caller's class.
     */
    public native Class<?> defineClass(String name, byte[] b, int off, int len,
                                       ClassLoader loader,
                                       ProtectionDomain protectionDomain);

    /**
     * Define a class but do not make it known to the class loader or system dictionary.
     * <p>
     * For each CP entry, the corresponding CP patch must either be null or have
     * the a format that matches its tag:
     * <ul>
     * <li>Integer, Long, Float, Double: the corresponding wrapper object type from java.lang
     * <li>Utf8: a string (must have suitable syntax if used as signature or name)
     * <li>Class: any java.lang.Class object
     * <li>String: any object (not just a java.lang.String)
     * <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
     * </ul>
     * @params hostClass context for linkage, access control, protection domain, and class loader
     * @params data      bytes of a class file
     * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data
     */
    public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);


    /** Allocate an instance but do not run any constructor.
        Initializes the class if it has not yet been. */
    public native Object allocateInstance(Class<?> cls)
        throws InstantiationException;

    /** Lock the object.  It must get unlocked via {@link #monitorExit}. */
    public native void monitorEnter(Object o);

    /**
     * Unlock the object.  It must have been locked via {@link
     * #monitorEnter}.
     */
    public native void monitorExit(Object o);

    /**
     * Tries to lock the object.  Returns true or false to indicate
     * whether the lock succeeded.  If it did, the object must be
     * unlocked via {@link #monitorExit}.
     */
    public native boolean tryMonitorEnter(Object o);

    /** Throw the exception without telling the verifier. */
    public native void throwException(Throwable ee);


    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapObject(Object o, long offset,
                                                     Object expected,
                                                     Object x);

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapLong(Object o, long offset,
                                                   long expected,
                                                   long x);

    /**
     * Fetches a reference value from a given Java variable, with volatile
     * load semantics. Otherwise identical to {@link #getObject(Object, long)}
     */
    public native Object getObjectVolatile(Object o, long offset);

    /**
     * Stores a reference value into a given Java variable, with
     * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)}
     */
    public native void    putObjectVolatile(Object o, long offset, Object x);

    /** Volatile version of {@link #getInt(Object, long)}  */
    public native int     getIntVolatile(Object o, long offset);

    /** Volatile version of {@link #putInt(Object, long, int)}  */
    public native void    putIntVolatile(Object o, long offset, int x);

    /** Volatile version of {@link #getBoolean(Object, long)}  */
    public native boolean getBooleanVolatile(Object o, long offset);

    /** Volatile version of {@link #putBoolean(Object, long, boolean)}  */
    public native void    putBooleanVolatile(Object o, long offset, boolean x);

    /** Volatile version of {@link #getByte(Object, long)}  */
    public native byte    getByteVolatile(Object o, long offset);

    /** Volatile version of {@link #putByte(Object, long, byte)}  */
    public native void    putByteVolatile(Object o, long offset, byte x);

    /** Volatile version of {@link #getShort(Object, long)}  */
    public native short   getShortVolatile(Object o, long offset);

    /** Volatile version of {@link #putShort(Object, long, short)}  */
    public native void    putShortVolatile(Object o, long offset, short x);

    /** Volatile version of {@link #getChar(Object, long)}  */
    public native char    getCharVolatile(Object o, long offset);

    /** Volatile version of {@link #putChar(Object, long, char)}  */
    public native void    putCharVolatile(Object o, long offset, char x);

    /** Volatile version of {@link #getLong(Object, long)}  */
    public native long    getLongVolatile(Object o, long offset);

    /** Volatile version of {@link #putLong(Object, long, long)}  */
    public native void    putLongVolatile(Object o, long offset, long x);

    /** Volatile version of {@link #getFloat(Object, long)}  */
    public native float   getFloatVolatile(Object o, long offset);

    /** Volatile version of {@link #putFloat(Object, long, float)}  */
    public native void    putFloatVolatile(Object o, long offset, float x);

    /** Volatile version of {@link #getDouble(Object, long)}  */
    public native double  getDoubleVolatile(Object o, long offset);

    /** Volatile version of {@link #putDouble(Object, long, double)}  */
    public native void    putDoubleVolatile(Object o, long offset, double x);

    /**
     * Version of {@link #putObjectVolatile(Object, long, Object)}
     * that does not guarantee immediate visibility of the store to
     * other threads. This method is generally only useful if the
     * underlying field is a Java volatile (or if an array cell, one
     * that is otherwise only accessed using volatile accesses).
     */
    public native void    putOrderedObject(Object o, long offset, Object x);

    /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
    public native void    putOrderedInt(Object o, long offset, int x);

    /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
    public native void    putOrderedLong(Object o, long offset, long x);

    /**
     * Unblock the given thread blocked on <tt>park</tt>, or, if it is
     * not blocked, cause the subsequent call to <tt>park</tt> not to
     * block.  Note: this operation is "unsafe" solely because the
     * caller must somehow ensure that the thread has not been
     * destroyed. Nothing special is usually required to ensure this
     * when called from Java (in which there will ordinarily be a live
     * reference to the thread) but this is not nearly-automatically
     * so when calling from native code.
     * @param thread the thread to unpark.
     *
     */
    public native void unpark(Object thread);

    /**
     * Block current thread, returning when a balancing
     * <tt>unpark</tt> occurs, or a balancing <tt>unpark</tt> has
     * already occurred, or the thread is interrupted, or, if not
     * absolute and time is not zero, the given time nanoseconds have
     * elapsed, or if absolute, the given deadline in milliseconds
     * since Epoch has passed, or spuriously (i.e., returning for no
     * "reason"). Note: This operation is in the Unsafe class only
     * because <tt>unpark</tt> is, so it would be strange to place it
     * elsewhere.
     */
    public native void park(boolean isAbsolute, long time);

    /**
     * Gets the load average in the system run queue assigned
     * to the available processors averaged over various periods of time.
     * This method retrieves the given <tt>nelem</tt> samples and
     * assigns to the elements of the given <tt>loadavg</tt> array.
     * The system imposes a maximum of 3 samples, representing
     * averages over the last 1,  5,  and  15 minutes, respectively.
     *
     * @params loadavg an array of double of size nelems
     * @params nelems the number of samples to be retrieved and
     *         must be 1 to 3.
     *
     * @return the number of samples actually retrieved; or -1
     *         if the load average is unobtainable.
     */
    public native int getLoadAverage(double[] loadavg, int nelems);

    // The following contain CAS-based Java implementations used on
    // platforms not supporting native instructions

    /**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object <code>o</code>
     * at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */
    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }

    /**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object <code>o</code>
     * at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */
    public final long getAndAddLong(Object o, long offset, long delta) {
        long v;
        do {
            v = getLongVolatile(o, offset);
        } while (!compareAndSwapLong(o, offset, v, v + delta));
        return v;
    }

    /**
     * Atomically exchanges the given value with the current value of
     * a field or array element within the given object <code>o</code>
     * at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param newValue new value
     * @return the previous value
     * @since 1.8
     */
    public final int getAndSetInt(Object o, long offset, int newValue) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, newValue));
        return v;
    }

    /**
     * Atomically exchanges the given value with the current value of
     * a field or array element within the given object <code>o</code>
     * at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param newValue new value
     * @return the previous value
     * @since 1.8
     */
    public final long getAndSetLong(Object o, long offset, long newValue) {
        long v;
        do {
            v = getLongVolatile(o, offset);
        } while (!compareAndSwapLong(o, offset, v, newValue));
        return v;
    }

    /**
     * Atomically exchanges the given reference value with the current
     * reference value of a field or array element within the given
     * object <code>o</code> at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param newValue new value
     * @return the previous value
     * @since 1.8
     */
    public final Object getAndSetObject(Object o, long offset, Object newValue) {
        Object v;
        do {
            v = getObjectVolatile(o, offset);
        } while (!compareAndSwapObject(o, offset, v, newValue));
        return v;
    }


    /**
     * Ensures lack of reordering of loads before the fence
     * with loads or stores after the fence.
     * @since 1.8
     */
    public native void loadFence();

    /**
     * Ensures lack of reordering of stores before the fence
     * with loads or stores after the fence.
     * @since 1.8
     */
    public native void storeFence();

    /**
     * Ensures lack of reordering of loads or stores before the fence
     * with loads or stores after the fence.
     * @since 1.8
     */
    public native void fullFence();

    /**
     * Throws IllegalAccessError; for use by the VM.
     * @since 1.8
     */
    private static void throwIllegalAccessError() {
       throw new IllegalAccessError();
    }

}

1. 假如数据库有表emp:

empno name   age
001      lucy      22
002      lily        null
003      lilei       null
004      lucy      null
005      pear      null
006      pear      null

当执行sql:

SELECT empno FROM `emp` where age != 22;

返回的竟然一条都没有;

结论:null值是不参与比较的, age != 22 , 所有null的那些列都被过滤去掉了.

关键时刻注意这个特性. 

2. SQL中,NULL值与任何值比较(即使是NULL)永不为“真”

包含NULL的表达式总是会导出NULL值  

“Mysql参考文档”:

包含NULL的表达式总是会导出NULL值


[2016-10-26]

数据库表:

select * from rec order by rst,game_time;

ID GAME_TIME RST
2 01-1月 -11 F
6 01-1月 -11 F
3 02-1月 -11 F
9 02-1月 -11 F
7 03-1月 -11 F
1 01-1月 -11 W
4 01-1月 -11 W
8 01-1月 -11 W
5 02-1月 -11 W

要求结果:

比赛日期 结果 结果统计
02-1月 -11 失败 2
03-1月 -11 失败 1
02-1月 -11 胜利 1
01-1月 -11 失败 2
01-1月 -11 胜利 3

写出SQL1:decode函数

select  
     game_time as 比赛日期,  
     decode(rst,'F','失败','W','胜利','无结果') as 结果,  
     count(rst) as 结果统计  
 from rec  
 group by game_time,rst; 

SQL2:case语句:

select   
game_time as 比赛日期,  
(case rst  when 'W' then '胜利'  
                when 'F' then '失败'  
                else '无结果'  
end)结果,  
count(rst) as 结果统计  
from rec  
group by game_time,rst;

记录下:

2. decode函数用法:

decode(表达式1,条件1,结果1[条件2,结果2][default]);

3. case 语句用法:

case 表达式
when 表达式1 then ....
when 表达式2 then ....
else  ......
end 表达式

—[2013-07-02]—