PostgreSQL和MySQL之间的比较

对比表如下:

featuresPostgreSQLMySQL
Known asThe world’s most advanced open source databaseThe world’s most popular open source database
DevelopmentPostgreSQL is an open source projectMySQL is an open-source product
Pronunciationpost gress queue ellmy ess queue ell
LicensingMIT-style licenseGNU General Public License
Implementation programming languageCC/C++
GUI toolPgAdminMySQL Workbench
ACIDYesYes
Storage engineSingle storage engineMultiple storage engines e.g., InnoDB and MyISAM
Full-text searchYesYes
Drop a temporary tableNo TEMP or TEMPORARY keyword in DROP TABLE statementMySQL supports the TEMP or TEMPORARY keyword in the DROP TABLE statement that allows you to remove the temporary table only.
DROP TABLESupport CASCADE option to drop table’s dependent objects e.g., tables, views, etc.Does not support CASCADE option
TRUNCATE TABLEPostgreSQL TRUNCATE TABLE supports more features like CASCADE, RESTART IDENTITY, CONTINUE IDENTITY, transaction-safe, etc.MySQL TRUNCATE TABLE does not support CASCADE and transaction safe i.e,. once data is deleted, it cannot be rolled back.
Auto increment ColumnSERIALAUTO_INCREMENT
Analytic functionsYesNo
Data typesSupport many advanced types such as array, hstore, user-defined type, etc.SQL-standard types
Unsigned integerNoYes
Boolean typeYesUse TINYINT(1) internally for Boolean
IP address data typeYesNo
Set default value for a columnSupport both constant and function callMust be a constant or CURRENT_TIMESTAMP for TIMESTAMP or DATETIME columns
CTEYesNo
EXPLAIN outputMore detailedLess detailed
Materialized viewsYesNo
CHECK constraintYesNo (MySQL ignores the CHECK constraint)
Table inheritanceYesNo
Programming languages for stored proceduresRuby, Perl, Python, TCL, PL/pgSQL, SQL, JavaScript, etc.SQL:2003 syntax for stored procedures
FULL OUTER JOINYesNo
INTERSECTYesNo
EXCEPTYesNo
Partial indexesYesNo
Bitmap indexesYesNo
Expression indexesYesNo
Covering indexesYes (since version 9.2)Yes. MySQL supports covering indexes that allow data to be retrieved by scanning the index alone without touching the table data. This is advantageous with large tables with millions of rows.
Common table expression (CTE)YesNo
TriggersSupport triggers that can fire on most types of command, except for ones affecting the database globally e.g.,roles and tablespaces.Limited to some commands
PartitioningRANGE, LISTRANGE, LIST, HASH, KEY, and composite partitioning using a combination of RANGE or LIST with HASH or KEY subpartitions
Task SchedulepgAgentScheduled event
Connection ScalabilityEach new connection is an OS processEach new connection is an OS thread

JVM常用参数

1.Java堆参数

parameterexplainexample
-Xms堆最小值-Xms20M
-Xmx堆最大值-Xmm20M
-Xmn年轻代大小-Xmn2g (设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8)
-XX:NewSize新生代大小-XX:NewSize=20M
-XX:MaxNewSize年轻代最大值
-XX:SurvivorRatio新生代/年老代-XX:SurvivorRatio=2 (新生代是老年代的2倍)
-XX:NewRatio老年代/新生代-XX:NewRatio=4 (老年代是新生代的4倍)

例:

1
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

Read More

在nginx中配置Let's Encrypt的https证书

1 、获取 Let’s Encrypt 程序

1
2
3
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
chmod +x letsencrypt-auto

2 、执行安装证书

在执行一下命令之前要注意,你的域名下必须要有服务器正在运行,不然下面脚本无法执行成功。如果你的服务器里已经有Python环境,
直接执行python -m http.server 80开启一个http服务器。一般1024以下端口非管理员用户不能绑定,所以你还需要确保你的80端口可以在公网访问,如果不能请自行Google
寻找解决办法。

1
./letsencrypt-auto certonly -a webroot --webroot-path=/home/www/www.gentlehu.com --email [email protected] -d www.gentlehu.com

将上面的目录、邮箱和域名换成你自己的,如果在输出信息中没有看到错误提示并且在/etc/letsencrypt/live/www.gentlehu.com/目录看到如下五个文件就说明证书申请成功:

1
2
3
4
5
cert.pem  - Apache 服务器端证书  
chain.pem - Apache 根证书和中继证书
fullchain.pem - ssl_certificate
privkey.pem - ssl_certificate_key
README.md

然后就可以关闭之前的http服务器了。

Read More

java中一个强大的工具类-Unsafe

1.简介

Unsafe是java中一个强有力的工具类,很多高性能框架都用到了它,并且在我们常用的ConcurrentHashMap,AtomicInteger类中也用到了它。

ConcurrentHashMap.java中:
Unsafe_in_ConcurrentHashMap

AtomicInteger.java中:
Unsafe_in_AtomicInteger

高性能框架中(包括但不限于):

  • Netty
  • Hazelcast
  • Cassandra
  • Mockito / EasyMock / JMock / PowerMock
  • Scala Specs
  • Spock
  • Robolectric
  • Grails
  • Neo4j
  • Spring Framework
  • Akka
  • Apache Kafka
  • Apache Wink
  • Apache Storm
  • Apache Hadoop
  • Apache Continuum

refer:https://blog.dripstat.com/removal-of-sun-misc-unsafe-a-disaster-in-the-making/
refer:https://adtmag.com/blogs/watersworks/2015/08/java-9-hack.aspx

这个类强大之处在于可以像C的指针一样,直接操作内存。正如它的名字一样,它是不安全的,如果处理不好,会导致程序问题发生的概率增大,官方也不建议使用。

2.限制

Unsafe内部使用的是单例模式,通过其静态方法getUnsafe方法获取,但是有些限制,那就是只有系统类加载器可以加载这个类,其他加载器加载会抛出异常,这个我们可以通过看源码来证明:
Unsafe_of_getUnsafe

但是这个限制也只是普通限制,别忘了java还有一个更强大的特性:反射(reflect)。如果你也想试一试这个类,可以通过下面的方法获取其实例:

1
2
3
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

里面具体有哪些方法,读者可以自己去源码看下,但是这个类没有官方文档,仅有的是源码上的注释,不过可以通过自己试验来了解和验证其功能。我这里找到一个blog,博主详细介绍了Unsafe中的方法,有兴趣可以去看下:http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

3.移除

Unfortunately,oracle计划在jdk1.9中移除它,而且是没有原因的。下面的是Oracle邮件中部分内容:

Let me be blunt – sun.misc.Unsafe must die in a fire. It is – wait
for it – Unsafe. It must go. Ignore any kind of theoretical rope and
start the path to righteousness

refer:https://blog.dripstat.com/removal-of-sun-misc-unsafe-a-disaster-in-the-making/

如果直接移除的话,必然导致很多框架不得不修改源码以让其可以正常运行,不然就没法使用。

就像java8中使用java.time包来代替java.util中Date类一样,提供一个过渡期,不强制移除Unsafe类,而是在启动JVM的时候提供一个开关,这个开启的时候我们才可以使用Unsafe,默认是关闭的。

SpringBoot之SpringBootApplication注解

前言

我们在写SpringBoot项目的时候总是会在启动类里添加@SpringBootApplication注解,然后我们就可以启动
项目了,那么这个注解到底帮我们做了些什么事情呢?接下来我们就通过看源码来找答案。

@SpringBootApplication

我们先写下面一段代码,然后通过IDE看下这个@SpringBootApplication注解类的内容

1
2
3
4
5
6
@SpringApplication
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

可以看到这是一个复合注解,包含了很多其他注解,但是可能跟Spring有关的就是这个三个@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan。那么我们现在住主要分析一下这三个。
首先是@SpringBootConfiguration,点进去发现这也是一个符合注解,包含了@Configuration注解。

1
2
3
4
5
6
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

这个就是SpringBoot里比较重要的注解了。这个注解的作用就是替代之前繁琐的xml文件配置,比如之前要是配置一个bean要这样写xml:

1
2
<bean id="userService" class="com.gentlehu.project.service.impl.UserServiceImpl">
</bean>

而现在在这个注解的帮助下我们只需要这样写就实现了对等的效果。

1
2
3
4
5
6
7
8
@Configuration
public class DemoConfigration{

@Bean
public UserService userService(){
return new UserServiceImpl();
}
}

是不是感觉在配置的时候直观了很多呢。

@ComponentScan

这个注解就看名知意了。就是扫描组件并注册用的。默认在当前所在类的package包开始扫描。所以,当你的项目
发现有些组件没有自动注册的话看下这个组件是不是在@ComponentScan所在包或者子包下。

@EnableAutoConfiguration

这个注解看名字也很好理解,是就开启自动配置。秉承SpringBoot的设计理念:约定优于配置,意思是
如果我们共同遵守一个约定,那么有些东西我们是不需要手动配置的,大家都使用默认的就好了。打个比方:
学校开会,老师说大家按照学号的顺序坐下,然后大家遵守这个约定,就不需要每个人都去问下老师我应该坐在哪呀。
降低了沟通成本。而在项目中是降低了配置成本。

我们再继续点进@EnableAutoConfiguration类里面有什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* Enable auto-configuration of the Spring Application Context, attempting to guess and
* configure beans that you are likely to need. Auto-configuration classes are usually
* applied based on your classpath and what beans you have defined. For example, If you
* have {@code tomcat-embedded.jar} on your classpath you are likely to want a
* {@link TomcatEmbeddedServletContainerFactory} (unless you have defined your own
* {@link EmbeddedServletContainerFactory} bean).
* <p>
* When using {@link SpringBootApplication}, the auto-configuration of the context is
* automatically enabled and adding this annotation has therefore no additional effect.
* <p>
* Auto-configuration tries to be as intelligent as possible and will back-away as you
* define more of your own configuration. You can always manually {@link #exclude()} any
* configuration that you never want to apply (use {@link #excludeName()} if you don't
* have access to them). You can also exclude them via the
* {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied
* after user-defined beans have been registered.
* <p>
* The package of the class that is annotated with {@code @EnableAutoConfiguration},
* usually via {@code @SpringBootApplication}, has specific significance and is often used
* as a 'default'. For example, it will be used when scanning for {@code @Entity} classes.
* It is generally recommended that you place {@code @EnableAutoConfiguration} (if you're
* not using {@code @SpringBootApplication}) in a root package so that all sub-packages
* and classes can be searched.
* <p>
* Auto-configuration classes are regular Spring {@link Configuration} beans. They are
* located using the {@link SpringFactoriesLoader} mechanism (keyed against this class).
* Generally auto-configuration beans are {@link Conditional @Conditional} beans (most
* often using {@link ConditionalOnClass @ConditionalOnClass} and
* {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
*
* @author Phillip Webb
* @author Stephane Nicoll
* @see ConditionalOnBean
* @see ConditionalOnMissingBean
* @see ConditionalOnClass
* @see AutoConfigureAfter
* @see SpringBootApplication
*/
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}

开启自动配置只是我们在名字上得到的信息,具体做了什么还是要看类上面作者的注释。有朋友肯定会说,哎?我点击去怎么没看见注释呢,那是因为你没有导入源码包,这个你在网上下载源码包然后在IDE里配置下就好了。
我建议把项目中依赖的源码包都下载下来,方便我们在源码上理解框架所做的事情。可能有人又说我依赖很多包,难到要我手动的一个一个的下载?当然不是,如果你用的IDEA系列的工具,是可以自动下载源码包的,这个不需要我们操心。

下面是我自己对这个注释的翻译,如有错误还请斧正*(联系方式可以在About页面找到)*。

开启Spring应用上下文的 auto-configuration,尝试猜测和配置你可能需要的beans,Auto-configuration类通常被应用在你的classpath下面的你希望定义的beans下面。

例如:如果你有tomcat-embedded.jar在你的classpath下面那么你可能想要TomcatEmbeddedServletContainerFactory(除非你已经定义了EmbeddedServletContainerFactory bean).当你使用SpringBootApplication,上下文的auto-configuration就会自动开启并且添加这个没有副作用的注解。

auto-configuration 尽可能智能的尝试并且当你定义更多你自己的配置的时候回退。你随时可以用 excludeName 手动排除任何你不需要应用的配置。你也可以通过 spring.autoconfigure.exclude 属性排除他们。

auto-configuration 总是在用户定义beans注册之后被应用。这个你用 @EnableAutoConfiguration 注解的类所在的包,通常是用 @SpringBootApplication 这个有特殊意义的注解作为默认。

例如:它在扫描 @Entity 类的时候被使用。一般建议你配置 @EnableAutoConfiguration (如果你不用 @SpringBootApplication) 在 root package 下,如此你的所有子包和类就可以被检索到。

Auto-configuration 类是普通的Spring @Configuration beans.他们被SpringFactoriesLoader的机制定位。一般 auto-configuration beans 都是 @Conditional beans(大部分时候使用 @ConditionalOnClass@ConditionalOnMissingBean 注解).

这个类借助@Import的支持自动将所有符合条件的类加载到Spring容器。

还有个@AutoConfigurationPackage类,继续点进去看下,发现还是用了@Import注解把自动配置用到的Registrar类导入。

AutoConfigurationPackage:

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

}

Registrar.cass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
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.<Object>singleton(new PackageImport(metadata));
}

}

到这里你肯定会问了。那Spring怎么知道哪些类需要自动配置呢,这个时候查看源码包会发现了spring.factories文件。
这个文件位于spring-boot-test-autoconfigure-1.5.7.RELEASE.jar的/META-INF/下,并且不只是这个包下面有,很多jar包下面都有。

spring.factories:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# AutoConfigureCache auto-configuration imports
org.springframework.boot.test.autoconfigure.core.AutoConfigureCache=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

# AutoConfigureDataJpa auto-configuration imports
org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa=\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

# AutoConfigureDataMongo auto-configuration imports
org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo=\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration

# AutoConfigureJdbc auto-configuration imports
org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

# AutoConfigureTestDatabase auto-configuration imports
org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase=\
org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

# AutoConfigureJson auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJson=\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

# AutoConfigureJsonTesters auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters=\
org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

# AutoConfigureMockMvc auto-configuration imports
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc=\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityAutoConfiguration,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration

# AutoConfigureMockRestServiceServer
org.springframework.boot.test.autoconfigure.web.client.AutoConfigureMockRestServiceServer=\
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerAutoConfiguration

# AutoConfigureRestDocs auto-configuration imports
org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs=\
org.springframework.boot.test.autoconfigure.restdocs.RestDocsAutoConfiguration

# AutoConfigureTestDatabase auto-configuration imports
org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase=\
org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

# AutoConfigureTestEntityManager auto-configuration imports
org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestEntityManager=\
org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration

# AutoConfigureWebClient auto-configuration imports
org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient=\
org.springframework.boot.test.autoconfigure.web.client.WebClientRestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration

# AutoConfigureWebMvc auto-configuration imports
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc=\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration

# DefaultTestExecutionListenersPostProcessors
org.springframework.boot.test.context.DefaultTestExecutionListenersPostProcessor=\
org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener$PostProcessor

# Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory

# Test Execution Listeners
org.springframework.test.context.TestExecutionListener=\
org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener

那么一切都清晰了,其实Spring就是搜索classpath下所有的META-INF/spring.factories文件,然后通过反射自动注册到Spring容器中,就完成了自动配置。那么这个配置是由哪个类来读取呢?通过查找源码包发现了这么一个抽象类,有一个关键静态成员:FACTORIES_RESOURCE_LOCATION,它的值刚好是META-INF/spring.factories
通过阅读代码逻辑和注释发现就是这个类读取的spring.factories文件,帮我们完成了SpringBoot的自动配置,为我们减轻了负担。

这仅仅是通过读源码发现的,其实你随意启动一个SpringBoot项目打开调试模式也能发现这样的逻辑。

org.springframework.core.io.support.SpringFactoriesLoader:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

/**
* General purpose factory loading mechanism for internal use within the framework.
*
* <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates
* factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which
* may be present in multiple JAR files in the classpath. The {@code spring.factories}
* file must be in {@link Properties} format, where the key is the fully qualified
* name of the interface or abstract class, and the value is a comma-separated list of
* implementation class names. For example:
*
* <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
*
* where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}
* and {@code MyServiceImpl2} are two implementations.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.2
*/
public abstract class SpringFactoriesLoader {

private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


/**
* Load and instantiate the factory implementations of the given type from
* {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.
* <p>The returned factories are sorted in accordance with the {@link AnnotationAwareOrderComparator}.
* <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}
* to obtain all registered factory names.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
* @see #loadFactoryNames
* @throws IllegalArgumentException if any factory implementation class cannot
* be loaded or if an error occurs while instantiating any factory
*/
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<T>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}

/**
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
* class loader.
* @param factoryClass 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
* @see #loadFactories
* @throws IllegalArgumentException if an error occurs while loading factory names
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException(
"Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
}
Constructor<?> constructor = instanceClass.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return (T) constructor.newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);
}
}

}

很多同学知道SpringBoot项目在入口类上加上@EnableAutoConfiguration就能自动配置,却不知道为何SpringBoot为何如此智能的帮我配置了,
希望读者看到这篇博文后知道SpringBoot是如何实现自动配置的。更详细的过程可以自己动手调试SpringBoot项目了解。