├── .gitignore ├── Dockerfile ├── README.adoc ├── faster.adoc ├── images ├── flame_20.svg ├── flame_21.svg ├── flame_after.svg ├── flame_auto.svg ├── flame_before.svg ├── flame_bunc.svg ├── flame_cached.svg ├── flame_demo.svg ├── flame_func.svg ├── flame_lazee.svg ├── flame_micro.svg ├── flame_naut.svg ├── flame_vanilla.svg ├── jmc.png └── startup-progress.png ├── manual.adoc ├── pom.xml ├── profiling.jfc └── src ├── main ├── java │ ├── com │ │ └── example │ │ │ ├── aspects │ │ │ ├── AnnotationProcessingInterceptor.java │ │ │ ├── BootApplicationInterceptor.java │ │ │ └── BootAutoConfigureInterceptor.java │ │ │ ├── auto │ │ │ ├── AutoApplication.java │ │ │ ├── AutoConfigurations.java │ │ │ └── ConditionEvaluator.java │ │ │ ├── bench │ │ │ ├── AnnotatedMethodBenchmark.java │ │ │ ├── BeanCreationAllocations.java │ │ │ ├── BeanCreationBenchmark.java │ │ │ ├── BinderAllocations.java │ │ │ ├── BinderBenchmark.java │ │ │ ├── CallCounter.java │ │ │ ├── ConfigFileAllocations.java │ │ │ ├── ConfigFileBenchmark.java │ │ │ ├── IndexAnnotationBenchmark.java │ │ │ ├── LauncherState.java │ │ │ ├── LoggingSystemAllocations.java │ │ │ ├── MainBenchmark.java │ │ │ ├── MetadataBenchmark.java │ │ │ ├── ResolvableTypeAllocations.java │ │ │ ├── SimpleMetadataReaderAllocations.java │ │ │ └── WrappedConfigurationPropertySource.java │ │ │ ├── boot │ │ │ └── BootApplication.java │ │ │ ├── config │ │ │ ├── ApplicationBuilder.java │ │ │ ├── BeanCountingApplicationListener.java │ │ │ ├── ComponentIndex.java │ │ │ ├── ComponentIndexer.java │ │ │ ├── ConfigurationClassInitializer.java │ │ │ ├── LazyInitBeanFactoryPostProcessor.java │ │ │ ├── ShutdownApplicationListener.java │ │ │ ├── SpringBeanInfoFactory.java │ │ │ ├── SpringBootBeanInfo.java │ │ │ ├── StandardMetadataReaderFactory.java │ │ │ └── StartupApplicationListener.java │ │ │ ├── demo │ │ │ └── DemoApplication.java │ │ │ ├── func │ │ │ ├── BuncApplication.java │ │ │ ├── CuncApplication.java │ │ │ ├── FuncApplication.java │ │ │ └── ObjectProviders.java │ │ │ ├── manual │ │ │ └── ManualApplication.java │ │ │ └── micro │ │ │ └── MicroApplication.java │ └── org │ │ └── springframework │ │ ├── boot │ │ └── context │ │ │ └── config │ │ │ └── AnsiOutputApplicationListener.java │ │ └── context │ │ └── annotation │ │ └── PublicConfigurationClassEnhancer.java └── resources │ ├── META-INF │ ├── foo.components │ ├── services │ │ └── com.example.config.ComponentIndexer │ └── spring.factories │ ├── application.properties │ ├── logback.xml │ └── org │ └── aspectj │ └── aop.xml └── test └── java └── com └── example ├── bench └── LauncherStateTests.java └── demo ├── DemoApplicationTests.java └── StaticApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | dependency-reduced-pom.xml 4 | 5 | .#* 6 | \#*# 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .attach_* 16 | .sts4-cache/ 17 | 18 | ### IntelliJ IDEA ### 19 | .idea 20 | *.iws 21 | *.iml 22 | *.ipr 23 | 24 | ### NetBeans ### 25 | nbproject/private/ 26 | build/ 27 | nbbuild/ 28 | dist/ 29 | nbdist/ 30 | .nb-gradle/ 31 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-alpine 2 | VOLUME /tmp 3 | COPY target/benchmarks.jar / 4 | CMD ["com.example.demo.DemoApplication"] 5 | ENTRYPOINT ["java","-Xmx128m","-Djava.security.egd=file:/dev/./urandom","-XX:TieredStopAtLevel=1","-noverify","-Dspring.jmx.enabled=false", "-Dspring.config.location=classpath:/application.properties","-cp","benchmarks.jar"] 6 | -------------------------------------------------------------------------------- /images/jmc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsyer/spring-boot-allocations/4fc69e7a0329062c09e1d67788358e6b3dcf03fa/images/jmc.png -------------------------------------------------------------------------------- /images/startup-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsyer/spring-boot-allocations/4fc69e7a0329062c09e1d67788358e6b3dcf03fa/images/startup-progress.png -------------------------------------------------------------------------------- /manual.adoc: -------------------------------------------------------------------------------- 1 | = Manual Bean Definitions in Spring Boot 2 | 3 | Suppose you want to use Spring Boot, but you don't want to 4 | `@EnableAutoConfiguration`. What should you do exactly? In an 5 | https://spring.io/blog/2018/12/12/how-fast-is-spring[earlier article] 6 | I showed that Spring is intrinsically fast and lightweight, but one of 7 | the short pieces of advice improve startup time was to consider 8 | manually importing the Spring Boot autoconfigurations, instead of 9 | sucking them all in automatically. It won't be the right thing to do 10 | for all applications, but it might help, and it certainly won't hurt 11 | to understand what the options are. In this piece we explore various 12 | ways of doing manual configuration and assess their impact. 13 | 14 | == Full Autoconfiguration: Hello World WebFlux 15 | 16 | As a baseline, let's look at a Spring Boot application that has a 17 | single HTTP endpoint: 18 | 19 | ```java 20 | @SpringBootApplication 21 | @RestController 22 | public class DemoApplication { 23 | 24 | @GetMapping("/") 25 | public Mono home() { 26 | return Mono.just("Hello World"); 27 | } 28 | 29 | public void main(String[] args) { 30 | SpringApplication.run(DemoApplication.class, args); 31 | } 32 | 33 | } 34 | ``` 35 | 36 | If you run this app with all the tweaks suggested in the earlier 37 | article it should start in round about a second, or a bit longer 38 | depending on your hardware. It does a lot in that time - sets up a 39 | logging system, reads and binds to configuration files, starts Netty 40 | and listens on port 8080, providing a route to the `@GetMapping` in 41 | the application, and also provides default error handling. If the 42 | Spring Boot Actuator is on the classpath, you also get a /health and 43 | an /info endpoint (and it will take a bit longer to start up because 44 | of that). 45 | 46 | The `@SpringBootApplication` annotation, in case you didn't know, is 47 | meta-annotated with `@EnableAutoConfiguration` and this is what 48 | provides all that useful functionality for free. That's what makes 49 | Spring Boot popular, so we don't want to lose any of it, but we can 50 | take a closer look at what is actually happening and maybe do some of 51 | it manually, to see if we learn anything. 52 | 53 | NOTE: if you want to try this code out, it's easy to get an empty 54 | WebFlux app from the https://spring.io[Spring Initializr]. Just select 55 | the "Reactive Web" checkbox and download the project. 56 | 57 | == Manual Imports of Autoconfiguration 58 | 59 | While the `@EnableAutoConfiguration` feature makes adding features to 60 | an application easy, it also takes away some control over which 61 | features are enabled. Most people are happy to make that compromise - 62 | the ease of use outweighs the loss of control. Potentially there are 63 | performance penalties - the application might start a bit slower 64 | because Spring Boot has to do some work to find all those features and 65 | install them. In fact there is not a significant amount of effort 66 | involved in finding the right features: there is no classpath scan, 67 | and condition evaluation is extremely fast, after careful 68 | optimization. The bulk (80% or so) of startup time for one of this 69 | application is taken up by the JVM loading classes, so practically the 70 | only way to make it start up quicker is to ask it to do less, by 71 | installing fewer features. 72 | 73 | Autoconfiguration can always be disabled using the `exclude` attribute 74 | in the `@EnableAutoConfiguration` annotation. Some individual 75 | autoconfigurations also have their own boolean configuration flag that 76 | can be set externally, e.g. for JMX we could use 77 | `spring.jmx.enabled=false` (as a System property or in a properties 78 | file, for example). We could go down that road and manually switch off 79 | everything we didn't want to use, but that gets a bit clumsy and 80 | doesn't stop additional things being switched on if the classpath 81 | changes. 82 | 83 | Instead, let's see what we can do using the existing autoconfiguration 84 | classes but just applying the ones we know we want to use, 85 | corresponding to the features we like. We could call this the "a la 86 | carte" approach, as opposed to "all you can eat" that comes with full 87 | autoconfiguration. Autoconfiguration classes are just regular 88 | `@Configuration` so in principle we can `@Import` them into an 89 | application that does not `@EnableAutoConfiguration`. 90 | 91 | WARNING: Don't do this without reading the rest of the article. It's 92 | not the right way to use Spring Boot Autoconfiguration. It might break 93 | something, but as always your mileage may vary. 94 | 95 | For example, here is the application above, with all the features we 96 | want (excluding actuators): 97 | 98 | ```java 99 | @SpringBootConfiguration 100 | @Import({ 101 | WebFluxAutoConfiguration.class, 102 | ReactiveWebServerFactoryAutoConfiguration.class, 103 | ErrorWebFluxAutoConfiguration.class, 104 | HttpHandlerAutoConfiguration.class, 105 | ConfigurationPropertiesAutoConfiguration.class, 106 | PropertyPlaceholderAutoConfiguration.class 107 | }) 108 | @RestController 109 | public class DemoApplication { 110 | 111 | @GetMapping("/") 112 | public Mono home() { 113 | return Mono.just("Hello World"); 114 | } 115 | 116 | public void main(String[] args) { 117 | SpringApplication.run(DemoApplication.class, args); 118 | } 119 | 120 | } 121 | ``` 122 | 123 | This version of the application will still have all the features we 124 | described above, but will start faster (probably by 30% or so). So 125 | what did we give up to get that faster start up? Here's a quick 126 | rundown: 127 | 128 | * The full features set of Spring Boot autoconfiguration includes 129 | other stuff that might actually be needed in a real application, as 130 | opposed to the specific tiny sample. In other words, the 30% speed up 131 | is not going to be available for all applications, and your mileage 132 | may vary. 133 | 134 | * The manual configuration is brittle, and hard to guess. If you wrote 135 | another application that did slightly different things, you would need 136 | a different configuration import. You can mitigate this by extracting 137 | it into a convenience class or annotation, and re-using it. 138 | 139 | * An `@Import` does not behave the same way as 140 | `@EnableAutoConfiguration` in relation to ordering of configuration 141 | classes. The order is important within the `@Import` in case some 142 | classes have conditional behaviour that depend on earlier 143 | classes. To mitigate you just have to be careful. 144 | 145 | * There is another ordering problem in a typical real-world 146 | application. To mimic the behaviour of `@EnableAutoConfiguration` you 147 | need the user configurations to be processed first, so that they can 148 | override the conditional configuration in Spring Boot. If you use 149 | `@ComponentScan`, you can't control the order of the scan, or the 150 | order those classes are processed compared to `@Imports`. You can 151 | mitigate this by using a different annotation (see below). 152 | 153 | * The Spring Boot autoconfigurations were actually never designed to 154 | be used this way, and doing so might introduce subtle bugs in your 155 | application. The only mitigations for this are exhaustive testing that 156 | it works the way you expect, and being cautious about upgrades. 157 | 158 | === Adding Actuators 159 | 160 | We can also add the actuators if they are on the classpath: 161 | 162 | ```java 163 | @SpringBootConfiguration 164 | @Import({ 165 | WebFluxAutoConfiguration.class, 166 | ReactiveWebServerFactoryAutoConfiguration.class, 167 | ErrorWebFluxAutoConfiguration.class, 168 | HttpHandlerAutoConfiguration.class, 169 | EndpointAutoConfiguration.class, 170 | HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class, 171 | InfoEndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, 172 | ReactiveManagementContextAutoConfiguration.class, 173 | ManagementContextAutoConfiguration.class, 174 | ConfigurationPropertiesAutoConfiguration.class, 175 | PropertyPlaceholderAutoConfiguration.class 176 | }) 177 | @RestController 178 | public class DemoApplication { 179 | 180 | @GetMapping("/") 181 | public Mono home() { 182 | return Mono.just("Hello World"); 183 | } 184 | 185 | public void main(String[] args) { 186 | SpringApplication.run(DemoApplication.class, args); 187 | } 188 | 189 | } 190 | ``` 191 | 192 | This app starts even faster comparative to the full 193 | `@EndpointAutoConfiguration` application (maybe even 50% faster), 194 | because we only included the configuration relevant to the two default 195 | endpoints. Spring Boot activates all endpoints by default but does not 196 | expose them to HTTP. If we only care about /health and /info that is 197 | wasteful, but of course it also leaves a lot of really useful features 198 | on the table. 199 | 200 | NOTE: Spring Boot may well do more in the future to disable actuators 201 | that have not been exposed or have not been used. E.g. see issues on 202 | https://github.com/spring-projects/spring-boot/issues/7578[lazy 203 | actuators] and 204 | https://github.com/spring-projects/spring-boot/issues/15451[conditional 205 | endpoints] (which is already in Spring Boot 2.1.2). 206 | 207 | === What's the difference? 208 | 209 | The manually configured application has 51 beans, while the fully 210 | leaded autoconfigured application has 107 beans (not counting 211 | actuators). So it's maybe not a surprise that it starts up a bit 212 | quicker. Before we move on to a different way to implement the sample 213 | application, let's take a look at what we have left out in order to 214 | get it to start up faster. If you list the bean definitions in both 215 | apps you will see that all the differences come from the 216 | autoconfigurations that we left out, and which would not have been 217 | conditionally excluded by Spring Boot. Here's the list (assuming that 218 | you are using `spring-boot-start-webflux` with no manual exclusions): 219 | 220 | ``` 221 | AutoConfigurationPackages 222 | CodecsAutoConfiguration 223 | JacksonAutoConfiguration 224 | JmxAutoConfiguration 225 | ProjectInfoAutoConfiguration 226 | ReactorCoreAutoConfiguration 227 | TaskExecutionAutoConfiguration 228 | TaskSchedulingAutoConfiguration 229 | ValidationAutoConfiguration 230 | HttpMessageConvertersAutoConfiguration 231 | RestTemplateAutoConfiguration 232 | WebClientAutoConfiguration 233 | ``` 234 | 235 | So that's 12 autoconfigurations that we didn't need (yet anyway) and 236 | which led to 56 additional beans in the autoconfigured 237 | application. They all provide useful features, so we might want to 238 | include them again one day, but for now let's assume that we are happy 239 | to live without whatever they are doing. 240 | 241 | NOTE: `spring-boot-autoconfigure` has 122 autoconfigurations (there 242 | are more in `spring-boot-actuator-autoconfigure`), and the fully leaded 243 | autoconfigured sample application above only used 18 of them. The 244 | computation of which ones to use takes place very early and most of 245 | them are discarded by Spring Boot before any classes are even 246 | loaded. It's very fast (a few milliseconds). 247 | 248 | == Spring Boot Autoconfiguration Imports 249 | 250 | The ordering issue associated with the difference between user 251 | configuration (which has to be applied first) and autoconfiguration can 252 | be addressed partially by using a different annotation. Spring Boot 253 | provides an annotation for this: `@ImportAutoConfiguration`, which is 254 | from `spring-boot-autoconfigure` but used in the 255 | https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-tests[test 256 | slices] features that ship with Spring Boot Test. So you can replace 257 | the `@Import` annotation in the examples above with 258 | `@ImportAutoConfiguration` and the effect is to defer processing of 259 | the autoconfigurations until after all the user configurations 260 | (e.g. picked up via `@ComponentScan` or `@Import`). 261 | 262 | We can even go a stage further than that if we are prepared to curate 263 | the list of autoconfigurations into a custom annotation. Instead of 264 | just copying them into an explicit `@ImportAutoConfiguration`, we can 265 | write a custom annotation like this: 266 | 267 | ```java 268 | @Target(ElementType.TYPE) 269 | @Retention(RetentionPolicy.RUNTIME) 270 | @Documented 271 | @Inherited 272 | @ImportAutoConfiguration 273 | public @interface EnableWebFluxAutoConfiguration { 274 | } 275 | ``` 276 | 277 | The main feature of this annotation is that it is meta-annotated with 278 | `@ImportAutoConfiguration`. With that in place we can add the new 279 | annotation to our application: 280 | 281 | ```java 282 | @SpringBootConfiguration 283 | @EnableWebFluxAutoConfiguration 284 | @RestController 285 | public class DemoApplication { 286 | 287 | @GetMapping("/") 288 | public Mono home() { 289 | return Mono.just("Hello World"); 290 | } 291 | 292 | public void main(String[] args) { 293 | SpringApplication.run(DemoApplication.class, args); 294 | } 295 | 296 | } 297 | ``` 298 | 299 | and list the actual configuration classes in `/META-INF/spring.factories`: 300 | 301 | ``` 302 | com.example.config.EnableWebFluxAutoConfiguration=\ 303 | org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ 304 | org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ 305 | org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\ 306 | org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ 307 | org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ 308 | org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration 309 | ``` 310 | 311 | The advantages of doing this are that the application code no longer 312 | has to manually enumerate the configurations, and also that the 313 | ordering is now taken care of by Spring Boot (the properties file 314 | entry is sorted before it is used). The disadvantage is that it is 315 | only useful for applications which need precisely these features, and 316 | has to be replaced or augmented in any application that wants to do 317 | something a bit different. It is still fast though - Spring Boot does 318 | a little bit of extra work for the book keeping (sorting and 319 | ordering), but not really very much. It will probably still start in 320 | less than 700ms on the right hardware, with the right JVM flags. 321 | 322 | == Functional Bean Definitions 323 | 324 | In the earlier article I mentioned that functional bean definitions 325 | would be the most efficient way to get an application started with 326 | Spring. This is still the case, and we can squeeze an extra 10% or so 327 | out of this application by re-writing all the Spring Boot 328 | autoconfigurations as `ApplicationContextInitializers`. You could do 329 | that manually, or you could use some initializers that have already 330 | been prepared for you, as long as you don't mind trying out some 331 | experimental features. There are 2 projects currently active exploring 332 | the idea of new tools and new programming models based on functional 333 | bean definitions: https://github.com/spring-projects/spring-fu[Spring 334 | Fu] and 335 | https://github.com/spring-projects-experimental/spring-init[Spring 336 | Init]. Both provide at least a minimal set of functional bean 337 | definitions replacing or wrapping the Spring Boot 338 | autoconfigurations. Spring Fu is API (DSL) based, and doesn't use 339 | reflection or annotations. Spring Init has the functional bean 340 | definitions and also has a prototype of an annotation-based 341 | programming model for "a la carte" configuration. Both are covered 342 | in more detail elsewhere. 343 | 344 | The main point to note here is that functional bean definitions are 345 | faster, but if that is your main concern, remember that it is only a 346 | 10% effect. As soon as you put all the features back in the 347 | application that we stripped down above, you are back to loading all 348 | the necessary classes and back to roughly the same approximate startup 349 | time as well. To put this another way, the cost of the 350 | `@Configuration` processing at runtime is not completely negligible, 351 | but it also isn't very high (10% or so in these tiny apps, or maybe 352 | 100ms). 353 | 354 | == Summary and Future Directions 355 | 356 | Here's a graph summarizing some benchmark results from a different 357 | application, the 358 | https://github.com/spring-projects/spring-petclinic[Spring PetClinic]: 359 | 360 | .Petclinic Startup Time (Seconds) 361 | image::https://docs.google.com/spreadsheets/d/e/2PACX-1vQpSEfx0Y1W9aD3XVyn91-S0jtUp2DRCQSy_W_LMGyMR91YLAQ1mL7MiR1BRd8VzshvtuxzL6WAnlxf/pubchart?oid=1003506885&format=image[] 362 | 363 | It's not a "real" application, but it is heavier than the simple 364 | sample, and uses a lot more features at runtime (like Hibernate for 365 | example), so it is somewhat more realistic. There are two 366 | versions, "demo" and "actr", where the latter is just the same but 367 | with Actuators. For both samples, the fastest startup time is the 368 | yellow dot, which is functional bean definitions, but only 10% behind 369 | that (about 200ms in this app) are the "a la carte" options (green and 370 | red). Green uses a custom annotation like the 371 | `@EnableWebFluxAutoConfiguration` one above. Red is a different "a la 372 | carte" option where groups of autoconfigurations can be imported 373 | together via a different custom annotation, currently named 374 | `@SpringInitApplication` and being prototyped in Spring Init. Blue is 375 | the fully leaded autoconfiguration (out of the box Spring Boot). 376 | 377 | Spring Boot autoconfiguration is hugely convenient, but can be 378 | characterized as "all you can eat". Currently (as of 2.1.x) it is 379 | maybe providing more features than some applications use or 380 | require. In the "a la carte" approach, you can use Spring Boot as a 381 | convenient collection of prepared and pre-tested configurations and 382 | choose which parts you use. If you do that then 383 | `@ImportAutoConfiguration` is an important part of the toolkit, but 384 | exactly how you should best use it might change as we research this 385 | topic further. Future versions of Spring Boot, and possibly other new 386 | projects like Spring Fu or Spring Init, will make it easier to narrow 387 | the choice of configurations used at runtime, either automatically or 388 | by explicit choice. At the end of the day, `@Configuration` processing 389 | at runtime is not free, but it isn't particularly expensive either 390 | (especially with Spring Boot 2.1.x). The smaller number of features 391 | you use, the fewer classes are loaded, which lead to faster 392 | startup. At the end of the day we don't expect 393 | `@EnableAutoConfiguration` to lose its value or its popularity, and 394 | remember your mileage may vary: the PetClinic and simple samples in 395 | this article are not a guide to what you can expect with larger, more 396 | complex applications. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | spring-boot-allocations 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | static 12 | Demo project for Spring Boot 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 1.21 19 | 2.2.0.BUILD-SNAPSHOT 20 | Hoxton.BUILD-SNAPSHOT 21 | 1.0.0.RELEASE 22 | 1.0.22.RELEASE 23 | benchmarks 24 | org.openjdk.jmh.Main 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven 31 | maven-aether-provider 32 | 3.3.9 33 | 34 | 35 | org.apache.maven 36 | maven-settings-builder 37 | 3.3.9 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-dependencies 42 | ${spring-boot.version} 43 | pom 44 | import 45 | 46 | 47 | org.springframework.cloud 48 | spring-cloud-dependencies 49 | ${spring-cloud.version} 50 | pom 51 | import 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-logging 64 | 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-webflux 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-logging 74 | 75 | 76 | org.springframework.boot 77 | spring-boot-starter-json 78 | 79 | 80 | org.hibernate.validator 81 | hibernate-validator 82 | 83 | 84 | io.netty 85 | netty-transport-native-epoll 86 | 87 | 88 | jakarta.validation 89 | jakarta.validation-api 90 | 91 | 92 | 93 | 94 | org.springframework 95 | spring-context-indexer 96 | provided 97 | 98 | 99 | org.slf4j 100 | slf4j-jdk14 101 | 102 | 103 | com.google.code.gson 104 | gson 105 | 106 | 107 | org.aspectj 108 | aspectjweaver 109 | 110 | 111 | 112 | org.openjdk.jmh 113 | jmh-core 114 | ${jmh.version} 115 | true 116 | 117 | 118 | org.openjdk.jmh 119 | jmh-generator-annprocess 120 | ${jmh.version} 121 | provided 122 | 123 | 124 | 125 | org.springframework.boot 126 | spring-boot-starter-test 127 | test 128 | 129 | 130 | 131 | 132 | ${uberjar.name} 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-compiler-plugin 137 | 3.8.0 138 | 139 | ${java.version} 140 | ${java.version} 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-surefire-plugin 146 | 2.19.1 147 | 148 | 149 | **/*Tests.java 150 | **/*Test.java 151 | 152 | 153 | **/Abstract*.java 154 | 155 | 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-shade-plugin 160 | 2.4.3 161 | 162 | 163 | 164 | META-INF/spring.handlers 165 | 166 | 167 | META-INF/spring.factories 168 | 169 | 170 | META-INF/spring.schemas 171 | 172 | 173 | META-INF/spring.components 174 | 175 | 176 | ${start-class} 177 | 178 | 179 | 180 | 182 | META-INF/sisu/javax.inject.Named 183 | 184 | 185 | 186 | 187 | 188 | package 189 | 190 | shade 191 | 192 | 193 | 194 | 195 | 196 | org.springframework.boot 197 | spring-boot-maven-plugin 198 | ${spring-boot.version} 199 | 200 | 201 | org.springframework.boot.experimental 202 | spring-boot-thin-launcher-shade-locator 203 | ${locator.version} 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | spring-snapshots 213 | Spring Snapshots 214 | https://repo.spring.io/snapshot 215 | 216 | true 217 | 218 | 219 | 220 | spring-milestones 221 | Spring Milestones 222 | https://repo.spring.io/milestone 223 | 224 | false 225 | 226 | 227 | 228 | 229 | 230 | spring-snapshots 231 | Spring Snapshots 232 | https://repo.spring.io/snapshot 233 | 234 | true 235 | 236 | 237 | 238 | spring-milestones 239 | Spring Milestones 240 | https://repo.spring.io/milestone 241 | 242 | false 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /profiling.jfc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | true 8 | everyChunk 9 | 10 | 11 | 12 | true 13 | 1000 ms 14 | 15 | 16 | 17 | true 18 | 1000 ms 19 | 20 | 21 | 22 | true 23 | 24 | 25 | 26 | true 27 | 28 | 29 | 30 | true 31 | true 32 | 10 ms 33 | 34 | 35 | 36 | true 37 | true 38 | 10 ms 39 | 40 | 41 | 42 | true 43 | true 44 | 10 ms 45 | 46 | 47 | 48 | true 49 | true 50 | 10 ms 51 | 52 | 53 | 54 | true 55 | true 56 | 0 ms 57 | 58 | 59 | 60 | true 61 | 62 | 63 | 64 | true 65 | everyChunk 66 | 67 | 68 | 69 | true 70 | everyChunk 71 | 72 | 73 | 74 | true 75 | 10 ms 76 | 77 | 78 | 79 | true 80 | 1 ms 81 | 82 | 83 | 84 | true 85 | 10 ms 86 | 87 | 88 | 89 | true 90 | 60 s 91 | 92 | 93 | 94 | true 95 | everyChunk 96 | 97 | 98 | 99 | true 100 | everyChunk 101 | 102 | 103 | 104 | true 105 | everyChunk 106 | 107 | 108 | 109 | true 110 | everyChunk 111 | 112 | 113 | 114 | true 115 | everyChunk 116 | 117 | 118 | 119 | true 120 | 121 | 122 | 123 | true 124 | 125 | 126 | 127 | true 128 | 129 | 130 | 131 | true 132 | 133 | 134 | 135 | true 136 | 137 | 138 | 139 | true 140 | everyChunk 141 | 142 | 143 | 144 | true 145 | everyChunk 146 | 147 | 148 | 149 | true 150 | everyChunk 151 | 152 | 153 | 154 | true 155 | everyChunk 156 | 157 | 158 | 159 | true 160 | everyChunk 161 | 162 | 163 | 164 | true 165 | everyChunk 166 | 167 | 168 | 169 | true 170 | 171 | 172 | 173 | true 174 | 175 | 176 | 177 | true 178 | 179 | 180 | 181 | true 182 | 183 | 184 | 185 | true 186 | 187 | 188 | 189 | true 190 | true 191 | 192 | 193 | 194 | true 195 | true 196 | 197 | 198 | 199 | true 200 | 201 | 202 | 203 | true 204 | 0 ms 205 | 206 | 207 | 208 | true 209 | 0 ms 210 | 211 | 212 | 213 | true 214 | 0 ms 215 | 216 | 217 | 218 | true 219 | 0 ms 220 | 221 | 222 | 223 | true 224 | 0 ms 225 | 226 | 227 | 228 | true 229 | 0 ms 230 | 231 | 232 | 233 | true 234 | 0 ms 235 | 236 | 237 | 238 | true 239 | 0 ms 240 | 241 | 242 | 243 | true 244 | 0 ms 245 | 246 | 247 | 248 | true 249 | 250 | 251 | 252 | true 253 | 254 | 255 | 256 | true 257 | 258 | 259 | 260 | true 261 | 262 | 263 | 264 | true 265 | 266 | 267 | 268 | true 269 | true 270 | 271 | 272 | 273 | true 274 | everyChunk 275 | 276 | 277 | 278 | true 279 | 1000 ms 280 | 281 | 282 | 283 | true 284 | 0 ms 285 | 286 | 287 | 288 | true 289 | 0 s 290 | 291 | 292 | 293 | true 294 | 295 | 296 | 297 | true 298 | everyChunk 299 | 300 | 301 | 302 | true 303 | everyChunk 304 | 305 | 306 | 307 | true 308 | 0 ms 309 | 310 | 311 | 312 | true 313 | everyChunk 314 | 315 | 316 | 317 | true 318 | everyChunk 319 | 320 | 321 | 322 | true 323 | 324 | 325 | 326 | true 327 | everyChunk 328 | 329 | 330 | 331 | true 332 | everyChunk 333 | 334 | 335 | 336 | true 337 | 10 s 338 | 339 | 340 | 341 | true 342 | 1000 ms 343 | 344 | 345 | 346 | true 347 | everyChunk 348 | 349 | 350 | 351 | false 352 | everyChunk 353 | 354 | 355 | 356 | false 357 | everyChunk 358 | 359 | 360 | 361 | true 362 | everyChunk 363 | 364 | 365 | 366 | true 367 | true 368 | 369 | 370 | 371 | true 372 | true 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | true 381 | true 382 | 10 ms 383 | 384 | 385 | 386 | true 387 | true 388 | 10 ms 389 | 390 | 391 | 392 | true 393 | true 394 | 10 ms 395 | 396 | 397 | 398 | true 399 | true 400 | 10 ms 401 | 402 | 403 | 404 | true 405 | true 406 | 407 | 408 | 409 | true 410 | true 411 | 412 | 413 | 414 | true 415 | 1000 ms 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | true 424 | 425 | 426 | 427 | true 428 | 429 | 430 | 431 | 432 | 433 | -------------------------------------------------------------------------------- /src/main/java/com/example/aspects/AnnotationProcessingInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.example.aspects; 2 | 3 | import java.util.Arrays; 4 | 5 | import com.example.config.StandardMetadataReaderFactory; 6 | 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.annotation.Around; 11 | import org.aspectj.lang.annotation.Aspect; 12 | 13 | @Aspect 14 | public class AnnotationProcessingInterceptor { 15 | 16 | private static Log logger = LogFactory.getLog(AnnotationProcessingInterceptor.class); 17 | 18 | @Around("within(org.springframework.context.annotation.ConfigurationClassUtils) && " 19 | + "execution( * hasNestedConfigurationClass(..))") 20 | public Object nested(ProceedingJoinPoint joinPoint) throws Throwable { 21 | if (logger.isDebugEnabled()) { 22 | logger.debug(joinPoint.toShortString() + ": " 23 | + Arrays.asList(joinPoint.getArgs())); 24 | } 25 | return false; 26 | } 27 | 28 | @Around("execution( * org.springframework.context.annotation.ConfigurationClassUtils.checkConfigurationClassCandidate(..))") 29 | public Object check(ProceedingJoinPoint joinPoint) throws Throwable { 30 | if (logger.isDebugEnabled()) { 31 | logger.debug(joinPoint.toShortString() + ": " 32 | + Arrays.asList(joinPoint.getArgs())); 33 | } 34 | return false; 35 | } 36 | 37 | @Around("within(org.springframework.context.annotation.ConfigurationClassPostProcessor) && " 38 | + "execution( * setMetadataReaderFactory(..))") 39 | public void metadata(ProceedingJoinPoint joinPoint) throws Throwable { 40 | if (logger.isDebugEnabled()) { 41 | logger.debug(joinPoint.toShortString()); 42 | } 43 | joinPoint.proceed(new Object[] { new StandardMetadataReaderFactory() }); 44 | } 45 | 46 | static AnnotationProcessingInterceptor instance = new AnnotationProcessingInterceptor(); 47 | 48 | public static AnnotationProcessingInterceptor aspectOf() { 49 | return instance; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/example/aspects/BootApplicationInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.example.aspects; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | import org.aspectj.lang.annotation.Around; 7 | import org.aspectj.lang.annotation.Aspect; 8 | 9 | import org.springframework.util.ClassUtils; 10 | 11 | @Aspect 12 | public class BootApplicationInterceptor { 13 | 14 | private static Log logger = LogFactory.getLog(BootApplicationInterceptor.class); 15 | 16 | @Around("execution(* org.springframework.boot.system.ApplicationHome.findSource(..))") 17 | public Object source(ProceedingJoinPoint joinPoint) throws Throwable { 18 | return proceed(joinPoint); 19 | } 20 | 21 | @Around("execution(* org.springframework.boot.SpringApplication.isWebApplicationContext(..))") 22 | public Object webApplication(ProceedingJoinPoint joinPoint) throws Throwable { 23 | if (!ClassUtils.isPresent("org.springframework.web.context.WebApplicationContext", 24 | null)) { 25 | return false; 26 | } 27 | return proceed(joinPoint); 28 | } 29 | 30 | private Object proceed(ProceedingJoinPoint joinPoint) { 31 | try { 32 | Object result = joinPoint.proceed(); 33 | if (logger.isDebugEnabled()) { 34 | logger.debug(joinPoint.toShortString() + ": " + result); 35 | } 36 | return result; 37 | } 38 | catch (Throwable t) { 39 | if (logger.isDebugEnabled()) { 40 | logger.debug(joinPoint.toShortString() + ": " + t); 41 | } 42 | return null; 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/example/aspects/BootAutoConfigureInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.example.aspects; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | import org.aspectj.lang.annotation.Around; 7 | import org.aspectj.lang.annotation.Aspect; 8 | 9 | import org.springframework.util.ClassUtils; 10 | 11 | @Aspect 12 | public class BootAutoConfigureInterceptor { 13 | 14 | private static Log logger = LogFactory.getLog(BootAutoConfigureInterceptor.class); 15 | 16 | @Around("within(org.springframework.boot.autoconfigure.BackgroundPreinitializer) && call(* run*(..))") 17 | public Object background(ProceedingJoinPoint joinPoint) throws Throwable { 18 | return proceed(joinPoint); 19 | } 20 | 21 | @Around("execution(* org.springframework.boot.autoconfigure.BackgroundPreinitializer.ValidationInitializer.run(..))") 22 | public Object validation(ProceedingJoinPoint joinPoint) throws Throwable { 23 | if (!ClassUtils.isPresent("javax.validation.Validation", null)) { 24 | return null; 25 | } 26 | return proceed(joinPoint); 27 | } 28 | 29 | @Around("execution(* org.springframework.boot.autoconfigure.BackgroundPreinitializer.MBeanFactoryInitializer.run(..))") 30 | public Object tomcat(ProceedingJoinPoint joinPoint) throws Throwable { 31 | if (!ClassUtils.isPresent("org.apache.catalina.mbeans.MBeanFactory", null)) { 32 | return null; 33 | } 34 | return proceed(joinPoint); 35 | } 36 | 37 | @Around("within(org.springframework.boot.autoconfigure.BackgroundPreinitializer.JacksonInitializer) && execution(* run(..))") 38 | public Object jackson(ProceedingJoinPoint joinPoint) throws Throwable { 39 | if (!ClassUtils.isPresent("org.springframework.http.converter.json.JsonFactory", 40 | null)) { 41 | return null; 42 | } 43 | return proceed(joinPoint); 44 | } 45 | 46 | private Object proceed(ProceedingJoinPoint joinPoint) { 47 | try { 48 | Object result = joinPoint.proceed(); 49 | if (logger.isDebugEnabled()) { 50 | logger.debug(joinPoint.toShortString() + ": " + result); 51 | } 52 | return result; 53 | } 54 | catch (Throwable t) { 55 | if (logger.isDebugEnabled()) { 56 | logger.debug(joinPoint.toShortString() + ": " + t); 57 | } 58 | return null; 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/example/auto/AutoApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.auto; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | 21 | import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; 22 | import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver; 23 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 24 | import org.springframework.boot.SpringApplication; 25 | import org.springframework.boot.autoconfigure.AutoConfigurationPackages; 26 | import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar; 27 | import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext; 28 | import org.springframework.context.ApplicationContext; 29 | import org.springframework.context.ApplicationContextInitializer; 30 | import org.springframework.context.ConfigurableApplicationContext; 31 | import org.springframework.context.support.GenericApplicationContext; 32 | import org.springframework.core.annotation.AnnotationAwareOrderComparator; 33 | import org.springframework.util.ClassUtils; 34 | import org.springframework.web.bind.annotation.GetMapping; 35 | import org.springframework.web.bind.annotation.RestController; 36 | 37 | /** 38 | * @author Dave Syer 39 | * 40 | */ 41 | @RestController 42 | public class AutoApplication implements Runnable, Closeable, 43 | ApplicationContextInitializer { 44 | 45 | public static final String MARKER = "Benchmark app started"; 46 | 47 | private ConfigurableApplicationContext context; 48 | 49 | @GetMapping 50 | public String home() { 51 | return "Hello"; 52 | } 53 | 54 | public static void main(String[] args) throws Exception { 55 | long t0 = System.currentTimeMillis(); 56 | AutoApplication bean = new AutoApplication(); 57 | bean.run(); 58 | System.err.println("Started: " + (System.currentTimeMillis() - t0) + "ms"); 59 | if (Boolean.getBoolean("demo.close")) { 60 | bean.close(); 61 | } 62 | } 63 | 64 | @Override 65 | public void close() throws IOException { 66 | if (context != null) { 67 | context.close(); 68 | } 69 | } 70 | 71 | @Override 72 | public void run() { 73 | SpringApplication application = new SpringApplication(AutoApplication.class) { 74 | @Override 75 | protected void load(ApplicationContext context, Object[] sources) { 76 | // We don't want the annotation bean definition reader 77 | // super.load(context, sources); 78 | } 79 | }; 80 | application.addInitializers(this); 81 | application.setApplicationContextClass(ReactiveWebServerApplicationContext.class); 82 | this.context = application.run(); 83 | System.err.println(MARKER); 84 | } 85 | 86 | @Override 87 | public void initialize(GenericApplicationContext context) { 88 | DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); 89 | if (beanFactory != null) { 90 | if (!(beanFactory 91 | .getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { 92 | beanFactory 93 | .setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); 94 | } 95 | // N.B. ContextAnnotationAutowireCandidateResolver is normal, but that's more 96 | // expensive 97 | // (checks for @Lazy) 98 | if (!(beanFactory 99 | .getAutowireCandidateResolver() instanceof QualifierAnnotationAutowireCandidateResolver)) { 100 | beanFactory.setAutowireCandidateResolver( 101 | new QualifierAnnotationAutowireCandidateResolver()); 102 | } 103 | beanFactory.addBeanPostProcessor( 104 | beanFactory.createBean(AutowiredAnnotationBeanPostProcessor.class)); 105 | } 106 | AutoConfigurationPackages.register(context, 107 | ClassUtils.getPackageName(getClass())); 108 | new ConfigurationPropertiesBindingPostProcessorRegistrar() 109 | .registerBeanDefinitions(null, context); 110 | context.addBeanFactoryPostProcessor(new AutoConfigurations(context)); 111 | context.registerBean(AutoApplication.class, () -> this); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/example/auto/AutoConfigurations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.auto; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Inherited; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | import java.lang.reflect.Method; 26 | import java.lang.reflect.Modifier; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | import java.util.Set; 30 | import java.util.function.Supplier; 31 | 32 | import com.example.auto.AutoConfigurations.EnableActuatorAutoConfigurations; 33 | 34 | import org.springframework.beans.BeansException; 35 | import org.springframework.beans.factory.BeanCreationException; 36 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 37 | import org.springframework.beans.factory.config.DependencyDescriptor; 38 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 39 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 40 | import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; 41 | import org.springframework.beans.factory.support.RootBeanDefinition; 42 | import org.springframework.boot.autoconfigure.AutoConfigurationImportSelector; 43 | import org.springframework.boot.autoconfigure.AutoConfigurationPackage; 44 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 45 | import org.springframework.context.annotation.Bean; 46 | import org.springframework.context.annotation.Configuration; 47 | import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; 48 | import org.springframework.context.annotation.Import; 49 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; 50 | import org.springframework.context.support.GenericApplicationContext; 51 | import org.springframework.core.MethodParameter; 52 | import org.springframework.core.annotation.AnnotationUtils; 53 | import org.springframework.core.type.AnnotationMetadata; 54 | import org.springframework.core.type.MethodMetadata; 55 | import org.springframework.util.ClassUtils; 56 | import org.springframework.util.ReflectionUtils; 57 | 58 | @Configuration 59 | @EnableActuatorAutoConfigurations 60 | class AutoConfigurations extends AutoConfigurationImportSelector 61 | implements BeanDefinitionRegistryPostProcessor { 62 | 63 | private GenericApplicationContext context; 64 | 65 | public AutoConfigurations(GenericApplicationContext applicationContext) { 66 | this.context = applicationContext; 67 | setBeanFactory(applicationContext.getDefaultListableBeanFactory()); 68 | setBeanClassLoader(applicationContext.getClassLoader()); 69 | setEnvironment(applicationContext.getEnvironment()); 70 | setResourceLoader(applicationContext); 71 | } 72 | 73 | public Class[] config() { 74 | String[] imports = selectImports( 75 | AnnotationMetadata.introspect(AutoConfigurations.class)); 76 | Class[] types = new Class[imports.length]; 77 | int i = 0; 78 | for (String config : imports) { 79 | Class type = ClassUtils.resolveClassName(config, getBeanClassLoader()); 80 | types[i++] = type; 81 | } 82 | org.springframework.boot.autoconfigure.AutoConfigurations autos = org.springframework.boot.autoconfigure.AutoConfigurations 83 | .of(types); 84 | return org.springframework.boot.autoconfigure.AutoConfigurations 85 | .getClasses(autos); 86 | } 87 | 88 | @Target(ElementType.TYPE) 89 | @Retention(RetentionPolicy.RUNTIME) 90 | @Documented 91 | @Inherited 92 | @AutoConfigurationPackage 93 | static @interface EnableActuatorAutoConfigurations { 94 | Class[] exclude() default {}; 95 | 96 | String[] excludeName() default {}; 97 | } 98 | 99 | @Override 100 | protected Class getAnnotationClass() { 101 | return EnableActuatorAutoConfigurations.class; 102 | } 103 | 104 | @Override 105 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 106 | throws BeansException { 107 | } 108 | 109 | @Override 110 | public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 111 | throws BeansException { 112 | try { 113 | register(registry, this.context.getDefaultListableBeanFactory()); 114 | } 115 | catch (BeansException e) { 116 | throw e; 117 | } 118 | catch (RuntimeException e) { 119 | throw e; 120 | } 121 | catch (Exception e) { 122 | throw new BeanCreationException("Cannot register from " + getClass(), e); 123 | } 124 | } 125 | 126 | protected void register(BeanDefinitionRegistry registry, 127 | ConfigurableListableBeanFactory factory) throws Exception { 128 | ConditionEvaluator evaluator = new ConditionEvaluator(registry, getEnvironment(), 129 | getResourceLoader()); 130 | for (Class type : config()) { 131 | AnnotationMetadata metadata = AnnotationMetadata.introspect(type); 132 | if (evaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { 133 | continue; 134 | } 135 | register(registry, evaluator, type, metadata); 136 | } 137 | } 138 | 139 | private void register(BeanDefinitionRegistry registry, ConditionEvaluator evaluator, 140 | Class type, AnnotationMetadata metadata) { 141 | for (Class nested : type.getDeclaredClasses()) { 142 | if (Modifier.isStatic(nested.getModifiers())) { 143 | try { 144 | if (!registry.containsBeanDefinition(nested.getName())) { 145 | AnnotationMetadata nestedMetadata = AnnotationMetadata 146 | .introspect(nested); 147 | if (nestedMetadata.hasAnnotation(Configuration.class.getName())) { 148 | if (!evaluator.shouldSkip(nestedMetadata, 149 | ConfigurationPhase.REGISTER_BEAN)) { 150 | register(registry, evaluator, nested, nestedMetadata); 151 | } 152 | } 153 | } 154 | } 155 | catch (ArrayStoreException e) { 156 | // TODO: use ASM to avoid this? 157 | } 158 | } 159 | } 160 | if (metadata.hasAnnotation(Import.class.getName())) { 161 | Object[] props = (Object[]) metadata 162 | .getAnnotationAttributes(Import.class.getName()).get("value"); 163 | if (props != null && props.length > 0) { 164 | for (Object object : props) { 165 | Class imported = (Class) object; 166 | if (ImportBeanDefinitionRegistrar.class.isAssignableFrom(imported)) { 167 | ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar) getBeanFactory() 168 | .createBean(imported); 169 | registrar.registerBeanDefinitions(metadata, registry); 170 | } 171 | try { 172 | AnnotationMetadata nestedMetadata = AnnotationMetadata 173 | .introspect(imported); 174 | if (!registry.containsBeanDefinition(imported.getName()) 175 | && !evaluator.shouldSkip(nestedMetadata, 176 | ConfigurationPhase.REGISTER_BEAN)) { 177 | register(registry, evaluator, imported, nestedMetadata); 178 | } 179 | } 180 | catch (ArrayStoreException e) { 181 | // TODO: use ASM to avoid this? 182 | } 183 | } 184 | } 185 | } 186 | if (metadata.hasAnnotation(EnableConfigurationProperties.class.getName())) { 187 | Object[] props = (Object[]) metadata.getAnnotationAttributes( 188 | EnableConfigurationProperties.class.getName()).get("value"); 189 | if (props != null && props.length > 0) { 190 | for (Object object : props) { 191 | Class prop = (Class) object; 192 | String name = prop.getName(); 193 | if (!registry.containsBeanDefinition(name)) { 194 | registry.registerBeanDefinition(name, BeanDefinitionBuilder 195 | .genericBeanDefinition(prop).getRawBeanDefinition()); 196 | } 197 | } 198 | } 199 | } 200 | registry.registerBeanDefinition(type.getName(), 201 | BeanDefinitionBuilder.genericBeanDefinition(type).getRawBeanDefinition()); 202 | Set methods = metadata.getAnnotatedMethods(Bean.class.getName()); 203 | Map beans = new HashMap<>(); 204 | for (MethodMetadata method : methods) { 205 | beans.put(method.getMethodName(), method); 206 | } 207 | for (Method method : ReflectionUtils.getUniqueDeclaredMethods(type)) { 208 | if (AnnotationUtils.findAnnotation(method, Bean.class) != null) { 209 | register(registry, evaluator, type, method, beans.get(method.getName())); 210 | } 211 | } 212 | } 213 | 214 | private void register(BeanDefinitionRegistry registry, ConditionEvaluator evaluator, 215 | Class type, Method method, MethodMetadata metadata) { 216 | try { 217 | if (!evaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { 218 | Class beanClass = method.getReturnType(); 219 | Supplier supplier = () -> { 220 | Object[] args = params(method, getBeanFactory()); 221 | ReflectionUtils.makeAccessible(method); 222 | Object result = ReflectionUtils.invokeMethod(method, 223 | getBean(method, type), args); 224 | return result; 225 | }; 226 | RootBeanDefinition definition = new RootBeanDefinition(); 227 | definition.setTargetType(beanClass); 228 | definition.setInstanceSupplier(supplier); 229 | definition.setFactoryMethodName(method.getName()); 230 | // Bean name for factory... 231 | definition.setFactoryBeanName(type.getName()); 232 | registry.registerBeanDefinition(method.getName(), definition); 233 | } 234 | } 235 | catch (ArrayStoreException e) { 236 | // TODO: use ASM to avoid this? 237 | } 238 | } 239 | 240 | private Object getBean(Method method, Class type) { 241 | if (Modifier.isStatic(method.getModifiers())) { 242 | return null; 243 | } 244 | // We have to use getBeansOfType() to avoid eager instantiation of everything when 245 | // this is a factory for a bean factory post processor 246 | Map beans = getBeanFactory().getBeansOfType(type, false, false); 247 | // TODO: deal with no unique bean 248 | return beans.values().iterator().next(); 249 | } 250 | 251 | private Object[] params(Method method, ConfigurableListableBeanFactory factory) { 252 | Object[] params = new Object[method.getParameterCount()]; 253 | for (int i = 0; i < params.length; i++) { 254 | // TODO: deal with required flag 255 | params[i] = factory.resolveDependency( 256 | new DependencyDescriptor(new MethodParameter(method, i), false), 257 | method.getName()); 258 | } 259 | return params; 260 | } 261 | 262 | } -------------------------------------------------------------------------------- /src/main/java/com/example/auto/ConditionEvaluator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.auto; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.List; 22 | 23 | import org.springframework.beans.BeanUtils; 24 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 25 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 26 | import org.springframework.context.ConfigurableApplicationContext; 27 | import org.springframework.context.annotation.Condition; 28 | import org.springframework.context.annotation.ConditionContext; 29 | import org.springframework.context.annotation.Conditional; 30 | import org.springframework.context.annotation.Configuration; 31 | import org.springframework.context.annotation.ConfigurationCondition; 32 | import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; 33 | import org.springframework.core.annotation.AnnotationAwareOrderComparator; 34 | import org.springframework.core.env.Environment; 35 | import org.springframework.core.env.EnvironmentCapable; 36 | import org.springframework.core.env.StandardEnvironment; 37 | import org.springframework.core.io.DefaultResourceLoader; 38 | import org.springframework.core.io.ResourceLoader; 39 | import org.springframework.core.type.AnnotatedTypeMetadata; 40 | import org.springframework.core.type.AnnotationMetadata; 41 | import org.springframework.lang.Nullable; 42 | import org.springframework.util.Assert; 43 | import org.springframework.util.ClassUtils; 44 | import org.springframework.util.MultiValueMap; 45 | 46 | /** 47 | * Internal class used to evaluate {@link Conditional} annotations. 48 | * 49 | * @author Phillip Webb 50 | * @author Juergen Hoeller 51 | * @since 4.0 52 | */ 53 | class ConditionEvaluator { 54 | 55 | private final ConditionContextImpl context; 56 | 57 | /** 58 | * Create a new {@link ConditionEvaluator} instance. 59 | */ 60 | public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry, 61 | @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { 62 | 63 | this.context = new ConditionContextImpl(registry, environment, resourceLoader); 64 | } 65 | 66 | /** 67 | * Determine if an item should be skipped based on {@code @Conditional} annotations. 68 | * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a 69 | * {@code @Configuration} class will be 70 | * {@link ConfigurationPhase#PARSE_CONFIGURATION}) 71 | * @param metadata the meta data 72 | * @return if the item should be skipped 73 | */ 74 | public boolean shouldSkip(AnnotatedTypeMetadata metadata) { 75 | return shouldSkip(metadata, null); 76 | } 77 | 78 | /** 79 | * Determine if an item should be skipped based on {@code @Conditional} annotations. 80 | * @param metadata the meta data 81 | * @param phase the phase of the call 82 | * @return if the item should be skipped 83 | */ 84 | public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, 85 | @Nullable ConfigurationPhase phase) { 86 | if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { 87 | return false; 88 | } 89 | 90 | if (phase == null) { 91 | if (metadata instanceof AnnotationMetadata && ConditionEvaluator 92 | .isConfigurationCandidate((AnnotationMetadata) metadata)) { 93 | return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); 94 | } 95 | return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); 96 | } 97 | 98 | List conditions = new ArrayList<>(); 99 | for (String[] conditionClasses : getConditionClasses(metadata)) { 100 | for (String conditionClass : conditionClasses) { 101 | Condition condition = getCondition(conditionClass, 102 | this.context.getClassLoader()); 103 | conditions.add(condition); 104 | } 105 | } 106 | 107 | AnnotationAwareOrderComparator.sort(conditions); 108 | 109 | for (Condition condition : conditions) { 110 | ConfigurationPhase requiredPhase = null; 111 | if (condition instanceof ConfigurationCondition) { 112 | requiredPhase = ((ConfigurationCondition) condition) 113 | .getConfigurationPhase(); 114 | } 115 | if ((requiredPhase == null || requiredPhase == phase) 116 | && !condition.matches(this.context, metadata)) { 117 | return true; 118 | } 119 | } 120 | 121 | return false; 122 | } 123 | 124 | public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { 125 | return metadata.isAnnotated(Configuration.class.getName()); 126 | } 127 | 128 | @SuppressWarnings("unchecked") 129 | private List getConditionClasses(AnnotatedTypeMetadata metadata) { 130 | MultiValueMap attributes = metadata 131 | .getAllAnnotationAttributes(Conditional.class.getName(), true); 132 | Object values = (attributes != null ? attributes.get("value") : null); 133 | return (List) (values != null ? values : Collections.emptyList()); 134 | } 135 | 136 | private Condition getCondition(String conditionClassName, 137 | @Nullable ClassLoader classloader) { 138 | Class conditionClass = ClassUtils.resolveClassName(conditionClassName, 139 | classloader); 140 | return (Condition) BeanUtils.instantiateClass(conditionClass); 141 | } 142 | 143 | /** 144 | * Implementation of a {@link ConditionContext}. 145 | */ 146 | private static class ConditionContextImpl implements ConditionContext { 147 | 148 | @Nullable 149 | private final BeanDefinitionRegistry registry; 150 | 151 | @Nullable 152 | private final ConfigurableListableBeanFactory beanFactory; 153 | 154 | private final Environment environment; 155 | 156 | private final ResourceLoader resourceLoader; 157 | 158 | @Nullable 159 | private final ClassLoader classLoader; 160 | 161 | public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry, 162 | @Nullable Environment environment, 163 | @Nullable ResourceLoader resourceLoader) { 164 | 165 | this.registry = registry; 166 | this.beanFactory = deduceBeanFactory(registry); 167 | this.environment = (environment != null ? environment 168 | : deduceEnvironment(registry)); 169 | this.resourceLoader = (resourceLoader != null ? resourceLoader 170 | : deduceResourceLoader(registry)); 171 | this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory); 172 | } 173 | 174 | @Nullable 175 | private ConfigurableListableBeanFactory deduceBeanFactory( 176 | @Nullable BeanDefinitionRegistry source) { 177 | if (source instanceof ConfigurableListableBeanFactory) { 178 | return (ConfigurableListableBeanFactory) source; 179 | } 180 | if (source instanceof ConfigurableApplicationContext) { 181 | return (((ConfigurableApplicationContext) source).getBeanFactory()); 182 | } 183 | return null; 184 | } 185 | 186 | private Environment deduceEnvironment(@Nullable BeanDefinitionRegistry source) { 187 | if (source instanceof EnvironmentCapable) { 188 | return ((EnvironmentCapable) source).getEnvironment(); 189 | } 190 | return new StandardEnvironment(); 191 | } 192 | 193 | private ResourceLoader deduceResourceLoader( 194 | @Nullable BeanDefinitionRegistry source) { 195 | if (source instanceof ResourceLoader) { 196 | return (ResourceLoader) source; 197 | } 198 | return new DefaultResourceLoader(); 199 | } 200 | 201 | @Nullable 202 | private ClassLoader deduceClassLoader(@Nullable ResourceLoader resourceLoader, 203 | @Nullable ConfigurableListableBeanFactory beanFactory) { 204 | 205 | if (resourceLoader != null) { 206 | ClassLoader classLoader = resourceLoader.getClassLoader(); 207 | if (classLoader != null) { 208 | return classLoader; 209 | } 210 | } 211 | if (beanFactory != null) { 212 | return beanFactory.getBeanClassLoader(); 213 | } 214 | return ClassUtils.getDefaultClassLoader(); 215 | } 216 | 217 | @Override 218 | public BeanDefinitionRegistry getRegistry() { 219 | Assert.state(this.registry != null, "No BeanDefinitionRegistry available"); 220 | return this.registry; 221 | } 222 | 223 | @Override 224 | @Nullable 225 | public ConfigurableListableBeanFactory getBeanFactory() { 226 | return this.beanFactory; 227 | } 228 | 229 | @Override 230 | public Environment getEnvironment() { 231 | return this.environment; 232 | } 233 | 234 | @Override 235 | public ResourceLoader getResourceLoader() { 236 | return this.resourceLoader; 237 | } 238 | 239 | @Override 240 | @Nullable 241 | public ClassLoader getClassLoader() { 242 | return this.classLoader; 243 | } 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/AnnotatedMethodBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import org.openjdk.jmh.annotations.Benchmark; 19 | import org.openjdk.jmh.annotations.Fork; 20 | import org.openjdk.jmh.annotations.Level; 21 | import org.openjdk.jmh.annotations.Measurement; 22 | import org.openjdk.jmh.annotations.Scope; 23 | import org.openjdk.jmh.annotations.State; 24 | import org.openjdk.jmh.annotations.TearDown; 25 | import org.openjdk.jmh.annotations.Warmup; 26 | 27 | import org.springframework.context.annotation.Bean; 28 | import org.springframework.core.type.AnnotationMetadata; 29 | 30 | @Measurement(iterations = 5) 31 | @Warmup(iterations = 3) 32 | @Fork(value = 1, warmups = 0) 33 | public class AnnotatedMethodBenchmark { 34 | 35 | @Benchmark 36 | public void count10(SimpleState state) throws Exception { 37 | state.setTarget(Bogus1.class); 38 | state.run(); 39 | } 40 | 41 | @Benchmark 42 | public void count20(SimpleState state) throws Exception { 43 | state.setTarget(Bogus2.class); 44 | state.run(); 45 | } 46 | 47 | @Benchmark 48 | public void count30(SimpleState state) throws Exception { 49 | state.setTarget(Bogus3.class); 50 | state.run(); 51 | } 52 | 53 | @State(Scope.Thread) 54 | public static class SimpleState extends AnnotatedMethodState { 55 | } 56 | 57 | public static void main(String[] args) { 58 | SimpleState state = new SimpleState(); 59 | state.setTarget(Bogus3.class); 60 | state.run(); 61 | } 62 | 63 | public static abstract class AnnotatedMethodState { 64 | 65 | private Class target = Bogus1.class; 66 | 67 | public void setTarget(Class target) { 68 | this.target = target; 69 | } 70 | 71 | @TearDown(Level.Invocation) 72 | public void clear() { 73 | } 74 | 75 | public void run() { 76 | if (AnnotationMetadata.introspect(target).hasAnnotatedMethods(Bean.class.getName())) { 77 | throw new IllegalStateException(); 78 | } 79 | } 80 | 81 | } 82 | 83 | static class Bogus1 { 84 | public void method0() {} 85 | public void method1() {} 86 | public void method2() {} 87 | public void method3() {} 88 | public void method4() {} 89 | public void method5() {} 90 | public void method6() {} 91 | public void method7() {} 92 | public void method8() {} 93 | public void method9() {} 94 | } 95 | 96 | static class Bogus2 { 97 | public void method0() {} 98 | public void method1() {} 99 | public void method2() {} 100 | public void method3() {} 101 | public void method4() {} 102 | public void method5() {} 103 | public void method6() {} 104 | public void method7() {} 105 | public void method8() {} 106 | public void method9() {} 107 | public void method10() {} 108 | public void method11() {} 109 | public void method12() {} 110 | public void method13() {} 111 | public void method14() {} 112 | public void method15() {} 113 | public void method16() {} 114 | public void method17() {} 115 | public void method18() {} 116 | public void method19() {} 117 | } 118 | 119 | static class Bogus3 { 120 | public void method0() {} 121 | public void method1() {} 122 | public void method2() {} 123 | public void method3() {} 124 | public void method4() {} 125 | public void method5() {} 126 | public void method6() {} 127 | public void method7() {} 128 | public void method8() {} 129 | public void method9() {} 130 | public void method10() {} 131 | public void method11() {} 132 | public void method12() {} 133 | public void method13() {} 134 | public void method14() {} 135 | public void method15() {} 136 | public void method16() {} 137 | public void method17() {} 138 | public void method18() {} 139 | public void method19() {} 140 | public void method30() {} 141 | public void method31() {} 142 | public void method32() {} 143 | public void method33() {} 144 | public void method34() {} 145 | public void method35() {} 146 | public void method36() {} 147 | public void method37() {} 148 | public void method38() {} 149 | public void method39() {} 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/BeanCreationAllocations.java: -------------------------------------------------------------------------------- 1 | package com.example.bench; 2 | 3 | import java.io.IOException; 4 | 5 | public class BeanCreationAllocations { 6 | 7 | public static void main(String[] args) throws IOException { 8 | while (true) { 9 | long start = System.currentTimeMillis(); 10 | BeanCreationBenchmark.ProcessorState state = new BeanCreationBenchmark.ProcessorState(); 11 | for (int i = 0; i < 1000; i++) { 12 | state.start(); 13 | state.run(); 14 | state.clear(); 15 | } 16 | long end = System.currentTimeMillis(); 17 | System.out.println("Duration: " + (end - start)); 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/BeanCreationBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import org.aspectj.lang.Aspects; 19 | import org.openjdk.jmh.annotations.Benchmark; 20 | import org.openjdk.jmh.annotations.Fork; 21 | import org.openjdk.jmh.annotations.Level; 22 | import org.openjdk.jmh.annotations.Measurement; 23 | import org.openjdk.jmh.annotations.Scope; 24 | import org.openjdk.jmh.annotations.Setup; 25 | import org.openjdk.jmh.annotations.State; 26 | import org.openjdk.jmh.annotations.TearDown; 27 | import org.openjdk.jmh.annotations.Warmup; 28 | 29 | import org.springframework.aop.framework.ProxyFactory; 30 | import org.springframework.beans.BeanUtils; 31 | import org.springframework.beans.factory.annotation.Autowired; 32 | import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; 33 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 34 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 35 | import org.springframework.context.ApplicationContextInitializer; 36 | import org.springframework.context.annotation.Configuration; 37 | import org.springframework.context.annotation.ConfigurationClassPostProcessor; 38 | import org.springframework.context.annotation.PublicConfigurationClassEnhancer; 39 | import org.springframework.context.support.GenericApplicationContext; 40 | import org.springframework.core.env.StandardEnvironment; 41 | 42 | @Measurement(iterations = 5) 43 | @Warmup(iterations = 3) 44 | @Fork(value = 1, warmups = 0) 45 | public class BeanCreationBenchmark { 46 | 47 | @Benchmark 48 | public void simple(SimpleState state) throws Exception { 49 | state.run(); 50 | } 51 | 52 | @Benchmark 53 | public void reflect(ReflectState state) throws Exception { 54 | state.run(); 55 | } 56 | 57 | @Benchmark 58 | public void proxy(ProxyState state) throws Exception { 59 | state.run(); 60 | } 61 | 62 | @Benchmark 63 | public void cglib(CglibState state) throws Exception { 64 | state.run(); 65 | } 66 | 67 | @Benchmark 68 | public void enhnc(EnhancerState state) throws Exception { 69 | state.run(); 70 | } 71 | 72 | @Benchmark 73 | public void proce(ProcessorState state) throws Exception { 74 | state.run(); 75 | } 76 | 77 | @Benchmark 78 | public void unpro(UnprocessorState state) throws Exception { 79 | state.run(); 80 | } 81 | 82 | @Benchmark 83 | public void funcs(FuncState state) throws Exception { 84 | state.run(); 85 | } 86 | 87 | @Benchmark 88 | public void bare(BareState state) throws Exception { 89 | state.run(); 90 | } 91 | 92 | public static void main(String[] args) { 93 | // FuncState state = new FuncState(); 94 | // UnprocessorState state = new UnprocessorState(); 95 | ProcessorState state = new ProcessorState(); 96 | state.start(); 97 | //SimpleState state = new SimpleState(); 98 | state.run(); 99 | Aspects.aspectOf(CallCounter.class).log(); 100 | } 101 | 102 | @State(Scope.Thread) 103 | public static class BareState { 104 | 105 | public void run() { 106 | MyConfiguration config = new MyConfiguration(); 107 | MyBean bean = config.bean(config.foo()); 108 | assert bean.getFoo() != null; 109 | } 110 | } 111 | 112 | @State(Scope.Thread) 113 | public static class ReflectState { 114 | public void run() { 115 | MyBean bean; 116 | try { 117 | Foo foo = Foo.class.getConstructor(String.class).newInstance("bar"); 118 | bean = MyBean.class.getConstructor(Foo.class).newInstance(foo); 119 | assert bean.getFoo() != null; 120 | } 121 | catch (Exception e) { 122 | } 123 | } 124 | } 125 | 126 | @State(Scope.Thread) 127 | public static class ProxyState { 128 | private Foo foo = new Foo("bar"); 129 | 130 | public void run() { 131 | ProxyFactory factory = new ProxyFactory(new MyBean()); 132 | factory.setProxyTargetClass(false); 133 | Bean bean; 134 | try { 135 | bean = (Bean) factory.getProxy(); 136 | bean.setFoo(foo); 137 | assert bean.getFoo() != null; 138 | } 139 | catch (Exception e) { 140 | } 141 | } 142 | } 143 | 144 | @State(Scope.Thread) 145 | public static class CglibState { 146 | private Foo foo = new Foo("bar"); 147 | 148 | public void run() { 149 | ProxyFactory factory = new ProxyFactory(new MyBean()); 150 | factory.setProxyTargetClass(true); 151 | MyBean bean; 152 | try { 153 | bean = (MyBean) factory.getProxy(); 154 | bean.setFoo(foo); 155 | assert bean.getFoo() != null; 156 | } 157 | catch (Exception e) { 158 | } 159 | } 160 | } 161 | 162 | @State(Scope.Thread) 163 | public static class EnhancerState { 164 | 165 | public void run() { 166 | Class enhanced = new PublicConfigurationClassEnhancer().enhance( 167 | MyConfiguration.class, MyConfiguration.class.getClassLoader()); 168 | MyConfiguration config = (MyConfiguration) BeanUtils 169 | .instantiateClass(enhanced); 170 | assert config.foo() != null; 171 | assert config.bean(config.foo()).getFoo() != null; 172 | } 173 | } 174 | 175 | @State(Scope.Thread) 176 | public static class ProcessorState { 177 | 178 | private GenericApplicationContext registry; 179 | 180 | @Setup(Level.Invocation) 181 | public void start() { 182 | ConfigurationClassPostProcessor processor = new ConfigurationClassPostProcessor(); 183 | processor.setBeanClassLoader(MyConfiguration.class.getClassLoader()); 184 | processor.setEnvironment(new StandardEnvironment()); 185 | registry = new GenericApplicationContext(); 186 | registry.setClassLoader(MyConfiguration.class.getClassLoader()); 187 | registry.registerBean(AutowiredAnnotationBeanPostProcessor.class); 188 | registry.registerBean(MyConfiguration.class); 189 | registry.addBeanFactoryPostProcessor(processor); 190 | } 191 | 192 | @TearDown(Level.Invocation) 193 | public void clear() { 194 | if (registry != null) { 195 | registry.close(); 196 | } 197 | } 198 | 199 | public void run() { 200 | registry.refresh(); 201 | MyConfiguration config = registry.getBean(MyConfiguration.class); 202 | Foo foo = config.foo(); 203 | assert foo != null; 204 | assert foo == config.foo(); 205 | assert config.bean(foo).getFoo() != null; 206 | } 207 | } 208 | 209 | @State(Scope.Thread) 210 | public static class UnprocessorState { 211 | 212 | private GenericApplicationContext registry; 213 | 214 | public UnprocessorState() { 215 | } 216 | 217 | @Setup(Level.Invocation) 218 | public void start() { 219 | registry = new GenericApplicationContext(); 220 | registry.registerBean(MyConfiguration.class); 221 | } 222 | 223 | @TearDown(Level.Invocation) 224 | public void clear() { 225 | if (registry != null) { 226 | registry.close(); 227 | } 228 | } 229 | 230 | public void run() { 231 | registry.refresh(); 232 | MyConfiguration config = registry.getBean(MyConfiguration.class); 233 | Foo foo = config.foo(); 234 | assert foo != null; 235 | assert foo != config.foo(); 236 | assert config.bean(foo).getFoo() != null; 237 | } 238 | } 239 | 240 | @State(Scope.Thread) 241 | public static class FuncState { 242 | 243 | private GenericApplicationContext registry; 244 | 245 | public FuncState() { 246 | } 247 | 248 | @Setup(Level.Invocation) 249 | public void start() { 250 | registry = new GenericApplicationContext(); 251 | new FuncConfiguration().initialize(registry); 252 | } 253 | 254 | @TearDown(Level.Invocation) 255 | public void clear() { 256 | if (registry != null) { 257 | registry.close(); 258 | } 259 | } 260 | 261 | public void run() { 262 | registry.refresh(); 263 | Foo foo = registry.getBean(Foo.class); 264 | MyBean bean = registry.getBean(MyBean.class); 265 | assert foo != null; 266 | assert foo == bean.getFoo(); 267 | } 268 | } 269 | 270 | @State(Scope.Thread) 271 | public static class SimpleState { 272 | 273 | @TearDown(Level.Invocation) 274 | public void clear() { 275 | } 276 | 277 | public void run() { 278 | DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 279 | factory.registerBeanDefinition("foo", 280 | BeanDefinitionBuilder.genericBeanDefinition(Foo.class) 281 | .addConstructorArgReference("value").getBeanDefinition()); 282 | factory.registerSingleton("value", "bar"); 283 | MyBean bean = factory.createBean(MyBean.class); 284 | assert bean.getFoo() != null; 285 | } 286 | 287 | } 288 | 289 | interface Bean { 290 | void setFoo(Foo foo); 291 | 292 | Foo getFoo(); 293 | } 294 | 295 | static class MyBean implements Bean { 296 | @Autowired 297 | private Foo foo; 298 | 299 | public MyBean() { 300 | } 301 | 302 | public MyBean(Foo foo) { 303 | this.foo = foo; 304 | } 305 | 306 | public void setFoo(Foo foo) { 307 | this.foo = foo; 308 | } 309 | 310 | public Foo getFoo() { 311 | return this.foo; 312 | } 313 | } 314 | 315 | @Configuration 316 | static class MyConfiguration { 317 | @org.springframework.context.annotation.Bean 318 | public MyBean bean(Foo foo) { 319 | return new MyBean(foo); 320 | } 321 | 322 | @org.springframework.context.annotation.Bean 323 | public Foo foo() { 324 | return new Foo(value()); 325 | } 326 | 327 | @org.springframework.context.annotation.Bean 328 | public String value() { 329 | return "bar"; 330 | } 331 | } 332 | 333 | static class FuncConfiguration 334 | implements ApplicationContextInitializer { 335 | @Override 336 | public void initialize(GenericApplicationContext registry) { 337 | MyConfiguration config = new MyConfiguration(); 338 | registry.registerBean(String.class, () -> config.value()); 339 | registry.registerBean(Foo.class, () -> config.foo()); 340 | registry.registerBean(MyBean.class, 341 | () -> config.bean(registry.getBean(Foo.class))); 342 | } 343 | } 344 | } 345 | 346 | class Foo { 347 | 348 | private String value; 349 | 350 | public Foo() { 351 | } 352 | 353 | public Foo(String value) { 354 | this.value = value; 355 | } 356 | 357 | public String getValue() { 358 | return this.value; 359 | } 360 | 361 | public void setValue(String value) { 362 | this.value = value; 363 | } 364 | 365 | @Override 366 | public String toString() { 367 | return "Foo [value=" + this.value + "]"; 368 | } 369 | 370 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/BinderAllocations.java: -------------------------------------------------------------------------------- 1 | package com.example.bench; 2 | 3 | import java.io.IOException; 4 | 5 | public class BinderAllocations { 6 | 7 | public static void main(String[] args) throws IOException { 8 | while (true) { 9 | long start = System.currentTimeMillis(); 10 | for (int i = 0; i < 1000; i++) { 11 | new BinderBenchmark.WrappedState().run(); 12 | } 13 | long end = System.currentTimeMillis(); 14 | System.out.println("Duration: " + (end - start)); 15 | } 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/BinderBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.openjdk.jmh.annotations.Benchmark; 23 | import org.openjdk.jmh.annotations.Fork; 24 | import org.openjdk.jmh.annotations.Measurement; 25 | import org.openjdk.jmh.annotations.Scope; 26 | import org.openjdk.jmh.annotations.State; 27 | import org.openjdk.jmh.annotations.Warmup; 28 | 29 | import org.springframework.boot.context.properties.bind.Binder; 30 | import org.springframework.boot.context.properties.source.ConfigurationPropertySource; 31 | import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; 32 | import org.springframework.core.env.ConfigurableEnvironment; 33 | import org.springframework.core.env.MapPropertySource; 34 | import org.springframework.core.env.MutablePropertySources; 35 | import org.springframework.core.env.StandardEnvironment; 36 | 37 | @Measurement(iterations = 5) 38 | @Warmup(iterations = 3) 39 | @Fork(value = 1, warmups = 0) 40 | public class BinderBenchmark { 41 | 42 | @Benchmark 43 | public void direct(DirectState state) throws Exception { 44 | state.run(); 45 | } 46 | 47 | @Benchmark 48 | public void map(MapState state) throws Exception { 49 | state.run(); 50 | } 51 | 52 | @Benchmark 53 | public void wrapped(WrappedState state) throws Exception { 54 | state.run(); 55 | } 56 | 57 | @Benchmark 58 | public void binder(BinderState state) throws Exception { 59 | state.run(); 60 | } 61 | 62 | @State(Scope.Thread) 63 | public static class DirectState extends EnvironmentState { 64 | private String[] value; 65 | 66 | public void run() { 67 | value = environment.getProperty("spring.profiles.active", String[].class); 68 | assert value.length == 2; 69 | } 70 | } 71 | 72 | @State(Scope.Thread) 73 | public static class MapState extends EnvironmentState { 74 | private String[] value; 75 | private MapConfigurationPropertySource source; 76 | 77 | protected void init() { 78 | super.init(); 79 | Map map = new HashMap<>(); 80 | map.put("spring.profiles.active", "one,two"); 81 | source = new MapConfigurationPropertySource(map); 82 | } 83 | 84 | public void run() { 85 | Binder binder = new Binder(source); 86 | value = binder.bind("spring.profiles.active", String[].class).get(); 87 | assert value.length == 2; 88 | } 89 | } 90 | 91 | @State(Scope.Thread) 92 | public static class WrappedState extends EnvironmentState { 93 | private String[] value; 94 | private ConfigurationPropertySource source; 95 | 96 | protected void init() { 97 | super.init(); 98 | source = new WrappedConfigurationPropertySource(environment); 99 | } 100 | 101 | public void run() { 102 | Binder binder = new Binder(source); 103 | value = binder.bind("spring.profiles.active", String[].class).get(); 104 | assert value.length == 2; 105 | } 106 | } 107 | 108 | @State(Scope.Thread) 109 | public static class BinderState extends EnvironmentState { 110 | private String[] value; 111 | 112 | public void run() { 113 | Binder binder = Binder.get(this.environment); 114 | value = binder.bind("spring.profiles.active", String[].class).get(); 115 | assert value.length == 2; 116 | } 117 | } 118 | 119 | public static void main(String[] args) { 120 | new WrappedState().run(); 121 | } 122 | 123 | public static abstract class EnvironmentState { 124 | 125 | ConfigurableEnvironment environment; 126 | 127 | public EnvironmentState() { 128 | init(); 129 | } 130 | 131 | protected void init() { 132 | environment = new StandardEnvironment(); 133 | MutablePropertySources propertySources = environment.getPropertySources(); 134 | propertySources.addLast(new MapPropertySource("profiles", 135 | Collections.singletonMap("spring.profiles.active", "one,two"))); 136 | }; 137 | 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/CallCounter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.bench; 18 | 19 | import java.util.LinkedHashMap; 20 | import java.util.Map; 21 | 22 | import org.aspectj.lang.ProceedingJoinPoint; 23 | import org.aspectj.lang.annotation.Around; 24 | import org.aspectj.lang.annotation.Aspect; 25 | 26 | /** 27 | * @author Dave Syer 28 | * 29 | */ 30 | @Aspect 31 | public class CallCounter { 32 | 33 | private Map counts = new LinkedHashMap<>(); 34 | 35 | @Around("execution(* org.springframework.asm.Type.getInternalName())") 36 | public Object count(ProceedingJoinPoint jp) throws Throwable { 37 | String result = (String) jp.proceed(); 38 | counts.compute(result, (k, v) -> v!=null ? v+1 : 1); 39 | return result; 40 | } 41 | 42 | public void log() { 43 | counts.entrySet() 44 | .forEach(e -> System.err.println(e.getKey() + ": " + e.getValue())); 45 | System.err.println( 46 | "Count: " + counts.values().stream().mapToInt(Integer::intValue).sum()); 47 | } 48 | 49 | static CallCounter instance = new CallCounter(); 50 | 51 | public static CallCounter aspectOf() { 52 | return instance; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/ConfigFileAllocations.java: -------------------------------------------------------------------------------- 1 | package com.example.bench; 2 | 3 | import java.io.IOException; 4 | 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.context.config.ConfigFileApplicationListener; 7 | import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 8 | import org.springframework.boot.context.event.ApplicationPreparedEvent; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.support.GenericApplicationContext; 12 | import org.springframework.core.env.ConfigurableEnvironment; 13 | import org.springframework.core.env.StandardEnvironment; 14 | 15 | public class ConfigFileAllocations { 16 | 17 | public static void main(String[] args) throws IOException { 18 | ConfigFileApplicationListener listener = new ConfigFileApplicationListener(); 19 | listener.setSearchLocations("file:./src/main/resources/application.properties"); 20 | SpringApplication application = new SpringApplication(TestConfiguration.class); 21 | ConfigurableEnvironment environment = new StandardEnvironment(); 22 | while (true) { 23 | long start = System.currentTimeMillis(); 24 | for (int i = 0; i < 100; i++) { 25 | ApplicationEnvironmentPreparedEvent envPrep = new ApplicationEnvironmentPreparedEvent(application, new String[0], environment ); 26 | listener.onApplicationEvent(envPrep); 27 | GenericApplicationContext context = new GenericApplicationContext(); 28 | context.setEnvironment(environment); 29 | ApplicationPreparedEvent appPrep = new ApplicationPreparedEvent(application, new String[0], context); 30 | listener.onApplicationEvent(appPrep); 31 | } 32 | long end = System.currentTimeMillis(); 33 | System.out.println("Duration: " + (end - start)); 34 | } 35 | } 36 | 37 | @Configuration 38 | static class TestConfiguration { 39 | 40 | @Bean 41 | public MyBean myBean() { 42 | return new MyBean(); 43 | } 44 | 45 | } 46 | 47 | static class MyBean { 48 | 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/ConfigFileBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import org.openjdk.jmh.annotations.Benchmark; 19 | import org.openjdk.jmh.annotations.Fork; 20 | import org.openjdk.jmh.annotations.Level; 21 | import org.openjdk.jmh.annotations.Measurement; 22 | import org.openjdk.jmh.annotations.Scope; 23 | import org.openjdk.jmh.annotations.State; 24 | import org.openjdk.jmh.annotations.TearDown; 25 | import org.openjdk.jmh.annotations.Warmup; 26 | 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.boot.context.config.ConfigFileApplicationListener; 30 | import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 31 | import org.springframework.boot.context.event.ApplicationPreparedEvent; 32 | import org.springframework.context.support.GenericApplicationContext; 33 | import org.springframework.core.env.ConfigurableEnvironment; 34 | import org.springframework.core.env.StandardEnvironment; 35 | 36 | @Measurement(iterations = 5) 37 | @Warmup(iterations = 3) 38 | @Fork(value = 1, warmups = 0) 39 | public class ConfigFileBenchmark { 40 | 41 | @Benchmark 42 | public void simple(SimpleState state) throws Exception { 43 | state.run(); 44 | } 45 | 46 | @Benchmark 47 | public void directory(DirectoryState state) throws Exception { 48 | state.run(); 49 | } 50 | 51 | @Benchmark 52 | public void file(FileState state) throws Exception { 53 | state.run(); 54 | } 55 | 56 | @State(Scope.Thread) 57 | public static class FileState extends ConfigFileState { 58 | @Override 59 | protected ConfigFileApplicationListener create() { 60 | ConfigFileApplicationListener listener = new ConfigFileApplicationListener(); 61 | listener.setSearchLocations("file:./src/main/resources/application.properties"); 62 | return listener; 63 | } 64 | } 65 | 66 | @State(Scope.Thread) 67 | public static class DirectoryState extends ConfigFileState { 68 | @Override 69 | protected ConfigFileApplicationListener create() { 70 | ConfigFileApplicationListener listener = new ConfigFileApplicationListener(); 71 | listener.setSearchLocations("file:./src/main/resources/"); 72 | return listener; 73 | } 74 | } 75 | 76 | @State(Scope.Thread) 77 | public static class SimpleState extends ConfigFileState { 78 | } 79 | 80 | public static void main(String[] args) { 81 | new SimpleState().run(); 82 | } 83 | 84 | public static abstract class ConfigFileState { 85 | 86 | private ConfigFileApplicationListener listener; 87 | 88 | public ConfigFileState() { 89 | this.listener = create(); 90 | } 91 | 92 | protected ConfigFileApplicationListener create() { 93 | return new ConfigFileApplicationListener(); 94 | } 95 | 96 | @TearDown(Level.Invocation) 97 | public void clear() { 98 | } 99 | 100 | public void run() { 101 | SpringApplication application = new SpringApplication(MyApplication.class); 102 | ConfigurableEnvironment environment = new StandardEnvironment(); 103 | ApplicationEnvironmentPreparedEvent envPrep = new ApplicationEnvironmentPreparedEvent(application, new String[0], environment ); 104 | listener.onApplicationEvent(envPrep); 105 | GenericApplicationContext context = new GenericApplicationContext(); 106 | context.setEnvironment(environment); 107 | ApplicationPreparedEvent appPrep = new ApplicationPreparedEvent(application, new String[0], context); 108 | listener.onApplicationEvent(appPrep); 109 | } 110 | } 111 | 112 | @SpringBootApplication 113 | static class MyApplication {} 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/IndexAnnotationBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import java.io.IOException; 19 | import java.net.URL; 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.Enumeration; 23 | import java.util.List; 24 | import java.util.Properties; 25 | import java.util.ServiceLoader; 26 | 27 | import com.example.config.ComponentIndex; 28 | import com.example.config.ComponentIndex.Entry; 29 | import com.example.config.ComponentIndexer; 30 | import org.openjdk.jmh.annotations.Benchmark; 31 | import org.openjdk.jmh.annotations.Fork; 32 | import org.openjdk.jmh.annotations.Measurement; 33 | import org.openjdk.jmh.annotations.Scope; 34 | import org.openjdk.jmh.annotations.State; 35 | import org.openjdk.jmh.annotations.Warmup; 36 | 37 | import org.springframework.core.io.UrlResource; 38 | import org.springframework.core.io.support.PropertiesLoaderUtils; 39 | import org.springframework.core.type.AnnotationMetadata; 40 | import org.springframework.util.LinkedMultiValueMap; 41 | import org.springframework.util.MultiValueMap; 42 | import org.springframework.util.StringUtils; 43 | 44 | @Measurement(iterations = 5) 45 | @Warmup(iterations = 3) 46 | @Fork(value = 1, warmups = 0) 47 | public class IndexAnnotationBenchmark { 48 | 49 | @Benchmark 50 | public void anno(AnnoState state) throws Exception { 51 | state.setCount(8); 52 | state.run(); 53 | } 54 | 55 | @Benchmark 56 | public void svce(ServiceState state) throws Exception { 57 | state.setCount(8); 58 | state.run(); 59 | } 60 | 61 | @Benchmark 62 | public void prop(PropertiesState state) throws Exception { 63 | state.run(); 64 | } 65 | 66 | public static void main(String[] args) { 67 | ServiceState state = new ServiceState(); 68 | state.run(); 69 | AnnoState anno = new AnnoState(); 70 | anno.run(); 71 | PropertiesState prop = new PropertiesState(); 72 | prop.run(); 73 | } 74 | 75 | public static void create(MultiValueMap map, Class target) { 76 | AnnotationMetadata.introspect(target) 77 | .getAnnotationAttributes(ComponentIndex.class.getName(), true) 78 | .forEach((name, object) -> { 79 | Entry[] entries = (Entry[]) object; 80 | for (Entry entry : entries) { 81 | for (String value : entry.value()) { 82 | map.add(entry.key(), value); 83 | } 84 | } 85 | }); 86 | } 87 | 88 | @State(Scope.Thread) 89 | public static class AnnoState { 90 | private int count = 1; 91 | 92 | public void run() { 93 | MultiValueMap map = new LinkedMultiValueMap<>(); 94 | for (int i = 0; i < count; i++) { 95 | Class target = Bogus.class; 96 | create(map, target); 97 | } 98 | if (map.isEmpty()) { 99 | throw new IllegalStateException(); 100 | } 101 | } 102 | 103 | public void setCount(int count) { 104 | this.count = count; 105 | } 106 | 107 | } 108 | 109 | @State(Scope.Thread) 110 | public static class ServiceState { 111 | 112 | private int count = 1; 113 | 114 | public void setCount(int count) { 115 | this.count = count; 116 | } 117 | 118 | public void run() { 119 | ServiceLoader loaded = ServiceLoader 120 | .load(ComponentIndexer.class); 121 | MultiValueMap map = new LinkedMultiValueMap<>(); 122 | for (ComponentIndexer indexer : loaded) { 123 | for (Class target : indexer.indexes()) { 124 | for (int i = 0; i < count; i++) { 125 | create(map, target); 126 | } 127 | } 128 | } 129 | if (map.isEmpty()) { 130 | throw new IllegalStateException(); 131 | } 132 | } 133 | 134 | } 135 | 136 | @State(Scope.Thread) 137 | public static class PropertiesState { 138 | 139 | private String target = "META-INF/foo.components"; 140 | 141 | public void setTarget(String target) { 142 | this.target = target; 143 | } 144 | 145 | public void run() { 146 | MultiValueMap map = new LinkedMultiValueMap<>(); 147 | ClassLoader classLoader = getClass().getClassLoader(); 148 | try { 149 | Enumeration urls = classLoader.getResources(target); 150 | if (!urls.hasMoreElements()) { 151 | return; 152 | } 153 | List result = new ArrayList<>(); 154 | while (urls.hasMoreElements()) { 155 | URL url = urls.nextElement(); 156 | Properties properties = PropertiesLoaderUtils 157 | .loadProperties(new UrlResource(url)); 158 | result.add(properties); 159 | } 160 | result.forEach(p -> p.forEach((k, v) -> { 161 | String key = (String) k; 162 | String[] values = StringUtils 163 | .commaDelimitedListToStringArray((String) v); 164 | for (String value : values) { 165 | map.add(key, value); 166 | } 167 | })); 168 | } 169 | catch (IOException ex) { 170 | throw new IllegalStateException( 171 | "Unable to load indexes from location [" + target + "]", ex); 172 | } 173 | if (map.isEmpty()) { 174 | throw new IllegalStateException(); 175 | } 176 | } 177 | 178 | } 179 | 180 | // @formatter:off 181 | @ComponentIndex({ 182 | @Entry(key="foo0", value= {"bar"}), 183 | @Entry(key="foo1", value= {"bar0", "bar1"}), 184 | @Entry(key="foo2", value= {"bar0", "bar1", "bar2"}), 185 | @Entry(key="foo3", value= {"bar0", "bar1", "bar2", "bar3"}), 186 | @Entry(key="foo4", value= {"bar0"}), 187 | @Entry(key="foo5", value= {"bar0", "bar1", "bar2"}), 188 | @Entry(key="foo6", value= {"bar0"}), 189 | @Entry(key="foo7", value= {"bar0", "bar1", "bar2"}), 190 | @Entry(key="foo8", value= {"bar0"}), 191 | @Entry(key="foo9", value= {"bar0", "bar1", "bar2"}), 192 | @Entry(key="foo10", value= {"bar"}), 193 | @Entry(key="foo11", value= {"bar0", "bar1"}), 194 | @Entry(key="foo12", value= {"bar0", "bar1", "bar2"}), 195 | @Entry(key="foo13", value= {"bar0", "bar1", "bar2", "bar3"}), 196 | @Entry(key="foo14", value= {"bar0"}), 197 | @Entry(key="foo15", value= {"bar0", "bar1", "bar2"}), 198 | @Entry(key="foo16", value= {"bar0"}), 199 | @Entry(key="foo17", value= {"bar0", "bar1", "bar2"}), 200 | @Entry(key="foo18", value= {"bar0"}), 201 | @Entry(key="foo19", value= {"bar0", "bar1", "bar2"}), 202 | @Entry(key="foo20", value= {"bar"}), 203 | @Entry(key="foo21", value= {"bar0", "bar1"}), 204 | @Entry(key="foo22", value= {"bar0", "bar1", "bar2"}), 205 | @Entry(key="foo23", value= {"bar0", "bar1", "bar2", "bar3"}), 206 | @Entry(key="foo24", value= {"bar0"}), 207 | @Entry(key="foo25", value= {"bar0", "bar1", "bar2"}), 208 | @Entry(key="foo26", value= {"bar0"}), 209 | @Entry(key="foo27", value= {"bar0", "bar1", "bar2"}), 210 | @Entry(key="foo28", value= {"bar0"}), 211 | @Entry(key="foo29", value= {"bar0", "bar1", "bar2"}) 212 | }) 213 | // @formatter:on 214 | static class Bogus { 215 | } 216 | 217 | public static class Indexer implements ComponentIndexer { 218 | 219 | @Override 220 | public List> indexes() { 221 | return Collections.singletonList(Bogus.class); 222 | } 223 | 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/LauncherState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.net.URL; 21 | import java.net.URLClassLoader; 22 | 23 | import com.example.demo.DemoApplication; 24 | 25 | import org.openjdk.jmh.annotations.Level; 26 | import org.openjdk.jmh.annotations.Scope; 27 | import org.openjdk.jmh.annotations.Setup; 28 | import org.openjdk.jmh.annotations.State; 29 | import org.openjdk.jmh.annotations.TearDown; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | import org.springframework.util.ClassUtils; 34 | 35 | @State(Scope.Benchmark) 36 | public class LauncherState implements Runnable, Closeable { 37 | 38 | private static final Logger log = LoggerFactory.getLogger(LauncherState.class); 39 | 40 | private Closeable instance; 41 | 42 | private URLClassLoader loader; 43 | 44 | private ClassLoader orig; 45 | 46 | private Thread runThread; 47 | 48 | protected Throwable error; 49 | 50 | private long timeout = 120000; 51 | 52 | private Class mainClass = DemoApplication.class; 53 | 54 | public void setMainClass(Class mainClass) { 55 | this.mainClass = mainClass; 56 | } 57 | 58 | @Setup(Level.Trial) 59 | public void start() throws Exception { 60 | System.setProperty("server.port", "0"); 61 | System.setProperty("spring.main.lazy-initialization", "true"); 62 | System.setProperty("spring.config.location", 63 | "file:./src/main/resources/application.properties"); 64 | System.setProperty("spring.main.logStartupInfo", "false"); 65 | } 66 | 67 | public void shared() throws Exception { 68 | instance = (Closeable) mainClass.getConstructor().newInstance(); 69 | run(); 70 | } 71 | 72 | public void isolated() throws Exception { 73 | Class mainClass = loadMainClass(this.mainClass); 74 | instance = (Closeable) mainClass.getConstructor().newInstance(); 75 | this.runThread = new Thread(() -> { 76 | try { 77 | run(); 78 | } 79 | catch (Throwable ex) { 80 | error = ex; 81 | } 82 | }); 83 | this.runThread.start(); 84 | try { 85 | this.runThread.join(timeout); 86 | } 87 | catch (InterruptedException e) { 88 | Thread.currentThread().interrupt(); 89 | } 90 | } 91 | 92 | @Override 93 | @TearDown(Level.Invocation) 94 | public void close() throws IOException { 95 | // CachedIntrospectionResults.clearClassLoader(getClass().getClassLoader()); 96 | if (instance != null) { 97 | instance.close(); 98 | } 99 | if (runThread != null) { 100 | runThread.setContextClassLoader(null); 101 | runThread = null; 102 | } 103 | if (orig != null) { 104 | ClassUtils.overrideThreadContextClassLoader(orig); 105 | } 106 | if (loader != null) { 107 | try { 108 | loader.close(); 109 | loader = null; 110 | } 111 | catch (Exception e) { 112 | log.error("Failed to close loader", e); 113 | } 114 | } 115 | System.gc(); 116 | } 117 | 118 | @Override 119 | public void run() { 120 | ((Runnable) instance).run(); 121 | } 122 | 123 | private Class loadMainClass(Class type) throws ClassNotFoundException { 124 | URL[] urls = ((URLClassLoader) getClass().getClassLoader()).getURLs(); 125 | loader = new URLClassLoader(urls, getClass().getClassLoader().getParent()); 126 | orig = ClassUtils.overrideThreadContextClassLoader(loader); 127 | return loader.loadClass(type.getName()); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/LoggingSystemAllocations.java: -------------------------------------------------------------------------------- 1 | package com.example.bench; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 8 | import org.springframework.boot.context.event.ApplicationFailedEvent; 9 | import org.springframework.boot.context.event.ApplicationStartingEvent; 10 | import org.springframework.boot.context.logging.LoggingApplicationListener; 11 | import org.springframework.core.env.ConfigurableEnvironment; 12 | import org.springframework.core.env.StandardEnvironment; 13 | 14 | public class LoggingSystemAllocations { 15 | 16 | public static void main(String[] args) throws Exception { 17 | while (true) { 18 | long start = System.currentTimeMillis(); 19 | LauncherState state = new LauncherState(); 20 | state.setMainClass(TestApplication.class); 21 | state.shared(); 22 | for (int i = 0; i < 1000; i++) { 23 | state.run(); 24 | } 25 | state.close(); 26 | long end = System.currentTimeMillis(); 27 | System.out.println("Duration: " + (end - start)); 28 | } 29 | } 30 | 31 | public static class TestApplication implements Runnable, Closeable { 32 | private LoggingApplicationListener listener; 33 | private SpringApplication application; 34 | private static ConfigurableEnvironment environment = new StandardEnvironment(); 35 | @Override 36 | public void close() throws IOException { 37 | listener.onApplicationEvent(new ApplicationFailedEvent(application, 38 | new String[0], null, new RuntimeException())); 39 | } 40 | public void run() { 41 | listener = new LoggingApplicationListener(); 42 | application = new SpringApplication(TestApplication.class); 43 | listener.onApplicationEvent( 44 | new ApplicationStartingEvent(application, new String[0])); 45 | ConfigurableEnvironment environment = TestApplication.environment; 46 | listener.onApplicationEvent(new ApplicationEnvironmentPreparedEvent( 47 | application, new String[0], environment)); 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/MainBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import com.example.auto.AutoApplication; 19 | import com.example.boot.BootApplication; 20 | import com.example.func.BuncApplication; 21 | import com.example.func.CuncApplication; 22 | import com.example.func.FuncApplication; 23 | import com.example.manual.ManualApplication; 24 | 25 | import org.openjdk.jmh.annotations.Benchmark; 26 | import org.openjdk.jmh.annotations.BenchmarkMode; 27 | import org.openjdk.jmh.annotations.Fork; 28 | import org.openjdk.jmh.annotations.Measurement; 29 | import org.openjdk.jmh.annotations.Mode; 30 | import org.openjdk.jmh.annotations.Warmup; 31 | 32 | @Measurement(iterations = 5) 33 | @Warmup(iterations = 1) 34 | @Fork(value = 2, warmups = 0) 35 | @BenchmarkMode(Mode.SingleShotTime) 36 | public class MainBenchmark { 37 | 38 | @Benchmark 39 | public void demo(LauncherState state) throws Exception { 40 | state.isolated(); 41 | } 42 | 43 | @Benchmark 44 | public void boot(LauncherState state) throws Exception { 45 | state.setMainClass(BootApplication.class); 46 | state.isolated(); 47 | } 48 | 49 | @Benchmark 50 | public void manual(LauncherState state) throws Exception { 51 | state.setMainClass(ManualApplication.class); 52 | state.isolated(); 53 | } 54 | 55 | @Benchmark 56 | public void cunc(LauncherState state) throws Exception { 57 | state.setMainClass(CuncApplication.class); 58 | state.isolated(); 59 | } 60 | 61 | @Benchmark 62 | public void bunc(LauncherState state) throws Exception { 63 | state.setMainClass(BuncApplication.class); 64 | state.isolated(); 65 | } 66 | 67 | @Benchmark 68 | public void func(LauncherState state) throws Exception { 69 | state.setMainClass(FuncApplication.class); 70 | state.isolated(); 71 | } 72 | 73 | @Benchmark 74 | public void auto(LauncherState state) throws Exception { 75 | state.setMainClass(AutoApplication.class); 76 | state.isolated(); 77 | } 78 | 79 | @Benchmark 80 | public void shared(LauncherState state) throws Exception { 81 | state.shared(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/MetadataBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.bench; 17 | 18 | import com.example.demo.DemoApplication; 19 | 20 | import org.openjdk.jmh.annotations.Benchmark; 21 | import org.openjdk.jmh.annotations.Fork; 22 | import org.openjdk.jmh.annotations.Level; 23 | import org.openjdk.jmh.annotations.Measurement; 24 | import org.openjdk.jmh.annotations.Scope; 25 | import org.openjdk.jmh.annotations.State; 26 | import org.openjdk.jmh.annotations.TearDown; 27 | import org.openjdk.jmh.annotations.Warmup; 28 | 29 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 30 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 31 | import org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory; 32 | import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; 33 | import org.springframework.context.annotation.ConfigurationClassPostProcessor; 34 | import org.springframework.core.type.classreading.CachingMetadataReaderFactory; 35 | import org.springframework.core.type.classreading.MetadataReaderFactory; 36 | import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; 37 | 38 | @Measurement(iterations = 5) 39 | @Warmup(iterations = 3) 40 | @Fork(value = 1, warmups = 0) 41 | public class MetadataBenchmark { 42 | 43 | @Benchmark 44 | public void caching(CachingState state) throws Exception { 45 | state.run(); 46 | } 47 | 48 | @Benchmark 49 | public void reference(ReferenceState state) throws Exception { 50 | state.run(); 51 | } 52 | 53 | @Benchmark 54 | public void simple(SimpleState state) throws Exception { 55 | state.run(); 56 | } 57 | 58 | @State(Scope.Thread) 59 | public static class CachingState extends MetadataState { 60 | protected MetadataReaderFactory createFactory() { 61 | return new CachingMetadataReaderFactory(); 62 | } 63 | } 64 | 65 | @State(Scope.Thread) 66 | public static class ReferenceState extends MetadataState { 67 | protected MetadataReaderFactory createFactory() { 68 | return new ConcurrentReferenceCachingMetadataReaderFactory(); 69 | } 70 | } 71 | 72 | @State(Scope.Thread) 73 | public static class SimpleState extends MetadataState { 74 | protected MetadataReaderFactory createFactory() { 75 | return new SimpleMetadataReaderFactory(); 76 | } 77 | } 78 | 79 | public static void main(String[] args) { 80 | new SimpleState().run(); 81 | } 82 | 83 | public static abstract class MetadataState { 84 | 85 | MetadataReaderFactory factory; 86 | 87 | public MetadataState() { 88 | this.factory = createFactory(); 89 | } 90 | 91 | protected abstract MetadataReaderFactory createFactory(); 92 | 93 | @TearDown(Level.Invocation) 94 | public void clear() { 95 | if (factory instanceof CachingMetadataReaderFactory) { 96 | ((CachingMetadataReaderFactory) factory).clearCache(); 97 | } 98 | if (factory instanceof ConcurrentReferenceCachingMetadataReaderFactory) { 99 | ((ConcurrentReferenceCachingMetadataReaderFactory) factory).clearCache(); 100 | } 101 | } 102 | 103 | public void run() { 104 | ConfigurationClassPostProcessor processor = new ConfigurationClassPostProcessor(); 105 | processor.setMetadataReaderFactory(factory); 106 | @SuppressWarnings("resource") 107 | MyContext context = new MyContext(); 108 | context.register(DemoApplication.class); 109 | context.postProcessBeanFactory(context.getBeanFactory()); 110 | processor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) context.getBeanFactory()); 111 | processor.postProcessBeanFactory(context.getBeanFactory()); 112 | } 113 | 114 | } 115 | 116 | static class MyContext extends AnnotationConfigReactiveWebServerApplicationContext { 117 | @Override 118 | public void postProcessBeanFactory( 119 | ConfigurableListableBeanFactory beanFactory) { 120 | super.postProcessBeanFactory(beanFactory); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/example/bench/ResolvableTypeAllocations.java: -------------------------------------------------------------------------------- 1 | package com.example.bench; 2 | 3 | import java.io.IOException; 4 | 5 | import org.springframework.context.ApplicationEvent; 6 | import org.springframework.context.ApplicationListener; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.event.ContextRefreshedEvent; 9 | import org.springframework.core.ResolvableType; 10 | 11 | public class ResolvableTypeAllocations { 12 | 13 | public static void main(String[] args) throws IOException { 14 | while (true) { 15 | long start = System.currentTimeMillis(); 16 | for (int i = 0; i < 100000; i++) { 17 | ResolvableType test = ResolvableType.forClass(TestListener.class).as(ApplicationListener.class); 18 | ResolvableType testGeneric = test.hasGenerics() ? test.getGeneric() : null; 19 | ResolvableType un = ResolvableType.forClass(UnListener.class).as(ApplicationListener.class); 20 | ResolvableType unGeneric = un.hasGenerics() ? un.getGeneric() : null; 21 | assert testGeneric != unGeneric; 22 | } 23 | long end = System.currentTimeMillis(); 24 | System.out.println("Duration: " + (end - start)); 25 | } 26 | } 27 | 28 | @Configuration 29 | static class TestListener implements ApplicationListener { 30 | @Override 31 | public void onApplicationEvent(ContextRefreshedEvent event) { 32 | } 33 | } 34 | 35 | @SuppressWarnings("rawtypes") 36 | @Configuration 37 | static class UnListener implements ApplicationListener { 38 | @Override 39 | public void onApplicationEvent(ApplicationEvent event) { 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/SimpleMetadataReaderAllocations.java: -------------------------------------------------------------------------------- 1 | package com.example.bench; 2 | 3 | import java.io.IOException; 4 | 5 | import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.core.io.ClassPathResource; 9 | import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; 10 | import org.springframework.util.ClassUtils; 11 | 12 | public class SimpleMetadataReaderAllocations { 13 | 14 | public static void main(String[] args) throws IOException { 15 | ClassPathResource resource = new ClassPathResource( 16 | ClassUtils.convertClassNameToResourcePath( 17 | WebMvcAutoConfiguration.class.getName()) + ".class"); 18 | SimpleMetadataReaderFactory factory = new SimpleMetadataReaderFactory(); 19 | while (true) { 20 | long start = System.currentTimeMillis(); 21 | for (int i = 0; i < 10000; i++) { 22 | factory.getMetadataReader(resource).getAnnotationMetadata(); 23 | } 24 | long end = System.currentTimeMillis(); 25 | System.out.println("Duration: " + (end - start)); 26 | } 27 | } 28 | 29 | @Configuration 30 | static class TestConfiguration { 31 | 32 | @Bean 33 | public MyBean myBean() { 34 | return new MyBean(); 35 | } 36 | 37 | } 38 | 39 | static class MyBean { 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/main/java/com/example/bench/WrappedConfigurationPropertySource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.bench; 18 | 19 | import org.springframework.boot.context.properties.source.ConfigurationProperty; 20 | import org.springframework.boot.context.properties.source.ConfigurationPropertyName; 21 | import org.springframework.boot.context.properties.source.ConfigurationPropertySource; 22 | import org.springframework.boot.origin.Origin; 23 | import org.springframework.core.env.ConfigurableEnvironment; 24 | 25 | public class WrappedConfigurationPropertySource implements ConfigurationPropertySource { 26 | 27 | private ConfigurableEnvironment environment; 28 | private Origin origin; 29 | 30 | public WrappedConfigurationPropertySource(ConfigurableEnvironment environment) { 31 | this.environment = environment; 32 | this.origin = Origin.from(environment); 33 | } 34 | 35 | @Override 36 | public ConfigurationProperty getConfigurationProperty( 37 | ConfigurationPropertyName name) { 38 | String key = name.toString(); 39 | if (this.environment.containsProperty(key)) { 40 | return new ConfigurationProperty(name, 41 | (Object) this.environment.getProperty(key), this.origin); 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public Object getUnderlyingSource() { 48 | return environment; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/java/com/example/boot/BootApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.boot; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | import reactor.core.publisher.Mono; 7 | 8 | import org.springframework.boot.SpringBootConfiguration; 9 | import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration; 10 | import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; 11 | import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; 12 | import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; 13 | import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; 14 | import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration; 15 | import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; 16 | import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration; 17 | import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration; 18 | import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration; 19 | import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; 20 | import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration; 21 | import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; 22 | import org.springframework.boot.builder.SpringApplicationBuilder; 23 | import org.springframework.context.ApplicationContextInitializer; 24 | import org.springframework.context.ConfigurableApplicationContext; 25 | import org.springframework.context.annotation.Import; 26 | import org.springframework.context.support.GenericApplicationContext; 27 | import org.springframework.web.reactive.function.server.RouterFunction; 28 | 29 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 30 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 31 | import static org.springframework.web.reactive.function.server.ServerResponse.ok; 32 | 33 | public class BootApplication implements Runnable, Closeable, 34 | ApplicationContextInitializer { 35 | 36 | private ConfigurableApplicationContext context; 37 | 38 | public RouterFunction userEndpoints() { 39 | return route(GET("/"), request -> ok().body(Mono.just("Hello"), String.class)); 40 | } 41 | 42 | public static void main(String[] args) throws Exception { 43 | long t0 = System.currentTimeMillis(); 44 | BootApplication bean = new BootApplication(); 45 | bean.run(); 46 | System.err.println( 47 | "Started HttpServer: " + (System.currentTimeMillis() - t0) + "ms"); 48 | if (Boolean.getBoolean("demo.close")) { 49 | bean.close(); 50 | } 51 | } 52 | 53 | @Override 54 | public void close() throws IOException { 55 | if (context != null) { 56 | context.close(); 57 | } 58 | } 59 | 60 | @Override 61 | public void run() { 62 | this.context = create(); 63 | } 64 | 65 | private ConfigurableApplicationContext create() { 66 | ConfigurableApplicationContext context = new SpringApplicationBuilder( 67 | AutoConfiguration.class).initializers(this).run(); 68 | return context; 69 | } 70 | 71 | @Override 72 | public void initialize(GenericApplicationContext applicationContext) { 73 | registerDemoApplication(applicationContext); 74 | } 75 | 76 | private void registerDemoApplication(GenericApplicationContext context) { 77 | context.registerBean(RouterFunction.class, () -> userEndpoints()); 78 | } 79 | 80 | @Import({ // LazyInitBeanFactoryPostProcessor.class, 81 | PropertyPlaceholderAutoConfiguration.class, 82 | ReactiveWebServerFactoryAutoConfiguration.class, 83 | CodecsAutoConfiguration.class, ErrorWebFluxAutoConfiguration.class, 84 | WebFluxAutoConfiguration.class, HttpHandlerAutoConfiguration.class, 85 | ConfigurationPropertiesAutoConfiguration.class, GsonAutoConfiguration.class, 86 | HttpMessageConvertersAutoConfiguration.class, 87 | ProjectInfoAutoConfiguration.class, ReactiveSecurityAutoConfiguration.class, 88 | EmbeddedWebServerFactoryCustomizerAutoConfiguration.class, 89 | WebClientAutoConfiguration.class }) 90 | // @EnableAutoConfiguration 91 | @SpringBootConfiguration 92 | public static class AutoConfiguration { 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/ApplicationBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.config; 17 | 18 | import java.lang.management.ManagementFactory; 19 | import java.time.Duration; 20 | import java.util.function.Consumer; 21 | 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 26 | import org.springframework.context.ConfigurableApplicationContext; 27 | import org.springframework.context.support.AbstractApplicationContext; 28 | import org.springframework.http.server.reactive.HttpHandler; 29 | import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; 30 | import org.springframework.web.server.adapter.WebHttpHandlerBuilder; 31 | 32 | import reactor.netty.DisposableServer; 33 | import reactor.netty.http.server.HttpServer; 34 | 35 | /** 36 | * @author Dave Syer 37 | * 38 | */ 39 | public class ApplicationBuilder { 40 | 41 | private static final String SHUTDOWN_LISTENER = "SHUTDOWN_LISTENER"; 42 | public static final String STARTUP = "Benchmark app started"; 43 | private static Log logger = LogFactory.getLog(StartupApplicationListener.class); 44 | 45 | public static void start(ConfigurableApplicationContext context) { 46 | start(context, null); 47 | } 48 | 49 | public static void start(ConfigurableApplicationContext context, 50 | Consumer callback) { 51 | if (!hasListeners(context)) { 52 | ((DefaultListableBeanFactory) context.getBeanFactory()) 53 | .registerDisposableBean(SHUTDOWN_LISTENER, 54 | new ShutdownApplicationListener()); 55 | new BeanCountingApplicationListener().log(context); 56 | logger.info(STARTUP); 57 | } 58 | 59 | HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build(); 60 | ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); 61 | HttpServer httpServer = HttpServer.create().host("localhost").port( 62 | context.getEnvironment().getProperty("server.port", Integer.class, 8080)) 63 | .handle(adapter); 64 | httpServer.bindUntilJavaShutdown(Duration.ofSeconds(60), callback(callback)); 65 | } 66 | 67 | private static Consumer callback( 68 | Consumer callback) { 69 | return context -> { 70 | try { 71 | double uptime = ManagementFactory.getRuntimeMXBean().getUptime(); 72 | System.err.println("JVM running for " + uptime + "ms"); 73 | } 74 | catch (Throwable e) { 75 | } 76 | if (callback != null) { 77 | callback.accept(context); 78 | } 79 | }; 80 | } 81 | 82 | private static boolean hasListeners(ConfigurableApplicationContext context) { 83 | if (context.getBeanNamesForType(ShutdownApplicationListener.class).length != 0) { 84 | return true; 85 | } 86 | if (context instanceof AbstractApplicationContext) { 87 | if (((AbstractApplicationContext) context).getApplicationListeners().stream() 88 | .anyMatch(l -> l instanceof ShutdownApplicationListener)) { 89 | return true; 90 | } 91 | } 92 | if ((DefaultListableBeanFactory) context.getBeanFactory() 93 | .getSingleton(SHUTDOWN_LISTENER) != null) { 94 | return true; 95 | } 96 | return false; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/BeanCountingApplicationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.config; 17 | 18 | import java.lang.management.ManagementFactory; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import org.springframework.beans.BeansException; 27 | import org.springframework.boot.context.event.ApplicationReadyEvent; 28 | import org.springframework.context.ApplicationContext; 29 | import org.springframework.context.ApplicationContextAware; 30 | import org.springframework.context.ApplicationListener; 31 | import org.springframework.context.ConfigurableApplicationContext; 32 | import org.springframework.core.annotation.AnnotationUtils; 33 | import org.springframework.stereotype.Component; 34 | 35 | /** 36 | * @author Dave Syer 37 | * 38 | */ 39 | public class BeanCountingApplicationListener 40 | implements ApplicationListener, ApplicationContextAware { 41 | 42 | private static Log logger = LogFactory.getLog(BeanCountingApplicationListener.class); 43 | private ApplicationContext context; 44 | 45 | @Override 46 | public void setApplicationContext(ApplicationContext context) throws BeansException { 47 | this.context = context; 48 | } 49 | 50 | @Override 51 | public void onApplicationEvent(ApplicationReadyEvent event) { 52 | if (!event.getApplicationContext().equals(this.context)) { 53 | return; 54 | } 55 | ConfigurableApplicationContext context = event.getApplicationContext(); 56 | log(context); 57 | } 58 | 59 | public void log(ConfigurableApplicationContext context) { 60 | int count = 0; 61 | String id = context.getId(); 62 | List names = new ArrayList<>(); 63 | while (context != null) { 64 | count += context.getBeanDefinitionCount(); 65 | names.addAll(Arrays.asList(context.getBeanDefinitionNames())); 66 | if (logger.isDebugEnabled()) { 67 | for (String name : context.getBeanDefinitionNames()) { 68 | Class type = context.getType(name); 69 | if (AnnotationUtils.findAnnotation(type, Component.class)!=null) { 70 | logger.debug("Component: " + type); 71 | } 72 | } 73 | } 74 | context = (ConfigurableApplicationContext) context.getParent(); 75 | } 76 | logger.info("Bean count: " + id + "=" + count); 77 | logger.debug("Bean names: " + id + "=" + names); 78 | try { 79 | logger.info("Class count: " + id + "=" + ManagementFactory 80 | .getClassLoadingMXBean().getTotalLoadedClassCount()); 81 | } 82 | catch (Exception e) { 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/ComponentIndex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.config; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * @author Dave Syer 27 | * 28 | */ 29 | @Target({ ElementType.TYPE }) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Documented 32 | public @interface ComponentIndex { 33 | 34 | Entry[] value(); 35 | 36 | @Retention(RetentionPolicy.RUNTIME) 37 | @Documented 38 | static @interface Entry { 39 | String key(); 40 | 41 | String[] value(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/ComponentIndexer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.config; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * @author Dave Syer 23 | * 24 | */ 25 | public interface ComponentIndexer { 26 | List> indexes(); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/ConfigurationClassInitializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.config; 18 | 19 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 20 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 21 | import org.springframework.context.ApplicationContextInitializer; 22 | import org.springframework.context.ConfigurableApplicationContext; 23 | import org.springframework.context.annotation.AnnotationConfigUtils; 24 | 25 | /** 26 | * @author Dave Syer 27 | * 28 | */ 29 | public class ConfigurationClassInitializer 30 | implements ApplicationContextInitializer { 31 | 32 | @Override 33 | public void initialize(ConfigurableApplicationContext context) { 34 | if (context instanceof BeanDefinitionRegistry) { 35 | overrideConfigurationClassProcessor((BeanDefinitionRegistry) context); 36 | } 37 | } 38 | 39 | private void overrideConfigurationClassProcessor(BeanDefinitionRegistry context) { 40 | if (!context.containsBeanDefinition( 41 | AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { 42 | context.registerBeanDefinition( 43 | AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME, 44 | BeanDefinitionBuilder.genericBeanDefinition(Object.class) 45 | .getBeanDefinition()); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/LazyInitBeanFactoryPostProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.config; 17 | 18 | import org.springframework.beans.BeansException; 19 | import org.springframework.beans.factory.config.BeanDefinition; 20 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 21 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 22 | import org.springframework.stereotype.Component; 23 | 24 | @Component 25 | public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 26 | 27 | private Class[] exclusionList; 28 | 29 | public LazyInitBeanFactoryPostProcessor() { 30 | } 31 | 32 | public LazyInitBeanFactoryPostProcessor(Class[] exclusionList) { 33 | this.exclusionList = exclusionList; 34 | } 35 | 36 | @Override 37 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 38 | throws BeansException { 39 | 40 | // Iterate over all beans, mark them as lazy if they are not in the exclusion list. 41 | for (String beanName : beanFactory.getBeanDefinitionNames()) { 42 | if (isLazy(beanName, beanFactory)) { 43 | BeanDefinition definition = beanFactory.getBeanDefinition(beanName); 44 | definition.setLazyInit(true); 45 | } 46 | } 47 | } 48 | 49 | private boolean isLazy(String beanName, ConfigurableListableBeanFactory beanFactory) { 50 | if (exclusionList == null || exclusionList.length == 0) { 51 | return true; 52 | } 53 | for (Class clazz : exclusionList) { 54 | if (beanFactory.isTypeMatch(beanName, clazz)) { 55 | return false; 56 | } 57 | } 58 | return true; 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/com/example/config/ShutdownApplicationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.config; 17 | 18 | import java.lang.reflect.Method; 19 | import java.util.LinkedHashSet; 20 | import java.util.Set; 21 | 22 | import org.springframework.beans.BeansException; 23 | import org.springframework.beans.factory.DisposableBean; 24 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 25 | import org.springframework.boot.SpringApplication; 26 | import org.springframework.boot.SpringBootConfiguration; 27 | import org.springframework.boot.context.event.ApplicationReadyEvent; 28 | import org.springframework.context.ApplicationContext; 29 | import org.springframework.context.ApplicationContextAware; 30 | import org.springframework.context.ApplicationListener; 31 | import org.springframework.core.Ordered; 32 | import org.springframework.core.annotation.AnnotatedElementUtils; 33 | import org.springframework.core.annotation.Order; 34 | import org.springframework.util.ClassUtils; 35 | import org.springframework.util.ReflectionUtils; 36 | 37 | /** 38 | * @author Dave Syer 39 | * 40 | */ 41 | @Order(Ordered.HIGHEST_PRECEDENCE) 42 | public class ShutdownApplicationListener 43 | implements ApplicationListener, DisposableBean, 44 | ApplicationContextAware { 45 | 46 | private static final String SHUTDOWN_LISTENER = "SHUTDOWN_LISTENER"; 47 | public static final String MARKER = "Benchmark app stopped"; 48 | 49 | private ApplicationContext context; 50 | 51 | @Override 52 | public void setApplicationContext(ApplicationContext context) throws BeansException { 53 | this.context = context; 54 | } 55 | 56 | @Override 57 | public void onApplicationEvent(ApplicationReadyEvent event) { 58 | if (!event.getApplicationContext().equals(this.context)) { 59 | return; 60 | } 61 | if (isSpringBootApplication(sources(event))) { 62 | ((DefaultListableBeanFactory) event.getApplicationContext().getBeanFactory()) 63 | .registerDisposableBean(SHUTDOWN_LISTENER, this); 64 | } 65 | } 66 | 67 | @Override 68 | public void destroy() throws Exception { 69 | try { 70 | System.out.println(MARKER); 71 | } 72 | catch (Exception e) { 73 | } 74 | } 75 | 76 | private boolean isSpringBootApplication(Set> sources) { 77 | for (Class source : sources) { 78 | if (AnnotatedElementUtils.hasAnnotation(source, 79 | SpringBootConfiguration.class)) { 80 | return true; 81 | } 82 | } 83 | return false; 84 | } 85 | 86 | private Set> sources(ApplicationReadyEvent event) { 87 | Method method = ReflectionUtils.findMethod(SpringApplication.class, 88 | "getAllSources"); 89 | if (method == null) { 90 | method = ReflectionUtils.findMethod(SpringApplication.class, "getSources"); 91 | } 92 | ReflectionUtils.makeAccessible(method); 93 | @SuppressWarnings("unchecked") 94 | Set objects = (Set) ReflectionUtils.invokeMethod(method, 95 | event.getSpringApplication()); 96 | Set> result = new LinkedHashSet<>(); 97 | for (Object object : objects) { 98 | if (object instanceof String) { 99 | object = ClassUtils.resolveClassName((String) object, null); 100 | } 101 | result.add((Class) object); 102 | } 103 | return result; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/SpringBeanInfoFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.config; 18 | 19 | import java.beans.BeanInfo; 20 | import java.beans.IntrospectionException; 21 | import java.beans.Introspector; 22 | import java.beans.PropertyDescriptor; 23 | import java.util.Collections; 24 | import java.util.LinkedHashSet; 25 | import java.util.Set; 26 | 27 | import org.springframework.beans.BeanInfoFactory; 28 | import org.springframework.core.annotation.Order; 29 | 30 | /** 31 | * {@link BeanInfoFactory} that uses a simpler algorithm than {@link Introspector} since 32 | * we know that our beans don't implement any of the more exotic specifications and Spring 33 | * only really needs {@link PropertyDescriptor PropertyDescriptors}. 34 | * 35 | * @author Phillip Webb 36 | * @since 2.0.0 37 | */ 38 | @Order 39 | public class SpringBeanInfoFactory implements BeanInfoFactory { 40 | 41 | private static String SPRING_PACKAGE = "org.springframework"; 42 | 43 | private static Set SUPPORTED_PACKAGES; 44 | 45 | static { 46 | Set supportedPackages = new LinkedHashSet<>(); 47 | supportedPackages.add("org.springframework.boot."); 48 | supportedPackages.add("org.springframework.context."); 49 | supportedPackages.add("org.springframework.http."); 50 | supportedPackages.add("org.springframework.jmx."); 51 | supportedPackages.add("org.springframework.web."); 52 | SUPPORTED_PACKAGES = Collections.unmodifiableSet(supportedPackages); 53 | } 54 | 55 | @Override 56 | public BeanInfo getBeanInfo(Class beanClass) throws IntrospectionException { 57 | String name = beanClass.getName(); 58 | if (name.startsWith(SPRING_PACKAGE) && isSupportedSpringPackage(name)) { 59 | if (beanClass.getName().startsWith("org.springframework.boot")) { 60 | return new SpringBootBeanInfo(beanClass); 61 | } 62 | } 63 | return null; 64 | } 65 | 66 | private boolean isSupportedSpringPackage(String name) { 67 | for (String candidate : SUPPORTED_PACKAGES) { 68 | if (name.startsWith(candidate)) { 69 | return true; 70 | } 71 | } 72 | return false; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/SpringBootBeanInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.config; 18 | 19 | import java.awt.Image; 20 | import java.beans.BeanDescriptor; 21 | import java.beans.BeanInfo; 22 | import java.beans.EventSetDescriptor; 23 | import java.beans.IntrospectionException; 24 | import java.beans.MethodDescriptor; 25 | import java.beans.PropertyDescriptor; 26 | import java.lang.reflect.Method; 27 | import java.lang.reflect.Modifier; 28 | import java.util.ArrayList; 29 | import java.util.LinkedHashMap; 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | import org.springframework.util.ReflectionUtils; 34 | 35 | /** 36 | * {@link BeanInfo} implementation returned from {@link SpringBeanInfoFactory}. 37 | * 38 | * @author Phillip Webb 39 | */ 40 | class SpringBootBeanInfo implements BeanInfo { 41 | 42 | private final PropertyDescriptor[] propertyDescriptors; 43 | 44 | public SpringBootBeanInfo(Class beanClass) throws IntrospectionException { 45 | this.propertyDescriptors = extractPropertyDescriptors(beanClass); 46 | } 47 | 48 | private PropertyDescriptor[] extractPropertyDescriptors(Class beanClass) 49 | throws IntrospectionException { 50 | Map getters = new LinkedHashMap<>(); 51 | Map setters = new LinkedHashMap<>(); 52 | Method[] methods = ReflectionUtils.getAllDeclaredMethods(beanClass); 53 | for (Method method : methods) { 54 | collectGetterSetterMethod(method, getters, setters); 55 | } 56 | List descriptors = new ArrayList<>(methods.length); 57 | for (Map.Entry entry : getters.entrySet()) { 58 | String name = entry.getKey(); 59 | Method getter = entry.getValue(); 60 | Method setter = setters.remove(name); 61 | if (setter != null && !getter.getReturnType() 62 | .isAssignableFrom(setter.getParameterTypes()[0])) { 63 | setter = null; 64 | } 65 | descriptors.add(new PropertyDescriptor(name, getter, setter)); 66 | } 67 | for (Map.Entry entry : setters.entrySet()) { 68 | Method setter = entry.getValue(); 69 | String name = entry.getKey(); 70 | descriptors.add(new PropertyDescriptor(name, null, setter)); 71 | } 72 | return descriptors.toArray(new PropertyDescriptor[descriptors.size()]); 73 | } 74 | 75 | private void collectGetterSetterMethod(Method method, Map getters, 76 | Map setters) { 77 | int argSize = method.getParameterTypes().length; 78 | if (!Modifier.isStatic(method.getModifiers()) && argSize <= 1) { 79 | String name = method.getName(); 80 | if (argSize == 0 && name.length() > 3 && name.startsWith("get")) { 81 | getters.putIfAbsent(name.substring(3), method); 82 | } 83 | else if (argSize == 0 && name.length() > 2 && name.startsWith("is") 84 | && method.getReturnType() == boolean.class) { 85 | getters.putIfAbsent(name.substring(2), method); 86 | } 87 | else if (argSize == 1 && name.length() > 3 && name.startsWith("set")) { 88 | setters.putIfAbsent(name.substring(3), method); 89 | } 90 | } 91 | } 92 | 93 | @Override 94 | public BeanDescriptor getBeanDescriptor() { 95 | throw new UnsupportedOperationException(); 96 | } 97 | 98 | @Override 99 | public EventSetDescriptor[] getEventSetDescriptors() { 100 | throw new UnsupportedOperationException(); 101 | } 102 | 103 | @Override 104 | public int getDefaultEventIndex() { 105 | throw new UnsupportedOperationException(); 106 | } 107 | 108 | @Override 109 | public int getDefaultPropertyIndex() { 110 | throw new UnsupportedOperationException(); 111 | } 112 | 113 | @Override 114 | public MethodDescriptor[] getMethodDescriptors() { 115 | throw new UnsupportedOperationException(); 116 | } 117 | 118 | @Override 119 | public BeanInfo[] getAdditionalBeanInfo() { 120 | throw new UnsupportedOperationException(); 121 | } 122 | 123 | @Override 124 | public Image getIcon(int iconKind) { 125 | throw new UnsupportedOperationException(); 126 | } 127 | 128 | @Override 129 | public PropertyDescriptor[] getPropertyDescriptors() { 130 | return this.propertyDescriptors; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/StandardMetadataReaderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.config; 18 | 19 | import java.io.IOException; 20 | 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | 24 | import org.springframework.core.io.ClassPathResource; 25 | import org.springframework.core.io.Resource; 26 | import org.springframework.core.type.AnnotationMetadata; 27 | import org.springframework.core.type.ClassMetadata; 28 | import org.springframework.core.type.classreading.MetadataReader; 29 | import org.springframework.core.type.classreading.MetadataReaderFactory; 30 | import org.springframework.util.ClassUtils; 31 | 32 | public class StandardMetadataReaderFactory implements MetadataReaderFactory { 33 | 34 | @Override 35 | public MetadataReader getMetadataReader(String className) throws IOException { 36 | ClassPathResource resource = new ClassPathResource( 37 | ClassUtils.convertClassNameToResourcePath(className)); 38 | return getMetadataReader(resource, className); 39 | } 40 | 41 | private StandardMetadataReader getMetadataReader(ClassPathResource resource, 42 | String className) { 43 | if (ClassUtils.isPresent(className, null)) { 44 | return new StandardMetadataReader(resource, 45 | ClassUtils.resolveClassName(className, null)); 46 | } 47 | throw new IllegalArgumentException("Cannot load class: " + className); 48 | } 49 | 50 | @Override 51 | public MetadataReader getMetadataReader(Resource resource) throws IOException { 52 | if (resource instanceof ClassPathResource) { 53 | ClassPathResource cp = (ClassPathResource) resource; 54 | String className = ClassUtils.convertResourcePathToClassName(cp.getPath()); 55 | return getMetadataReader(cp, className); 56 | } 57 | throw new IllegalArgumentException("Cannot find class name for: " + resource); 58 | } 59 | 60 | } 61 | 62 | class StandardMetadataReader implements MetadataReader { 63 | 64 | private static Log logger = LogFactory.getLog(StandardMetadataReader.class); 65 | 66 | private ClassPathResource resource; 67 | 68 | private AnnotationMetadata metadata; 69 | 70 | public StandardMetadataReader(ClassPathResource resource, Class type) { 71 | this.resource = resource; 72 | this.metadata = AnnotationMetadata.introspect(type); 73 | logger.info("Metadata: " + type + " = " + metadata); 74 | } 75 | 76 | @Override 77 | public Resource getResource() { 78 | return resource; 79 | } 80 | 81 | @Override 82 | public ClassMetadata getClassMetadata() { 83 | return metadata; 84 | } 85 | 86 | @Override 87 | public AnnotationMetadata getAnnotationMetadata() { 88 | return metadata; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/example/config/StartupApplicationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.config; 17 | 18 | import java.lang.reflect.Method; 19 | import java.util.LinkedHashSet; 20 | import java.util.Set; 21 | 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import org.springframework.beans.BeansException; 26 | import org.springframework.boot.SpringApplication; 27 | import org.springframework.boot.SpringBootConfiguration; 28 | import org.springframework.boot.context.event.ApplicationReadyEvent; 29 | import org.springframework.context.ApplicationContext; 30 | import org.springframework.context.ApplicationContextAware; 31 | import org.springframework.context.ApplicationListener; 32 | import org.springframework.core.annotation.AnnotatedElementUtils; 33 | import org.springframework.util.ClassUtils; 34 | import org.springframework.util.ReflectionUtils; 35 | 36 | /** 37 | * @author Dave Syer 38 | * 39 | */ 40 | public class StartupApplicationListener 41 | implements ApplicationListener, ApplicationContextAware { 42 | 43 | public static final String MARKER = "Benchmark app started"; 44 | private static Log logger = LogFactory.getLog(StartupApplicationListener.class); 45 | private ApplicationContext context; 46 | 47 | @Override 48 | public void setApplicationContext(ApplicationContext context) throws BeansException { 49 | this.context = context; 50 | } 51 | 52 | @Override 53 | public void onApplicationEvent(ApplicationReadyEvent event) { 54 | if (!event.getApplicationContext().equals(this.context)) { 55 | return; 56 | } 57 | if (isSpringBootApplication(sources(event))) { 58 | try { 59 | logger.info(MARKER); 60 | } 61 | catch (Exception e) { 62 | } 63 | } 64 | } 65 | 66 | private boolean isSpringBootApplication(Set> sources) { 67 | for (Class source : sources) { 68 | if (AnnotatedElementUtils.hasAnnotation(source, 69 | SpringBootConfiguration.class)) { 70 | return true; 71 | } 72 | } 73 | return false; 74 | } 75 | 76 | private Set> sources(ApplicationReadyEvent event) { 77 | Method method = ReflectionUtils.findMethod(SpringApplication.class, 78 | "getAllSources"); 79 | if (method == null) { 80 | method = ReflectionUtils.findMethod(SpringApplication.class, "getSources"); 81 | } 82 | ReflectionUtils.makeAccessible(method); 83 | @SuppressWarnings("unchecked") 84 | Set objects = (Set) ReflectionUtils.invokeMethod(method, 85 | event.getSpringApplication()); 86 | Set> result = new LinkedHashSet<>(); 87 | for (Object object : objects) { 88 | if (object instanceof String) { 89 | object = ClassUtils.resolveClassName((String) object, null); 90 | } 91 | result.add((Class) object); 92 | } 93 | return result; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.builder.SpringApplicationBuilder; 8 | import org.springframework.context.ConfigurableApplicationContext; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.web.reactive.function.server.RouterFunction; 11 | 12 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 13 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 14 | import static org.springframework.web.reactive.function.server.ServerResponse.ok; 15 | 16 | import reactor.core.publisher.Mono; 17 | 18 | @SpringBootApplication(proxyBeanMethods = false) 19 | public class DemoApplication implements Runnable, Closeable { 20 | 21 | private ConfigurableApplicationContext context; 22 | 23 | @Bean 24 | public RouterFunction userEndpoints() { 25 | return route(GET("/"), request -> ok().body(Mono.just("Hello"), String.class)); 26 | } 27 | 28 | public static void main(String[] args) throws Exception { 29 | DemoApplication last = new DemoApplication(); 30 | last.run(); 31 | if (Boolean.getBoolean("demo.close")) { 32 | last.close(); 33 | } 34 | } 35 | 36 | @Override 37 | public void close() throws IOException { 38 | if (context != null) { 39 | context.close(); 40 | } 41 | } 42 | 43 | @Override 44 | public void run() { 45 | context = new SpringApplicationBuilder(DemoApplication.class) 46 | .properties("--server.port=0", "--spring.jmx.enabled=false").run(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/example/func/BuncApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.func; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext; 21 | import org.springframework.context.ApplicationContext; 22 | import org.springframework.context.annotation.ConfigurationClassPostProcessor; 23 | 24 | /** 25 | * Functional bean definitions. With Spring Boot, but without {@link ConfigurationClassPostProcessor}. 26 | * 27 | * @author Dave Syer 28 | * 29 | */ 30 | public class BuncApplication extends FuncApplication { 31 | 32 | public static void main(String[] args) throws Exception { 33 | long t0 = System.currentTimeMillis(); 34 | BuncApplication bean = new BuncApplication(); 35 | bean.run(); 36 | System.err.println( 37 | "Started HttpServer: " + (System.currentTimeMillis() - t0) + "ms"); 38 | if (Boolean.getBoolean("demo.close")) { 39 | bean.close(); 40 | } 41 | } 42 | 43 | @Override 44 | public void run() { 45 | SpringApplication application = new SpringApplication(BuncApplication.class) { 46 | @Override 47 | protected void load(ApplicationContext context, Object[] sources) { 48 | // We don't want the annotation bean definition reader 49 | // super.load(context, sources); 50 | } 51 | }; 52 | application.addInitializers(this); 53 | application.setApplicationContextClass(ReactiveWebServerApplicationContext.class); 54 | application.run(); 55 | System.err.println(MARKER); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/example/func/CuncApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.func; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext; 21 | import org.springframework.context.annotation.ConfigurationClassPostProcessor; 22 | 23 | /** 24 | * Functional bean definitions. With Spring Boot, including {@link ConfigurationClassPostProcessor}. 25 | * 26 | * @author Dave Syer 27 | * 28 | */ 29 | public class CuncApplication extends FuncApplication { 30 | 31 | public static void main(String[] args) throws Exception { 32 | long t0 = System.currentTimeMillis(); 33 | CuncApplication bean = new CuncApplication(); 34 | bean.run(); 35 | System.err.println( 36 | "Started HttpServer: " + (System.currentTimeMillis() - t0) + "ms"); 37 | if (Boolean.getBoolean("demo.close")) { 38 | bean.close(); 39 | } 40 | } 41 | 42 | @Override 43 | public void run() { 44 | SpringApplication application = new SpringApplication(CuncApplication.class); 45 | application.addInitializers(this); 46 | application.setApplicationContextClass(ReactiveWebServerApplicationContext.class); 47 | application.run(); 48 | System.err.println(MARKER); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/example/func/FuncApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.func; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import com.example.config.BeanCountingApplicationListener; 9 | import com.google.gson.Gson; 10 | import com.google.gson.GsonBuilder; 11 | import reactor.core.publisher.Mono; 12 | 13 | import org.springframework.beans.factory.ObjectProvider; 14 | import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; 15 | import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; 16 | import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; 17 | import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer; 18 | import org.springframework.boot.autoconfigure.gson.GsonProperties; 19 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 20 | import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; 21 | import org.springframework.boot.autoconfigure.http.HttpProperties; 22 | import org.springframework.boot.autoconfigure.web.ResourceProperties; 23 | import org.springframework.boot.autoconfigure.web.ServerProperties; 24 | import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration; 25 | import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer; 26 | import org.springframework.boot.autoconfigure.web.reactive.ResourceHandlerRegistrationCustomizer; 27 | import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration.EnableWebFluxConfiguration; 28 | import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration.WebFluxConfig; 29 | import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; 30 | import org.springframework.boot.autoconfigure.web.reactive.WebFluxRegistrations; 31 | import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration; 32 | import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; 33 | import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar; 34 | import org.springframework.boot.web.codec.CodecCustomizer; 35 | import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; 36 | import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext; 37 | import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; 38 | import org.springframework.boot.web.reactive.error.ErrorAttributes; 39 | import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; 40 | import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; 41 | import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; 42 | import org.springframework.context.ApplicationContextInitializer; 43 | import org.springframework.context.annotation.Bean; 44 | import org.springframework.context.support.GenericApplicationContext; 45 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 46 | import org.springframework.core.ReactiveAdapterRegistry; 47 | import org.springframework.core.ResolvableType; 48 | import org.springframework.format.support.DefaultFormattingConversionService; 49 | import org.springframework.format.support.FormattingConversionService; 50 | import org.springframework.http.codec.ServerCodecConfigurer; 51 | import org.springframework.http.converter.HttpMessageConverter; 52 | import org.springframework.http.converter.StringHttpMessageConverter; 53 | import org.springframework.http.converter.json.GsonHttpMessageConverter; 54 | import org.springframework.http.server.reactive.HttpHandler; 55 | import org.springframework.validation.Validator; 56 | import org.springframework.web.reactive.DispatcherHandler; 57 | import org.springframework.web.reactive.HandlerMapping; 58 | import org.springframework.web.reactive.accept.RequestedContentTypeResolver; 59 | import org.springframework.web.reactive.config.WebFluxConfigurer; 60 | import org.springframework.web.reactive.function.client.WebClient; 61 | import org.springframework.web.reactive.function.server.RouterFunction; 62 | import org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter; 63 | import org.springframework.web.reactive.function.server.support.RouterFunctionMapping; 64 | import org.springframework.web.reactive.function.server.support.ServerResponseResultHandler; 65 | import org.springframework.web.reactive.resource.ResourceUrlProvider; 66 | import org.springframework.web.reactive.result.SimpleHandlerAdapter; 67 | import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; 68 | import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; 69 | import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; 70 | import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; 71 | import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler; 72 | import org.springframework.web.reactive.result.view.ViewResolutionResultHandler; 73 | import org.springframework.web.reactive.result.view.ViewResolver; 74 | import org.springframework.web.server.WebExceptionHandler; 75 | import org.springframework.web.server.adapter.WebHttpHandlerBuilder; 76 | import org.springframework.web.server.i18n.LocaleContextResolver; 77 | 78 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 79 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 80 | import static org.springframework.web.reactive.function.server.ServerResponse.ok; 81 | 82 | /** 83 | * Functional bean definitions. No Spring Boot. 84 | * 85 | * @author Dave Syer 86 | * 87 | */ 88 | public class FuncApplication implements Runnable, Closeable, 89 | ApplicationContextInitializer { 90 | 91 | public static final String MARKER = "Benchmark app started"; 92 | 93 | private GenericApplicationContext context; 94 | 95 | @Bean 96 | public RouterFunction userEndpoints() { 97 | return route(GET("/"), request -> ok().body(Mono.just("Hello"), String.class)); 98 | } 99 | 100 | public static void main(String[] args) throws Exception { 101 | long t0 = System.currentTimeMillis(); 102 | FuncApplication bean = new FuncApplication(); 103 | bean.run(); 104 | System.err.println( 105 | "Started HttpServer: " + (System.currentTimeMillis() - t0) + "ms"); 106 | if (Boolean.getBoolean("demo.close")) { 107 | bean.close(); 108 | } 109 | } 110 | 111 | @Override 112 | public void close() throws IOException { 113 | if (context != null) { 114 | context.close(); 115 | } 116 | } 117 | 118 | @Override 119 | public void run() { 120 | ReactiveWebServerApplicationContext context = new ReactiveWebServerApplicationContext(); 121 | initialize(context); 122 | context.refresh(); 123 | System.err.println(MARKER); 124 | new BeanCountingApplicationListener().log(context); 125 | } 126 | 127 | @Override 128 | public void initialize(GenericApplicationContext context) { 129 | this.context = context; 130 | performPreinitialization(); 131 | context.registerBean(AutowiredAnnotationBeanPostProcessor.class); 132 | registerDemoApplication(); 133 | registerWebServerFactoryCustomizerBeanPostProcessor(); 134 | registerConfigurationProperties(); 135 | // context.registerBean(LazyInitBeanFactoryPostProcessor.class); 136 | registerPropertyPlaceholderAutoConfiguration(); 137 | registerReactiveWebServerFactoryAutoConfiguration(); 138 | registerErrorWebFluxAutoConfiguration(); 139 | registerWebFluxAutoConfiguration(); 140 | registerHttpHandlerAutoConfiguration(); 141 | registerGsonAutoConfiguration(); 142 | registerHttpMessageConvertersAutoConfiguration(); 143 | registerWebClientAutoConfiguration(); 144 | } 145 | 146 | private void performPreinitialization() { 147 | try { 148 | Thread thread = new Thread(new Runnable() { 149 | 150 | @Override 151 | public void run() { 152 | runSafely(() -> new DefaultFormattingConversionService()); 153 | } 154 | 155 | public void runSafely(Runnable runnable) { 156 | try { 157 | runnable.run(); 158 | } 159 | catch (Throwable ex) { 160 | // Ignore 161 | } 162 | } 163 | 164 | }, "background-preinit"); 165 | thread.start(); 166 | } 167 | catch (Exception ex) { 168 | } 169 | } 170 | 171 | private void registerConfigurationProperties() { 172 | new ConfigurationPropertiesBindingPostProcessorRegistrar() 173 | .registerBeanDefinitions(null, context); 174 | context.registerBean(ServerProperties.class, () -> new ServerProperties()); 175 | context.registerBean(ResourceProperties.class, () -> new ResourceProperties()); 176 | context.registerBean(WebFluxProperties.class, () -> new WebFluxProperties()); 177 | context.registerBean(GsonProperties.class, () -> new GsonProperties()); 178 | context.registerBean(HttpProperties.class, () -> new HttpProperties()); 179 | } 180 | 181 | private void registerWebServerFactoryCustomizerBeanPostProcessor() { 182 | context.registerBean("webServerFactoryCustomizerBeanPostProcessor", 183 | WebServerFactoryCustomizerBeanPostProcessor.class); 184 | } 185 | 186 | private void registerPropertyPlaceholderAutoConfiguration() { 187 | context.registerBean(PropertySourcesPlaceholderConfigurer.class, 188 | () -> PropertyPlaceholderAutoConfiguration 189 | .propertySourcesPlaceholderConfigurer()); 190 | } 191 | 192 | private void registerReactiveWebServerFactoryAutoConfiguration() { 193 | ReactiveWebServerFactoryAutoConfiguration config = new ReactiveWebServerFactoryAutoConfiguration(); 194 | context.registerBean(ReactiveWebServerFactoryCustomizer.class, 195 | () -> config.reactiveWebServerFactoryCustomizer( 196 | context.getBean(ServerProperties.class))); 197 | context.registerBean(NettyReactiveWebServerFactory.class, 198 | () -> new NettyReactiveWebServerFactory()); 199 | } 200 | 201 | private void registerErrorWebFluxAutoConfiguration() { 202 | context.registerBean(ErrorAttributes.class, () -> new DefaultErrorAttributes( 203 | context.getBean(ServerProperties.class).getError().isIncludeException())); 204 | context.registerBean(ErrorWebExceptionHandler.class, () -> { 205 | return errorWebFluxAutoConfiguration().errorWebExceptionHandler( 206 | context.getBean(ErrorAttributes.class), 207 | context.getBean(ResourceProperties.class), 208 | context.getDefaultListableBeanFactory() 209 | .getBeanProvider(ResolvableType.forClassWithGenerics( 210 | List.class, ViewResolver.class)), 211 | context.getBean(ServerCodecConfigurer.class), context); 212 | }); 213 | } 214 | 215 | private ErrorWebFluxAutoConfiguration errorWebFluxAutoConfiguration() { 216 | ServerProperties serverProperties = context.getBean(ServerProperties.class); 217 | return new ErrorWebFluxAutoConfiguration(serverProperties); 218 | } 219 | 220 | /** 221 | * Trivial subclass to prevent @Bean configuration kicking in in 222 | * {@link CuncApplication}. 223 | * 224 | */ 225 | private static class NotEnableWebFluxConfiguration 226 | extends EnableWebFluxConfiguration { 227 | 228 | public NotEnableWebFluxConfiguration(WebFluxProperties webFluxProperties, 229 | ObjectProvider webFluxRegistrations) { 230 | super(webFluxProperties, webFluxRegistrations); 231 | } 232 | } 233 | 234 | private void registerWebFluxAutoConfiguration() { 235 | context.registerBean(NotEnableWebFluxConfiguration.class, 236 | () -> new NotEnableWebFluxConfiguration( 237 | context.getBean(WebFluxProperties.class), 238 | context.getBeanProvider(WebFluxRegistrations.class))); 239 | context.registerBean(HandlerFunctionAdapter.class, () -> context 240 | .getBean(EnableWebFluxConfiguration.class).handlerFunctionAdapter()); 241 | context.registerBean(WebHttpHandlerBuilder.LOCALE_CONTEXT_RESOLVER_BEAN_NAME, 242 | LocaleContextResolver.class, 243 | () -> context.getBean(EnableWebFluxConfiguration.class) 244 | .localeContextResolver()); 245 | context.registerBean(RequestMappingHandlerAdapter.class, 246 | () -> context.getBean(EnableWebFluxConfiguration.class) 247 | .requestMappingHandlerAdapter( 248 | context.getBean(ReactiveAdapterRegistry.class), 249 | context.getBean(ServerCodecConfigurer.class), 250 | context.getBean(FormattingConversionService.class), 251 | context.getBean(Validator.class))); 252 | context.registerBean(RequestMappingHandlerMapping.class, 253 | () -> context.getBean(EnableWebFluxConfiguration.class) 254 | .requestMappingHandlerMapping( 255 | context.getBean(RequestedContentTypeResolver.class))); 256 | context.registerBean(ResourceUrlProvider.class, () -> context 257 | .getBean(EnableWebFluxConfiguration.class).resourceUrlProvider()); 258 | context.registerBean(HandlerMapping.class, 259 | () -> context.getBean(EnableWebFluxConfiguration.class) 260 | .resourceHandlerMapping( 261 | context.getBean(ResourceUrlProvider.class))); 262 | context.registerBean(ResponseBodyResultHandler.class, 263 | () -> context.getBean(EnableWebFluxConfiguration.class) 264 | .responseBodyResultHandler( 265 | context.getBean(ReactiveAdapterRegistry.class), 266 | context.getBean(ServerCodecConfigurer.class), 267 | context.getBean(RequestedContentTypeResolver.class))); 268 | context.registerBean(ResponseEntityResultHandler.class, 269 | () -> context.getBean(EnableWebFluxConfiguration.class) 270 | .responseEntityResultHandler( 271 | context.getBean(ReactiveAdapterRegistry.class), 272 | context.getBean(ServerCodecConfigurer.class), 273 | context.getBean(RequestedContentTypeResolver.class))); 274 | context.registerBean(WebExceptionHandler.class, 275 | () -> context.getBean(EnableWebFluxConfiguration.class) 276 | .responseStatusExceptionHandler()); 277 | context.registerBean(RouterFunctionMapping.class, 278 | () -> context.getBean(EnableWebFluxConfiguration.class) 279 | .routerFunctionMapping( 280 | context.getBean(ServerCodecConfigurer.class))); 281 | context.registerBean(WebHttpHandlerBuilder.SERVER_CODEC_CONFIGURER_BEAN_NAME, 282 | ServerCodecConfigurer.class, 283 | () -> context.getBean(EnableWebFluxConfiguration.class) 284 | .serverCodecConfigurer()); 285 | context.registerBean(ServerResponseResultHandler.class, 286 | () -> context.getBean(EnableWebFluxConfiguration.class) 287 | .serverResponseResultHandler( 288 | context.getBean(ServerCodecConfigurer.class))); 289 | context.registerBean(SimpleHandlerAdapter.class, () -> context 290 | .getBean(EnableWebFluxConfiguration.class).simpleHandlerAdapter()); 291 | context.registerBean(ViewResolutionResultHandler.class, 292 | () -> context.getBean(EnableWebFluxConfiguration.class) 293 | .viewResolutionResultHandler( 294 | context.getBean(ReactiveAdapterRegistry.class), 295 | context.getBean(RequestedContentTypeResolver.class))); 296 | context.registerBean(ReactiveAdapterRegistry.class, () -> context 297 | .getBean(EnableWebFluxConfiguration.class).webFluxAdapterRegistry()); 298 | context.registerBean(RequestedContentTypeResolver.class, () -> context 299 | .getBean(EnableWebFluxConfiguration.class).webFluxContentTypeResolver()); 300 | context.registerBean(FormattingConversionService.class, () -> context 301 | .getBean(EnableWebFluxConfiguration.class).webFluxConversionService()); 302 | context.registerBean(Validator.class, () -> context 303 | .getBean(EnableWebFluxConfiguration.class).webFluxValidator()); 304 | context.registerBean(WebHttpHandlerBuilder.WEB_HANDLER_BEAN_NAME, 305 | DispatcherHandler.class, 306 | () -> context.getBean(EnableWebFluxConfiguration.class).webHandler()); 307 | context.registerBean(WebFluxConfigurer.class, 308 | () -> new WebFluxConfig(context.getBean(ResourceProperties.class), 309 | context.getBean(WebFluxProperties.class), context, 310 | context.getBeanProvider(HandlerMethodArgumentResolver.class), 311 | context.getBeanProvider(CodecCustomizer.class), 312 | context.getBeanProvider( 313 | ResourceHandlerRegistrationCustomizer.class), 314 | context.getBeanProvider(ViewResolver.class))); 315 | } 316 | 317 | private void registerHttpHandlerAutoConfiguration() { 318 | context.registerBean(HttpHandler.class, 319 | () -> WebHttpHandlerBuilder.applicationContext(context).build()); 320 | } 321 | 322 | private void registerDemoApplication() { 323 | context.registerBean(RouterFunction.class, () -> userEndpoints()); 324 | } 325 | 326 | private void registerGsonAutoConfiguration() { 327 | GsonAutoConfiguration config = new GsonAutoConfiguration(); 328 | context.registerBean(GsonBuilder.class, () -> config.gsonBuilder(new ArrayList<>( 329 | context.getBeansOfType(GsonBuilderCustomizer.class).values()))); 330 | context.registerBean(Gson.class, 331 | () -> config.gson(context.getBean(GsonBuilder.class))); 332 | context.registerBean(GsonBuilderCustomizer.class, () -> config 333 | .standardGsonBuilderCustomizer(context.getBean(GsonProperties.class))); 334 | } 335 | 336 | private void registerHttpMessageConvertersAutoConfiguration() { 337 | context.registerBean(HttpMessageConverters.class, () -> { 338 | HttpMessageConvertersAutoConfiguration config = new HttpMessageConvertersAutoConfiguration(); 339 | return config.messageConverters(context.getBeanProvider(ResolvableType 340 | .forClassWithGenerics(List.class, HttpMessageConverter.class))); 341 | }); 342 | context.registerBean(StringHttpMessageConverter.class, 343 | this::stringHttpMessageConverter); 344 | context.registerBean(GsonHttpMessageConverter.class, 345 | () -> new GsonHttpMessageConverter(context.getBean(Gson.class))); 346 | } 347 | 348 | StringHttpMessageConverter stringHttpMessageConverter() { 349 | StringHttpMessageConverter converter = new StringHttpMessageConverter( 350 | context.getBean(HttpProperties.class).getEncoding().getCharset()); 351 | converter.setWriteAcceptCharset(false); 352 | return converter; 353 | } 354 | 355 | private void registerWebClientAutoConfiguration() { 356 | context.registerBean(WebClient.Builder.class, () -> { 357 | WebClientAutoConfiguration config = new WebClientAutoConfiguration( 358 | context.getBeanProvider(ResolvableType.forClassWithGenerics( 359 | List.class, WebClientCustomizer.class))); 360 | return config.webClientBuilder(); 361 | }); 362 | } 363 | 364 | } 365 | -------------------------------------------------------------------------------- /src/main/java/com/example/func/ObjectProviders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.func; 17 | 18 | import java.lang.reflect.Constructor; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.springframework.beans.BeansException; 23 | import org.springframework.beans.factory.ObjectProvider; 24 | import org.springframework.beans.factory.config.DependencyDescriptor; 25 | import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; 26 | import org.springframework.context.ApplicationContext; 27 | import org.springframework.core.MethodParameter; 28 | 29 | /** 30 | * @author Dave Syer 31 | * 32 | */ 33 | public class ObjectProviders { 34 | 35 | /** 36 | * Create an {@link ObjectProvider} for the unique constructor argument in the target 37 | * class. 38 | */ 39 | public static ObjectProvider provider(ApplicationContext context, 40 | Class target) { 41 | return new LazyObjectProvider<>(context, target, -1); 42 | } 43 | 44 | /** 45 | * Create an {@link ObjectProvider} for the constructor argument with the provided 46 | * index, in the target class with a single constructor. 47 | */ 48 | public static ObjectProvider provider(ApplicationContext context, 49 | Class target, int index) { 50 | return new LazyObjectProvider<>(context, target, index); 51 | } 52 | 53 | /** 54 | * Create an {@link ObjectProvider} for the constructor argument with the provided 55 | * index, in the target class with a constructor having the parameter type provided. 56 | */ 57 | public static ObjectProvider provider(ApplicationContext context, 58 | Class target, int index, Class... params) { 59 | return new LazyObjectProvider<>(context, target, index, params); 60 | } 61 | 62 | static class LazyObjectProvider implements ObjectProvider { 63 | 64 | private Map, Constructor> constructors = new HashMap<>(); 65 | 66 | private ApplicationContext context; 67 | private Class target; 68 | private int index; 69 | private Class[] params; 70 | private ObjectProvider delegate; 71 | 72 | public LazyObjectProvider(ApplicationContext context, Class target, int index, 73 | Class... params) { 74 | this.context = context; 75 | this.target = target; 76 | this.index = index; 77 | this.params = params; 78 | } 79 | 80 | @Override 81 | public T getObject() throws BeansException { 82 | if (delegate == null) { 83 | delegate = provider(context, target, params); 84 | } 85 | return delegate.getObject(); 86 | } 87 | 88 | @Override 89 | public T getObject(Object... args) throws BeansException { 90 | if (delegate == null) { 91 | delegate = provider(context, target, params); 92 | } 93 | return delegate.getObject(args); 94 | } 95 | 96 | @Override 97 | public T getIfAvailable() throws BeansException { 98 | if (delegate == null) { 99 | delegate = provider(context, target, params); 100 | } 101 | return delegate.getIfAvailable(); 102 | } 103 | 104 | @Override 105 | public T getIfUnique() throws BeansException { 106 | if (delegate == null) { 107 | delegate = provider(context, target, params); 108 | } 109 | return delegate.getIfUnique(); 110 | } 111 | 112 | private ObjectProvider provider(ApplicationContext context, Class target, 113 | Class[] params) { 114 | Constructor constructor = constructors.computeIfAbsent(target, 115 | this::constructor); 116 | int index = index(constructor); 117 | MethodParameter methodParameter = new MethodParameter(constructor, index); 118 | @SuppressWarnings("unchecked") 119 | ObjectProvider provider = (ObjectProvider) context 120 | .getAutowireCapableBeanFactory() 121 | .resolveDependency(new DependencyDescriptor(methodParameter, false), 122 | ErrorWebExceptionHandler.class.getName()); 123 | return provider; 124 | } 125 | 126 | private int index(Constructor constructor) { 127 | if (this.index >= 0) { 128 | return this.index; 129 | } 130 | Class[] types = constructor.getParameterTypes(); 131 | for (int i = 0; i < types.length; i++) { 132 | Class type = types[i]; 133 | if (ObjectProvider.class.isAssignableFrom(type)) { 134 | return i; 135 | } 136 | } 137 | return 0; 138 | } 139 | 140 | private Constructor constructor(Class target) { 141 | Constructor constructor; 142 | try { 143 | Constructor[] constructors = target.getConstructors(); 144 | if (constructors.length == 1) { 145 | return constructors[0]; 146 | } 147 | constructor = target.getConstructor(params); 148 | } 149 | catch (Exception e) { 150 | throw new IllegalStateException("Cannot resolve constructor", e); 151 | } 152 | return constructor; 153 | } 154 | 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/example/manual/ManualApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.manual; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | import reactor.core.publisher.Mono; 7 | 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration; 10 | import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; 11 | import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; 12 | import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; 13 | import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; 14 | import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration; 15 | import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; 16 | import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration; 17 | import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration; 18 | import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration; 19 | import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; 20 | import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration; 21 | import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; 22 | import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; 23 | import org.springframework.context.ConfigurableApplicationContext; 24 | import org.springframework.context.annotation.Configuration; 25 | import org.springframework.context.annotation.Import; 26 | import org.springframework.context.support.GenericApplicationContext; 27 | import org.springframework.web.reactive.function.server.RouterFunction; 28 | 29 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 30 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 31 | import static org.springframework.web.reactive.function.server.ServerResponse.ok; 32 | 33 | /** 34 | * Imports autoconfigurations manually. Doesn't use {@link SpringApplication}. 35 | * 36 | * @author Dave Syer 37 | * 38 | */ 39 | public class ManualApplication implements Runnable, Closeable { 40 | 41 | public static final String MARKER = "Benchmark app started"; 42 | 43 | private ConfigurableApplicationContext context; 44 | 45 | public RouterFunction userEndpoints() { 46 | return route(GET("/"), request -> ok().body(Mono.just("Hello"), String.class)); 47 | } 48 | 49 | public static void main(String[] args) throws Exception { 50 | long t0 = System.currentTimeMillis(); 51 | ManualApplication bean = new ManualApplication(); 52 | bean.run(); 53 | System.err.println( 54 | "Started HttpServer: " + (System.currentTimeMillis() - t0) + "ms"); 55 | if (Boolean.getBoolean("demo.close")) { 56 | bean.close(); 57 | } 58 | } 59 | 60 | @Override 61 | public void close() throws IOException { 62 | if (context != null) { 63 | context.close(); 64 | } 65 | } 66 | 67 | @Override 68 | public void run() { 69 | this.context = create(); 70 | System.err.println(MARKER); 71 | } 72 | 73 | private ConfigurableApplicationContext create() { 74 | AnnotationConfigReactiveWebServerApplicationContext context = new AnnotationConfigReactiveWebServerApplicationContext(); 75 | registerDemoApplication(context); 76 | context.register(AutoConfiguration.class); 77 | context.refresh(); 78 | return context; 79 | } 80 | 81 | private void registerDemoApplication(GenericApplicationContext context) { 82 | context.registerBean(RouterFunction.class, () -> userEndpoints()); 83 | } 84 | 85 | @Import({ // LazyInitBeanFactoryPostProcessor.class, 86 | PropertyPlaceholderAutoConfiguration.class, 87 | ReactiveWebServerFactoryAutoConfiguration.class, 88 | CodecsAutoConfiguration.class, ErrorWebFluxAutoConfiguration.class, 89 | WebFluxAutoConfiguration.class, HttpHandlerAutoConfiguration.class, 90 | ConfigurationPropertiesAutoConfiguration.class, GsonAutoConfiguration.class, 91 | HttpMessageConvertersAutoConfiguration.class, 92 | ProjectInfoAutoConfiguration.class, ReactiveSecurityAutoConfiguration.class, 93 | EmbeddedWebServerFactoryCustomizerAutoConfiguration.class, 94 | WebClientAutoConfiguration.class }) 95 | // @EnableAutoConfiguration 96 | @Configuration 97 | public static class AutoConfiguration { 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/example/micro/MicroApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.micro; 17 | 18 | import com.example.config.ApplicationBuilder; 19 | 20 | import org.springframework.context.support.GenericApplicationContext; 21 | import org.springframework.web.reactive.function.server.RouterFunction; 22 | import org.springframework.web.reactive.function.server.RouterFunctions; 23 | import org.springframework.web.server.WebHandler; 24 | 25 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 26 | import static org.springframework.web.reactive.function.server.ServerResponse.ok; 27 | 28 | import reactor.core.publisher.Mono; 29 | 30 | /** 31 | * @author Dave Syer 32 | * 33 | */ 34 | public class MicroApplication { 35 | 36 | public static void main(String[] args) throws Exception { 37 | long t0 = System.currentTimeMillis(); 38 | GenericApplicationContext context = new GenericApplicationContext(); 39 | context.registerBean(RouterFunction.class, () -> RouterFunctions.route(GET("/"), 40 | request -> ok().body(Mono.just("Hello"), String.class))); 41 | context.registerBean("webHandler", WebHandler.class, () -> RouterFunctions 42 | .toWebHandler(context.getBean(RouterFunction.class))); 43 | context.refresh(); 44 | ApplicationBuilder.start(context, b -> { 45 | System.err.println("Started HttpServer: " + (System.currentTimeMillis() - t0) + "ms"); 46 | }); 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.boot.context.config; 18 | 19 | import org.springframework.boot.ansi.AnsiOutput; 20 | import org.springframework.boot.ansi.AnsiOutput.Enabled; 21 | import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 22 | import org.springframework.context.ApplicationListener; 23 | import org.springframework.core.Ordered; 24 | import org.springframework.core.env.ConfigurableEnvironment; 25 | 26 | /** 27 | * An {@link ApplicationListener} that configures {@link AnsiOutput} depending on the 28 | * value of the property {@code spring.output.ansi.enabled}. See {@link Enabled} for valid 29 | * values. 30 | * 31 | * @author Raphael von der Grün 32 | * @author Madhura Bhave 33 | * @since 1.2.0 34 | */ 35 | public class AnsiOutputApplicationListener 36 | implements ApplicationListener, Ordered { 37 | 38 | @Override 39 | public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { 40 | ConfigurableEnvironment environment = event.getEnvironment(); 41 | Enabled enabled = environment.getProperty("spring.output.ansi.enabled", AnsiOutput.Enabled.class); 42 | if (enabled!=null) { 43 | AnsiOutput.setEnabled(enabled); 44 | } 45 | AnsiOutput.setConsoleAvailable(environment 46 | .getProperty("spring.output.ansi.console-available", Boolean.class)); 47 | } 48 | 49 | @Override 50 | public int getOrder() { 51 | // Apply after ConfigFileApplicationListener has called EnvironmentPostProcessors 52 | return ConfigFileApplicationListener.DEFAULT_ORDER + 1; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/context/annotation/PublicConfigurationClassEnhancer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.context.annotation; 18 | 19 | /** 20 | * @author Dave Syer 21 | * 22 | */ 23 | public class PublicConfigurationClassEnhancer extends ConfigurationClassEnhancer { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/foo.components: -------------------------------------------------------------------------------- 1 | foo0=bar 2 | foo1=bar0,bar1 3 | foo2=bar0,bar1,bar2 4 | foo10=bar0,bar1,bar2,bar3 5 | foo4=bar0 6 | foo5=bar0,bar1,bar2 7 | foo6=bar0 8 | foo7=bar0,bar1,bar2 9 | foo8=bar0 10 | foo9=bar0,bar1,bar2 11 | foo10=bar 12 | foo11=bar0,bar1 13 | foo12=bar0,bar1,bar2 14 | foo13=bar0,bar1,bar2,bar3 15 | foo14=bar0 16 | foo15=bar0,bar1,bar2 17 | foo16=bar0 18 | foo17=bar0,bar1,bar2 19 | foo18=bar0 20 | foo19=bar0,bar1,bar2 21 | foo20=bar 22 | foo21=bar0,bar1 23 | foo22=bar0,bar1,bar2 24 | foo23=bar0,bar1,bar2,bar3 25 | foo24=bar0 26 | foo25=bar0,bar1,bar2 27 | foo26=bar0 28 | foo27=bar0,bar1,bar2 29 | foo28=bar0 30 | foo29=bar0,bar1,bar2 31 | foo100=bar 32 | foo101=bar0,bar1 33 | foo102=bar0,bar1,bar2 34 | foo103=bar0,bar1,bar2,bar3 35 | foo104=bar0 36 | foo105=bar0,bar1,bar2 37 | foo106=bar0 38 | foo107=bar0,bar1,bar2 39 | foo108=bar0 40 | foo109=bar0,bar1,bar2 41 | foo110=bar 42 | foo111=bar0,bar1 43 | foo112=bar0,bar1,bar2 44 | foo113=bar0,bar1,bar2,bar3 45 | foo114=bar0 46 | foo115=bar0,bar1,bar2 47 | foo116=bar0 48 | foo117=bar0,bar1,bar2 49 | foo118=bar0 50 | foo119=bar0,bar1,bar2 51 | foo120=bar 52 | foo121=bar0,bar1 53 | foo122=bar0,bar1,bar2 54 | foo123=bar0,bar1,bar2,bar3 55 | foo124=bar0 56 | foo125=bar0,bar1,bar2 57 | foo126=bar0 58 | foo127=bar0,bar1,bar2 59 | foo128=bar0 60 | foo129=bar0,bar1,bar2 61 | foo1100=bar 62 | foo1101=bar0,bar1 63 | foo1102=bar0,bar1,bar2 64 | foo1103=bar0,bar1,bar2,bar3 65 | foo1104=bar0 66 | foo1105=bar0,bar1,bar2 67 | foo1106=bar0 68 | foo1107=bar0,bar1,bar2 69 | foo1108=bar0 70 | foo1109=bar0,bar1,bar2 71 | foo1110=bar 72 | foo1111=bar0,bar1 73 | foo1112=bar0,bar1,bar2 74 | foo1113=bar0,bar1,bar2,bar3 75 | foo1114=bar0 76 | foo1115=bar0,bar1,bar2 77 | foo1116=bar0 78 | foo1117=bar0,bar1,bar2 79 | foo1118=bar0 80 | foo1119=bar0,bar1,bar2 81 | foo1120=bar 82 | foo1121=bar0,bar1 83 | foo1122=bar0,bar1,bar2 84 | foo1123=bar0,bar1,bar2,bar3 85 | foo1124=bar0 86 | foo1125=bar0,bar1,bar2 87 | foo1126=bar0 88 | foo1127=bar0,bar1,bar2 89 | foo1128=bar0 90 | foo1129=bar0,bar1,bar2 91 | foo2100=bar 92 | foo2101=bar0,bar1 93 | foo2102=bar0,bar1,bar2 94 | foo2103=bar0,bar1,bar2,bar3 95 | foo2104=bar0 96 | foo2105=bar0,bar1,bar2 97 | foo2106=bar0 98 | foo2107=bar0,bar1,bar2 99 | foo2108=bar0 100 | foo2109=bar0,bar1,bar2 101 | foo2110=bar 102 | foo2111=bar0,bar1 103 | foo2112=bar0,bar1,bar2 104 | foo2113=bar0,bar1,bar2,bar3 105 | foo2114=bar0 106 | foo2115=bar0,bar1,bar2 107 | foo2116=bar0 108 | foo2117=bar0,bar1,bar2 109 | foo2118=bar0 110 | foo2119=bar0,bar1,bar2 111 | foo2120=bar 112 | foo2121=bar0,bar1 113 | foo2122=bar0,bar1,bar2 114 | foo2123=bar0,bar1,bar2,bar3 115 | foo2124=bar0 116 | foo2125=bar0,bar1,bar2 117 | foo2126=bar0 118 | foo2127=bar0,bar1,bar2 119 | foo2128=bar0 120 | foo2129=bar0,bar1,bar2 121 | foo10=bar 122 | foo11=bar0,bar1 123 | foo12=bar0,bar1,bar2 124 | foo110=bar0,bar1,bar2,bar3 125 | foo14=bar0 126 | foo15=bar0,bar1,bar2 127 | foo16=bar0 128 | foo17=bar0,bar1,bar2 129 | foo18=bar0 130 | foo19=bar0,bar1,bar2 131 | foo110=bar 132 | foo111=bar0,bar1 133 | foo112=bar0,bar1,bar2 134 | foo113=bar0,bar1,bar2,bar3 135 | foo114=bar0 136 | foo115=bar0,bar1,bar2 137 | foo116=bar0 138 | foo117=bar0,bar1,bar2 139 | foo118=bar0 140 | foo119=bar0,bar1,bar2 141 | foo120=bar 142 | foo121=bar0,bar1 143 | foo122=bar0,bar1,bar2 144 | foo123=bar0,bar1,bar2,bar3 145 | foo124=bar0 146 | foo125=bar0,bar1,bar2 147 | foo126=bar0 148 | foo127=bar0,bar1,bar2 149 | foo128=bar0 150 | foo129=bar0,bar1,bar2 151 | foo1100=bar 152 | foo1101=bar0,bar1 153 | foo1102=bar0,bar1,bar2 154 | foo1103=bar0,bar1,bar2,bar3 155 | foo1104=bar0 156 | foo1105=bar0,bar1,bar2 157 | foo1106=bar0 158 | foo1107=bar0,bar1,bar2 159 | foo1108=bar0 160 | foo1109=bar0,bar1,bar2 161 | foo1110=bar 162 | foo1111=bar0,bar1 163 | foo1112=bar0,bar1,bar2 164 | foo1113=bar0,bar1,bar2,bar3 165 | foo1114=bar0 166 | foo1115=bar0,bar1,bar2 167 | foo1116=bar0 168 | foo1117=bar0,bar1,bar2 169 | foo1118=bar0 170 | foo1119=bar0,bar1,bar2 171 | foo1120=bar 172 | foo1121=bar0,bar1 173 | foo1122=bar0,bar1,bar2 174 | foo1123=bar0,bar1,bar2,bar3 175 | foo1124=bar0 176 | foo1125=bar0,bar1,bar2 177 | foo1126=bar0 178 | foo1127=bar0,bar1,bar2 179 | foo1128=bar0 180 | foo1129=bar0,bar1,bar2 181 | foo11100=bar 182 | foo11101=bar0,bar1 183 | foo11102=bar0,bar1,bar2 184 | foo11103=bar0,bar1,bar2,bar3 185 | foo11104=bar0 186 | foo11105=bar0,bar1,bar2 187 | foo11106=bar0 188 | foo11107=bar0,bar1,bar2 189 | foo11108=bar0 190 | foo11109=bar0,bar1,bar2 191 | foo11110=bar 192 | foo11111=bar0,bar1 193 | foo11112=bar0,bar1,bar2 194 | foo11113=bar0,bar1,bar2,bar3 195 | foo11114=bar0 196 | foo11115=bar0,bar1,bar2 197 | foo11116=bar0 198 | foo11117=bar0,bar1,bar2 199 | foo11118=bar0 200 | foo11119=bar0,bar1,bar2 201 | foo11120=bar 202 | foo11121=bar0,bar1 203 | foo11122=bar0,bar1,bar2 204 | foo11123=bar0,bar1,bar2,bar3 205 | foo11124=bar0 206 | foo11125=bar0,bar1,bar2 207 | foo11126=bar0 208 | foo11127=bar0,bar1,bar2 209 | foo11128=bar0 210 | foo11129=bar0,bar1,bar2 211 | foo12100=bar 212 | foo12101=bar0,bar1 213 | foo12102=bar0,bar1,bar2 214 | foo12103=bar0,bar1,bar2,bar3 215 | foo12104=bar0 216 | foo12105=bar0,bar1,bar2 217 | foo12106=bar0 218 | foo12107=bar0,bar1,bar2 219 | foo12108=bar0 220 | foo12109=bar0,bar1,bar2 221 | foo12110=bar 222 | foo12111=bar0,bar1 223 | foo12112=bar0,bar1,bar2 224 | foo12113=bar0,bar1,bar2,bar3 225 | foo12114=bar0 226 | foo12115=bar0,bar1,bar2 227 | foo12116=bar0 228 | foo12117=bar0,bar1,bar2 229 | foo12118=bar0 230 | foo12119=bar0,bar1,bar2 231 | foo12120=bar 232 | foo12121=bar0,bar1 233 | foo12122=bar0,bar1,bar2 234 | foo12123=bar0,bar1,bar2,bar3 235 | foo12124=bar0 236 | foo12125=bar0,bar1,bar2 237 | foo12126=bar0 238 | foo12127=bar0,bar1,bar2 239 | foo12128=bar0 240 | foo12129=bar0,bar1,bar2 241 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.example.config.ComponentIndexer: -------------------------------------------------------------------------------- 1 | com.example.bench.IndexAnnotationBenchmark$Indexer -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.context.ApplicationListener=\ 2 | com.example.config.BeanCountingApplicationListener,\ 3 | com.example.config.StartupApplicationListener,\ 4 | com.example.config.ShutdownApplicationListener 5 | 6 | # BeanInfo Factory (doesn't have much of an impact) 7 | #org.springframework.beans.BeanInfoFactory=\ 8 | #com.example.config.SpringBeanInfoFactory 9 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # logging.level.org.springframework=DEBUG 2 | # logging.level.com.example=DEBUG 3 | # logging.level.org.springframework.beans.factory.support=DEBUG 4 | #management.security.enabled=false -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/resources/org/aspectj/aop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/test/java/com/example/bench/LauncherStateTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.bench; 18 | 19 | import com.example.auto.AutoApplication; 20 | import com.example.boot.BootApplication; 21 | import com.example.func.FuncApplication; 22 | import org.junit.jupiter.api.AfterEach; 23 | import org.junit.jupiter.api.BeforeEach; 24 | import org.junit.jupiter.api.Test; 25 | import org.junit.jupiter.api.extension.ExtendWith; 26 | 27 | import org.springframework.boot.test.system.CapturedOutput; 28 | import org.springframework.boot.test.system.OutputCaptureExtension; 29 | 30 | import static org.assertj.core.api.Assertions.assertThat; 31 | 32 | /** 33 | * @author Dave Syer 34 | * 35 | */ 36 | @ExtendWith(OutputCaptureExtension.class) 37 | public class LauncherStateTests { 38 | 39 | private LauncherState state; 40 | 41 | @BeforeEach 42 | public void init() throws Exception { 43 | state = new LauncherState(); 44 | state.start(); 45 | } 46 | 47 | @AfterEach 48 | public void close() throws Exception { 49 | if (state != null) { 50 | state.close(); 51 | } 52 | } 53 | 54 | @Test 55 | public void isolated(CapturedOutput output) throws Exception { 56 | state.isolated(); 57 | assertThat(output.toString()).contains("Benchmark app started"); 58 | } 59 | 60 | @Test 61 | public void shared(CapturedOutput output) throws Exception { 62 | // System.setProperty("bench.args", "-verbose:class"); 63 | state.shared(); 64 | assertThat(output.toString()).contains("Benchmark app started"); 65 | } 66 | 67 | @Test 68 | public void func(CapturedOutput output) throws Exception { 69 | // System.setProperty("bench.args", "-verbose:class"); 70 | state.setMainClass(FuncApplication.class); 71 | state.shared(); 72 | assertThat(output.toString()).contains("Benchmark app started"); 73 | } 74 | 75 | @Test 76 | public void boot(CapturedOutput output) throws Exception { 77 | // System.setProperty("bench.args", "-verbose:class"); 78 | state.setMainClass(BootApplication.class); 79 | state.shared(); 80 | assertThat(output.toString()).contains("Benchmark app started"); 81 | } 82 | 83 | @Test 84 | public void auto(CapturedOutput output) throws Exception { 85 | // System.setProperty("bench.args", "-verbose:class"); 86 | state.setMainClass(AutoApplication.class); 87 | state.shared(); 88 | assertThat(output.toString()).contains("Benchmark app started"); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.annotation.DirtiesContext; 9 | import org.springframework.test.web.reactive.server.WebTestClient; 10 | 11 | @SpringBootTest 12 | @DirtiesContext 13 | @AutoConfigureWebTestClient 14 | public class DemoApplicationTests { 15 | 16 | @Autowired 17 | private WebTestClient rest; 18 | 19 | @Test 20 | public void contextLoads() throws Exception { 21 | rest.get().uri("/").exchange().expectBody(String.class).isEqualTo("Hello"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/example/demo/StaticApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.util.LinkedHashSet; 4 | import java.util.Map.Entry; 5 | import java.util.Set; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; 11 | import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes; 12 | import org.springframework.context.ConfigurableApplicationContext; 13 | import org.springframework.util.StringUtils; 14 | 15 | public class StaticApplicationTests { 16 | 17 | @Test 18 | public void test() throws Exception { 19 | StringBuilder builder = new StringBuilder("package com.sample;\n\n" 20 | + "import org.springframework.boot.SpringApplication;\n" 21 | + "import org.springframework.context.annotation.Configuration;\n" 22 | + "import org.springframework.context.annotation.Import;\n"); 23 | Set imports = new LinkedHashSet<>(); 24 | Set configs = new LinkedHashSet<>(); 25 | ConfigurableApplicationContext context = SpringApplication 26 | .run(DemoApplication.class, "--debug", "--server.port=0"); 27 | ConditionEvaluationReport report = context 28 | .getBean(ConditionEvaluationReport.class); 29 | for (Entry entry : report 30 | .getConditionAndOutcomesBySource().entrySet()) { 31 | ConditionAndOutcomes outcomes = entry.getValue(); 32 | String name = entry.getKey(); 33 | if (outcomes.isFullMatch() && isAutoConfig(name)) { 34 | imports.add(name); 35 | configs.add(StringUtils.getFilenameExtension(name)); 36 | } 37 | } 38 | for (String type : report.getUnconditionalClasses()) { 39 | imports.add(type); 40 | configs.add(StringUtils.getFilenameExtension(type)); 41 | } 42 | for (String string : imports) { 43 | builder.append("import " + string + ";\n"); 44 | } 45 | builder.append("\n@Configuration\n"); 46 | builder.append("@Import({"); 47 | builder.append( 48 | StringUtils.collectionToDelimitedString(configs, ".class, \n ")); 49 | builder.append(".class})\n"); 50 | builder.append("public class SlimApplication {\n" + "\n" 51 | + " public static void main(String[] args) {\n" 52 | + " SpringApplication.run(SlimApplication.class, args);\n" + " }\n" 53 | + "}\n" + ""); 54 | context.close(); 55 | System.err.println(builder); 56 | } 57 | 58 | private boolean isAutoConfig(String name) { 59 | return !name.contains("#") && !name.contains("$") 60 | && name.contains("AutoConfiguration"); 61 | } 62 | } 63 | --------------------------------------------------------------------------------