initial commit
This commit is contained in:
commit
0bb5cf504b
258 changed files with 39412 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/data
|
||||||
|
/work
|
||||||
|
/logs
|
||||||
|
/.idea
|
||||||
|
/target
|
||||||
|
.DS_Store
|
||||||
|
*.iml
|
||||||
|
/.settings
|
||||||
|
/.classpath
|
||||||
|
/.project
|
||||||
|
/.gradle
|
||||||
|
/build
|
7
.travis.yml
Normal file
7
.travis.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
sudo: false
|
||||||
|
language: java
|
||||||
|
jdk:
|
||||||
|
- oraclejdk8
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.m2
|
202
LICENSE.txt
Normal file
202
LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
32
README.md
Normal file
32
README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
= Guice
|
||||||
|
|
||||||
|
This is a xbib Guice, a build of Guice with the following differences to the
|
||||||
|
original [Google Guice Core Library](https://github.com/google/guice):
|
||||||
|
|
||||||
|
- only external dependencies are
|
||||||
|
-- javax.inject:javax.inject:1
|
||||||
|
-- javax.annotation:javax.annotation-api:1.2
|
||||||
|
-- com.google.guava:guava:19.0
|
||||||
|
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
|
||||||
|
- removed all logging, uses of java.util.logger.* stuff and logger default binding
|
||||||
|
- removed all java.io.Serializable dependent stuff
|
||||||
|
- reformatted source by IntelliJ and cleaned up source of @author and @since tags
|
||||||
|
- target of compilation is Java 8, no support for Java 6/7
|
||||||
|
- introduced Gradle as build system
|
||||||
|
- junit tests made compatible to junit 4.12
|
||||||
|
|
||||||
|
All credits belong to the original authors, especially Bob Lee http://blog.crazybob.org/
|
||||||
|
|
||||||
|
= License
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
90
build.gradle
Normal file
90
build.gradle
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
|
||||||
|
def xbibGroup = 'org.xbib'
|
||||||
|
def xbibVersion = '4.0'
|
||||||
|
|
||||||
|
group = xbibGroup
|
||||||
|
version = xbibVersion
|
||||||
|
|
||||||
|
println "Current JVM: " + org.gradle.internal.jvm.Jvm.current()
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'maven'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
maven {
|
||||||
|
url "http://xbib.org/repository"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
wagon
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile "javax.inject:javax.inject:1"
|
||||||
|
compile 'javax.annotation:javax.annotation-api:1.2'
|
||||||
|
compile "com.google.guava:guava:19.0"
|
||||||
|
testCompile "junit:junit:4.12"
|
||||||
|
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.5"
|
||||||
|
testCompile "org.apache.logging.log4j:log4j-core:2.5"
|
||||||
|
testCompile "javax.inject:javax.inject-tck:1"
|
||||||
|
testCompile "com.google.guava:guava-testlib:19.0"
|
||||||
|
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
|
[compileJava, compileTestJava]*.options.collect { options ->
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
options.compilerArgs << '-Xlint:-serial,-path,-rawtypes,-unchecked'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
exclude '*$*'
|
||||||
|
exclude '**/ErrorHandlingTest*'
|
||||||
|
exclude '**/OSGiContainerTest*'
|
||||||
|
exclude '**/ScopesTest*'
|
||||||
|
exclude '**/TypeConversionTest*'
|
||||||
|
testLogging {
|
||||||
|
showStandardStreams = false
|
||||||
|
exceptionFormat = 'full'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||||
|
from sourceSets.main.allSource
|
||||||
|
classifier 'sources'
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
archives sourcesJar
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadArchives {
|
||||||
|
repositories {
|
||||||
|
if (project.hasProperty("xbibUsername")) {
|
||||||
|
mavenDeployer {
|
||||||
|
configuration = configurations.wagon
|
||||||
|
repository(
|
||||||
|
id: 'xbib.org',
|
||||||
|
url: uri('scpexe://xbib.org/repository'),
|
||||||
|
authentication: [userName: xbibUsername, privateKey: xbibPrivateKey]
|
||||||
|
)
|
||||||
|
pom.project {
|
||||||
|
inceptionYear '2016'
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name 'The Apache Software License, Version 2.0'
|
||||||
|
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
distribution 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
pom.xml
Normal file
100
pom.xml
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.xbib</groupId>
|
||||||
|
<artifactId>guice</artifactId>
|
||||||
|
<version>4.0</version>
|
||||||
|
|
||||||
|
<name>Guice</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.inject</groupId>
|
||||||
|
<artifactId>javax.inject</artifactId>
|
||||||
|
<version>1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
<version>1.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>19.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.inject</groupId>
|
||||||
|
<artifactId>javax.inject-tck</artifactId>
|
||||||
|
<version>1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava-testlib</artifactId>
|
||||||
|
<version>19.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.3</version>
|
||||||
|
<configuration>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<optimize>true</optimize>
|
||||||
|
<debug>true</debug>
|
||||||
|
<showDeprecation>true</showDeprecation>
|
||||||
|
<showWarnings>true</showWarnings>
|
||||||
|
<compilerArgument>-Xlint:all,-serial,-path,-rawtypes,-unchecked</compilerArgument>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.19.1</version>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>**/*$*</exclude>
|
||||||
|
<exclude>**/ErrorHandlingTest*</exclude>
|
||||||
|
<exclude>**/OSGiContainerTest*</exclude>
|
||||||
|
<exclude>**/ScopesTest*</exclude>
|
||||||
|
<exclude>**/TypeConversionTest*</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>LICENSE</exclude>
|
||||||
|
<exclude>NOTICE</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>2.4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.10.3</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'guice'
|
220
src/main/java/com/google/inject/AbstractModule.java
Normal file
220
src/main/java/com/google/inject/AbstractModule.java
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.binder.AnnotatedBindingBuilder;
|
||||||
|
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
||||||
|
import com.google.inject.binder.LinkedBindingBuilder;
|
||||||
|
import com.google.inject.matcher.Matcher;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import com.google.inject.spi.ProvisionListener;
|
||||||
|
import com.google.inject.spi.TypeConverter;
|
||||||
|
import com.google.inject.spi.TypeListener;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A support class for {@link Module}s which reduces repetition and results in
|
||||||
|
* a more readable configuration. Simply extend this class, implement {@link
|
||||||
|
* #configure()}, and call the inherited methods which mirror those found in
|
||||||
|
* {@link Binder}. For example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class MyModule extends AbstractModule {
|
||||||
|
* protected void configure() {
|
||||||
|
* bind(Service.class).to(ServiceImpl.class).in(Singleton.class);
|
||||||
|
* bind(CreditCardPaymentService.class);
|
||||||
|
* bind(PaymentService.class).to(CreditCardPaymentService.class);
|
||||||
|
* bindConstant().annotatedWith(Names.named("port")).to(8080);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AbstractModule implements Module {
|
||||||
|
|
||||||
|
Binder binder;
|
||||||
|
|
||||||
|
public final synchronized void configure(Binder builder) {
|
||||||
|
checkState(this.binder == null, "Re-entry is not allowed.");
|
||||||
|
|
||||||
|
this.binder = checkNotNull(builder, "builder");
|
||||||
|
try {
|
||||||
|
configure();
|
||||||
|
} finally {
|
||||||
|
this.binder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a {@link Binder} via the exposed methods.
|
||||||
|
*/
|
||||||
|
protected abstract void configure();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets direct access to the underlying {@code Binder}.
|
||||||
|
*/
|
||||||
|
protected Binder binder() {
|
||||||
|
checkState(binder != null, "The binder can only be used inside configure()");
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindScope(Class, Scope)
|
||||||
|
*/
|
||||||
|
protected void bindScope(Class<? extends Annotation> scopeAnnotation,
|
||||||
|
Scope scope) {
|
||||||
|
binder().bindScope(scopeAnnotation, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bind(Key)
|
||||||
|
*/
|
||||||
|
protected <T> LinkedBindingBuilder<T> bind(Key<T> key) {
|
||||||
|
return binder().bind(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bind(TypeLiteral)
|
||||||
|
*/
|
||||||
|
protected <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
|
||||||
|
return binder().bind(typeLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bind(Class)
|
||||||
|
*/
|
||||||
|
protected <T> AnnotatedBindingBuilder<T> bind(Class<T> clazz) {
|
||||||
|
return binder().bind(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindConstant()
|
||||||
|
*/
|
||||||
|
protected AnnotatedConstantBindingBuilder bindConstant() {
|
||||||
|
return binder().bindConstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#install(Module)
|
||||||
|
*/
|
||||||
|
protected void install(Module module) {
|
||||||
|
binder().install(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#addError(String, Object[])
|
||||||
|
*/
|
||||||
|
protected void addError(String message, Object... arguments) {
|
||||||
|
binder().addError(message, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#addError(Throwable)
|
||||||
|
*/
|
||||||
|
protected void addError(Throwable t) {
|
||||||
|
binder().addError(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#addError(Message)
|
||||||
|
*/
|
||||||
|
protected void addError(Message message) {
|
||||||
|
binder().addError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#requestInjection(Object)
|
||||||
|
*/
|
||||||
|
protected void requestInjection(Object instance) {
|
||||||
|
binder().requestInjection(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#requestStaticInjection(Class[])
|
||||||
|
*/
|
||||||
|
protected void requestStaticInjection(Class<?>... types) {
|
||||||
|
binder().requestStaticInjection(types);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a dependency from this module to {@code key}. When the injector is
|
||||||
|
* created, Guice will report an error if {@code key} cannot be injected.
|
||||||
|
* Note that this requirement may be satisfied by implicit binding, such as
|
||||||
|
* a public no-arguments constructor.
|
||||||
|
*/
|
||||||
|
protected void requireBinding(Key<?> key) {
|
||||||
|
binder().getProvider(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a dependency from this module to {@code type}. When the injector is
|
||||||
|
* created, Guice will report an error if {@code type} cannot be injected.
|
||||||
|
* Note that this requirement may be satisfied by implicit binding, such as
|
||||||
|
* a public no-arguments constructor.
|
||||||
|
*/
|
||||||
|
protected void requireBinding(Class<?> type) {
|
||||||
|
binder().getProvider(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getProvider(Key)
|
||||||
|
*/
|
||||||
|
protected <T> Provider<T> getProvider(Key<T> key) {
|
||||||
|
return binder().getProvider(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getProvider(Class)
|
||||||
|
*/
|
||||||
|
protected <T> Provider<T> getProvider(Class<T> type) {
|
||||||
|
return binder().getProvider(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#convertToTypes
|
||||||
|
*/
|
||||||
|
protected void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
|
||||||
|
TypeConverter converter) {
|
||||||
|
binder().convertToTypes(typeMatcher, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#currentStage()
|
||||||
|
*/
|
||||||
|
protected Stage currentStage() {
|
||||||
|
return binder().currentStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getMembersInjector(Class)
|
||||||
|
*/
|
||||||
|
protected <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
|
return binder().getMembersInjector(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getMembersInjector(TypeLiteral)
|
||||||
|
*/
|
||||||
|
protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
||||||
|
return binder().getMembersInjector(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindListener(com.google.inject.matcher.Matcher,
|
||||||
|
* com.google.inject.spi.TypeListener)
|
||||||
|
*/
|
||||||
|
protected void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
|
||||||
|
TypeListener listener) {
|
||||||
|
binder().bindListener(typeMatcher, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindListener(Matcher, ProvisionListener...)
|
||||||
|
*/
|
||||||
|
protected void bindListener(Matcher<? super Binding<?>> bindingMatcher,
|
||||||
|
ProvisionListener... listener) {
|
||||||
|
binder().bindListener(bindingMatcher, listener);
|
||||||
|
}
|
||||||
|
}
|
451
src/main/java/com/google/inject/Binder.java
Normal file
451
src/main/java/com/google/inject/Binder.java
Normal file
|
@ -0,0 +1,451 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.binder.AnnotatedBindingBuilder;
|
||||||
|
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
||||||
|
import com.google.inject.binder.LinkedBindingBuilder;
|
||||||
|
import com.google.inject.matcher.Matcher;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
|
||||||
|
import com.google.inject.spi.ProvisionListener;
|
||||||
|
import com.google.inject.spi.TypeConverter;
|
||||||
|
import com.google.inject.spi.TypeListener;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects configuration information (primarily <i>bindings</i>) which will be
|
||||||
|
* used to create an {@link Injector}. Guice provides this object to your
|
||||||
|
* application's {@link Module} implementors so they may each contribute
|
||||||
|
* their own bindings and other registrations.
|
||||||
|
*
|
||||||
|
* <h3>The Guice Binding EDSL</h3>
|
||||||
|
*
|
||||||
|
* Guice uses an <i>embedded domain-specific language</i>, or EDSL, to help you
|
||||||
|
* create bindings simply and readably. This approach is great for overall
|
||||||
|
* usability, but it does come with a small cost: <b>it is difficult to
|
||||||
|
* learn how to use the Binding EDSL by reading
|
||||||
|
* method-level javadocs</b>. Instead, you should consult the series of
|
||||||
|
* examples below. To save space, these examples omit the opening
|
||||||
|
* {@code binder}, just as you will if your module extends
|
||||||
|
* {@link AbstractModule}.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(ServiceImpl.class);</pre>
|
||||||
|
*
|
||||||
|
* This statement does essentially nothing; it "binds the {@code ServiceImpl}
|
||||||
|
* class to itself" and does not change Guice's default behavior. You may still
|
||||||
|
* want to use this if you prefer your {@link Module} class to serve as an
|
||||||
|
* explicit <i>manifest</i> for the services it provides. Also, in rare cases,
|
||||||
|
* Guice may be unable to validate a binding at injector creation time unless it
|
||||||
|
* is given explicitly.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(Service.class).to(ServiceImpl.class);</pre>
|
||||||
|
*
|
||||||
|
* Specifies that a request for a {@code Service} instance with no binding
|
||||||
|
* annotations should be treated as if it were a request for a
|
||||||
|
* {@code ServiceImpl} instance. This <i>overrides</i> the function of any
|
||||||
|
* {@link ImplementedBy @ImplementedBy} or {@link ProvidedBy @ProvidedBy}
|
||||||
|
* annotations found on {@code Service}, since Guice will have already
|
||||||
|
* "moved on" to {@code ServiceImpl} before it reaches the point when it starts
|
||||||
|
* looking for these annotations.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(Service.class).toProvider(ServiceProvider.class);</pre>
|
||||||
|
*
|
||||||
|
* In this example, {@code ServiceProvider} must extend or implement
|
||||||
|
* {@code Provider<Service>}. This binding specifies that Guice should resolve
|
||||||
|
* an unannotated injection request for {@code Service} by first resolving an
|
||||||
|
* instance of {@code ServiceProvider} in the regular way, then calling
|
||||||
|
* {@link Provider#get get()} on the resulting Provider instance to obtain the
|
||||||
|
* {@code Service} instance.
|
||||||
|
*
|
||||||
|
* <p>The {@link Provider} you use here does not have to be a "factory"; that
|
||||||
|
* is, a provider which always <i>creates</i> each instance it provides.
|
||||||
|
* However, this is generally a good practice to follow. You can then use
|
||||||
|
* Guice's concept of {@link Scope scopes} to guide when creation should happen
|
||||||
|
* -- "letting Guice work for you".
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(Service.class).annotatedWith(Red.class).to(ServiceImpl.class);</pre>
|
||||||
|
*
|
||||||
|
* Like the previous example, but only applies to injection requests that use
|
||||||
|
* the binding annotation {@code @Red}. If your module also includes bindings
|
||||||
|
* for particular <i>values</i> of the {@code @Red} annotation (see below),
|
||||||
|
* then this binding will serve as a "catch-all" for any values of {@code @Red}
|
||||||
|
* that have no exact match in the bindings.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(ServiceImpl.class).in(Singleton.class);
|
||||||
|
* // or, alternatively
|
||||||
|
* bind(ServiceImpl.class).in(Scopes.SINGLETON);</pre>
|
||||||
|
*
|
||||||
|
* Either of these statements places the {@code ServiceImpl} class into
|
||||||
|
* singleton scope. Guice will create only one instance of {@code ServiceImpl}
|
||||||
|
* and will reuse it for all injection requests of this type. Note that it is
|
||||||
|
* still possible to bind another instance of {@code ServiceImpl} if the second
|
||||||
|
* binding is qualified by an annotation as in the previous example. Guice is
|
||||||
|
* not overly concerned with <i>preventing</i> you from creating multiple
|
||||||
|
* instances of your "singletons", only with <i>enabling</i> your application to
|
||||||
|
* share only one instance if that's all you tell Guice you need.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> a scope specified in this way <i>overrides</i> any scope that
|
||||||
|
* was specified with an annotation on the {@code ServiceImpl} class.
|
||||||
|
*
|
||||||
|
* <p>Besides {@link Singleton}/{@link Scopes#SINGLETON}, there are
|
||||||
|
* servlet-specific scopes available in
|
||||||
|
* {@code com.google.inject.servlet.ServletScopes}, and your Modules can
|
||||||
|
* contribute their own custom scopes for use here as well.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(new TypeLiteral<PaymentService<CreditCard>>() {})
|
||||||
|
* .to(CreditCardPaymentService.class);</pre>
|
||||||
|
*
|
||||||
|
* This admittedly odd construct is the way to bind a parameterized type. It
|
||||||
|
* tells Guice how to honor an injection request for an element of type
|
||||||
|
* {@code PaymentService<CreditCard>}. The class
|
||||||
|
* {@code CreditCardPaymentService} must implement the
|
||||||
|
* {@code PaymentService<CreditCard>} interface. Guice cannot currently bind or
|
||||||
|
* inject a generic type, such as {@code Set<E>}; all type parameters must be
|
||||||
|
* fully specified.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(Service.class).toInstance(new ServiceImpl());
|
||||||
|
* // or, alternatively
|
||||||
|
* bind(Service.class).toInstance(SomeLegacyRegistry.getService());</pre>
|
||||||
|
*
|
||||||
|
* In this example, your module itself, <i>not Guice</i>, takes responsibility
|
||||||
|
* for obtaining a {@code ServiceImpl} instance, then asks Guice to always use
|
||||||
|
* this single instance to fulfill all {@code Service} injection requests. When
|
||||||
|
* the {@link Injector} is created, it will automatically perform field
|
||||||
|
* and method injection for this instance, but any injectable constructor on
|
||||||
|
* {@code ServiceImpl} is simply ignored. Note that using this approach results
|
||||||
|
* in "eager loading" behavior that you can't control.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bindConstant().annotatedWith(ServerHost.class).to(args[0]);</pre>
|
||||||
|
*
|
||||||
|
* Sets up a constant binding. Constant injections must always be annotated.
|
||||||
|
* When a constant binding's value is a string, it is eligile for conversion to
|
||||||
|
* all primitive types, to {@link Enum#valueOf(Class, String) all enums}, and to
|
||||||
|
* {@link Class#forName class literals}. Conversions for other types can be
|
||||||
|
* configured using {@link #convertToTypes(Matcher, TypeConverter)
|
||||||
|
* convertToTypes()}.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@literal @}Color("red") Color red; // A member variable (field)
|
||||||
|
* . . .
|
||||||
|
* red = MyModule.class.getDeclaredField("red").getAnnotation(Color.class);
|
||||||
|
* bind(Service.class).annotatedWith(red).to(RedService.class);</pre>
|
||||||
|
*
|
||||||
|
* If your binding annotation has parameters you can apply different bindings to
|
||||||
|
* different specific values of your annotation. Getting your hands on the
|
||||||
|
* right instance of the annotation is a bit of a pain -- one approach, shown
|
||||||
|
* above, is to apply a prototype annotation to a field in your module class, so
|
||||||
|
* that you can read this annotation instance and give it to Guice.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(Service.class)
|
||||||
|
* .annotatedWith(Names.named("blue"))
|
||||||
|
* .to(BlueService.class);</pre>
|
||||||
|
*
|
||||||
|
* Differentiating by names is a common enough use case that we provided a
|
||||||
|
* standard annotation, {@link com.google.inject.name.Named @Named}. Because of
|
||||||
|
* Guice's library support, binding by name is quite easier than in the
|
||||||
|
* arbitrary binding annotation case we just saw. However, remember that these
|
||||||
|
* names will live in a single flat namespace with all the other names used in
|
||||||
|
* your application.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Constructor<T> loneCtor = getLoneCtorFromServiceImplViaReflection();
|
||||||
|
* bind(ServiceImpl.class)
|
||||||
|
* .toConstructor(loneCtor);</pre>
|
||||||
|
*
|
||||||
|
* In this example, we directly tell Guice which constructor to use in a concrete
|
||||||
|
* class implementation. It means that we do not need to place {@literal @}Inject
|
||||||
|
* on any of the constructors and that Guice treats the provided constructor as though
|
||||||
|
* it were annotated so. It is useful for cases where you cannot modify existing
|
||||||
|
* classes and is a bit simpler than using a {@link Provider}.
|
||||||
|
*
|
||||||
|
* <p>The above list of examples is far from exhaustive. If you can think of
|
||||||
|
* how the concepts of one example might coexist with the concepts from another,
|
||||||
|
* you can most likely weave the two together. If the two concepts make no
|
||||||
|
* sense with each other, you most likely won't be able to do it. In a few
|
||||||
|
* cases Guice will let something bogus slip by, and will then inform you of
|
||||||
|
* the problems at runtime, as soon as you try to create your Injector.
|
||||||
|
*
|
||||||
|
* <p>The other methods of Binder such as {@link #bindScope},
|
||||||
|
* {@link #install}, {@link #requestStaticInjection},
|
||||||
|
* {@link #addError} and {@link #currentStage} are not part of the Binding EDSL;
|
||||||
|
* you can learn how to use these in the usual way, from the method
|
||||||
|
* documentation.
|
||||||
|
*/
|
||||||
|
public interface Binder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a scope to an annotation.
|
||||||
|
*/
|
||||||
|
void bindScope(Class<? extends Annotation> annotationType, Scope scope);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link Binder}.
|
||||||
|
*/
|
||||||
|
<T> LinkedBindingBuilder<T> bind(Key<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link Binder}.
|
||||||
|
*/
|
||||||
|
<T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link Binder}.
|
||||||
|
*/
|
||||||
|
<T> AnnotatedBindingBuilder<T> bind(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link Binder}.
|
||||||
|
*/
|
||||||
|
AnnotatedConstantBindingBuilder bindConstant();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upon successful creation, the {@link Injector} will inject instance fields
|
||||||
|
* and methods of the given object.
|
||||||
|
*
|
||||||
|
* @param type of instance
|
||||||
|
* @param instance for which members will be injected
|
||||||
|
*/
|
||||||
|
<T> void requestInjection(TypeLiteral<T> type, T instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upon successful creation, the {@link Injector} will inject instance fields
|
||||||
|
* and methods of the given object.
|
||||||
|
*
|
||||||
|
* @param instance for which members will be injected
|
||||||
|
*/
|
||||||
|
void requestInjection(Object instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upon successful creation, the {@link Injector} will inject static fields
|
||||||
|
* and methods in the given classes.
|
||||||
|
*
|
||||||
|
* @param types for which static members will be injected
|
||||||
|
*/
|
||||||
|
void requestStaticInjection(Class<?>... types);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given module to configure more bindings.
|
||||||
|
*/
|
||||||
|
void install(Module module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current stage.
|
||||||
|
*/
|
||||||
|
Stage currentStage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records an error message which will be presented to the user at a later
|
||||||
|
* time. Unlike throwing an exception, this enable us to continue
|
||||||
|
* configuring the Injector and discover more errors. Uses {@link
|
||||||
|
* String#format(String, Object[])} to insert the arguments into the
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
void addError(String message, Object... arguments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records an exception, the full details of which will be logged, and the
|
||||||
|
* message of which will be presented to the user at a later
|
||||||
|
* time. If your Module calls something that you worry may fail, you should
|
||||||
|
* catch the exception and pass it into this.
|
||||||
|
*/
|
||||||
|
void addError(Throwable t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records an error message to be presented to the user at a later time.
|
||||||
|
*/
|
||||||
|
void addError(Message message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider used to obtain instances for the given injection key.
|
||||||
|
* The returned provider will not be valid until the {@link Injector} has been
|
||||||
|
* created. The provider will throw an {@code IllegalStateException} if you
|
||||||
|
* try to use it beforehand.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
<T> Provider<T> getProvider(Key<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider used to obtain instances for the given injection key.
|
||||||
|
* The returned provider will be attached to the injection point and will
|
||||||
|
* follow the nullability specified in the dependency.
|
||||||
|
* Additionally, the returned provider will not be valid until the {@link Injector}
|
||||||
|
* has been created. The provider will throw an {@code IllegalStateException} if you
|
||||||
|
* try to use it beforehand.
|
||||||
|
*/
|
||||||
|
<T> Provider<T> getProvider(Dependency<T> dependency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider used to obtain instances for the given injection type.
|
||||||
|
* The returned provider will not be valid until the {@link Injector} has been
|
||||||
|
* created. The provider will throw an {@code IllegalStateException} if you
|
||||||
|
* try to use it beforehand.
|
||||||
|
*/
|
||||||
|
<T> Provider<T> getProvider(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the members injector used to inject dependencies into methods and fields on instances
|
||||||
|
* of the given type {@code T}. The returned members injector will not be valid until the main
|
||||||
|
* {@link Injector} has been created. The members injector will throw an {@code
|
||||||
|
* IllegalStateException} if you try to use it beforehand.
|
||||||
|
*
|
||||||
|
* @param typeLiteral type to get members injector for
|
||||||
|
*/
|
||||||
|
<T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the members injector used to inject dependencies into methods and fields on instances
|
||||||
|
* of the given type {@code T}. The returned members injector will not be valid until the main
|
||||||
|
* {@link Injector} has been created. The members injector will throw an {@code
|
||||||
|
* IllegalStateException} if you try to use it beforehand.
|
||||||
|
*
|
||||||
|
* @param type type to get members injector for
|
||||||
|
*/
|
||||||
|
<T> MembersInjector<T> getMembersInjector(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a type converter. The injector will use the given converter to
|
||||||
|
* convert string constants to matching types as needed.
|
||||||
|
*
|
||||||
|
* @param typeMatcher matches types the converter can handle
|
||||||
|
* @param converter converts values
|
||||||
|
*/
|
||||||
|
void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
|
||||||
|
TypeConverter converter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener for injectable types. Guice will notify the listener when it encounters
|
||||||
|
* injectable types matched by the given type matcher.
|
||||||
|
*
|
||||||
|
* @param typeMatcher that matches injectable types the listener should be notified of
|
||||||
|
* @param listener for injectable types matched by typeMatcher
|
||||||
|
*/
|
||||||
|
void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
|
||||||
|
TypeListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers listeners for provisioned objects. Guice will notify the
|
||||||
|
* listeners just before and after the object is provisioned. Provisioned
|
||||||
|
* objects that are also injectable (everything except objects provided
|
||||||
|
* through Providers) can also be notified through TypeListeners registered in
|
||||||
|
* {@link #bindListener}.
|
||||||
|
*
|
||||||
|
* @param bindingMatcher that matches bindings of provisioned objects the listener
|
||||||
|
* should be notified of
|
||||||
|
* @param listeners for provisioned objects matched by bindingMatcher
|
||||||
|
*/
|
||||||
|
void bindListener(Matcher<? super Binding<?>> bindingMatcher, ProvisionListener... listeners);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a binder that uses {@code source} as the reference location for
|
||||||
|
* configuration errors. This is typically a {@link StackTraceElement}
|
||||||
|
* for {@code .java} source but it could any binding source, such as the
|
||||||
|
* path to a {@code .properties} file.
|
||||||
|
*
|
||||||
|
* @param source any object representing the source location and has a
|
||||||
|
* concise {@link Object#toString() toString()} value
|
||||||
|
* @return a binder that shares its configuration with this binder
|
||||||
|
*/
|
||||||
|
Binder withSource(Object source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a binder that skips {@code classesToSkip} when identify the
|
||||||
|
* calling code. The caller's {@link StackTraceElement} is used to locate
|
||||||
|
* the source of configuration errors.
|
||||||
|
*
|
||||||
|
* @param classesToSkip library classes that create bindings on behalf of
|
||||||
|
* their clients.
|
||||||
|
* @return a binder that shares its configuration with this binder.
|
||||||
|
*/
|
||||||
|
Binder skipSources(Class... classesToSkip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new private child environment for bindings and other configuration. The returned
|
||||||
|
* binder can be used to add and configuration information in this environment. See {@link
|
||||||
|
* PrivateModule} for details.
|
||||||
|
*
|
||||||
|
* @return a binder that inherits configuration from this binder. Only exposed configuration on
|
||||||
|
* the returned binder will be visible to this binder.
|
||||||
|
*/
|
||||||
|
PrivateBinder newPrivateBinder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs the Injector that bindings must be listed in a Module in order to
|
||||||
|
* be injected. Classes that are not explicitly bound in a module cannot be
|
||||||
|
* injected. Bindings created through a linked binding
|
||||||
|
* (<code>bind(Foo.class).to(FooImpl.class)</code>) are allowed, but the
|
||||||
|
* implicit binding (<code>FooImpl</code>) cannot be directly injected unless
|
||||||
|
* it is also explicitly bound (<code>bind(FooImpl.class)</code>).
|
||||||
|
* <p>
|
||||||
|
* Tools can still retrieve bindings for implicit bindings (bindings created
|
||||||
|
* through a linked binding) if explicit bindings are required, however
|
||||||
|
* {@link Binding#getProvider} will fail.
|
||||||
|
* <p>
|
||||||
|
* By default, explicit bindings are not required.
|
||||||
|
* <p>
|
||||||
|
* If a parent injector requires explicit bindings, then all child injectors
|
||||||
|
* (and private modules within that injector) also require explicit bindings.
|
||||||
|
* If a parent does not require explicit bindings, a child injector or private
|
||||||
|
* module may optionally declare itself as requiring explicit bindings. If it
|
||||||
|
* does, the behavior is limited only to that child or any grandchildren. No
|
||||||
|
* siblings of the child will require explicit bindings.
|
||||||
|
* <p>
|
||||||
|
* In the absence of an explicit binding for the target, linked bindings in
|
||||||
|
* child injectors create a binding for the target in the parent. Since this
|
||||||
|
* behavior can be surprising, it causes an error instead if explicit bindings
|
||||||
|
* are required. To avoid this error, add an explicit binding for the target,
|
||||||
|
* either in the child or the parent.
|
||||||
|
*/
|
||||||
|
void requireExplicitBindings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents Guice from constructing a {@link Proxy} when a circular dependency
|
||||||
|
* is found. By default, circular proxies are not disabled.
|
||||||
|
* <p>
|
||||||
|
* If a parent injector disables circular proxies, then all child injectors
|
||||||
|
* (and private modules within that injector) also disable circular proxies.
|
||||||
|
* If a parent does not disable circular proxies, a child injector or private
|
||||||
|
* module may optionally declare itself as disabling circular proxies. If it
|
||||||
|
* does, the behavior is limited only to that child or any grandchildren. No
|
||||||
|
* siblings of the child will disable circular proxies.
|
||||||
|
*/
|
||||||
|
void disableCircularProxies();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires that a {@literal @}{@link Inject} annotation exists on a constructor in order for
|
||||||
|
* Guice to consider it an eligible injectable class. By default, Guice will inject classes that
|
||||||
|
* have a no-args constructor if no {@literal @}{@link Inject} annotation exists on any
|
||||||
|
* constructor.
|
||||||
|
* <p>
|
||||||
|
* If the class is bound using {@link LinkedBindingBuilder#toConstructor}, Guice will still inject
|
||||||
|
* that constructor regardless of annotations.
|
||||||
|
*/
|
||||||
|
void requireAtInjectOnConstructors();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires that Guice finds an exactly matching binding annotation. This disables the
|
||||||
|
* error-prone feature in Guice where it can substitute a binding for
|
||||||
|
* <code>{@literal @}Named Foo</code> when attempting to inject
|
||||||
|
* <code>{@literal @}Named("foo") Foo</code>.
|
||||||
|
*/
|
||||||
|
void requireExactBindingAnnotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a scanner that will look in all installed modules for annotations the scanner can parse,
|
||||||
|
* and binds them like {@literal @}Provides methods. Scanners apply to all modules installed in
|
||||||
|
* the injector. Scanners installed in child injectors or private modules do not impact modules in
|
||||||
|
* siblings or parents, however scanners installed in parents do apply to all child injectors and
|
||||||
|
* private modules.
|
||||||
|
*/
|
||||||
|
void scanModulesForAnnotatedMethods(ModuleAnnotatedMethodScanner scanner);
|
||||||
|
}
|
71
src/main/java/com/google/inject/Binding.java
Normal file
71
src/main/java/com/google/inject/Binding.java
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.spi.BindingScopingVisitor;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping from a key (type and optional annotation) to the strategy for getting instances of the
|
||||||
|
* type. This interface is part of the introspection API and is intended primarily for use by
|
||||||
|
* tools.
|
||||||
|
*
|
||||||
|
* <p>Bindings are created in several ways:
|
||||||
|
* <ul>
|
||||||
|
* <li>Explicitly in a module, via {@code bind()} and {@code bindConstant()}
|
||||||
|
* statements:
|
||||||
|
* <pre>
|
||||||
|
* bind(Service.class).annotatedWith(Red.class).to(ServiceImpl.class);
|
||||||
|
* bindConstant().annotatedWith(ServerHost.class).to(args[0]);</pre></li>
|
||||||
|
* <li>Implicitly by the Injector by following a type's {@link ImplementedBy
|
||||||
|
* pointer} {@link ProvidedBy annotations} or by using its {@link Inject annotated} or
|
||||||
|
* default constructor.</li>
|
||||||
|
* <li>By converting a bound instance to a different type.</li>
|
||||||
|
* <li>For {@link Provider providers}, by delegating to the binding for the provided type.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <p>They exist on both modules and on injectors, and their behaviour is different for each:
|
||||||
|
* <ul>
|
||||||
|
* <li><strong>Module bindings</strong> are incomplete and cannot be used to provide instances.
|
||||||
|
* This is because the applicable scopes and interceptors may not be known until an injector
|
||||||
|
* is created. From a tool's perspective, module bindings are like the injector's source
|
||||||
|
* code. They can be inspected or rewritten, but this analysis must be done statically.</li>
|
||||||
|
* <li><strong>Injector bindings</strong> are complete and valid and can be used to provide
|
||||||
|
* instances. From a tools' perspective, injector bindings are like reflection for an
|
||||||
|
* injector. They have full runtime information, including the complete graph of injections
|
||||||
|
* necessary to satisfy a binding.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> the bound type. The injected is always assignable to this type.
|
||||||
|
*/
|
||||||
|
public interface Binding<T> extends Element {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key for this binding.
|
||||||
|
*/
|
||||||
|
Key<T> getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scoped provider guice uses to fulfill requests for this
|
||||||
|
* binding.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException when invoked on a {@link Binding}
|
||||||
|
* created via {@link com.google.inject.spi.Elements#getElements}. This
|
||||||
|
* method is only supported on {@link Binding}s returned from an injector.
|
||||||
|
*/
|
||||||
|
Provider<T> getProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a target visitor. Invokes the visitor method specific to this binding's target.
|
||||||
|
*
|
||||||
|
* @param visitor to call back on
|
||||||
|
*/
|
||||||
|
<V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a scoping visitor. Invokes the visitor method specific to this binding's scoping.
|
||||||
|
*
|
||||||
|
* @param visitor to call back on
|
||||||
|
*/
|
||||||
|
<V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor);
|
||||||
|
}
|
25
src/main/java/com/google/inject/BindingAnnotation.java
Normal file
25
src/main/java/com/google/inject/BindingAnnotation.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates annotations which are used for binding. Only one such annotation
|
||||||
|
* may apply to a single injection point. You must also annotate binder
|
||||||
|
* annotations with {@code @Retention(RUNTIME)}. For example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code @}Retention(RUNTIME)
|
||||||
|
* {@code @}Target({ FIELD, PARAMETER, METHOD })
|
||||||
|
* {@code @}BindingAnnotation
|
||||||
|
* public {@code @}interface Transactional {}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Target(ANNOTATION_TYPE)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface BindingAnnotation {
|
||||||
|
}
|
64
src/main/java/com/google/inject/ConfigurationException.java
Normal file
64
src/main/java/com/google/inject/ConfigurationException.java
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.internal.Errors;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a programming error such as a misplaced annotation, illegal binding, or unsupported
|
||||||
|
* scope is found. Clients should catch this exception, log it, and stop execution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public final class ConfigurationException extends RuntimeException {
|
||||||
|
|
||||||
|
private final ImmutableSet<Message> messages;
|
||||||
|
private Object partialValue = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ConfigurationException containing {@code messages}.
|
||||||
|
*/
|
||||||
|
public ConfigurationException(Iterable<Message> messages) {
|
||||||
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
|
initCause(Errors.getOnlyCause(this.messages));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of this configuration exception with the specified partial value.
|
||||||
|
*/
|
||||||
|
public ConfigurationException withPartialValue(Object partialValue) {
|
||||||
|
checkState(this.partialValue == null,
|
||||||
|
"Can't clobber existing partial value %s with %s", this.partialValue, partialValue);
|
||||||
|
ConfigurationException result = new ConfigurationException(messages);
|
||||||
|
result.partialValue = partialValue;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns messages for the errors that caused this exception.
|
||||||
|
*/
|
||||||
|
public Collection<Message> getErrorMessages() {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value that was only partially computed due to this exception. The caller can use
|
||||||
|
* this while collecting additional configuration problems.
|
||||||
|
*
|
||||||
|
* @return the partial value, or {@code null} if none was set. The type of the partial value is
|
||||||
|
* specified by the throwing method.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked") // this is *extremely* unsafe. We trust the caller here.
|
||||||
|
public <E> E getPartialValue() {
|
||||||
|
return (E) partialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return Errors.format("Guice configuration errors", messages);
|
||||||
|
}
|
||||||
|
}
|
40
src/main/java/com/google/inject/CreationException.java
Normal file
40
src/main/java/com/google/inject/CreationException.java
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.internal.Errors;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered
|
||||||
|
* errors. Clients should catch this exception, log it, and stop execution.
|
||||||
|
*/
|
||||||
|
public class CreationException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
private final ImmutableSet<Message> messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a CreationException containing {@code messages}.
|
||||||
|
*/
|
||||||
|
public CreationException(Collection<Message> messages) {
|
||||||
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
|
checkArgument(!this.messages.isEmpty());
|
||||||
|
initCause(Errors.getOnlyCause(this.messages));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns messages for the errors that caused this exception.
|
||||||
|
*/
|
||||||
|
public Collection<Message> getErrorMessages() {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return Errors.format("Unable to create injector, see the following errors", messages);
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/com/google/inject/Exposed.java
Normal file
19
src/main/java/com/google/inject/Exposed.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acccompanies a {@literal @}{@link com.google.inject.Provides Provides} method annotation in a
|
||||||
|
* private module to indicate that the provided binding is exposed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Exposed {
|
||||||
|
}
|
86
src/main/java/com/google/inject/Guice.java
Normal file
86
src/main/java/com/google/inject/Guice.java
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.internal.InternalInjectorCreator;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry point to the Guice framework. Creates {@link Injector}s from
|
||||||
|
* {@link Module}s.
|
||||||
|
*
|
||||||
|
* <p>Guice supports a model of development that draws clear boundaries between
|
||||||
|
* APIs, Implementations of these APIs, Modules which configure these
|
||||||
|
* implementations, and finally Applications which consist of a collection of
|
||||||
|
* Modules. It is the Application, which typically defines your {@code main()}
|
||||||
|
* method, that bootstraps the Guice Injector using the {@code Guice} class, as
|
||||||
|
* in this example:
|
||||||
|
* <pre>
|
||||||
|
* public class FooApplication {
|
||||||
|
* public static void main(String[] args) {
|
||||||
|
* Injector injector = Guice.createInjector(
|
||||||
|
* new ModuleA(),
|
||||||
|
* new ModuleB(),
|
||||||
|
* . . .
|
||||||
|
* new FooApplicationFlagsModule(args)
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* // Now just bootstrap the application and you're done
|
||||||
|
* FooStarter starter = injector.getInstance(FooStarter.class);
|
||||||
|
* starter.runApplication();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public final class Guice {
|
||||||
|
|
||||||
|
private Guice() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an injector for the given set of modules. This is equivalent to
|
||||||
|
* calling {@link #createInjector(Stage, Module...)} with Stage.DEVELOPMENT.
|
||||||
|
*
|
||||||
|
* @throws CreationException if one or more errors occur during injector
|
||||||
|
* construction
|
||||||
|
*/
|
||||||
|
public static Injector createInjector(Module... modules) {
|
||||||
|
return createInjector(Arrays.asList(modules));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an injector for the given set of modules. This is equivalent to
|
||||||
|
* calling {@link #createInjector(Stage, Iterable)} with Stage.DEVELOPMENT.
|
||||||
|
*
|
||||||
|
* @throws CreationException if one or more errors occur during injector
|
||||||
|
* creation
|
||||||
|
*/
|
||||||
|
public static Injector createInjector(Iterable<? extends Module> modules) {
|
||||||
|
return createInjector(Stage.DEVELOPMENT, modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an injector for the given set of modules, in a given development
|
||||||
|
* stage.
|
||||||
|
*
|
||||||
|
* @throws CreationException if one or more errors occur during injector
|
||||||
|
* creation.
|
||||||
|
*/
|
||||||
|
public static Injector createInjector(Stage stage, Module... modules) {
|
||||||
|
return createInjector(stage, Arrays.asList(modules));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an injector for the given set of modules, in a given development
|
||||||
|
* stage.
|
||||||
|
*
|
||||||
|
* @throws CreationException if one or more errors occur during injector
|
||||||
|
* construction
|
||||||
|
*/
|
||||||
|
public static Injector createInjector(Stage stage,
|
||||||
|
Iterable<? extends Module> modules) {
|
||||||
|
return new InternalInjectorCreator()
|
||||||
|
.stage(stage)
|
||||||
|
.addModules(modules)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
20
src/main/java/com/google/inject/ImplementedBy.java
Normal file
20
src/main/java/com/google/inject/ImplementedBy.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pointer to the default implementation of a type.
|
||||||
|
*/
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Target(TYPE)
|
||||||
|
public @interface ImplementedBy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation type.
|
||||||
|
*/
|
||||||
|
Class<?> value();
|
||||||
|
}
|
51
src/main/java/com/google/inject/Inject.java
Normal file
51
src/main/java/com/google/inject/Inject.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.CONSTRUCTOR;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates members of your implementation class (constructors, methods
|
||||||
|
* and fields) into which the {@link Injector} should inject values.
|
||||||
|
* The Injector fulfills injection requests for:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Every instance it constructs. The class being constructed must have
|
||||||
|
* exactly one of its constructors marked with {@code @Inject} or must have a
|
||||||
|
* constructor taking no parameters. The Injector then proceeds to perform
|
||||||
|
* field and method injections.
|
||||||
|
*
|
||||||
|
* <li>Pre-constructed instances passed to {@link Injector#injectMembers},
|
||||||
|
* {@link com.google.inject.binder.LinkedBindingBuilder#toInstance(Object)} and
|
||||||
|
* {@link com.google.inject.binder.LinkedBindingBuilder#toProvider(javax.inject.Provider)}.
|
||||||
|
* In this case all constructors are, of course, ignored.
|
||||||
|
*
|
||||||
|
* <li>Static fields and methods of classes which any {@link Module} has
|
||||||
|
* specifically requested static injection for, using
|
||||||
|
* {@link Binder#requestStaticInjection}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* In all cases, a member can be injected regardless of its Java access
|
||||||
|
* specifier (private, default, protected, public).
|
||||||
|
*/
|
||||||
|
@Target({METHOD, CONSTRUCTOR, FIELD})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Inject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, and the appropriate binding is not found,
|
||||||
|
* the Injector will skip injection of this method or field rather than
|
||||||
|
* produce an error. When applied to a field, any default value already
|
||||||
|
* assigned to the field will remain (guice will not actively null out the
|
||||||
|
* field). When applied to a method, the method will only be invoked if
|
||||||
|
* bindings for <i>all</i> parameters are found. When applied to a
|
||||||
|
* constructor, an error will result upon Injector creation.
|
||||||
|
*/
|
||||||
|
boolean optional() default false;
|
||||||
|
}
|
228
src/main/java/com/google/inject/Injector.java
Normal file
228
src/main/java/com/google/inject/Injector.java
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the graphs of objects that make up your application. The injector tracks the dependencies
|
||||||
|
* for each type and uses bindings to inject them. This is the core of Guice, although you rarely
|
||||||
|
* interact with it directly. This "behind-the-scenes" operation is what distinguishes dependency
|
||||||
|
* injection from its cousin, the service locator pattern.
|
||||||
|
*
|
||||||
|
* <p>Contains several default bindings:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>This {@link Injector} instance itself
|
||||||
|
* <li>A {@code Provider<T>} for each binding of type {@code T}
|
||||||
|
* <li>The {@link java.util.logging.Logger} for the class being injected
|
||||||
|
* <li>The {@link Stage} in which the Injector was created
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Injectors are created using the facade class {@link Guice}.
|
||||||
|
*
|
||||||
|
* <p>An injector can also {@link #injectMembers(Object) inject the dependencies} of
|
||||||
|
* already-constructed instances. This can be used to interoperate with objects created by other
|
||||||
|
* frameworks or services.
|
||||||
|
*
|
||||||
|
* <p>Injectors can be {@link #createChildInjector(Iterable) hierarchical}. Child injectors inherit
|
||||||
|
* the configuration of their parent injectors, but the converse does not hold.
|
||||||
|
*
|
||||||
|
* <p>The injector's {@link #getBindings() internal bindings} are available for introspection. This
|
||||||
|
* enables tools and extensions to operate on an injector reflectively.
|
||||||
|
*/
|
||||||
|
public interface Injector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
|
||||||
|
* absence of an injectable constructor.
|
||||||
|
*
|
||||||
|
* <p>Whenever Guice creates an instance, it performs this injection automatically (after first
|
||||||
|
* performing constructor injection), so if you're able to let Guice create all your objects for
|
||||||
|
* you, you'll never need to use this method.
|
||||||
|
*
|
||||||
|
* @param instance to inject members on
|
||||||
|
* @see Binder#getMembersInjector(Class) for a preferred alternative that supports checks before
|
||||||
|
* run time
|
||||||
|
*/
|
||||||
|
void injectMembers(Object instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the members injector used to inject dependencies into methods and fields on instances
|
||||||
|
* of the given type {@code T}.
|
||||||
|
*
|
||||||
|
* @param typeLiteral type to get members injector for
|
||||||
|
* @see Binder#getMembersInjector(TypeLiteral) for an alternative that offers up front error
|
||||||
|
* detection
|
||||||
|
*/
|
||||||
|
<T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the members injector used to inject dependencies into methods and fields on instances
|
||||||
|
* of the given type {@code T}. When feasible, use {@link Binder#getMembersInjector(TypeLiteral)}
|
||||||
|
* instead to get increased up front error detection.
|
||||||
|
*
|
||||||
|
* @param type type to get members injector for
|
||||||
|
* @see Binder#getMembersInjector(Class) for an alternative that offers up front error
|
||||||
|
* detection
|
||||||
|
*/
|
||||||
|
<T> MembersInjector<T> getMembersInjector(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this injector's <strong>explicit</strong> bindings.
|
||||||
|
*
|
||||||
|
* <p>The returned map does not include bindings inherited from a {@link #getParent() parent
|
||||||
|
* injector}, should one exist. The returned map is guaranteed to iterate (for example, with
|
||||||
|
* its {@link Map#entrySet()} iterator) in the order of insertion. In other words, the order in
|
||||||
|
* which bindings appear in user Modules.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
Map<Key<?>, Binding<?>> getBindings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a snapshot of this injector's bindings, <strong>both explicit and
|
||||||
|
* just-in-time</strong>. The returned map is immutable; it contains only the bindings that were
|
||||||
|
* present when {@code getAllBindings()} was invoked. Subsequent calls may return a map with
|
||||||
|
* additional just-in-time bindings.
|
||||||
|
*
|
||||||
|
* <p>The returned map does not include bindings inherited from a {@link #getParent() parent
|
||||||
|
* injector}, should one exist.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
Map<Key<?>, Binding<?>> getAllBindings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the binding for the given injection key. This will be an explicit bindings if the key
|
||||||
|
* was bound explicitly by a module, or an implicit binding otherwise. The implicit binding will
|
||||||
|
* be created if necessary.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if this injector cannot find or create the binding.
|
||||||
|
*/
|
||||||
|
<T> Binding<T> getBinding(Key<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the binding for the given type. This will be an explicit bindings if the injection key
|
||||||
|
* was bound explicitly by a module, or an implicit binding otherwise. The implicit binding will
|
||||||
|
* be created if necessary.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if this injector cannot find or create the binding.
|
||||||
|
*/
|
||||||
|
<T> Binding<T> getBinding(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the binding if it already exists, or null if does not exist. Unlike
|
||||||
|
* {@link #getBinding(Key)}, this does not attempt to create just-in-time bindings
|
||||||
|
* for keys that aren't bound.
|
||||||
|
*
|
||||||
|
* <p> This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
<T> Binding<T> getExistingBinding(Key<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all explicit bindings for {@code type}.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
<T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider used to obtain instances for the given injection key. When feasible, avoid
|
||||||
|
* using this method, in favor of having Guice inject your dependencies ahead of time.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if this injector cannot find or create the provider.
|
||||||
|
* @see Binder#getProvider(Key) for an alternative that offers up front error detection
|
||||||
|
*/
|
||||||
|
<T> Provider<T> getProvider(Key<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider used to obtain instances for the given type. When feasible, avoid
|
||||||
|
* using this method, in favor of having Guice inject your dependencies ahead of time.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if this injector cannot find or create the provider.
|
||||||
|
* @see Binder#getProvider(Class) for an alternative that offers up front error detection
|
||||||
|
*/
|
||||||
|
<T> Provider<T> getProvider(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the appropriate instance for the given injection key; equivalent to {@code
|
||||||
|
* getProvider(key).get()}. When feasible, avoid using this method, in favor of having Guice
|
||||||
|
* inject your dependencies ahead of time.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if this injector cannot find or create the provider.
|
||||||
|
* @throws ProvisionException if there was a runtime failure while providing an instance.
|
||||||
|
*/
|
||||||
|
<T> T getInstance(Key<T> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the appropriate instance for the given injection type; equivalent to {@code
|
||||||
|
* getProvider(type).get()}. When feasible, avoid using this method, in favor of having Guice
|
||||||
|
* inject your dependencies ahead of time.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if this injector cannot find or create the provider.
|
||||||
|
* @throws ProvisionException if there was a runtime failure while providing an instance.
|
||||||
|
*/
|
||||||
|
<T> T getInstance(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this injector's parent, or {@code null} if this is a top-level injector.
|
||||||
|
*/
|
||||||
|
Injector getParent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new injector that inherits all state from this injector. All bindings, scopes,
|
||||||
|
* interceptors and type converters are inherited -- they are visible to the child injector.
|
||||||
|
* Elements of the child injector are not visible to its parent.
|
||||||
|
*
|
||||||
|
* <p>Just-in-time bindings created for child injectors will be created in an ancestor injector
|
||||||
|
* whenever possible. This allows for scoped instances to be shared between injectors. Use
|
||||||
|
* explicit bindings to prevent bindings from being shared with the parent injector. Optional
|
||||||
|
* injections in just-in-time bindings (created in the parent injector) may be silently
|
||||||
|
* ignored if the optional dependencies are from the child injector.
|
||||||
|
*
|
||||||
|
* <p>No key may be bound by both an injector and one of its ancestors. This includes just-in-time
|
||||||
|
* bindings. The lone exception is the key for {@code Injector.class}, which is bound by each
|
||||||
|
* injector to itself.
|
||||||
|
*/
|
||||||
|
Injector createChildInjector(Iterable<? extends Module> modules);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new injector that inherits all state from this injector. All bindings, scopes,
|
||||||
|
* interceptors and type converters are inherited -- they are visible to the child injector.
|
||||||
|
* Elements of the child injector are not visible to its parent.
|
||||||
|
*
|
||||||
|
* <p>Just-in-time bindings created for child injectors will be created in an ancestor injector
|
||||||
|
* whenever possible. This allows for scoped instances to be shared between injectors. Use
|
||||||
|
* explicit bindings to prevent bindings from being shared with the parent injector.
|
||||||
|
*
|
||||||
|
* <p>No key may be bound by both an injector and one of its ancestors. This includes just-in-time
|
||||||
|
* bindings. The lone exception is the key for {@code Injector.class}, which is bound by each
|
||||||
|
* injector to itself.
|
||||||
|
*/
|
||||||
|
Injector createChildInjector(Module... modules);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map containing all scopes in the injector. The maps keys are scoping annotations
|
||||||
|
* like {@code Singleton.class}, and the values are scope instances, such as {@code
|
||||||
|
* Scopes.SINGLETON}. The returned map is immutable.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
Map<Class<? extends Annotation>, Scope> getScopeBindings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a set containing all type converter bindings in the injector. The returned set is
|
||||||
|
* immutable.
|
||||||
|
*
|
||||||
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
|
*/
|
||||||
|
Set<TypeConverterBinding> getTypeConverterBindings();
|
||||||
|
}
|
508
src/main/java/com/google/inject/Key.java
Normal file
508
src/main/java/com/google/inject/Key.java
Normal file
|
@ -0,0 +1,508 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.inject.internal.Annotations;
|
||||||
|
import com.google.inject.internal.MoreTypes;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.inject.internal.Annotations.generateAnnotation;
|
||||||
|
import static com.google.inject.internal.Annotations.isAllDefaultMethods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binding key consisting of an injection type and an optional annotation.
|
||||||
|
* Matches the type and annotation at a point of injection.
|
||||||
|
*
|
||||||
|
* <p>For example, {@code Key.get(Service.class, Transactional.class)} will
|
||||||
|
* match:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@literal @}Inject
|
||||||
|
* public void setService({@literal @}Transactional Service service) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>{@code Key} supports generic types via subclassing just like {@link
|
||||||
|
* TypeLiteral}.
|
||||||
|
*
|
||||||
|
* <p>Keys do not differentiate between primitive types (int, char, etc.) and
|
||||||
|
* their corresponding wrapper types (Integer, Character, etc.). Primitive
|
||||||
|
* types will be replaced with their wrapper types when keys are created.
|
||||||
|
*/
|
||||||
|
public class Key<T> {
|
||||||
|
|
||||||
|
private final AnnotationStrategy annotationStrategy;
|
||||||
|
|
||||||
|
private final TypeLiteral<T> typeLiteral;
|
||||||
|
private final int hashCode;
|
||||||
|
private final Supplier<String> toStringSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
|
*
|
||||||
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
||||||
|
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||||
|
* at runtime despite erasure.
|
||||||
|
*
|
||||||
|
* <p>Example usage for a binding of type {@code Foo} annotated with
|
||||||
|
* {@code @Bar}:
|
||||||
|
*
|
||||||
|
* <p>{@code new Key<Foo>(Bar.class) {}}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Key(Class<? extends Annotation> annotationType) {
|
||||||
|
this.annotationStrategy = strategyFor(annotationType);
|
||||||
|
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||||
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
|
this.hashCode = computeHashCode();
|
||||||
|
this.toStringSupplier = createToStringSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
|
*
|
||||||
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
||||||
|
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||||
|
* at runtime despite erasure.
|
||||||
|
*
|
||||||
|
* <p>Example usage for a binding of type {@code Foo} annotated with
|
||||||
|
* {@code @Bar}:
|
||||||
|
*
|
||||||
|
* <p>{@code new Key<Foo>(new Bar()) {}}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Key(Annotation annotation) {
|
||||||
|
// no usages, not test-covered
|
||||||
|
this.annotationStrategy = strategyFor(annotation);
|
||||||
|
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||||
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
|
this.hashCode = computeHashCode();
|
||||||
|
this.toStringSupplier = createToStringSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
|
*
|
||||||
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
||||||
|
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||||
|
* at runtime despite erasure.
|
||||||
|
*
|
||||||
|
* <p>Example usage for a binding of type {@code Foo}:
|
||||||
|
*
|
||||||
|
* <p>{@code new Key<Foo>() {}}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Key() {
|
||||||
|
this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
|
||||||
|
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||||
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
|
this.hashCode = computeHashCode();
|
||||||
|
this.toStringSupplier = createToStringSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsafe. Constructs a key from a manually specified type.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Key(Type type, AnnotationStrategy annotationStrategy) {
|
||||||
|
this.annotationStrategy = annotationStrategy;
|
||||||
|
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
||||||
|
this.hashCode = computeHashCode();
|
||||||
|
this.toStringSupplier = createToStringSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a key from a manually specified type.
|
||||||
|
*/
|
||||||
|
private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) {
|
||||||
|
this.annotationStrategy = annotationStrategy;
|
||||||
|
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
||||||
|
this.hashCode = computeHashCode();
|
||||||
|
this.toStringSupplier = createToStringSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation strategy.
|
||||||
|
*/
|
||||||
|
static <T> Key<T> get(Class<T> type,
|
||||||
|
AnnotationStrategy annotationStrategy) {
|
||||||
|
return new Key<T>(type, annotationStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> get(Class<T> type) {
|
||||||
|
return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation type.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> get(Class<T> type,
|
||||||
|
Class<? extends Annotation> annotationType) {
|
||||||
|
return new Key<T>(type, strategyFor(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> get(Class<T> type, Annotation annotation) {
|
||||||
|
return new Key<T>(type, strategyFor(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type.
|
||||||
|
*/
|
||||||
|
public static Key<?> get(Type type) {
|
||||||
|
return new Key<Object>(type, NullAnnotationStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation type.
|
||||||
|
*/
|
||||||
|
public static Key<?> get(Type type,
|
||||||
|
Class<? extends Annotation> annotationType) {
|
||||||
|
return new Key<Object>(type, strategyFor(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation.
|
||||||
|
*/
|
||||||
|
public static Key<?> get(Type type, Annotation annotation) {
|
||||||
|
return new Key<Object>(type, strategyFor(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
|
||||||
|
return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation type.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
|
||||||
|
Class<? extends Annotation> annotationType) {
|
||||||
|
return new Key<T>(typeLiteral, strategyFor(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for an injection type and an annotation.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
|
||||||
|
Annotation annotation) {
|
||||||
|
return new Key<T>(typeLiteral, strategyFor(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the strategy for an annotation.
|
||||||
|
*/
|
||||||
|
static AnnotationStrategy strategyFor(Annotation annotation) {
|
||||||
|
checkNotNull(annotation, "annotation");
|
||||||
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
ensureRetainedAtRuntime(annotationType);
|
||||||
|
ensureIsBindingAnnotation(annotationType);
|
||||||
|
|
||||||
|
if (Annotations.isMarker(annotationType)) {
|
||||||
|
return new AnnotationTypeStrategy(annotationType, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AnnotationInstanceStrategy(Annotations.canonicalizeIfNamed(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the strategy for an annotation type.
|
||||||
|
*/
|
||||||
|
static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
|
||||||
|
annotationType = Annotations.canonicalizeIfNamed(annotationType);
|
||||||
|
if (isAllDefaultMethods(annotationType)) {
|
||||||
|
return strategyFor(generateAnnotation(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNotNull(annotationType, "annotation type");
|
||||||
|
ensureRetainedAtRuntime(annotationType);
|
||||||
|
ensureIsBindingAnnotation(annotationType);
|
||||||
|
return new AnnotationTypeStrategy(annotationType, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureRetainedAtRuntime(
|
||||||
|
Class<? extends Annotation> annotationType) {
|
||||||
|
checkArgument(Annotations.isRetainedAtRuntime(annotationType),
|
||||||
|
"%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
|
||||||
|
annotationType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
checkArgument(Annotations.isBindingAnnotation(annotationType),
|
||||||
|
"%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
|
||||||
|
annotationType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the hash code for this key.
|
||||||
|
*/
|
||||||
|
private int computeHashCode() {
|
||||||
|
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a {@link Supplier} which memoizes the value for lazy initialization.
|
||||||
|
*/
|
||||||
|
private Supplier<String> createToStringSupplier() {
|
||||||
|
// The performance hit on access is acceptable since the intended use is for non-performance-
|
||||||
|
// critical applications.
|
||||||
|
return Suppliers.memoize(new Supplier<String>() {
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the key type.
|
||||||
|
*/
|
||||||
|
public final TypeLiteral<T> getTypeLiteral() {
|
||||||
|
return typeLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the annotation type.
|
||||||
|
*/
|
||||||
|
public final Class<? extends Annotation> getAnnotationType() {
|
||||||
|
return annotationStrategy.getAnnotationType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the annotation.
|
||||||
|
*/
|
||||||
|
public final Annotation getAnnotation() {
|
||||||
|
return annotationStrategy.getAnnotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasAnnotationType() {
|
||||||
|
return annotationStrategy.getAnnotationType() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAnnotationName() {
|
||||||
|
Annotation annotation = annotationStrategy.getAnnotation();
|
||||||
|
if (annotation != null) {
|
||||||
|
return annotation.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// not test-covered
|
||||||
|
return annotationStrategy.getAnnotationType().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<? super T> getRawType() {
|
||||||
|
return typeLiteral.getRawType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the key of this key's provider.
|
||||||
|
*/
|
||||||
|
Key<Provider<T>> providerKey() {
|
||||||
|
return ofType(typeLiteral.providerType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object o) {
|
||||||
|
if (o == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof Key<?>)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Key<?> other = (Key<?>) o;
|
||||||
|
return annotationStrategy.equals(other.annotationStrategy)
|
||||||
|
&& typeLiteral.equals(other.typeLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return this.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return toStringSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new key of the specified type with the same annotation as this
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
public <T> Key<T> ofType(Class<T> type) {
|
||||||
|
return new Key<T>(type, annotationStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new key of the specified type with the same annotation as this
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
public Key<?> ofType(Type type) {
|
||||||
|
return new Key<Object>(type, annotationStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new key of the specified type with the same annotation as this
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
public <T> Key<T> ofType(TypeLiteral<T> type) {
|
||||||
|
return new Key<T>(type, annotationStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this key has annotation attributes.
|
||||||
|
*/
|
||||||
|
public boolean hasAttributes() {
|
||||||
|
return annotationStrategy.hasAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this key without annotation attributes, i.e. with only the
|
||||||
|
* annotation type.
|
||||||
|
*/
|
||||||
|
public Key<T> withoutAttributes() {
|
||||||
|
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum NullAnnotationStrategy implements AnnotationStrategy {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
public boolean hasAttributes() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationStrategy withoutAttributes() {
|
||||||
|
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation getAnnotation() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[none]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AnnotationStrategy {
|
||||||
|
Annotation getAnnotation();
|
||||||
|
|
||||||
|
Class<? extends Annotation> getAnnotationType();
|
||||||
|
|
||||||
|
boolean hasAttributes();
|
||||||
|
|
||||||
|
AnnotationStrategy withoutAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this class not test-covered
|
||||||
|
static class AnnotationInstanceStrategy implements AnnotationStrategy {
|
||||||
|
|
||||||
|
final Annotation annotation;
|
||||||
|
|
||||||
|
AnnotationInstanceStrategy(Annotation annotation) {
|
||||||
|
this.annotation = checkNotNull(annotation, "annotation");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAttributes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationStrategy withoutAttributes() {
|
||||||
|
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
|
return annotation.annotationType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof AnnotationInstanceStrategy)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationInstanceStrategy other = (AnnotationInstanceStrategy) o;
|
||||||
|
return annotation.equals(other.annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return annotation.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return annotation.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AnnotationTypeStrategy implements AnnotationStrategy {
|
||||||
|
|
||||||
|
final Class<? extends Annotation> annotationType;
|
||||||
|
|
||||||
|
// Keep the instance around if we have it so the client can request it.
|
||||||
|
final Annotation annotation;
|
||||||
|
|
||||||
|
AnnotationTypeStrategy(Class<? extends Annotation> annotationType,
|
||||||
|
Annotation annotation) {
|
||||||
|
this.annotationType = checkNotNull(annotationType, "annotation type");
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAttributes() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationStrategy withoutAttributes() {
|
||||||
|
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
|
return annotationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof AnnotationTypeStrategy)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationTypeStrategy other = (AnnotationTypeStrategy) o;
|
||||||
|
return annotationType.equals(other.annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return annotationType.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "@" + annotationType.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/main/java/com/google/inject/MembersInjector.java
Normal file
22
src/main/java/com/google/inject/MembersInjector.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
|
||||||
|
* presence or absence of an injectable constructor.
|
||||||
|
*
|
||||||
|
* @param <T> type to inject members of
|
||||||
|
*/
|
||||||
|
public interface MembersInjector<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
|
||||||
|
* absence of an injectable constructor.
|
||||||
|
*
|
||||||
|
* <p>Whenever Guice creates an instance, it performs this injection automatically (after first
|
||||||
|
* performing constructor injection), so if you're able to let Guice create all your objects for
|
||||||
|
* you, you'll never need to use this method.
|
||||||
|
*
|
||||||
|
* @param instance to inject members on. May be {@code null}.
|
||||||
|
*/
|
||||||
|
void injectMembers(T instance);
|
||||||
|
}
|
27
src/main/java/com/google/inject/Module.java
Normal file
27
src/main/java/com/google/inject/Module.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A module contributes configuration information, typically interface
|
||||||
|
* bindings, which will be used to create an {@link Injector}. A Guice-based
|
||||||
|
* application is ultimately composed of little more than a set of
|
||||||
|
* {@code Module}s and some bootstrapping code.
|
||||||
|
*
|
||||||
|
* <p>Your Module classes can use a more streamlined syntax by extending
|
||||||
|
* {@link AbstractModule} rather than implementing this interface directly.
|
||||||
|
*
|
||||||
|
* <p>In addition to the bindings configured via {@link #configure}, bindings
|
||||||
|
* will be created for all methods annotated with {@literal @}{@link Provides}.
|
||||||
|
* Use scope and binding annotations on these methods to configure the
|
||||||
|
* bindings.
|
||||||
|
*/
|
||||||
|
public interface Module {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contributes bindings and other configurations for this module to {@code binder}.
|
||||||
|
*
|
||||||
|
* <p><strong>Do not invoke this method directly</strong> to install submodules. Instead use
|
||||||
|
* {@link Binder#install(Module)}, which ensures that {@link Provides provider methods} are
|
||||||
|
* discovered.
|
||||||
|
*/
|
||||||
|
void configure(Binder binder);
|
||||||
|
}
|
35
src/main/java/com/google/inject/PrivateBinder.java
Normal file
35
src/main/java/com/google/inject/PrivateBinder.java
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.binder.AnnotatedElementBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a binder whose configuration information is hidden from its environment by default. See
|
||||||
|
* {@link com.google.inject.PrivateModule PrivateModule} for details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface PrivateBinder extends Binder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the binding for {@code key} available to the enclosing environment
|
||||||
|
*/
|
||||||
|
void expose(Key<?> key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a binding for {@code type} available to the enclosing environment. Use {@link
|
||||||
|
* com.google.inject.binder.AnnotatedElementBuilder#annotatedWith(Class) annotatedWith()} to expose {@code type}
|
||||||
|
* with a
|
||||||
|
* binding annotation.
|
||||||
|
*/
|
||||||
|
AnnotatedElementBuilder expose(Class<?> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a binding for {@code type} available to the enclosing environment. Use {@link
|
||||||
|
* AnnotatedElementBuilder#annotatedWith(Class) annotatedWith()} to expose {@code type} with a
|
||||||
|
* binding annotation.
|
||||||
|
*/
|
||||||
|
AnnotatedElementBuilder expose(TypeLiteral<?> type);
|
||||||
|
|
||||||
|
PrivateBinder withSource(Object source);
|
||||||
|
|
||||||
|
PrivateBinder skipSources(Class... classesToSkip);
|
||||||
|
}
|
279
src/main/java/com/google/inject/PrivateModule.java
Normal file
279
src/main/java/com/google/inject/PrivateModule.java
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.binder.AnnotatedBindingBuilder;
|
||||||
|
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
||||||
|
import com.google.inject.binder.AnnotatedElementBuilder;
|
||||||
|
import com.google.inject.binder.LinkedBindingBuilder;
|
||||||
|
import com.google.inject.matcher.Matcher;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import com.google.inject.spi.ProvisionListener;
|
||||||
|
import com.google.inject.spi.TypeConverter;
|
||||||
|
import com.google.inject.spi.TypeListener;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A module whose configuration information is hidden from its environment by default. Only bindings
|
||||||
|
* that are explicitly exposed will be available to other modules and to the users of the injector.
|
||||||
|
* This module may expose the bindings it creates and the bindings of the modules it installs.
|
||||||
|
*
|
||||||
|
* <p>A private module can be nested within a regular module or within another private module using
|
||||||
|
* {@link Binder#install install()}. Its bindings live in a new environment that inherits bindings,
|
||||||
|
* type converters, scopes, and interceptors from the surrounding ("parent") environment. When you
|
||||||
|
* nest multiple private modules, the result is a tree of environments where the injector's
|
||||||
|
* environment is the root.
|
||||||
|
*
|
||||||
|
* <p>Guice EDSL bindings can be exposed with {@link #expose(Class) expose()}. {@literal @}{@link
|
||||||
|
* com.google.inject.Provides Provides} bindings can be exposed with the {@literal @}{@link
|
||||||
|
* Exposed} annotation:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class FooBarBazModule extends PrivateModule {
|
||||||
|
* protected void configure() {
|
||||||
|
* bind(Foo.class).to(RealFoo.class);
|
||||||
|
* expose(Foo.class);
|
||||||
|
*
|
||||||
|
* install(new TransactionalBarModule());
|
||||||
|
* expose(Bar.class).annotatedWith(Transactional.class);
|
||||||
|
*
|
||||||
|
* bind(SomeImplementationDetail.class);
|
||||||
|
* install(new MoreImplementationDetailsModule());
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* {@literal @}Provides {@literal @}Exposed
|
||||||
|
* public Baz provideBaz() {
|
||||||
|
* return new SuperBaz();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>Private modules are implemented using {@link Injector#createChildInjector(Module[]) parent
|
||||||
|
* injectors}. When it can satisfy their dependencies, just-in-time bindings will be created in the
|
||||||
|
* root environment. Such bindings are shared among all environments in the tree.
|
||||||
|
*
|
||||||
|
* <p>The scope of a binding is constrained to its environment. A singleton bound in a private
|
||||||
|
* module will be unique to its environment. But a binding for the same type in a different private
|
||||||
|
* module will yield a different instance.
|
||||||
|
*
|
||||||
|
* <p>A shared binding that injects the {@code Injector} gets the root injector, which only has
|
||||||
|
* access to bindings in the root environment. An explicit binding that injects the {@code Injector}
|
||||||
|
* gets access to all bindings in the child environment.
|
||||||
|
*
|
||||||
|
* <p>To promote a just-in-time binding to an explicit binding, bind it:
|
||||||
|
* <pre>
|
||||||
|
* bind(FooImpl.class);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class PrivateModule implements Module {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like abstract module, the binder of the current private module
|
||||||
|
*/
|
||||||
|
private PrivateBinder binder;
|
||||||
|
|
||||||
|
public final synchronized void configure(Binder binder) {
|
||||||
|
checkState(this.binder == null, "Re-entry is not allowed.");
|
||||||
|
|
||||||
|
// Guice treats PrivateModules specially and passes in a PrivateBinder automatically.
|
||||||
|
this.binder = (PrivateBinder) binder.skipSources(PrivateModule.class);
|
||||||
|
try {
|
||||||
|
configure();
|
||||||
|
} finally {
|
||||||
|
this.binder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates bindings and other configurations private to this module. Use {@link #expose(Class)
|
||||||
|
* expose()} to make the bindings in this module available externally.
|
||||||
|
*/
|
||||||
|
protected abstract void configure();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the binding for {@code key} available to other modules and the injector.
|
||||||
|
*/
|
||||||
|
protected final <T> void expose(Key<T> key) {
|
||||||
|
binder().expose(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a binding for {@code type} available to other modules and the injector. Use {@link
|
||||||
|
* AnnotatedElementBuilder#annotatedWith(Class) annotatedWith()} to expose {@code type} with a
|
||||||
|
* binding annotation.
|
||||||
|
*/
|
||||||
|
protected final AnnotatedElementBuilder expose(Class<?> type) {
|
||||||
|
return binder().expose(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a binding for {@code type} available to other modules and the injector. Use {@link
|
||||||
|
* AnnotatedElementBuilder#annotatedWith(Class) annotatedWith()} to expose {@code type} with a
|
||||||
|
* binding annotation.
|
||||||
|
*/
|
||||||
|
protected final AnnotatedElementBuilder expose(TypeLiteral<?> type) {
|
||||||
|
return binder().expose(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything below is copied from AbstractModule
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current binder.
|
||||||
|
*/
|
||||||
|
protected final PrivateBinder binder() {
|
||||||
|
checkState(binder != null, "The binder can only be used inside configure()");
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindScope(Class, Scope)
|
||||||
|
*/
|
||||||
|
protected final void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
|
||||||
|
binder().bindScope(scopeAnnotation, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bind(Key)
|
||||||
|
*/
|
||||||
|
protected final <T> LinkedBindingBuilder<T> bind(Key<T> key) {
|
||||||
|
return binder().bind(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bind(TypeLiteral)
|
||||||
|
*/
|
||||||
|
protected final <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
|
||||||
|
return binder().bind(typeLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bind(Class)
|
||||||
|
*/
|
||||||
|
protected final <T> AnnotatedBindingBuilder<T> bind(Class<T> clazz) {
|
||||||
|
return binder().bind(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindConstant()
|
||||||
|
*/
|
||||||
|
protected final AnnotatedConstantBindingBuilder bindConstant() {
|
||||||
|
return binder().bindConstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#install(Module)
|
||||||
|
*/
|
||||||
|
protected final void install(Module module) {
|
||||||
|
binder().install(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#addError(String, Object[])
|
||||||
|
*/
|
||||||
|
protected final void addError(String message, Object... arguments) {
|
||||||
|
binder().addError(message, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#addError(Throwable)
|
||||||
|
*/
|
||||||
|
protected final void addError(Throwable t) {
|
||||||
|
binder().addError(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#addError(Message)
|
||||||
|
*/
|
||||||
|
protected final void addError(Message message) {
|
||||||
|
binder().addError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#requestInjection(Object)
|
||||||
|
*/
|
||||||
|
protected final void requestInjection(Object instance) {
|
||||||
|
binder().requestInjection(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#requestStaticInjection(Class[])
|
||||||
|
*/
|
||||||
|
protected final void requestStaticInjection(Class<?>... types) {
|
||||||
|
binder().requestStaticInjection(types);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs Guice to require a binding to the given key.
|
||||||
|
*/
|
||||||
|
protected final void requireBinding(Key<?> key) {
|
||||||
|
binder().getProvider(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs Guice to require a binding to the given type.
|
||||||
|
*/
|
||||||
|
protected final void requireBinding(Class<?> type) {
|
||||||
|
binder().getProvider(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getProvider(Key)
|
||||||
|
*/
|
||||||
|
protected final <T> Provider<T> getProvider(Key<T> key) {
|
||||||
|
return binder().getProvider(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getProvider(Class)
|
||||||
|
*/
|
||||||
|
protected final <T> Provider<T> getProvider(Class<T> type) {
|
||||||
|
return binder().getProvider(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#convertToTypes(com.google.inject.matcher.Matcher, com.google.inject.spi.TypeConverter)
|
||||||
|
*/
|
||||||
|
protected final void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
|
||||||
|
TypeConverter converter) {
|
||||||
|
binder().convertToTypes(typeMatcher, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#currentStage()
|
||||||
|
*/
|
||||||
|
protected final Stage currentStage() {
|
||||||
|
return binder().currentStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getMembersInjector(Class)
|
||||||
|
*/
|
||||||
|
protected <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
|
return binder().getMembersInjector(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#getMembersInjector(TypeLiteral)
|
||||||
|
*/
|
||||||
|
protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
||||||
|
return binder().getMembersInjector(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindListener(com.google.inject.matcher.Matcher, com.google.inject.spi.TypeListener)
|
||||||
|
*/
|
||||||
|
protected void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
|
||||||
|
TypeListener listener) {
|
||||||
|
binder().bindListener(typeMatcher, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Binder#bindListener(Matcher, ProvisionListener...)
|
||||||
|
*/
|
||||||
|
protected void bindListener(Matcher<? super Binding<?>> bindingMatcher,
|
||||||
|
ProvisionListener... listeners) {
|
||||||
|
binder().bindListener(bindingMatcher, listeners);
|
||||||
|
}
|
||||||
|
}
|
20
src/main/java/com/google/inject/ProvidedBy.java
Normal file
20
src/main/java/com/google/inject/ProvidedBy.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pointer to the default provider type for a type.
|
||||||
|
*/
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Target(TYPE)
|
||||||
|
public @interface ProvidedBy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation type.
|
||||||
|
*/
|
||||||
|
Class<? extends Provider<?>> value();
|
||||||
|
}
|
36
src/main/java/com/google/inject/Provider.java
Normal file
36
src/main/java/com/google/inject/Provider.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object capable of providing instances of type {@code T}. Providers are used in numerous ways
|
||||||
|
* by Guice:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>When the default means for obtaining instances (an injectable or parameterless constructor)
|
||||||
|
* is insufficient for a particular binding, the module can specify a custom {@code Provider}
|
||||||
|
* instead, to control exactly how Guice creates or obtains instances for the binding.
|
||||||
|
*
|
||||||
|
* <li>An implementation class may always choose to have a {@code Provider<T>} instance injected,
|
||||||
|
* rather than having a {@code T} injected directly. This may give you access to multiple
|
||||||
|
* instances, instances you wish to safely mutate and discard, instances which are out of scope
|
||||||
|
* (e.g. using a {@code @RequestScoped} object from within a {@code @SessionScoped} object), or
|
||||||
|
* instances that will be initialized lazily.
|
||||||
|
*
|
||||||
|
* <li>A custom {@link Scope} is implemented as a decorator of {@code Provider<T>}, which decides
|
||||||
|
* when to delegate to the backing provider and when to provide the instance some other way.
|
||||||
|
*
|
||||||
|
* <li>The {@link Injector} offers access to the {@code Provider<T>} it uses to fulfill requests
|
||||||
|
* for a given key, via the {@link Injector#getProvider} methods.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> the type of object this provides
|
||||||
|
*/
|
||||||
|
public interface Provider<T> extends javax.inject.Provider<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an instance of {@code T}. Must never return {@code null}.
|
||||||
|
*
|
||||||
|
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
|
||||||
|
* and throwables to describe why provision failed.
|
||||||
|
*/
|
||||||
|
T get();
|
||||||
|
}
|
18
src/main/java/com/google/inject/Provides.java
Normal file
18
src/main/java/com/google/inject/Provides.java
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates methods of a {@link Module} to create a provider method binding. The method's return
|
||||||
|
* type is bound to its returned value. Guice will pass dependencies to the method as parameters.
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Target(METHOD)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface Provides {
|
||||||
|
}
|
49
src/main/java/com/google/inject/ProvisionException.java
Normal file
49
src/main/java/com/google/inject/ProvisionException.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.internal.Errors;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that there was a runtime failure while providing an instance.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ProvisionException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0;
|
||||||
|
private final ImmutableSet<Message> messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ProvisionException containing {@code messages}.
|
||||||
|
*/
|
||||||
|
public ProvisionException(Iterable<Message> messages) {
|
||||||
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
|
checkArgument(!this.messages.isEmpty());
|
||||||
|
initCause(Errors.getOnlyCause(this.messages));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProvisionException(String message, Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
this.messages = ImmutableSet.of(new Message(message, cause));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProvisionException(String message) {
|
||||||
|
this.messages = ImmutableSet.of(new Message(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns messages for the errors that caused this exception.
|
||||||
|
*/
|
||||||
|
public Collection<Message> getErrorMessages() {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return Errors.format("Unable to provision, see the following errors", messages);
|
||||||
|
}
|
||||||
|
}
|
41
src/main/java/com/google/inject/Scope.java
Normal file
41
src/main/java/com/google/inject/Scope.java
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scope is a level of visibility that instances provided by Guice may have.
|
||||||
|
* By default, an instance created by the {@link Injector} has <i>no scope</i>,
|
||||||
|
* meaning it has no state from the framework's perspective -- the
|
||||||
|
* {@code Injector} creates it, injects it once into the class that required it,
|
||||||
|
* and then immediately forgets it. Associating a scope with a particular
|
||||||
|
* binding allows the created instance to be "remembered" and possibly used
|
||||||
|
* again for other injections.
|
||||||
|
*
|
||||||
|
* <p>An example of a scope is {@link Scopes#SINGLETON}.
|
||||||
|
*/
|
||||||
|
public interface Scope {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scopes a provider. The returned provider returns objects from this scope.
|
||||||
|
* If an object does not exist in this scope, the provider can use the given
|
||||||
|
* unscoped provider to retrieve one.
|
||||||
|
*
|
||||||
|
* <p>Scope implementations are strongly encouraged to override
|
||||||
|
* {@link Object#toString} in the returned provider and include the backing
|
||||||
|
* provider's {@code toString()} output.
|
||||||
|
*
|
||||||
|
* @param key binding key
|
||||||
|
* @param unscoped locates an instance when one doesn't already exist in this
|
||||||
|
* scope.
|
||||||
|
* @return a new provider which only delegates to the given unscoped provider
|
||||||
|
* when an instance of the requested object doesn't already exist in this
|
||||||
|
* scope
|
||||||
|
*/
|
||||||
|
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A short but useful description of this scope. For comparison, the standard
|
||||||
|
* scopes that ship with guice use the descriptions
|
||||||
|
* {@code "Scopes.SINGLETON"}, {@code "ServletScopes.SESSION"} and
|
||||||
|
* {@code "ServletScopes.REQUEST"}.
|
||||||
|
*/
|
||||||
|
String toString();
|
||||||
|
}
|
24
src/main/java/com/google/inject/ScopeAnnotation.java
Normal file
24
src/main/java/com/google/inject/ScopeAnnotation.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates annotations which are used for scoping. Only one such annotation
|
||||||
|
* may apply to a single implementation class. You must also annotate scope
|
||||||
|
* annotations with {@code @Retention(RUNTIME)}. For example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code @}Retention(RUNTIME)
|
||||||
|
* {@code @}Target(TYPE, METHOD)
|
||||||
|
* {@code @}ScopeAnnotation
|
||||||
|
* public {@code @}interface SessionScoped {}
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Target(ANNOTATION_TYPE)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface ScopeAnnotation {
|
||||||
|
}
|
162
src/main/java/com/google/inject/Scopes.java
Normal file
162
src/main/java/com/google/inject/Scopes.java
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.internal.CircularDependencyProxy;
|
||||||
|
import com.google.inject.internal.LinkedBindingImpl;
|
||||||
|
import com.google.inject.internal.SingletonScope;
|
||||||
|
import com.google.inject.spi.BindingScopingVisitor;
|
||||||
|
import com.google.inject.spi.ExposedBinding;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Built-in scope implementations.
|
||||||
|
*/
|
||||||
|
public class Scopes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One instance per {@link Injector}. Also see {@code @}{@link Singleton}.
|
||||||
|
*/
|
||||||
|
public static final Scope SINGLETON = new SingletonScope();
|
||||||
|
/**
|
||||||
|
* No scope; the same as not applying any scope at all. Each time the
|
||||||
|
* Injector obtains an instance of an object with "no scope", it injects this
|
||||||
|
* instance then immediately forgets it. When the next request for the same
|
||||||
|
* binding arrives it will need to obtain the instance over again.
|
||||||
|
*
|
||||||
|
* <p>This exists only in case a class has been annotated with a scope
|
||||||
|
* annotation such as {@link Singleton @Singleton}, and you need to override
|
||||||
|
* this to "no scope" in your binding.
|
||||||
|
*/
|
||||||
|
public static final Scope NO_SCOPE = new Scope() {
|
||||||
|
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
||||||
|
return unscoped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Scopes.NO_SCOPE";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
||||||
|
= new BindingScopingVisitor<Boolean>() {
|
||||||
|
public Boolean visitNoScoping() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
||||||
|
return scopeAnnotation == Singleton.class
|
||||||
|
|| scopeAnnotation == javax.inject.Singleton.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean visitScope(Scope scope) {
|
||||||
|
return scope == Scopes.SINGLETON;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean visitEagerSingleton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private Scopes() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if {@code binding} is singleton-scoped. If the binding is a {@link
|
||||||
|
* com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
|
||||||
|
* was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
|
||||||
|
* also true if the target binding is singleton-scoped.
|
||||||
|
*/
|
||||||
|
public static boolean isSingleton(Binding<?> binding) {
|
||||||
|
do {
|
||||||
|
boolean singleton = binding.acceptScopingVisitor(IS_SINGLETON_VISITOR);
|
||||||
|
if (singleton) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binding instanceof LinkedBindingImpl) {
|
||||||
|
LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl) binding;
|
||||||
|
Injector injector = linkedBinding.getInjector();
|
||||||
|
if (injector != null) {
|
||||||
|
binding = injector.getBinding(linkedBinding.getLinkedKey());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (binding instanceof ExposedBinding) {
|
||||||
|
ExposedBinding<?> exposedBinding = (ExposedBinding) binding;
|
||||||
|
Injector injector = exposedBinding.getPrivateElements().getInjector();
|
||||||
|
if (injector != null) {
|
||||||
|
binding = injector.getBinding(exposedBinding.getKey());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if {@code binding} has the given scope. If the binding is a {@link
|
||||||
|
* com.google.inject.spi.LinkedKeyBinding linked key binding} and belongs to an injector (ie. it
|
||||||
|
* was retrieved via {@link Injector#getBinding Injector.getBinding()}), then this method will
|
||||||
|
* also true if the target binding has the given scope.
|
||||||
|
*
|
||||||
|
* @param binding binding to check
|
||||||
|
* @param scope scope implementation instance
|
||||||
|
* @param scopeAnnotation scope annotation class
|
||||||
|
*/
|
||||||
|
public static boolean isScoped(Binding<?> binding, final Scope scope,
|
||||||
|
final Class<? extends Annotation> scopeAnnotation) {
|
||||||
|
do {
|
||||||
|
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
||||||
|
public Boolean visitNoScoping() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
||||||
|
return visitedAnnotation == scopeAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean visitScope(Scope visitedScope) {
|
||||||
|
return visitedScope == scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean visitEagerSingleton() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binding instanceof LinkedBindingImpl) {
|
||||||
|
LinkedBindingImpl<?> linkedBinding = (LinkedBindingImpl) binding;
|
||||||
|
Injector injector = linkedBinding.getInjector();
|
||||||
|
if (injector != null) {
|
||||||
|
binding = injector.getBinding(linkedBinding.getLinkedKey());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (binding instanceof ExposedBinding) {
|
||||||
|
ExposedBinding<?> exposedBinding = (ExposedBinding) binding;
|
||||||
|
Injector injector = exposedBinding.getPrivateElements().getInjector();
|
||||||
|
if (injector != null) {
|
||||||
|
binding = injector.getBinding(exposedBinding.getKey());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the object is a proxy for a circular dependency,
|
||||||
|
* constructed by Guice because it encountered a circular dependency. Scope
|
||||||
|
* implementations should be careful to <b>not cache circular proxies</b>,
|
||||||
|
* because the proxies are not intended for general purpose use. (They are
|
||||||
|
* designed just to fulfill the immediate injection, not all injections.
|
||||||
|
* Caching them can lead to IllegalArgumentExceptions or ClassCastExceptions.)
|
||||||
|
*/
|
||||||
|
public static boolean isCircularProxy(Object object) {
|
||||||
|
return object instanceof CircularDependencyProxy;
|
||||||
|
}
|
||||||
|
}
|
17
src/main/java/com/google/inject/Singleton.java
Normal file
17
src/main/java/com/google/inject/Singleton.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply this to implementation classes when you want only one instance
|
||||||
|
* (per {@link Injector}) to be reused for all injections for that binding.
|
||||||
|
*/
|
||||||
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@ScopeAnnotation
|
||||||
|
public @interface Singleton {
|
||||||
|
}
|
26
src/main/java/com/google/inject/Stage.java
Normal file
26
src/main/java/com/google/inject/Stage.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stage we're running in.
|
||||||
|
*/
|
||||||
|
public enum Stage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We're running in a tool (an IDE plugin for example). We need binding meta data but not a
|
||||||
|
* functioning Injector. Do not inject members of instances. Do not load eager singletons. Do as
|
||||||
|
* little as possible so our tools run nice and snappy. Injectors created in this stage cannot
|
||||||
|
* be used to satisfy injections.
|
||||||
|
*/
|
||||||
|
TOOL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We want fast startup times at the expense of runtime performance and some up front error
|
||||||
|
* checking.
|
||||||
|
*/
|
||||||
|
DEVELOPMENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We want to catch errors as early as possible and take performance hits up front.
|
||||||
|
*/
|
||||||
|
PRODUCTION
|
||||||
|
}
|
321
src/main/java/com/google/inject/TypeLiteral.java
Normal file
321
src/main/java/com/google/inject/TypeLiteral.java
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.inject.internal.MoreTypes;
|
||||||
|
import com.google.inject.util.Types;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.GenericArrayType;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.lang.reflect.TypeVariable;
|
||||||
|
import java.lang.reflect.WildcardType;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.inject.internal.MoreTypes.canonicalize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a generic type {@code T}. Java doesn't yet provide a way to
|
||||||
|
* represent generic types, so this class does. Forces clients to create a
|
||||||
|
* subclass of this class which enables retrieval the type information even at
|
||||||
|
* runtime.
|
||||||
|
*
|
||||||
|
* <p>For example, to create a type literal for {@code List<String>}, you can
|
||||||
|
* create an empty anonymous inner class:
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
|
||||||
|
*
|
||||||
|
* <p>Along with modeling generic types, this class can resolve type parameters.
|
||||||
|
* For example, to figure out what type {@code keySet()} returns on a {@code
|
||||||
|
* Map<Integer, String>}, use this code:<pre> {@code
|
||||||
|
*
|
||||||
|
* TypeLiteral<Map<Integer, String>> mapType
|
||||||
|
* = new TypeLiteral<Map<Integer, String>>() {};
|
||||||
|
* TypeLiteral<?> keySetType
|
||||||
|
* = mapType.getReturnType(Map.class.getMethod("keySet"));
|
||||||
|
* System.out.println(keySetType); // prints "Set<Integer>"}</pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TypeLiteral<T> {
|
||||||
|
|
||||||
|
final Class<? super T> rawType;
|
||||||
|
final Type type;
|
||||||
|
final int hashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new type literal. Derives represented class from type
|
||||||
|
* parameter.
|
||||||
|
*
|
||||||
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
||||||
|
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||||
|
* at runtime despite erasure.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected TypeLiteral() {
|
||||||
|
this.type = getSuperclassTypeParameter(getClass());
|
||||||
|
this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
|
||||||
|
this.hashCode = type.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsafe. Constructs a type literal manually.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeLiteral(Type type) {
|
||||||
|
this.type = canonicalize(checkNotNull(type, "type"));
|
||||||
|
this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
|
||||||
|
this.hashCode = this.type.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type from super class's type parameter in {@link MoreTypes#canonicalize(Type)
|
||||||
|
* canonical form}.
|
||||||
|
*/
|
||||||
|
static Type getSuperclassTypeParameter(Class<?> subclass) {
|
||||||
|
Type superclass = subclass.getGenericSuperclass();
|
||||||
|
if (superclass instanceof Class) {
|
||||||
|
throw new RuntimeException("Missing type parameter.");
|
||||||
|
}
|
||||||
|
ParameterizedType parameterized = (ParameterizedType) superclass;
|
||||||
|
return canonicalize(parameterized.getActualTypeArguments()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets type literal from super class's type parameter.
|
||||||
|
*/
|
||||||
|
static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
|
||||||
|
return new TypeLiteral<Object>(getSuperclassTypeParameter(subclass));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets type literal for the given {@code Type} instance.
|
||||||
|
*/
|
||||||
|
public static TypeLiteral<?> get(Type type) {
|
||||||
|
return new TypeLiteral<Object>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets type literal for the given {@code Class} instance.
|
||||||
|
*/
|
||||||
|
public static <T> TypeLiteral<T> get(Class<T> type) {
|
||||||
|
return new TypeLiteral<T>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw (non-generic) type for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final Class<? super T> getRawType() {
|
||||||
|
return rawType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets underlying {@code Type} instance.
|
||||||
|
*/
|
||||||
|
public final Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of this type's provider.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final TypeLiteral<Provider<T>> providerType() {
|
||||||
|
// This cast is safe and wouldn't generate a warning if Type had a type
|
||||||
|
// parameter.
|
||||||
|
return (TypeLiteral<Provider<T>>) get(Types.providerOf(getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return this.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object o) {
|
||||||
|
return o instanceof TypeLiteral<?>
|
||||||
|
&& MoreTypes.equals(type, ((TypeLiteral) o).type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return MoreTypes.typeToString(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable list of the resolved types.
|
||||||
|
*/
|
||||||
|
private List<TypeLiteral<?>> resolveAll(Type[] types) {
|
||||||
|
TypeLiteral<?>[] result = new TypeLiteral<?>[types.length];
|
||||||
|
for (int t = 0; t < types.length; t++) {
|
||||||
|
result[t] = resolve(types[t]);
|
||||||
|
}
|
||||||
|
return ImmutableList.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves known type parameters in {@code toResolve} and returns the result.
|
||||||
|
*/
|
||||||
|
TypeLiteral<?> resolve(Type toResolve) {
|
||||||
|
return TypeLiteral.get(resolveType(toResolve));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type resolveType(Type toResolve) {
|
||||||
|
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
||||||
|
while (true) {
|
||||||
|
if (toResolve instanceof TypeVariable) {
|
||||||
|
TypeVariable original = (TypeVariable) toResolve;
|
||||||
|
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
||||||
|
if (toResolve == original) {
|
||||||
|
return toResolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (toResolve instanceof GenericArrayType) {
|
||||||
|
GenericArrayType original = (GenericArrayType) toResolve;
|
||||||
|
Type componentType = original.getGenericComponentType();
|
||||||
|
Type newComponentType = resolveType(componentType);
|
||||||
|
return componentType == newComponentType
|
||||||
|
? original
|
||||||
|
: Types.arrayOf(newComponentType);
|
||||||
|
|
||||||
|
} else if (toResolve instanceof ParameterizedType) {
|
||||||
|
ParameterizedType original = (ParameterizedType) toResolve;
|
||||||
|
Type ownerType = original.getOwnerType();
|
||||||
|
Type newOwnerType = resolveType(ownerType);
|
||||||
|
boolean changed = newOwnerType != ownerType;
|
||||||
|
|
||||||
|
Type[] args = original.getActualTypeArguments();
|
||||||
|
for (int t = 0, length = args.length; t < length; t++) {
|
||||||
|
Type resolvedTypeArgument = resolveType(args[t]);
|
||||||
|
if (resolvedTypeArgument != args[t]) {
|
||||||
|
if (!changed) {
|
||||||
|
args = args.clone();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
args[t] = resolvedTypeArgument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed
|
||||||
|
? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
|
||||||
|
: original;
|
||||||
|
|
||||||
|
} else if (toResolve instanceof WildcardType) {
|
||||||
|
WildcardType original = (WildcardType) toResolve;
|
||||||
|
Type[] originalLowerBound = original.getLowerBounds();
|
||||||
|
Type[] originalUpperBound = original.getUpperBounds();
|
||||||
|
|
||||||
|
if (originalLowerBound.length == 1) {
|
||||||
|
Type lowerBound = resolveType(originalLowerBound[0]);
|
||||||
|
if (lowerBound != originalLowerBound[0]) {
|
||||||
|
return Types.supertypeOf(lowerBound);
|
||||||
|
}
|
||||||
|
} else if (originalUpperBound.length == 1) {
|
||||||
|
Type upperBound = resolveType(originalUpperBound[0]);
|
||||||
|
if (upperBound != originalUpperBound[0]) {
|
||||||
|
return Types.subtypeOf(upperBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return toResolve;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the generic form of {@code supertype}. For example, if this is {@code
|
||||||
|
* ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
|
||||||
|
* Iterable.class}.
|
||||||
|
*
|
||||||
|
* @param supertype a superclass of, or interface implemented by, this.
|
||||||
|
*/
|
||||||
|
public TypeLiteral<?> getSupertype(Class<?> supertype) {
|
||||||
|
checkArgument(supertype.isAssignableFrom(rawType),
|
||||||
|
"%s is not a supertype of %s", supertype, this.type);
|
||||||
|
return resolve(MoreTypes.getGenericSupertype(type, rawType, supertype));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolved generic type of {@code field}.
|
||||||
|
*
|
||||||
|
* @param field a field defined by this or any superclass.
|
||||||
|
*/
|
||||||
|
public TypeLiteral<?> getFieldType(Field field) {
|
||||||
|
checkArgument(field.getDeclaringClass().isAssignableFrom(rawType),
|
||||||
|
"%s is not defined by a supertype of %s", field, type);
|
||||||
|
return resolve(field.getGenericType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolved generic parameter types of {@code methodOrConstructor}.
|
||||||
|
*
|
||||||
|
* @param methodOrConstructor a method or constructor defined by this or any supertype.
|
||||||
|
*/
|
||||||
|
public List<TypeLiteral<?>> getParameterTypes(Member methodOrConstructor) {
|
||||||
|
Type[] genericParameterTypes;
|
||||||
|
|
||||||
|
if (methodOrConstructor instanceof Method) {
|
||||||
|
Method method = (Method) methodOrConstructor;
|
||||||
|
checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
|
||||||
|
"%s is not defined by a supertype of %s", method, type);
|
||||||
|
genericParameterTypes = method.getGenericParameterTypes();
|
||||||
|
|
||||||
|
} else if (methodOrConstructor instanceof Constructor) {
|
||||||
|
Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
|
||||||
|
checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType),
|
||||||
|
"%s does not construct a supertype of %s", constructor, type);
|
||||||
|
genericParameterTypes = constructor.getGenericParameterTypes();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveAll(genericParameterTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolved generic exception types thrown by {@code constructor}.
|
||||||
|
*
|
||||||
|
* @param methodOrConstructor a method or constructor defined by this or any supertype.
|
||||||
|
*/
|
||||||
|
public List<TypeLiteral<?>> getExceptionTypes(Member methodOrConstructor) {
|
||||||
|
Type[] genericExceptionTypes;
|
||||||
|
|
||||||
|
if (methodOrConstructor instanceof Method) {
|
||||||
|
Method method = (Method) methodOrConstructor;
|
||||||
|
checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
|
||||||
|
"%s is not defined by a supertype of %s", method, type);
|
||||||
|
genericExceptionTypes = method.getGenericExceptionTypes();
|
||||||
|
|
||||||
|
} else if (methodOrConstructor instanceof Constructor) {
|
||||||
|
Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
|
||||||
|
checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType),
|
||||||
|
"%s does not construct a supertype of %s", constructor, type);
|
||||||
|
genericExceptionTypes = constructor.getGenericExceptionTypes();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveAll(genericExceptionTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolved generic return type of {@code method}.
|
||||||
|
*
|
||||||
|
* @param method a method defined by this or any supertype.
|
||||||
|
*/
|
||||||
|
public TypeLiteral<?> getReturnType(Method method) {
|
||||||
|
checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
|
||||||
|
"%s is not defined by a supertype of %s", method, type);
|
||||||
|
return resolve(method.getGenericReturnType());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject.binder;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
public interface AnnotatedBindingBuilder<T> extends LinkedBindingBuilder<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
LinkedBindingBuilder<T> annotatedWith(
|
||||||
|
Class<? extends Annotation> annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
LinkedBindingBuilder<T> annotatedWith(Annotation annotation);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject.binder;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
public interface AnnotatedConstantBindingBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ConstantBindingBuilder annotatedWith(
|
||||||
|
Class<? extends Annotation> annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ConstantBindingBuilder annotatedWith(Annotation annotation);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.google.inject.binder;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
public interface AnnotatedElementBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
void annotatedWith(Class<? extends Annotation> annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
void annotatedWith(Annotation annotation);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.google.inject.binder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds to a constant value.
|
||||||
|
*/
|
||||||
|
public interface ConstantBindingBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(long value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(boolean value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(double value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(short value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(char value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(byte value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
void to(Class<?> value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds constant to the given value.
|
||||||
|
*/
|
||||||
|
<E extends Enum<E>> void to(E value);
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.google.inject.binder;
|
||||||
|
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder to(Class<? extends T> implementation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder to(TypeLiteral<? extends T> implementation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder to(Key<? extends T> targetKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*
|
||||||
|
* @see com.google.inject.Injector#injectMembers
|
||||||
|
*/
|
||||||
|
void toInstance(T instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*
|
||||||
|
* @see com.google.inject.Injector#injectMembers
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder toProvider(Provider<? extends T> provider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*
|
||||||
|
* @see com.google.inject.Injector#injectMembers
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder toProvider(javax.inject.Provider<? extends T> provider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder toProvider(
|
||||||
|
Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder toProvider(
|
||||||
|
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
ScopedBindingBuilder toProvider(
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
<S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
<S extends T> ScopedBindingBuilder toConstructor(
|
||||||
|
Constructor<S> constructor, TypeLiteral<? extends S> type);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.google.inject.binder;
|
||||||
|
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
public interface ScopedBindingBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
void in(Class<? extends Annotation> scopeAnnotation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
void in(Scope scope);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs the {@link com.google.inject.Injector} to eagerly initialize this
|
||||||
|
* singleton-scoped binding upon creation. Useful for application
|
||||||
|
* initialization logic. See the EDSL examples at
|
||||||
|
* {@link com.google.inject.Binder}.
|
||||||
|
*/
|
||||||
|
void asEagerSingleton();
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.InstanceBinding;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a value or constant.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AbstractBindingBuilder<T> {
|
||||||
|
|
||||||
|
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
||||||
|
public static final String SINGLE_INSTANCE_AND_SCOPE
|
||||||
|
= "Setting the scope is not permitted when binding to a single instance.";
|
||||||
|
public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
|
||||||
|
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. "
|
||||||
|
+ "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
|
||||||
|
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
|
||||||
|
public static final String ANNOTATION_ALREADY_SPECIFIED
|
||||||
|
= "More than one annotation is specified for this binding.";
|
||||||
|
|
||||||
|
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
||||||
|
protected final Binder binder;
|
||||||
|
protected List<Element> elements;
|
||||||
|
protected int position;
|
||||||
|
private BindingImpl<T> binding;
|
||||||
|
|
||||||
|
public AbstractBindingBuilder(Binder binder, List<Element> elements, Object source, Key<T> key) {
|
||||||
|
this.binder = binder;
|
||||||
|
this.elements = elements;
|
||||||
|
this.position = elements.size();
|
||||||
|
this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED);
|
||||||
|
elements.add(position, this.binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BindingImpl<T> getBinding() {
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BindingImpl<T> setBinding(BindingImpl<T> binding) {
|
||||||
|
this.binding = binding;
|
||||||
|
elements.set(position, binding);
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the binding to a copy with the specified annotation on the bound key
|
||||||
|
*/
|
||||||
|
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
||||||
|
checkNotNull(annotationType, "annotationType");
|
||||||
|
checkNotAnnotated();
|
||||||
|
return setBinding(binding.withKey(
|
||||||
|
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the binding to a copy with the specified annotation on the bound key
|
||||||
|
*/
|
||||||
|
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
||||||
|
checkNotNull(annotation, "annotation");
|
||||||
|
checkNotAnnotated();
|
||||||
|
return setBinding(binding.withKey(
|
||||||
|
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void in(final Class<? extends Annotation> scopeAnnotation) {
|
||||||
|
checkNotNull(scopeAnnotation, "scopeAnnotation");
|
||||||
|
checkNotScoped();
|
||||||
|
setBinding(getBinding().withScoping(Scoping.forAnnotation(scopeAnnotation)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void in(final Scope scope) {
|
||||||
|
checkNotNull(scope, "scope");
|
||||||
|
checkNotScoped();
|
||||||
|
setBinding(getBinding().withScoping(Scoping.forInstance(scope)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void asEagerSingleton() {
|
||||||
|
checkNotScoped();
|
||||||
|
setBinding(getBinding().withScoping(Scoping.EAGER_SINGLETON));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean keyTypeIsSet() {
|
||||||
|
return !Void.class.equals(binding.getKey().getTypeLiteral().getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkNotTargetted() {
|
||||||
|
if (!(binding instanceof UntargettedBindingImpl)) {
|
||||||
|
binder.addError(IMPLEMENTATION_ALREADY_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkNotAnnotated() {
|
||||||
|
if (binding.getKey().getAnnotationType() != null) {
|
||||||
|
binder.addError(ANNOTATION_ALREADY_SPECIFIED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkNotScoped() {
|
||||||
|
// Scoping isn't allowed when we have only one instance.
|
||||||
|
if (binding instanceof InstanceBinding) {
|
||||||
|
binder.addError(SINGLE_INSTANCE_AND_SCOPE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binding.getScoping().isExplicitlyScoped()) {
|
||||||
|
binder.addError(SCOPE_ALREADY_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
import com.google.inject.Stage;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.DefaultBindingTargetVisitor;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guarantees that processing of Binding elements happens in a sane way.
|
||||||
|
*/
|
||||||
|
abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
// It's unfortunate that we have to maintain a blacklist of specific
|
||||||
|
// classes, but we can't easily block the whole package because of
|
||||||
|
// all our unit tests.
|
||||||
|
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(
|
||||||
|
AbstractModule.class,
|
||||||
|
Binder.class,
|
||||||
|
Binding.class,
|
||||||
|
Injector.class,
|
||||||
|
Key.class,
|
||||||
|
MembersInjector.class,
|
||||||
|
Module.class,
|
||||||
|
Provider.class,
|
||||||
|
Scope.class,
|
||||||
|
Stage.class,
|
||||||
|
TypeLiteral.class);
|
||||||
|
|
||||||
|
protected final ProcessedBindingData bindingData;
|
||||||
|
|
||||||
|
AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) {
|
||||||
|
super(errors);
|
||||||
|
this.bindingData = bindingData;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> UntargettedBindingImpl<T> invalidBinding(
|
||||||
|
InjectorImpl injector, Key<T> key, Object source) {
|
||||||
|
return new UntargettedBindingImpl<T>(injector, key, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putBinding(BindingImpl<?> binding) {
|
||||||
|
Key<?> key = binding.getKey();
|
||||||
|
|
||||||
|
Class<?> rawType = key.getTypeLiteral().getRawType();
|
||||||
|
if (FORBIDDEN_TYPES.contains(rawType)) {
|
||||||
|
errors.cannotBindToGuiceType(rawType.getSimpleName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<?> original = injector.getExistingBinding(key);
|
||||||
|
if (original != null) {
|
||||||
|
// If it failed because of an explicit duplicate binding...
|
||||||
|
if (injector.state.getExplicitBinding(key) != null) {
|
||||||
|
try {
|
||||||
|
if (!isOkayDuplicate(original, binding, injector.state)) {
|
||||||
|
errors.bindingAlreadySet(key, original.getSource());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
errors.errorCheckingDuplicateBinding(key, original.getSource(), t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, it failed because of a duplicate JIT binding
|
||||||
|
// in the parent
|
||||||
|
errors.jitBindingAlreadySet(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent the parent from creating a JIT binding for this key
|
||||||
|
injector.state.parent().blacklist(key, injector.state, binding.getSource());
|
||||||
|
injector.state.putBinding(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We tolerate duplicate bindings if one exposes the other or if the two bindings
|
||||||
|
* are considered duplicates.
|
||||||
|
*
|
||||||
|
* @param original the binding in the parent injector (candidate for an exposing binding)
|
||||||
|
* @param binding the binding to check (candidate for the exposed binding)
|
||||||
|
*/
|
||||||
|
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
|
||||||
|
if (original instanceof ExposedBindingImpl) {
|
||||||
|
ExposedBindingImpl exposed = (ExposedBindingImpl) original;
|
||||||
|
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
||||||
|
return (exposedFrom == binding.getInjector());
|
||||||
|
} else {
|
||||||
|
original = (BindingImpl<?>) state.getExplicitBindingsThisLevel().get(binding.getKey());
|
||||||
|
// If no original at this level, the original was on a parent, and we don't
|
||||||
|
// allow deduplication between parents & children.
|
||||||
|
return original != null && original.equals(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void validateKey(Object source, Key<T> key) {
|
||||||
|
Annotations.checkForMisplacedScopeAnnotations(
|
||||||
|
key.getTypeLiteral().getRawType(), source, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processor for visiting bindings. Each overriden method that wants to
|
||||||
|
* actually process the binding should call prepareBinding first.
|
||||||
|
*/
|
||||||
|
abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> {
|
||||||
|
final Object source;
|
||||||
|
final Key<T> key;
|
||||||
|
final Class<? super T> rawType;
|
||||||
|
Scoping scoping;
|
||||||
|
|
||||||
|
Processor(BindingImpl<T> binding) {
|
||||||
|
source = binding.getSource();
|
||||||
|
key = binding.getKey();
|
||||||
|
rawType = key.getTypeLiteral().getRawType();
|
||||||
|
scoping = binding.getScoping();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void prepareBinding() {
|
||||||
|
validateKey(source, key);
|
||||||
|
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void scheduleInitialization(final BindingImpl<?> binding) {
|
||||||
|
bindingData.addUninitializedBinding(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.DefaultElementVisitor;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for creating an injector from module elements.
|
||||||
|
*
|
||||||
|
* <p>Extending classes must return {@code true} from any overridden
|
||||||
|
* {@code visit*()} methods, in order for the element processor to remove the
|
||||||
|
* handled element.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class AbstractProcessor extends DefaultElementVisitor<Boolean> {
|
||||||
|
|
||||||
|
protected Errors errors;
|
||||||
|
protected InjectorImpl injector;
|
||||||
|
|
||||||
|
protected AbstractProcessor(Errors errors) {
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(Iterable<InjectorShell> isolatedInjectorBuilders) {
|
||||||
|
for (InjectorShell injectorShell : isolatedInjectorBuilders) {
|
||||||
|
process(injectorShell.getInjector(), injectorShell.getElements());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(InjectorImpl injector, List<Element> elements) {
|
||||||
|
Errors errorsAnyElement = this.errors;
|
||||||
|
this.injector = injector;
|
||||||
|
try {
|
||||||
|
for (Iterator<Element> i = elements.iterator(); i.hasNext(); ) {
|
||||||
|
Element element = i.next();
|
||||||
|
this.errors = errorsAnyElement.withSource(element.getSource());
|
||||||
|
Boolean allDone = element.acceptVisitor(this);
|
||||||
|
if (allDone) {
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.errors = errorsAnyElement;
|
||||||
|
this.injector = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean visitOther(Element element) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
334
src/main/java/com/google/inject/internal/Annotations.java
Normal file
334
src/main/java/com/google/inject/internal/Annotations.java
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.base.Joiner.MapJoiner;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.BindingAnnotation;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.ScopeAnnotation;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.util.Classes;
|
||||||
|
import com.google.inject.name.Named;
|
||||||
|
import com.google.inject.name.Names;
|
||||||
|
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation utilities.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Annotations {
|
||||||
|
|
||||||
|
private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
|
||||||
|
private static final Function<Object, String> DEEP_TO_STRING_FN = new Function<Object, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(Object arg) {
|
||||||
|
String s = Arrays.deepToString(new Object[]{arg});
|
||||||
|
return s.substring(1, s.length() - 1); // cut off brackets
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
|
||||||
|
CacheBuilder.newBuilder().weakKeys().build(
|
||||||
|
new CacheLoader<Class<? extends Annotation>, Annotation>() {
|
||||||
|
@Override
|
||||||
|
public Annotation load(Class<? extends Annotation> input) {
|
||||||
|
return generateAnnotationImpl(input);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
private static final AnnotationChecker scopeChecker = new AnnotationChecker(
|
||||||
|
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
|
||||||
|
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
|
||||||
|
Arrays.asList(BindingAnnotation.class, Qualifier.class));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the given annotation type has no attributes.
|
||||||
|
*/
|
||||||
|
public static boolean isMarker(Class<? extends Annotation> annotationType) {
|
||||||
|
return annotationType.getDeclaredMethods().length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAllDefaultMethods(Class<? extends Annotation> annotationType) {
|
||||||
|
boolean hasMethods = false;
|
||||||
|
for (Method m : annotationType.getDeclaredMethods()) {
|
||||||
|
hasMethods = true;
|
||||||
|
if (m.getDefaultValue() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an Annotation for the annotation class. Requires that the annotation is all
|
||||||
|
* optionals.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) {
|
||||||
|
Preconditions.checkState(
|
||||||
|
isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType);
|
||||||
|
return (T) cache.getUnchecked(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) {
|
||||||
|
final Map<String, Object> members = resolveMembers(annotationType);
|
||||||
|
return annotationType.cast(Proxy.newProxyInstance(
|
||||||
|
annotationType.getClassLoader(),
|
||||||
|
new Class<?>[]{annotationType},
|
||||||
|
new InvocationHandler() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
|
||||||
|
String name = method.getName();
|
||||||
|
if (name.equals("annotationType")) {
|
||||||
|
return annotationType;
|
||||||
|
} else if (name.equals("toString")) {
|
||||||
|
return annotationToString(annotationType, members);
|
||||||
|
} else if (name.equals("hashCode")) {
|
||||||
|
return annotationHashCode(annotationType, members);
|
||||||
|
} else if (name.equals("equals")) {
|
||||||
|
return annotationEquals(annotationType, members, args[0]);
|
||||||
|
} else {
|
||||||
|
return members.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableMap<String, Object> resolveMembers(
|
||||||
|
Class<? extends Annotation> annotationType) {
|
||||||
|
ImmutableMap.Builder<String, Object> result = ImmutableMap.builder();
|
||||||
|
for (Method method : annotationType.getDeclaredMethods()) {
|
||||||
|
result.put(method.getName(), method.getDefaultValue());
|
||||||
|
}
|
||||||
|
return result.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements {@link Annotation#equals}.
|
||||||
|
*/
|
||||||
|
private static boolean annotationEquals(Class<? extends Annotation> type,
|
||||||
|
Map<String, Object> members, Object other) throws Exception {
|
||||||
|
if (!type.isInstance(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Method method : type.getDeclaredMethods()) {
|
||||||
|
String name = method.getName();
|
||||||
|
if (!Arrays.deepEquals(
|
||||||
|
new Object[]{method.invoke(other)}, new Object[]{members.get(name)})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements {@link Annotation#hashCode}.
|
||||||
|
*/
|
||||||
|
private static int annotationHashCode(Class<? extends Annotation> type,
|
||||||
|
Map<String, Object> members) throws Exception {
|
||||||
|
int result = 0;
|
||||||
|
for (Method method : type.getDeclaredMethods()) {
|
||||||
|
String name = method.getName();
|
||||||
|
Object value = members.get(name);
|
||||||
|
result += (127 * name.hashCode()) ^ (Arrays.deepHashCode(new Object[]{value}) - 31);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements {@link Annotation#toString}.
|
||||||
|
*/
|
||||||
|
private static String annotationToString(Class<? extends Annotation> type,
|
||||||
|
Map<String, Object> members) throws Exception {
|
||||||
|
StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("(");
|
||||||
|
JOINER.appendTo(sb, Maps.transformValues(members, DEEP_TO_STRING_FN));
|
||||||
|
return sb.append(")").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given annotation is retained at runtime.
|
||||||
|
*/
|
||||||
|
public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) {
|
||||||
|
Retention retention = annotationType.getAnnotation(Retention.class);
|
||||||
|
return retention != null && retention.value() == RetentionPolicy.RUNTIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scope annotation on {@code type}, or null if none is specified.
|
||||||
|
*/
|
||||||
|
public static Class<? extends Annotation> findScopeAnnotation(
|
||||||
|
Errors errors, Class<?> implementation) {
|
||||||
|
return findScopeAnnotation(errors, implementation.getAnnotations());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scoping annotation, or null if there isn't one.
|
||||||
|
*/
|
||||||
|
public static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
|
||||||
|
Class<? extends Annotation> found = null;
|
||||||
|
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
if (isScopeAnnotation(annotationType)) {
|
||||||
|
if (found != null) {
|
||||||
|
errors.duplicateScopeAnnotations(found, annotationType);
|
||||||
|
} else {
|
||||||
|
found = annotationType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean containsComponentAnnotation(Annotation[] annotations) {
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
// TODO(user): Should we scope this down to dagger.Component?
|
||||||
|
if (annotation.annotationType().getSimpleName().equals("Component")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return scopeChecker.hasAnnotations(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error if there is a misplaced annotations on {@code type}. Scoping
|
||||||
|
* annotations are not allowed on abstract classes or interfaces.
|
||||||
|
*/
|
||||||
|
public static void checkForMisplacedScopeAnnotations(
|
||||||
|
Class<?> type, Object source, Errors errors) {
|
||||||
|
if (Classes.isConcrete(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type);
|
||||||
|
if (scopeAnnotation != null
|
||||||
|
// We let Dagger Components through to aid migrations.
|
||||||
|
&& !containsComponentAnnotation(type.getAnnotations())) {
|
||||||
|
errors.withSource(type).scopeAnnotationOnAbstractType(scopeAnnotation, type, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a key for the given type, member and annotations.
|
||||||
|
*/
|
||||||
|
public static Key<?> getKey(TypeLiteral<?> type, Member member, Annotation[] annotations,
|
||||||
|
Errors errors) throws ErrorsException {
|
||||||
|
int numErrorsBefore = errors.size();
|
||||||
|
Annotation found = findBindingAnnotation(errors, member, annotations);
|
||||||
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
return found == null ? Key.get(type) : Key.get(type, found);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the binding annotation on {@code member}, or null if there isn't one.
|
||||||
|
*/
|
||||||
|
public static Annotation findBindingAnnotation(
|
||||||
|
Errors errors, Member member, Annotation[] annotations) {
|
||||||
|
Annotation found = null;
|
||||||
|
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
if (isBindingAnnotation(annotationType)) {
|
||||||
|
if (found != null) {
|
||||||
|
errors.duplicateBindingAnnotations(member, found.annotationType(), annotationType);
|
||||||
|
} else {
|
||||||
|
found = annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if annotations of the specified type are binding annotations.
|
||||||
|
*/
|
||||||
|
public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return bindingAnnotationChecker.hasAnnotations(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the annotation is an instance of {@code javax.inject.Named}, canonicalizes to
|
||||||
|
* com.google.guice.name.Named. Returns the given annotation otherwise.
|
||||||
|
*/
|
||||||
|
public static Annotation canonicalizeIfNamed(Annotation annotation) {
|
||||||
|
if (annotation instanceof javax.inject.Named) {
|
||||||
|
return Names.named(((javax.inject.Named) annotation).value());
|
||||||
|
} else {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the annotation is the class {@code javax.inject.Named}, canonicalizes to
|
||||||
|
* com.google.guice.name.Named. Returns the given annotation class otherwise.
|
||||||
|
*/
|
||||||
|
public static Class<? extends Annotation> canonicalizeIfNamed(
|
||||||
|
Class<? extends Annotation> annotationType) {
|
||||||
|
if (annotationType == javax.inject.Named.class) {
|
||||||
|
return Named.class;
|
||||||
|
} else {
|
||||||
|
return annotationType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for the presence of annotations. Caches results because Android doesn't.
|
||||||
|
*/
|
||||||
|
static class AnnotationChecker {
|
||||||
|
private final Collection<Class<? extends Annotation>> annotationTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given class has one of the desired annotations.
|
||||||
|
*/
|
||||||
|
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
|
||||||
|
new CacheLoader<Class<? extends Annotation>, Boolean>() {
|
||||||
|
public Boolean load(Class<? extends Annotation> annotationType) {
|
||||||
|
for (Annotation annotation : annotationType.getAnnotations()) {
|
||||||
|
if (annotationTypes.contains(annotation.annotationType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
|
||||||
|
.build(hasAnnotations);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new checker that looks for annotations of the given types.
|
||||||
|
*/
|
||||||
|
AnnotationChecker(Collection<Class<? extends Annotation>> annotationTypes) {
|
||||||
|
this.annotationTypes = annotationTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given type has one of the desired annotations.
|
||||||
|
*/
|
||||||
|
boolean hasAnnotations(Class<? extends Annotation> annotated) {
|
||||||
|
return cache.getUnchecked(annotated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
167
src/main/java/com/google/inject/internal/BindingBuilder.java
Normal file
167
src/main/java/com/google/inject/internal/BindingBuilder.java
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.ConfigurationException;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.binder.AnnotatedBindingBuilder;
|
||||||
|
import com.google.inject.binder.ScopedBindingBuilder;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a non-constant key.
|
||||||
|
*/
|
||||||
|
public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
|
implements AnnotatedBindingBuilder<T> {
|
||||||
|
|
||||||
|
public BindingBuilder(Binder binder, List<Element> elements, Object source, Key<T> key) {
|
||||||
|
super(binder, elements, source, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
|
annotatedWithInternal(annotationType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
||||||
|
annotatedWithInternal(annotation);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
||||||
|
return to(Key.get(implementation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
||||||
|
return to(Key.get(implementation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
||||||
|
checkNotNull(linkedKey, "linkedKey");
|
||||||
|
checkNotTargetted();
|
||||||
|
BindingImpl<T> base = getBinding();
|
||||||
|
setBinding(new LinkedBindingImpl<T>(
|
||||||
|
base.getSource(), base.getKey(), base.getScoping(), linkedKey));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toInstance(T instance) {
|
||||||
|
checkNotTargetted();
|
||||||
|
|
||||||
|
// lookup the injection points, adding any errors to the binder's errors list
|
||||||
|
Set<InjectionPoint> injectionPoints;
|
||||||
|
if (instance != null) {
|
||||||
|
try {
|
||||||
|
injectionPoints = InjectionPoint.forInstanceMethodsAndFields(instance.getClass());
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
copyErrorsToBinder(e);
|
||||||
|
injectionPoints = e.getPartialValue();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binder.addError(BINDING_TO_NULL);
|
||||||
|
injectionPoints = ImmutableSet.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<T> base = getBinding();
|
||||||
|
setBinding(new InstanceBindingImpl<T>(
|
||||||
|
base.getSource(), base.getKey(), Scoping.EAGER_SINGLETON, injectionPoints, instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
||||||
|
return toProvider((javax.inject.Provider<T>) provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
||||||
|
checkNotNull(provider, "provider");
|
||||||
|
checkNotTargetted();
|
||||||
|
|
||||||
|
// lookup the injection points, adding any errors to the binder's errors list
|
||||||
|
Set<InjectionPoint> injectionPoints;
|
||||||
|
try {
|
||||||
|
injectionPoints = InjectionPoint.forInstanceMethodsAndFields(provider.getClass());
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
copyErrorsToBinder(e);
|
||||||
|
injectionPoints = e.getPartialValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<T> base = getBinding();
|
||||||
|
setBinding(new ProviderInstanceBindingImpl<T>(
|
||||||
|
base.getSource(), base.getKey(), base.getScoping(), injectionPoints, provider));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> toProvider(
|
||||||
|
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||||
|
return toProvider(Key.get(providerType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> toProvider(
|
||||||
|
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||||
|
return toProvider(Key.get(providerType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingBuilder<T> toProvider(
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||||
|
checkNotNull(providerKey, "providerKey");
|
||||||
|
checkNotTargetted();
|
||||||
|
|
||||||
|
BindingImpl<T> base = getBinding();
|
||||||
|
setBinding(new LinkedProviderBindingImpl<T>(
|
||||||
|
base.getSource(), base.getKey(), base.getScoping(), providerKey));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
||||||
|
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
||||||
|
TypeLiteral<? extends S> type) {
|
||||||
|
checkNotNull(constructor, "constructor");
|
||||||
|
checkNotNull(type, "type");
|
||||||
|
checkNotTargetted();
|
||||||
|
|
||||||
|
BindingImpl<T> base = getBinding();
|
||||||
|
|
||||||
|
Set<InjectionPoint> injectionPoints;
|
||||||
|
try {
|
||||||
|
injectionPoints = InjectionPoint.forInstanceMethodsAndFields(type);
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
copyErrorsToBinder(e);
|
||||||
|
injectionPoints = e.getPartialValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
InjectionPoint constructorPoint = InjectionPoint.forConstructor(constructor, type);
|
||||||
|
setBinding(new ConstructorBindingImpl<T>(base.getKey(), base.getSource(), base.getScoping(),
|
||||||
|
constructorPoint, injectionPoints));
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
copyErrorsToBinder(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BindingBuilder<" + getBinding().getKey().getTypeLiteral() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyErrorsToBinder(ConfigurationException e) {
|
||||||
|
for (Message message : e.getErrorMessages()) {
|
||||||
|
binder.addError(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
src/main/java/com/google/inject/internal/BindingImpl.java
Normal file
100
src/main/java/com/google/inject/internal/BindingImpl.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.spi.BindingScopingVisitor;
|
||||||
|
import com.google.inject.spi.ElementVisitor;
|
||||||
|
import com.google.inject.spi.InstanceBinding;
|
||||||
|
|
||||||
|
public abstract class BindingImpl<T> implements Binding<T> {
|
||||||
|
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private final Key<T> key;
|
||||||
|
private final Object source;
|
||||||
|
private final Scoping scoping;
|
||||||
|
private final InternalFactory<? extends T> internalFactory;
|
||||||
|
private volatile Provider<T> provider;
|
||||||
|
|
||||||
|
public BindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
InternalFactory<? extends T> internalFactory, Scoping scoping) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.key = key;
|
||||||
|
this.source = source;
|
||||||
|
this.internalFactory = internalFactory;
|
||||||
|
this.scoping = scoping;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BindingImpl(Object source, Key<T> key, Scoping scoping) {
|
||||||
|
this.internalFactory = null;
|
||||||
|
this.injector = null;
|
||||||
|
this.source = source;
|
||||||
|
this.key = key;
|
||||||
|
this.scoping = scoping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key<T> getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Provider<T> getProvider() {
|
||||||
|
if (provider == null) {
|
||||||
|
if (injector == null) {
|
||||||
|
throw new UnsupportedOperationException("getProvider() not supported for module bindings");
|
||||||
|
}
|
||||||
|
|
||||||
|
provider = injector.getProvider(key);
|
||||||
|
}
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalFactory<? extends T> getInternalFactory() {
|
||||||
|
return internalFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scoping getScoping() {
|
||||||
|
return scoping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a constant binding? This returns true for constant bindings as
|
||||||
|
* well as toInstance() bindings.
|
||||||
|
*/
|
||||||
|
public boolean isConstant() {
|
||||||
|
return this instanceof InstanceBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
||||||
|
return scoping.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BindingImpl<T> withKey(Key<T> key) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(Binding.class)
|
||||||
|
.add("key", key)
|
||||||
|
.add("scope", scoping)
|
||||||
|
.add("source", source)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InjectorImpl getInjector() {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
}
|
180
src/main/java/com/google/inject/internal/BindingProcessor.java
Normal file
180
src/main/java/com/google/inject/internal/BindingProcessor.java
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.spi.ConstructorBinding;
|
||||||
|
import com.google.inject.spi.ConvertedConstantBinding;
|
||||||
|
import com.google.inject.spi.ExposedBinding;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.InstanceBinding;
|
||||||
|
import com.google.inject.spi.LinkedKeyBinding;
|
||||||
|
import com.google.inject.spi.PrivateElements;
|
||||||
|
import com.google.inject.spi.ProviderBinding;
|
||||||
|
import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
|
import com.google.inject.spi.ProviderKeyBinding;
|
||||||
|
import com.google.inject.spi.UntargettedBinding;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@link Binder#bind} and {@link Binder#bindConstant} elements.
|
||||||
|
*/
|
||||||
|
final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
|
|
||||||
|
private final Initializer initializer;
|
||||||
|
|
||||||
|
BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) {
|
||||||
|
super(errors, bindingData);
|
||||||
|
this.initializer = initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Boolean visit(Binding<T> command) {
|
||||||
|
Class<?> rawType = command.getKey().getTypeLiteral().getRawType();
|
||||||
|
if (Void.class.equals(rawType)) {
|
||||||
|
if (command instanceof ProviderInstanceBinding
|
||||||
|
&& ((ProviderInstanceBinding) command).getUserSuppliedProvider()
|
||||||
|
instanceof ProviderMethod) {
|
||||||
|
errors.voidProviderMethod();
|
||||||
|
} else {
|
||||||
|
errors.missingConstantValues();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawType == Provider.class) {
|
||||||
|
errors.bindingToProvider();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) {
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
||||||
|
prepareBinding();
|
||||||
|
try {
|
||||||
|
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
|
||||||
|
binding.getConstructor(), source, scoping, errors, false, false);
|
||||||
|
scheduleInitialization(onInjector);
|
||||||
|
putBinding(onInjector);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
putBinding(invalidBinding(injector, key, source));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(InstanceBinding<? extends T> binding) {
|
||||||
|
prepareBinding();
|
||||||
|
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||||
|
T instance = binding.getInstance();
|
||||||
|
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
|
||||||
|
// the processor was constructed w/ it
|
||||||
|
Initializable<T> ref = initializer.requestInjection(
|
||||||
|
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
||||||
|
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
|
||||||
|
InternalFactory<? extends T> scopedFactory
|
||||||
|
= Scoping.scope(key, injector, factory, source, scoping);
|
||||||
|
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
|
||||||
|
instance));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
||||||
|
prepareBinding();
|
||||||
|
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
|
||||||
|
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||||
|
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
||||||
|
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
||||||
|
injector, provider, null, source, injectionPoints);
|
||||||
|
// always visited with Binding<T>
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
|
||||||
|
initializable, source,
|
||||||
|
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
||||||
|
InternalFactory<? extends T> scopedFactory
|
||||||
|
= Scoping.scope(key, injector, factory, source, scoping);
|
||||||
|
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
|
||||||
|
provider, injectionPoints));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
|
||||||
|
prepareBinding();
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
|
||||||
|
// always visited with Binding<T>
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
|
||||||
|
injector, providerKey, source,
|
||||||
|
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
||||||
|
bindingData.addCreationListener(boundProviderFactory);
|
||||||
|
InternalFactory<? extends T> scopedFactory = Scoping.scope(
|
||||||
|
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
|
||||||
|
putBinding(new LinkedProviderBindingImpl<T>(
|
||||||
|
injector, key, source, scopedFactory, scoping, providerKey));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(LinkedKeyBinding<? extends T> binding) {
|
||||||
|
prepareBinding();
|
||||||
|
Key<? extends T> linkedKey = binding.getLinkedKey();
|
||||||
|
if (key.equals(linkedKey)) {
|
||||||
|
errors.recursiveBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
|
||||||
|
bindingData.addCreationListener(factory);
|
||||||
|
InternalFactory<? extends T> scopedFactory
|
||||||
|
= Scoping.scope(key, injector, factory, source, scoping);
|
||||||
|
putBinding(
|
||||||
|
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(UntargettedBinding<? extends T> untargetted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ExposedBinding<? extends T> binding) {
|
||||||
|
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ConvertedConstantBinding<? extends T> binding) {
|
||||||
|
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ProviderBinding<? extends T> binding) {
|
||||||
|
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean visitOther(Binding<? extends T> binding) {
|
||||||
|
throw new IllegalStateException("BindingProcessor should override all visitations");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(PrivateElements privateElements) {
|
||||||
|
for (Key<?> key : privateElements.getExposedKeys()) {
|
||||||
|
bindExposed(privateElements, key);
|
||||||
|
}
|
||||||
|
return false; // leave the private elements for the PrivateElementsProcessor to handle
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
|
||||||
|
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
|
||||||
|
bindingData.addCreationListener(exposedKeyFactory);
|
||||||
|
putBinding(new ExposedBindingImpl<T>(
|
||||||
|
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.internal.InjectorImpl.JitLimitation;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to a custom factory which is also bound in the injector.
|
||||||
|
*/
|
||||||
|
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
|
||||||
|
|
||||||
|
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||||
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
||||||
|
|
||||||
|
BoundProviderFactory(
|
||||||
|
InjectorImpl injector,
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey,
|
||||||
|
Object source,
|
||||||
|
ProvisionListenerStackCallback<T> provisionCallback) {
|
||||||
|
super(source);
|
||||||
|
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
|
||||||
|
this.injector = injector;
|
||||||
|
this.providerKey = providerKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notify(Errors errors) {
|
||||||
|
try {
|
||||||
|
providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
context.pushState(providerKey, source);
|
||||||
|
try {
|
||||||
|
errors = errors.withSource(providerKey);
|
||||||
|
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
|
||||||
|
return circularGet(provider, errors, context, dependency, provisionCallback);
|
||||||
|
} finally {
|
||||||
|
context.popState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
||||||
|
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||||
|
try {
|
||||||
|
return super.provision(provider, errors, dependency, constructionContext);
|
||||||
|
} catch (RuntimeException userException) {
|
||||||
|
throw errors.errorInProvider(userException).toException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return providerKey.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
public interface CircularDependencyProxy {
|
||||||
|
// marker interface
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
||||||
|
import com.google.inject.binder.ConstantBindingBuilder;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a constant.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ConstantBindingBuilderImpl<T>
|
||||||
|
extends AbstractBindingBuilder<T>
|
||||||
|
implements AnnotatedConstantBindingBuilder, ConstantBindingBuilder {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // constant bindings start out with T unknown
|
||||||
|
public ConstantBindingBuilderImpl(Binder binder, List<Element> elements, Object source) {
|
||||||
|
super(binder, elements, source, (Key<T>) NULL_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
|
annotatedWithInternal(annotationType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
||||||
|
annotatedWithInternal(annotation);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final String value) {
|
||||||
|
toConstant(String.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final int value) {
|
||||||
|
toConstant(Integer.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final long value) {
|
||||||
|
toConstant(Long.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final boolean value) {
|
||||||
|
toConstant(Boolean.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final double value) {
|
||||||
|
toConstant(Double.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final float value) {
|
||||||
|
toConstant(Float.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final short value) {
|
||||||
|
toConstant(Short.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final char value) {
|
||||||
|
toConstant(Character.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final byte value) {
|
||||||
|
toConstant(Byte.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void to(final Class<?> value) {
|
||||||
|
toConstant(Class.class, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Enum<E>> void to(final E value) {
|
||||||
|
toConstant(value.getDeclaringClass(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toConstant(Class<?> type, Object instance) {
|
||||||
|
// this type will define T, so these assignments are safe
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Class<T> typeAsClassT = (Class<T>) type;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T instanceAsT = (T) instance;
|
||||||
|
|
||||||
|
if (keyTypeIsSet()) {
|
||||||
|
binder.addError(CONSTANT_VALUE_ALREADY_SET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<T> base = getBinding();
|
||||||
|
Key<T> key;
|
||||||
|
if (base.getKey().getAnnotation() != null) {
|
||||||
|
key = Key.get(typeAsClassT, base.getKey().getAnnotation());
|
||||||
|
} else if (base.getKey().getAnnotationType() != null) {
|
||||||
|
key = Key.get(typeAsClassT, base.getKey().getAnnotationType());
|
||||||
|
} else {
|
||||||
|
key = Key.get(typeAsClassT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instanceAsT == null) {
|
||||||
|
binder.addError(BINDING_TO_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
setBinding(new InstanceBindingImpl<T>(
|
||||||
|
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ConstantBindingBuilder";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
|
final class ConstantFactory<T> implements InternalFactory<T> {
|
||||||
|
|
||||||
|
private final Initializable<T> initializable;
|
||||||
|
|
||||||
|
public ConstantFactory(Initializable<T> initializable) {
|
||||||
|
this.initializable = initializable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
return initializable.get(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(ConstantFactory.class)
|
||||||
|
.add("value", initializable)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of a dependency construction. Used to manage circular references.
|
||||||
|
*/
|
||||||
|
final class ConstructionContext<T> {
|
||||||
|
|
||||||
|
T currentReference;
|
||||||
|
boolean constructing;
|
||||||
|
|
||||||
|
List<DelegatingInvocationHandler<T>> invocationHandlers;
|
||||||
|
|
||||||
|
public T getCurrentReference() {
|
||||||
|
return currentReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentReference(T currentReference) {
|
||||||
|
this.currentReference = currentReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCurrentReference() {
|
||||||
|
this.currentReference = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConstructing() {
|
||||||
|
return constructing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startConstruction() {
|
||||||
|
this.constructing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishConstruction() {
|
||||||
|
this.constructing = false;
|
||||||
|
invocationHandlers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createProxy(Errors errors, InjectorOptions injectorOptions,
|
||||||
|
Class<?> expectedType) throws ErrorsException {
|
||||||
|
if (injectorOptions.disableCircularProxies) {
|
||||||
|
throw errors.circularProxiesDisabled(expectedType).toException();
|
||||||
|
}
|
||||||
|
if (!expectedType.isInterface()) {
|
||||||
|
throw errors.cannotSatisfyCircularDependency(expectedType).toException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invocationHandlers == null) {
|
||||||
|
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
|
||||||
|
invocationHandlers.add(invocationHandler);
|
||||||
|
|
||||||
|
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
||||||
|
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
||||||
|
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
||||||
|
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyDelegates(T delegate) {
|
||||||
|
if (invocationHandlers != null) {
|
||||||
|
for (DelegatingInvocationHandler<T> handler : invocationHandlers) {
|
||||||
|
handler.setDelegate(delegate);
|
||||||
|
}
|
||||||
|
// initialization of each handler can happen no more than once
|
||||||
|
invocationHandlers = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxies calls to a {@link java.lang.reflect.Constructor} for a class
|
||||||
|
* {@code T}.
|
||||||
|
*/
|
||||||
|
interface ConstructionProxy<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of {@code T} for the given arguments.
|
||||||
|
*/
|
||||||
|
T newInstance(Object... arguments) throws InvocationTargetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the injection point for this constructor.
|
||||||
|
*/
|
||||||
|
InjectionPoint getInjectionPoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the injected constructor. If the injected constructor is synthetic (such as generated
|
||||||
|
* code for method interception), the natural constructor is returned.
|
||||||
|
*/
|
||||||
|
Constructor<T> getConstructor();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates {@link ConstructionProxy} instances.
|
||||||
|
*/
|
||||||
|
interface ConstructionProxyFactory<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a construction proxy for the given constructor.
|
||||||
|
*/
|
||||||
|
ConstructionProxy<T> create() throws ErrorsException;
|
||||||
|
}
|
|
@ -0,0 +1,258 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.ConfigurationException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.util.Classes;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.ConstructorBinding;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.inject.internal.Annotations.findScopeAnnotation;
|
||||||
|
|
||||||
|
final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
|
implements ConstructorBinding<T>, DelayedInitialize {
|
||||||
|
|
||||||
|
private final Factory<T> factory;
|
||||||
|
private final InjectionPoint constructorInjectionPoint;
|
||||||
|
|
||||||
|
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
|
||||||
|
InjectionPoint constructorInjectionPoint) {
|
||||||
|
super(injector, key, source, scopedFactory, scoping);
|
||||||
|
this.factory = factory;
|
||||||
|
this.constructorInjectionPoint = constructorInjectionPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
|
||||||
|
InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) {
|
||||||
|
super(source, key, scoping);
|
||||||
|
this.factory = new Factory<T>(false, key);
|
||||||
|
ConstructionProxy<T> constructionProxy
|
||||||
|
= new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
|
||||||
|
this.constructorInjectionPoint = constructorInjectionPoint;
|
||||||
|
factory.constructorInjector = new ConstructorInjector<T>(
|
||||||
|
injectionPoints, constructionProxy, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param constructorInjector the constructor to use, or {@code null} to use the default.
|
||||||
|
* @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should
|
||||||
|
* only succeed if retrieved from a linked binding
|
||||||
|
*/
|
||||||
|
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
|
||||||
|
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
|
||||||
|
boolean failIfNotLinked, boolean failIfNotExplicit)
|
||||||
|
throws ErrorsException {
|
||||||
|
int numErrors = errors.size();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent
|
||||||
|
Class<? super T> rawType = constructorInjector == null
|
||||||
|
? key.getTypeLiteral().getRawType()
|
||||||
|
: (Class) constructorInjector.getDeclaringType().getRawType();
|
||||||
|
|
||||||
|
// We can't inject abstract classes.
|
||||||
|
if (Modifier.isAbstract(rawType.getModifiers())) {
|
||||||
|
errors.missingImplementation(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error: Inner class.
|
||||||
|
if (Classes.isInnerClass(rawType)) {
|
||||||
|
errors.cannotInjectInnerClass(rawType);
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.throwIfNewErrors(numErrors);
|
||||||
|
|
||||||
|
// Find a constructor annotated @Inject
|
||||||
|
if (constructorInjector == null) {
|
||||||
|
try {
|
||||||
|
constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
|
||||||
|
if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
|
||||||
|
errors.atInjectRequired(rawType);
|
||||||
|
}
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
throw errors.merge(e.getErrorMessages()).toException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no scope is specified, look for a scoping annotation on the concrete class
|
||||||
|
if (!scoping.isExplicitlyScoped()) {
|
||||||
|
Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
|
||||||
|
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
|
||||||
|
if (scopeAnnotation != null) {
|
||||||
|
scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
|
||||||
|
injector, errors.withSource(rawType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.throwIfNewErrors(numErrors);
|
||||||
|
|
||||||
|
Factory<T> factoryFactory = new Factory<T>(failIfNotLinked, key);
|
||||||
|
InternalFactory<? extends T> scopedFactory
|
||||||
|
= Scoping.scope(key, injector, factoryFactory, source, scoping);
|
||||||
|
|
||||||
|
return new ConstructorBindingImpl<T>(
|
||||||
|
injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the inject annotation is on the constructor.
|
||||||
|
*/
|
||||||
|
private static boolean hasAtInject(Constructor cxtor) {
|
||||||
|
return cxtor.isAnnotationPresent(Inject.class)
|
||||||
|
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
|
||||||
|
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
|
factory.constructorInjector =
|
||||||
|
(ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
|
||||||
|
factory.provisionCallback =
|
||||||
|
injector.provisionListenerStore.get(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if this binding has been initialized and is ready for use.
|
||||||
|
*/
|
||||||
|
boolean isInitialized() {
|
||||||
|
return factory.constructorInjector != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an injection point that can be used to clean up the constructor store.
|
||||||
|
*/
|
||||||
|
InjectionPoint getInternalConstructor() {
|
||||||
|
if (factory.constructorInjector != null) {
|
||||||
|
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||||
|
} else {
|
||||||
|
return constructorInjectionPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a set of dependencies that can be iterated over to clean up stray JIT bindings.
|
||||||
|
*/
|
||||||
|
Set<Dependency<?>> getInternalDependencies() {
|
||||||
|
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||||
|
if (factory.constructorInjector == null) {
|
||||||
|
builder.add(constructorInjectionPoint);
|
||||||
|
// If the below throws, it's OK -- we just ignore those dependencies, because no one
|
||||||
|
// could have used them anyway.
|
||||||
|
try {
|
||||||
|
builder.addAll(InjectionPoint.forInstanceMethodsAndFields(constructorInjectionPoint.getDeclaringType()));
|
||||||
|
} catch (ConfigurationException ignored) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.add(getConstructor())
|
||||||
|
.addAll(getInjectableMembers());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dependency.forInjectionPoints(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
|
checkState(factory.constructorInjector != null, "not initialized");
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InjectionPoint getConstructor() {
|
||||||
|
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||||
|
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<InjectionPoint> getInjectableMembers() {
|
||||||
|
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||||
|
return factory.constructorInjector.getInjectableMembers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
|
||||||
|
.add(getConstructor())
|
||||||
|
.addAll(getInjectableMembers())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
|
return new ConstructorBindingImpl<T>(
|
||||||
|
null, getKey(), getSource(), factory, scoping, factory, constructorInjectionPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BindingImpl<T> withKey(Key<T> key) {
|
||||||
|
return new ConstructorBindingImpl<T>(
|
||||||
|
null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
|
||||||
|
public void applyTo(Binder binder) {
|
||||||
|
InjectionPoint constructor = getConstructor();
|
||||||
|
getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).toConstructor(
|
||||||
|
(Constructor) getConstructor().getMember(), (TypeLiteral) constructor.getDeclaringType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(ConstructorBinding.class)
|
||||||
|
.add("key", getKey())
|
||||||
|
.add("source", getSource())
|
||||||
|
.add("scope", getScoping())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ConstructorBindingImpl) {
|
||||||
|
ConstructorBindingImpl<?> o = (ConstructorBindingImpl<?>) obj;
|
||||||
|
return getKey().equals(o.getKey())
|
||||||
|
&& getScoping().equals(o.getScoping())
|
||||||
|
&& Objects.equal(constructorInjectionPoint, o.constructorInjectionPoint);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getKey(), getScoping(), constructorInjectionPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Factory<T> implements InternalFactory<T> {
|
||||||
|
private final boolean failIfNotLinked;
|
||||||
|
private final Key<?> key;
|
||||||
|
private ConstructorInjector<T> constructorInjector;
|
||||||
|
private ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
|
||||||
|
Factory(boolean failIfNotLinked, Key<?> key) {
|
||||||
|
this.failIfNotLinked = failIfNotLinked;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
checkState(constructorInjector != null, "Constructor not ready");
|
||||||
|
|
||||||
|
if (failIfNotLinked && !linked) {
|
||||||
|
throw errors.jitDisabled(key).toException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This may not actually be safe because it could return a super type of T (if that's all the
|
||||||
|
// client needs), but it should be OK in practice thanks to the wonders of erasure.
|
||||||
|
return (T) constructorInjector.construct(errors, context,
|
||||||
|
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates instances using an injectable constructor. After construction, all injectable fields and
|
||||||
|
* methods are injected.
|
||||||
|
*/
|
||||||
|
final class ConstructorInjector<T> {
|
||||||
|
|
||||||
|
private final ImmutableSet<InjectionPoint> injectableMembers;
|
||||||
|
private final SingleParameterInjector<?>[] parameterInjectors;
|
||||||
|
private final ConstructionProxy<T> constructionProxy;
|
||||||
|
private final MembersInjectorImpl<T> membersInjector;
|
||||||
|
|
||||||
|
ConstructorInjector(Set<InjectionPoint> injectableMembers,
|
||||||
|
ConstructionProxy<T> constructionProxy,
|
||||||
|
SingleParameterInjector<?>[] parameterInjectors,
|
||||||
|
MembersInjectorImpl<T> membersInjector) {
|
||||||
|
this.injectableMembers = ImmutableSet.copyOf(injectableMembers);
|
||||||
|
this.constructionProxy = constructionProxy;
|
||||||
|
this.parameterInjectors = parameterInjectors;
|
||||||
|
this.membersInjector = membersInjector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableSet<InjectionPoint> getInjectableMembers() {
|
||||||
|
return injectableMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructionProxy<T> getConstructionProxy() {
|
||||||
|
return constructionProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
||||||
|
* it may return a proxy.
|
||||||
|
*/
|
||||||
|
Object construct(final Errors errors, final InternalContext context,
|
||||||
|
Class<?> expectedType,
|
||||||
|
ProvisionListenerStackCallback<T> provisionCallback)
|
||||||
|
throws ErrorsException {
|
||||||
|
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||||
|
|
||||||
|
// We have a circular reference between constructors. Return a proxy.
|
||||||
|
if (constructionContext.isConstructing()) {
|
||||||
|
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
||||||
|
return constructionContext.createProxy(
|
||||||
|
errors, context.getInjectorOptions(), expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're re-entering this factory while injecting fields or methods,
|
||||||
|
// return the same instance. This prevents infinite loops.
|
||||||
|
T t = constructionContext.getCurrentReference();
|
||||||
|
if (t != null) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructionContext.startConstruction();
|
||||||
|
try {
|
||||||
|
// Optimization: Don't go through the callback stack if we have no listeners.
|
||||||
|
if (!provisionCallback.hasListeners()) {
|
||||||
|
return provision(errors, context, constructionContext);
|
||||||
|
} else {
|
||||||
|
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
||||||
|
public T call() throws ErrorsException {
|
||||||
|
return provision(errors, context, constructionContext);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
constructionContext.finishConstruction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provisions a new T.
|
||||||
|
*/
|
||||||
|
private T provision(Errors errors, InternalContext context,
|
||||||
|
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||||
|
try {
|
||||||
|
T t;
|
||||||
|
try {
|
||||||
|
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
||||||
|
t = constructionProxy.newInstance(parameters);
|
||||||
|
constructionContext.setProxyDelegates(t);
|
||||||
|
} finally {
|
||||||
|
constructionContext.finishConstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
||||||
|
constructionContext.setCurrentReference(t);
|
||||||
|
|
||||||
|
membersInjector.injectMembers(t, errors, context, false);
|
||||||
|
membersInjector.notifyListeners(t, errors);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
} catch (InvocationTargetException userException) {
|
||||||
|
Throwable cause = userException.getCause() != null
|
||||||
|
? userException.getCause()
|
||||||
|
: userException;
|
||||||
|
throw errors.withSource(constructionProxy.getInjectionPoint())
|
||||||
|
.errorInjectingConstructor(cause).toException();
|
||||||
|
} finally {
|
||||||
|
constructionContext.removeCurrentReference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor injectors by type.
|
||||||
|
*/
|
||||||
|
final class ConstructorInjectorStore {
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
|
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
|
||||||
|
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() {
|
||||||
|
@Override
|
||||||
|
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
|
return createConstructor(constructorInjector, errors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConstructorInjectorStore(InjectorImpl injector) {
|
||||||
|
this.injector = injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new complete constructor injector with injection listeners registered.
|
||||||
|
*/
|
||||||
|
public ConstructorInjector<?> get(InjectionPoint constructorInjector, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
|
return cache.get(constructorInjector, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purges an injection point from the cache. Use this only if the cache is not actually valid and
|
||||||
|
* needs to be purged. (See issue 319 and
|
||||||
|
* ImplicitBindingTest#testCircularJitBindingsLeaveNoResidue and
|
||||||
|
* #testInstancesRequestingProvidersForThemselvesWithChildInjectors for examples of when this is
|
||||||
|
* necessary.)
|
||||||
|
*
|
||||||
|
* Returns true if the injector for that point was stored in the cache, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean remove(InjectionPoint ip) {
|
||||||
|
return cache.remove(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> ConstructorInjector<T> createConstructor(InjectionPoint injectionPoint, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
|
int numErrorsBefore = errors.size();
|
||||||
|
|
||||||
|
SingleParameterInjector<?>[] constructorParameterInjectors
|
||||||
|
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
||||||
|
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
|
||||||
|
.get(injectionPoint.getDeclaringType(), errors);
|
||||||
|
|
||||||
|
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
|
||||||
|
|
||||||
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
|
||||||
|
return new ConstructorInjector<T>(membersInjector.getInjectionPoints(), factory.create(),
|
||||||
|
constructorParameterInjectors, membersInjector);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
interface ContextualCallable<T> {
|
||||||
|
T call(InternalContext context) throws ErrorsException;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Something that is notified upon creation.
|
||||||
|
*/
|
||||||
|
interface CreationListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies that creation should happen.
|
||||||
|
*/
|
||||||
|
void notify(Errors errors);
|
||||||
|
}
|
308
src/main/java/com/google/inject/internal/CycleDetectingLock.java
Normal file
308
src/main/java/com/google/inject/internal/CycleDetectingLock.java
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.Multimaps;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplified version of {@link Lock} that is special due to how it handles deadlocks detection.
|
||||||
|
*
|
||||||
|
* <p>Is an inherent part of {@link SingletonScope}, moved into a upper level class due
|
||||||
|
* to its size and complexity.
|
||||||
|
*
|
||||||
|
* @param <ID> Lock identification provided by the client, is returned unmodified to the client
|
||||||
|
* when lock cycle is detected to identify it. Only toString() needs to be implemented.
|
||||||
|
* Lock references this object internally,
|
||||||
|
* for the purposes of Garbage Collection you should not use heavy IDs.
|
||||||
|
* Lock is referenced by a lock factory as long as it's owned by a thread.
|
||||||
|
*/
|
||||||
|
interface CycleDetectingLock<ID> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a lock in a blocking fashion in case no potential deadlocks are detected.
|
||||||
|
* If the lock was successfully owned, returns an empty map indicating no detected potential
|
||||||
|
* deadlocks.
|
||||||
|
*
|
||||||
|
* Otherwise, a map indicating threads involved in a potential deadlock are returned.
|
||||||
|
* Map is ordered by dependency cycle and lists locks for each thread that are part of
|
||||||
|
* the loop in order. Returned map is created atomically.
|
||||||
|
*
|
||||||
|
* In case no cycle is detected performance is O(threads creating singletons),
|
||||||
|
* in case cycle is detected performance is O(singleton locks).
|
||||||
|
*/
|
||||||
|
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks previously locked lock.
|
||||||
|
*/
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps locks so they would never cause a deadlock. On each
|
||||||
|
* {@link CycleDetectingLock#lockOrDetectPotentialLocksCycle} we check for dependency cycles
|
||||||
|
* within locks created by the same factory. Either we detect a cycle and return it
|
||||||
|
* or take it atomically.
|
||||||
|
*
|
||||||
|
* <p>Important to note that we do not prevent deadlocks in the client code. As an example:
|
||||||
|
* Thread A takes lock L and creates singleton class CA depending on the singleton class CB.
|
||||||
|
* Meanwhile thread B is creating class CB and is waiting on the lock L. Issue happens
|
||||||
|
* due to client code creating interdependent classes and using locks, where
|
||||||
|
* no guarantees on the creation order from Guice are provided.
|
||||||
|
*
|
||||||
|
* <p>Instances of these locks are not intended to be exposed outside of {@link SingletonScope}.
|
||||||
|
*/
|
||||||
|
class CycleDetectingLockFactory<ID> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists locks that thread owns.
|
||||||
|
* Used only to populate locks in a potential cycle when it is detected.
|
||||||
|
*
|
||||||
|
* Key: thread id
|
||||||
|
* Value: stack of locks that were owned.
|
||||||
|
*
|
||||||
|
* Element is added inside {@link #lockOrDetectPotentialLocksCycle()} after {@link Lock#lock}
|
||||||
|
* is called. Element is removed inside {@link #unlock()} synchronously with
|
||||||
|
* {@link Lock#unlock()} call.
|
||||||
|
*
|
||||||
|
* Same lock can only be present several times for the same thread as locks are
|
||||||
|
* reentrant. Lock can not be owned by several different threads as the same time.
|
||||||
|
*
|
||||||
|
* Guarded by {@code this}.
|
||||||
|
*/
|
||||||
|
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread =
|
||||||
|
LinkedHashMultimap.create();
|
||||||
|
/**
|
||||||
|
* Specifies lock that thread is currently waiting on to own it.
|
||||||
|
* Used only for purposes of locks cycle detection.
|
||||||
|
*
|
||||||
|
* Key: thread id
|
||||||
|
* Value: lock that is being waited on
|
||||||
|
*
|
||||||
|
* Element is added inside {@link #lockOrDetectPotentialLocksCycle()} before {@link Lock#lock}
|
||||||
|
* is called. Element is removed inside {@link #lockOrDetectPotentialLocksCycle()} after
|
||||||
|
* {@link Lock#lock} and synchronously with adding it to {@link #locksOwnedByThread}.
|
||||||
|
*
|
||||||
|
* Same lock can be added for several threads in case all of them are trying to
|
||||||
|
* take it.
|
||||||
|
*
|
||||||
|
* Guarded by {@code this}.
|
||||||
|
*/
|
||||||
|
private Map<Long, ReentrantCycleDetectingLock> lockThreadIsWaitingOn = Maps.newHashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new lock within this factory context. We can guarantee that locks created by
|
||||||
|
* the same factory would not deadlock.
|
||||||
|
*
|
||||||
|
* @param newLockId lock id that would be used to report lock cycles if detected
|
||||||
|
*/
|
||||||
|
CycleDetectingLock<ID> create(ID newLockId) {
|
||||||
|
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation for {@link CycleDetectingLock}.
|
||||||
|
*/
|
||||||
|
class ReentrantCycleDetectingLock implements CycleDetectingLock<ID> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Underlying lock used for actual waiting when no potential deadlocks are detected.
|
||||||
|
*/
|
||||||
|
private final Lock lockImplementation;
|
||||||
|
/**
|
||||||
|
* User id for this lock.
|
||||||
|
*/
|
||||||
|
private final ID userLockId;
|
||||||
|
/**
|
||||||
|
* Thread id for the thread that owned this lock. Nullable.
|
||||||
|
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||||
|
*/
|
||||||
|
private Long lockOwnerThreadId = null;
|
||||||
|
/**
|
||||||
|
* Number of times that thread owned this lock.
|
||||||
|
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||||
|
*/
|
||||||
|
private int lockReentranceCount = 0;
|
||||||
|
|
||||||
|
ReentrantCycleDetectingLock(ID userLockId, Lock lockImplementation) {
|
||||||
|
this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
|
||||||
|
this.lockImplementation = Preconditions.checkNotNull(
|
||||||
|
lockImplementation, "lockImplementation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() {
|
||||||
|
final long currentThreadId = Thread.currentThread().getId();
|
||||||
|
synchronized (CycleDetectingLockFactory.this) {
|
||||||
|
checkState();
|
||||||
|
ListMultimap<Long, ID> locksInCycle = detectPotentialLocksCycle();
|
||||||
|
if (!locksInCycle.isEmpty()) {
|
||||||
|
// potential deadlock is found, we don't try to take this lock
|
||||||
|
return locksInCycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
lockThreadIsWaitingOn.put(currentThreadId, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this may be blocking, but we don't expect it to cause a deadlock
|
||||||
|
lockImplementation.lock();
|
||||||
|
|
||||||
|
synchronized (CycleDetectingLockFactory.this) {
|
||||||
|
// current thread is no longer waiting on this lock
|
||||||
|
lockThreadIsWaitingOn.remove(currentThreadId);
|
||||||
|
checkState();
|
||||||
|
|
||||||
|
// mark it as owned by us
|
||||||
|
lockOwnerThreadId = currentThreadId;
|
||||||
|
lockReentranceCount++;
|
||||||
|
// add this lock to the list of locks owned by a current thread
|
||||||
|
locksOwnedByThread.put(currentThreadId, this);
|
||||||
|
}
|
||||||
|
// no deadlock is found, locking successful
|
||||||
|
return ImmutableListMultimap.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlock() {
|
||||||
|
final long currentThreadId = Thread.currentThread().getId();
|
||||||
|
synchronized (CycleDetectingLockFactory.this) {
|
||||||
|
checkState();
|
||||||
|
Preconditions.checkState(lockOwnerThreadId != null,
|
||||||
|
"Thread is trying to unlock a lock that is not locked");
|
||||||
|
Preconditions.checkState(lockOwnerThreadId == currentThreadId,
|
||||||
|
"Thread is trying to unlock a lock owned by another thread");
|
||||||
|
|
||||||
|
// releasing underlying lock
|
||||||
|
lockImplementation.unlock();
|
||||||
|
|
||||||
|
// be sure to release the lock synchronously with updating internal state
|
||||||
|
lockReentranceCount--;
|
||||||
|
if (lockReentranceCount == 0) {
|
||||||
|
// we no longer own this lock
|
||||||
|
lockOwnerThreadId = null;
|
||||||
|
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this),
|
||||||
|
"Internal error: Can not find this lock in locks owned by a current thread");
|
||||||
|
if (locksOwnedByThread.get(currentThreadId).isEmpty()) {
|
||||||
|
// clearing memory
|
||||||
|
locksOwnedByThread.removeAll(currentThreadId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check consistency of an internal state.
|
||||||
|
*/
|
||||||
|
void checkState() throws IllegalStateException {
|
||||||
|
final long currentThreadId = Thread.currentThread().getId();
|
||||||
|
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId),
|
||||||
|
"Internal error: Thread should not be in a waiting thread on a lock now");
|
||||||
|
if (lockOwnerThreadId != null) {
|
||||||
|
// check state of a locked lock
|
||||||
|
Preconditions.checkState(lockReentranceCount >= 0,
|
||||||
|
"Internal error: Lock ownership and reentrance count internal states do not match");
|
||||||
|
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThreadId).contains(this),
|
||||||
|
"Internal error: Set of locks owned by a current thread and lock "
|
||||||
|
+ "ownership status do not match");
|
||||||
|
} else {
|
||||||
|
// check state of a non locked lock
|
||||||
|
Preconditions.checkState(lockReentranceCount == 0,
|
||||||
|
"Internal error: Reentrance count of a non locked lock is expect to be zero");
|
||||||
|
Preconditions.checkState(!locksOwnedByThread.values().contains(this),
|
||||||
|
"Internal error: Non locked lock should not be owned by any thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Algorithm to detect a potential lock cycle.
|
||||||
|
*
|
||||||
|
* For lock's thread owner check which lock is it trying to take.
|
||||||
|
* Repeat recursively. When current thread is found a potential cycle is detected.
|
||||||
|
*
|
||||||
|
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
||||||
|
*/
|
||||||
|
private ListMultimap<Long, ID> detectPotentialLocksCycle() {
|
||||||
|
final long currentThreadId = Thread.currentThread().getId();
|
||||||
|
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) {
|
||||||
|
// if nobody owns this lock, lock cycle is impossible
|
||||||
|
// if a current thread owns this lock, we let Guice to handle it
|
||||||
|
return ImmutableListMultimap.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||||
|
new LinkedHashMap<Long, Collection<ID>>(),
|
||||||
|
new Supplier<List<ID>>() {
|
||||||
|
@Override
|
||||||
|
public List<ID> get() {
|
||||||
|
return Lists.newArrayList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// lock that is a part of a potential locks cycle, starts with current lock
|
||||||
|
ReentrantCycleDetectingLock lockOwnerWaitingOn = this;
|
||||||
|
// try to find a dependency path between lock's owner thread and a current thread
|
||||||
|
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) {
|
||||||
|
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId;
|
||||||
|
// in case locks cycle exists lock we're waiting for is part of it
|
||||||
|
potentialLocksCycle.putAll(threadOwnerThreadWaits,
|
||||||
|
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn));
|
||||||
|
|
||||||
|
if (threadOwnerThreadWaits == currentThreadId) {
|
||||||
|
// owner thread depends on current thread, cycle detected
|
||||||
|
return potentialLocksCycle;
|
||||||
|
}
|
||||||
|
// going for the next thread we wait on indirectly
|
||||||
|
lockOwnerWaitingOn = lockThreadIsWaitingOn.get(threadOwnerThreadWaits);
|
||||||
|
}
|
||||||
|
// no dependency path from an owner thread to a current thread
|
||||||
|
return ImmutableListMultimap.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return locks owned by a thread after a lock specified, inclusive.
|
||||||
|
*/
|
||||||
|
private List<ID> getAllLockIdsAfter(long threadId, ReentrantCycleDetectingLock lock) {
|
||||||
|
List<ID> ids = Lists.newArrayList();
|
||||||
|
boolean found = false;
|
||||||
|
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId);
|
||||||
|
Preconditions.checkNotNull(ownedLocks,
|
||||||
|
"Internal error: No locks were found taken by a thread");
|
||||||
|
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) {
|
||||||
|
if (ownedLock == lock) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
ids.add(ownedLock.userLockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Preconditions.checkState(found, "Internal error: We can not find locks that "
|
||||||
|
+ "created a cycle that we detected");
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// copy is made to prevent a data race
|
||||||
|
// no synchronization is used, potentially stale data, should be good enough
|
||||||
|
Long localLockOwnerThreadId = this.lockOwnerThreadId;
|
||||||
|
if (localLockOwnerThreadId != null) {
|
||||||
|
return String.format("CycleDetectingLock[%s][locked by %s]",
|
||||||
|
userLockId, localLockOwnerThreadId);
|
||||||
|
} else {
|
||||||
|
return String.format("CycleDetectingLock[%s][unlocked]", userLockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces construction proxies that invoke the class constructor.
|
||||||
|
*/
|
||||||
|
final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFactory<T> {
|
||||||
|
|
||||||
|
private final InjectionPoint injectionPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param injectionPoint an injection point whose member is a constructor of {@code T}.
|
||||||
|
*/
|
||||||
|
DefaultConstructionProxyFactory(InjectionPoint injectionPoint) {
|
||||||
|
this.injectionPoint = injectionPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstructionProxy<T> create() {
|
||||||
|
@SuppressWarnings("unchecked") // the injection point is for a constructor of T
|
||||||
|
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
||||||
|
|
||||||
|
// Use FastConstructor if the constructor is public.
|
||||||
|
if (Modifier.isPublic(constructor.getModifiers())) {
|
||||||
|
Class<T> classToConstruct = constructor.getDeclaringClass();
|
||||||
|
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConstructionProxy<T>() {
|
||||||
|
public T newInstance(Object... arguments) throws InvocationTargetException {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(arguments);
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
throw new AssertionError(e); // shouldn't happen, we know this is a concrete type
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InjectionPoint getInjectionPoint() {
|
||||||
|
return injectionPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Constructor<T> getConstructor() {
|
||||||
|
return constructor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.MembersInjectorLookup;
|
||||||
|
import com.google.inject.spi.ProviderLookup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns providers and members injectors that haven't yet been initialized. As a part of injector
|
||||||
|
* creation it's necessary to {@link #initialize initialize} these lookups.
|
||||||
|
*/
|
||||||
|
final class DeferredLookups implements Lookups {
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private final List<Element> lookups = Lists.newArrayList();
|
||||||
|
|
||||||
|
DeferredLookups(InjectorImpl injector) {
|
||||||
|
this.injector = injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the specified lookups, either immediately or when the injector is created.
|
||||||
|
*/
|
||||||
|
void initialize(Errors errors) {
|
||||||
|
injector.lookups = injector;
|
||||||
|
new LookupProcessor(errors).process(injector, lookups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
|
ProviderLookup<T> lookup = new ProviderLookup<T>(key, key);
|
||||||
|
lookups.add(lookup);
|
||||||
|
return lookup.getProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
||||||
|
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<T>(type, type);
|
||||||
|
lookups.add(lookup);
|
||||||
|
return lookup.getMembersInjector();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Something that needs some delayed initialization, typically
|
||||||
|
* a binding or internal factory that needs to be created & put
|
||||||
|
* into the bindings map & then initialized later.
|
||||||
|
*/
|
||||||
|
interface DelayedInitialize {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this binding, throwing any errors if necessary.
|
||||||
|
*/
|
||||||
|
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
class DelegatingInvocationHandler<T> implements InvocationHandler {
|
||||||
|
|
||||||
|
private volatile boolean initialized;
|
||||||
|
|
||||||
|
private T delegate;
|
||||||
|
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args)
|
||||||
|
throws Throwable {
|
||||||
|
try {
|
||||||
|
// checking volatile field for synchronization
|
||||||
|
Preconditions.checkState(initialized,
|
||||||
|
"This is a proxy used to support"
|
||||||
|
+ " circular references. The object we're"
|
||||||
|
+ " proxying is not constructed yet. Please wait until after"
|
||||||
|
+ " injection has completed to use this object.");
|
||||||
|
Preconditions.checkNotNull(delegate,
|
||||||
|
"This is a proxy used to support"
|
||||||
|
+ " circular references. The object we're "
|
||||||
|
+ " proxying is initialized to null."
|
||||||
|
+ " No methods can be called.");
|
||||||
|
|
||||||
|
return method.invoke(delegate, args);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw e.getTargetException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDelegate(T delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
98
src/main/java/com/google/inject/internal/EncounterImpl.java
Normal file
98
src/main/java/com/google/inject/internal/EncounterImpl.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.InjectionListener;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import com.google.inject.spi.TypeEncounter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
|
|
||||||
|
private final Errors errors;
|
||||||
|
private final Lookups lookups;
|
||||||
|
private List<MembersInjector<? super T>> membersInjectors; // lazy
|
||||||
|
private List<InjectionListener<? super T>> injectionListeners; // lazy
|
||||||
|
private boolean valid = true;
|
||||||
|
|
||||||
|
EncounterImpl(Errors errors, Lookups lookups) {
|
||||||
|
this.errors = errors;
|
||||||
|
this.lookups = lookups;
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidate() {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<MembersInjector<? super T>> getMembersInjectors() {
|
||||||
|
return membersInjectors == null
|
||||||
|
? ImmutableSet.<MembersInjector<? super T>>of()
|
||||||
|
: ImmutableSet.copyOf(membersInjectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableSet<InjectionListener<? super T>> getInjectionListeners() {
|
||||||
|
return injectionListeners == null
|
||||||
|
? ImmutableSet.<InjectionListener<? super T>>of()
|
||||||
|
: ImmutableSet.copyOf(injectionListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(MembersInjector<? super T> membersInjector) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
|
||||||
|
if (membersInjectors == null) {
|
||||||
|
membersInjectors = Lists.newArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
membersInjectors.add(membersInjector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(InjectionListener<? super T> injectionListener) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
|
||||||
|
if (injectionListeners == null) {
|
||||||
|
injectionListeners = Lists.newArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
injectionListeners.add(injectionListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(String message, Object... arguments) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
errors.addMessage(message, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(Throwable t) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(Message message) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
errors.addMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
return lookups.getProvider(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Provider<T> getProvider(Class<T> type) {
|
||||||
|
return getProvider(Key.get(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||||
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
return lookups.getMembersInjector(typeLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
|
return getMembersInjector(TypeLiteral.get(type));
|
||||||
|
}
|
||||||
|
}
|
817
src/main/java/com/google/inject/internal/Errors.java
Normal file
817
src/main/java/com/google/inject/internal/Errors.java
Normal file
|
@ -0,0 +1,817 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Ordering;
|
||||||
|
import com.google.inject.ConfigurationException;
|
||||||
|
import com.google.inject.CreationException;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.ProvisionException;
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.util.Classes;
|
||||||
|
import com.google.inject.internal.util.SourceProvider;
|
||||||
|
import com.google.inject.internal.util.StackTraceElements;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.ElementSource;
|
||||||
|
import com.google.inject.spi.InjectionListener;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
import com.google.inject.spi.ScopeBinding;
|
||||||
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of error messages. If this type is passed as a method parameter, the method is
|
||||||
|
* considered to have executed successfully only if new errors were not added to this collection.
|
||||||
|
*
|
||||||
|
* <p>Errors can be chained to provide additional context. To add context, call {@link #withSource}
|
||||||
|
* to create a new Errors instance that contains additional context. All messages added to the
|
||||||
|
* returned instance will contain full context.
|
||||||
|
*
|
||||||
|
* <p>To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
|
||||||
|
* good rule of thumb is to assume a method's caller has already specified enough context to
|
||||||
|
* identify that method. When calling a method that's defined in a different context, call that
|
||||||
|
* method with an errors object that includes its context.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class Errors {
|
||||||
|
|
||||||
|
private static final Set<Dependency<?>> warnedDependencies =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<Dependency<?>, Boolean>());
|
||||||
|
private static final String CONSTRUCTOR_RULES =
|
||||||
|
"Classes must have either one (and only one) constructor "
|
||||||
|
+ "annotated with @Inject or a zero-argument constructor that is not private.";
|
||||||
|
private static final Collection<Converter<?>> converters = ImmutableList.of(
|
||||||
|
new Converter<Class>(Class.class) {
|
||||||
|
@Override
|
||||||
|
public String toString(Class c) {
|
||||||
|
return c.getName();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Converter<Member>(Member.class) {
|
||||||
|
@Override
|
||||||
|
public String toString(Member member) {
|
||||||
|
return Classes.toString(member);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Converter<Key>(Key.class) {
|
||||||
|
@Override
|
||||||
|
public String toString(Key key) {
|
||||||
|
if (key.getAnnotationType() != null) {
|
||||||
|
return key.getTypeLiteral() + " annotated with "
|
||||||
|
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
|
||||||
|
} else {
|
||||||
|
return key.getTypeLiteral().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* The root errors object. Used to access the list of error messages.
|
||||||
|
*/
|
||||||
|
private final Errors root;
|
||||||
|
/**
|
||||||
|
* The parent errors object. Used to obtain the chain of source objects.
|
||||||
|
*/
|
||||||
|
private final Errors parent;
|
||||||
|
/**
|
||||||
|
* The leaf source for errors added here.
|
||||||
|
*/
|
||||||
|
private final Object source;
|
||||||
|
/**
|
||||||
|
* null unless (root == this) and error messages exist. Never an empty list.
|
||||||
|
*/
|
||||||
|
private List<Message> errors; // lazy, use getErrorsForAdd()
|
||||||
|
|
||||||
|
public Errors() {
|
||||||
|
this.root = this;
|
||||||
|
this.parent = null;
|
||||||
|
this.source = SourceProvider.UNKNOWN_SOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors(Object source) {
|
||||||
|
this.root = this;
|
||||||
|
this.parent = null;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Errors(Errors parent, Object source) {
|
||||||
|
this.root = parent.root;
|
||||||
|
this.parent = parent;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
|
||||||
|
if (throwable instanceof ProvisionException) {
|
||||||
|
return ((ProvisionException) throwable).getErrorMessages();
|
||||||
|
} else if (throwable instanceof ConfigurationException) {
|
||||||
|
return ((ConfigurationException) throwable).getErrorMessages();
|
||||||
|
} else if (throwable instanceof CreationException) {
|
||||||
|
return ((CreationException) throwable).getErrorMessages();
|
||||||
|
} else {
|
||||||
|
return ImmutableSet.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(String messageFormat, Object... arguments) {
|
||||||
|
for (int i = 0; i < arguments.length; i++) {
|
||||||
|
arguments[i] = Errors.convert(arguments[i]);
|
||||||
|
}
|
||||||
|
return String.format(messageFormat, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the formatted message for an exception with the specified messages.
|
||||||
|
*/
|
||||||
|
public static String format(String heading, Collection<Message> errorMessages) {
|
||||||
|
Formatter fmt = new Formatter().format(heading).format(":%n%n");
|
||||||
|
int index = 1;
|
||||||
|
boolean displayCauses = getOnlyCause(errorMessages) == null;
|
||||||
|
|
||||||
|
for (Message errorMessage : errorMessages) {
|
||||||
|
fmt.format("%s) %s%n", index++, errorMessage.getMessage());
|
||||||
|
|
||||||
|
List<Object> dependencies = errorMessage.getSources();
|
||||||
|
for (int i = dependencies.size() - 1; i >= 0; i--) {
|
||||||
|
Object source = dependencies.get(i);
|
||||||
|
formatSource(fmt, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
Throwable cause = errorMessage.getCause();
|
||||||
|
if (displayCauses && cause != null) {
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
cause.printStackTrace(new PrintWriter(writer));
|
||||||
|
fmt.format("Caused by: %s", writer.getBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.format("%n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessages.size() == 1) {
|
||||||
|
fmt.format("1 error");
|
||||||
|
} else {
|
||||||
|
fmt.format("%s errors", errorMessages.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
|
||||||
|
* zero or multiple messages with causes, null is returned.
|
||||||
|
*/
|
||||||
|
public static Throwable getOnlyCause(Collection<Message> messages) {
|
||||||
|
Throwable onlyCause = null;
|
||||||
|
for (Message message : messages) {
|
||||||
|
Throwable messageCause = message.getCause();
|
||||||
|
if (messageCause == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlyCause != null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyCause = messageCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
return onlyCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object convert(Object o) {
|
||||||
|
ElementSource source = null;
|
||||||
|
if (o instanceof ElementSource) {
|
||||||
|
source = (ElementSource) o;
|
||||||
|
o = source.getDeclaringSource();
|
||||||
|
}
|
||||||
|
return convert(o, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object convert(Object o, ElementSource source) {
|
||||||
|
for (Converter<?> converter : converters) {
|
||||||
|
if (converter.appliesTo(o)) {
|
||||||
|
return appendModules(converter.convert(o), source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appendModules(o, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object appendModules(Object source, ElementSource elementSource) {
|
||||||
|
String modules = moduleSourceString(elementSource);
|
||||||
|
if (modules.length() == 0) {
|
||||||
|
return source;
|
||||||
|
} else {
|
||||||
|
return source + modules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String moduleSourceString(ElementSource elementSource) {
|
||||||
|
// if we only have one module (or don't know what they are), then don't bother
|
||||||
|
// reporting it, because the source already is going to report exactly that module.
|
||||||
|
if (elementSource == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
|
||||||
|
// Insert any original element sources w/ module info into the path.
|
||||||
|
while (elementSource.getOriginalElementSource() != null) {
|
||||||
|
elementSource = elementSource.getOriginalElementSource();
|
||||||
|
modules.addAll(0, elementSource.getModuleClassNames());
|
||||||
|
}
|
||||||
|
if (modules.size() <= 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ideally we'd do:
|
||||||
|
// return Joiner.on(" -> ")
|
||||||
|
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
|
||||||
|
// .append(")").toString();
|
||||||
|
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
|
||||||
|
StringBuilder builder = new StringBuilder(" (via modules: ");
|
||||||
|
for (int i = modules.size() - 1; i >= 0; i--) {
|
||||||
|
builder.append(modules.get(i));
|
||||||
|
if (i != 0) {
|
||||||
|
builder.append(" -> ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append(")");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void formatSource(Formatter formatter, Object source) {
|
||||||
|
ElementSource elementSource = null;
|
||||||
|
if (source instanceof ElementSource) {
|
||||||
|
elementSource = (ElementSource) source;
|
||||||
|
source = elementSource.getDeclaringSource();
|
||||||
|
}
|
||||||
|
formatSource(formatter, source, elementSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
|
||||||
|
String modules = moduleSourceString(elementSource);
|
||||||
|
if (source instanceof Dependency) {
|
||||||
|
Dependency<?> dependency = (Dependency<?>) source;
|
||||||
|
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
||||||
|
if (injectionPoint != null) {
|
||||||
|
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
|
||||||
|
} else {
|
||||||
|
formatSource(formatter, dependency.getKey(), elementSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (source instanceof InjectionPoint) {
|
||||||
|
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
|
||||||
|
|
||||||
|
} else if (source instanceof Class) {
|
||||||
|
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
|
||||||
|
|
||||||
|
} else if (source instanceof Member) {
|
||||||
|
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
|
||||||
|
|
||||||
|
} else if (source instanceof TypeLiteral) {
|
||||||
|
formatter.format(" while locating %s%s%n", source, modules);
|
||||||
|
|
||||||
|
} else if (source instanceof Key) {
|
||||||
|
Key<?> key = (Key<?>) source;
|
||||||
|
formatter.format(" while locating %s%n", convert(key, elementSource));
|
||||||
|
|
||||||
|
} else if (source instanceof Thread) {
|
||||||
|
formatter.format(" in thread %s%n", source);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
formatter.format(" at %s%s%n", source, modules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
|
||||||
|
InjectionPoint injectionPoint, ElementSource elementSource) {
|
||||||
|
Member member = injectionPoint.getMember();
|
||||||
|
Class<? extends Member> memberType = Classes.memberType(member);
|
||||||
|
|
||||||
|
if (memberType == Field.class) {
|
||||||
|
dependency = injectionPoint.getDependencies().get(0);
|
||||||
|
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
|
||||||
|
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
|
||||||
|
|
||||||
|
} else if (dependency != null) {
|
||||||
|
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
|
||||||
|
formatter.format(" for parameter %s at %s%n",
|
||||||
|
dependency.getParameterIndex(), StackTraceElements.forMember(member));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
formatSource(formatter, injectionPoint.getMember());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance that uses {@code source} as a reference point for newly added errors.
|
||||||
|
*/
|
||||||
|
public Errors withSource(Object source) {
|
||||||
|
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
|
||||||
|
? this
|
||||||
|
: new Errors(this, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use a fairly generic error message here. The motivation is to share the
|
||||||
|
* same message for both bind time errors:
|
||||||
|
* <pre><code>Guice.createInjector(new AbstractModule() {
|
||||||
|
* public void configure() {
|
||||||
|
* bind(Runnable.class);
|
||||||
|
* }
|
||||||
|
* }</code></pre>
|
||||||
|
* ...and at provide-time errors:
|
||||||
|
* <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
|
||||||
|
* Otherwise we need to know who's calling when resolving a just-in-time
|
||||||
|
* binding, which makes things unnecessarily complex.
|
||||||
|
*/
|
||||||
|
public Errors missingImplementation(Key key) {
|
||||||
|
return addMessage("No implementation for %s was bound.", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors jitDisabled(Key key) {
|
||||||
|
return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors jitDisabledInParent(Key<?> key) {
|
||||||
|
return addMessage(
|
||||||
|
"Explicit bindings are required and %s would be bound in a parent injector.%n"
|
||||||
|
+ "Please add an explicit binding for it, either in the child or the parent.",
|
||||||
|
key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors atInjectRequired(Class clazz) {
|
||||||
|
return addMessage(
|
||||||
|
"Explicit @Inject annotations are required on constructors,"
|
||||||
|
+ " but %s has no constructors annotated with @Inject.",
|
||||||
|
clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors converterReturnedNull(String stringValue, Object source,
|
||||||
|
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
|
||||||
|
return addMessage("Received null converting '%s' (bound at %s) to %s%n"
|
||||||
|
+ " using %s.",
|
||||||
|
stringValue, convert(source), type, typeConverterBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type,
|
||||||
|
TypeConverterBinding typeConverterBinding, Object converted) {
|
||||||
|
return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n"
|
||||||
|
+ " using %s.%n"
|
||||||
|
+ " Converter returned %s.",
|
||||||
|
stringValue, convert(source), type, typeConverterBinding, converted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors conversionError(String stringValue, Object source,
|
||||||
|
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding, RuntimeException cause) {
|
||||||
|
return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n"
|
||||||
|
+ " using %s.%n"
|
||||||
|
+ " Reason: %s",
|
||||||
|
stringValue, convert(source), type, typeConverterBinding, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
|
||||||
|
TypeConverterBinding a, TypeConverterBinding b) {
|
||||||
|
return addMessage("Multiple converters can convert '%s' (bound at %s) to %s:%n"
|
||||||
|
+ " %s and%n"
|
||||||
|
+ " %s.%n"
|
||||||
|
+ " Please adjust your type converter configuration to avoid overlapping matches.",
|
||||||
|
stringValue, convert(source), type, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors bindingToProvider() {
|
||||||
|
return addMessage("Binding to Provider is not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors subtypeNotProvided(Class<? extends Provider<?>> providerType,
|
||||||
|
Class<?> type) {
|
||||||
|
return addMessage("%s doesn't provide instances of %s.", providerType, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors notASubtype(Class<?> implementationType, Class<?> type) {
|
||||||
|
return addMessage("%s doesn't extend %s.", implementationType, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors recursiveImplementationType() {
|
||||||
|
return addMessage("@ImplementedBy points to the same class it annotates.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors recursiveProviderType() {
|
||||||
|
return addMessage("@ProvidedBy points to the same class it annotates.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
|
||||||
|
return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
|
||||||
|
return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors optionalConstructor(Constructor constructor) {
|
||||||
|
return addMessage("%s is annotated @Inject(optional=true), "
|
||||||
|
+ "but constructors cannot be optional.", constructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotBindToGuiceType(String simpleName) {
|
||||||
|
return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
|
||||||
|
return addMessage("No scope is bound to %s.", scopeAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors scopeAnnotationOnAbstractType(
|
||||||
|
Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
|
||||||
|
return addMessage("%s is annotated with %s, but scope annotations are not supported "
|
||||||
|
+ "for abstract types.%n Bound at %s.", type, scopeAnnotation, convert(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
|
||||||
|
return addMessage("%s is annotated with %s, but binding annotations should be applied "
|
||||||
|
+ "to its parameters instead.", member, bindingAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors missingConstructor(Class<?> implementation) {
|
||||||
|
return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES,
|
||||||
|
implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors tooManyConstructors(Class<?> implementation) {
|
||||||
|
return addMessage("%s has more than one constructor annotated with @Inject. "
|
||||||
|
+ CONSTRUCTOR_RULES, implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
|
||||||
|
return addMessage("%s does not define %s", type, constructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors duplicateScopes(ScopeBinding existing,
|
||||||
|
Class<? extends Annotation> annotationType, Scope scope) {
|
||||||
|
return addMessage("Scope %s is already bound to %s at %s.%n Cannot bind %s.",
|
||||||
|
existing.getScope(), annotationType, existing.getSource(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors voidProviderMethod() {
|
||||||
|
return addMessage("Provider methods must return a value. Do not return void.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors missingConstantValues() {
|
||||||
|
return addMessage("Missing constant value. Please call to(...).");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectInnerClass(Class<?> type) {
|
||||||
|
return addMessage("Injecting into inner classes is not supported. "
|
||||||
|
+ "Please use a 'static' class (top-level or nested) instead of %s.", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors duplicateBindingAnnotations(Member member,
|
||||||
|
Class<? extends Annotation> a, Class<? extends Annotation> b) {
|
||||||
|
return addMessage("%s has more than one annotation annotated with @BindingAnnotation: "
|
||||||
|
+ "%s and %s", member, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors staticInjectionOnInterface(Class<?> clazz) {
|
||||||
|
return addMessage("%s is an interface, but interfaces have no static injection points.", clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectFinalField(Field field) {
|
||||||
|
return addMessage("Injected field %s cannot be final.", field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectAbstractMethod(Method method) {
|
||||||
|
return addMessage("Injected method %s cannot be abstract.", method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectNonVoidMethod(Method method) {
|
||||||
|
return addMessage("Injected method %s must return void.", method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectMethodWithTypeParameters(Method method) {
|
||||||
|
return addMessage("Injected method %s cannot declare type parameters of its own.", method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors duplicateScopeAnnotations(
|
||||||
|
Class<? extends Annotation> a, Class<? extends Annotation> b) {
|
||||||
|
return addMessage("More than one scope annotation was found: %s and %s.", a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors recursiveBinding() {
|
||||||
|
return addMessage("Binding points to itself.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors bindingAlreadySet(Key<?> key, Object source) {
|
||||||
|
return addMessage("A binding to %s was already configured at %s.", key, convert(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors jitBindingAlreadySet(Key<?> key) {
|
||||||
|
return addMessage("A just-in-time binding to %s was already configured on a parent injector.", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors childBindingAlreadySet(Key<?> key, Set<Object> sources) {
|
||||||
|
Formatter allSources = new Formatter();
|
||||||
|
for (Object source : sources) {
|
||||||
|
if (source == null) {
|
||||||
|
allSources.format("%n (bound by a just-in-time binding)");
|
||||||
|
} else {
|
||||||
|
allSources.format("%n bound at %s", source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Errors errors = addMessage(
|
||||||
|
"Unable to create binding for %s."
|
||||||
|
+ " It was already configured on one or more child injectors or private modules"
|
||||||
|
+ "%s%n"
|
||||||
|
+ " If it was in a PrivateModule, did you forget to expose the binding?",
|
||||||
|
key, allSources.out());
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
|
||||||
|
return addMessage(
|
||||||
|
"A binding to %s was already configured at %s and an error was thrown "
|
||||||
|
+ "while checking duplicate bindings. Error: %s",
|
||||||
|
key, convert(source), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorInjectingMethod(Throwable cause) {
|
||||||
|
return errorInUserCode(cause, "Error injecting method, %s", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
|
||||||
|
TypeLiteral<?> type, Throwable cause) {
|
||||||
|
return errorInUserCode(cause,
|
||||||
|
"Error notifying TypeListener %s (bound at %s) of %s.%n"
|
||||||
|
+ " Reason: %s",
|
||||||
|
listener.getListener(), convert(listener.getSource()), type, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorInjectingConstructor(Throwable cause) {
|
||||||
|
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorInProvider(RuntimeException runtimeException) {
|
||||||
|
Throwable unwrapped = unwrap(runtimeException);
|
||||||
|
return errorInUserCode(unwrapped, "Error in custom provider, %s", unwrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorInUserInjector(
|
||||||
|
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
|
||||||
|
return errorInUserCode(cause, "Error injecting %s using %s.%n"
|
||||||
|
+ " Reason: %s", type, listener, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorNotifyingInjectionListener(
|
||||||
|
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
|
||||||
|
return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
|
||||||
|
+ " Reason: %s", listener, type, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors exposedButNotBound(Key<?> key) {
|
||||||
|
return addMessage("Could not expose() %s, it must be explicitly bound.", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors keyNotFullySpecified(TypeLiteral<?> typeLiteral) {
|
||||||
|
return addMessage("%s cannot be used as a key; It is not fully specified.", typeLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorEnhancingClass(Class<?> clazz, Throwable cause) {
|
||||||
|
return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
|
||||||
|
Collection<Message> messages = getMessagesFromThrowable(cause);
|
||||||
|
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
return merge(messages);
|
||||||
|
} else {
|
||||||
|
return addMessage(cause, messageFormat, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Throwable unwrap(RuntimeException runtimeException) {
|
||||||
|
if (runtimeException instanceof Exceptions.UnhandledCheckedUserException) {
|
||||||
|
return runtimeException.getCause();
|
||||||
|
} else {
|
||||||
|
return runtimeException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectRawProvider() {
|
||||||
|
return addMessage("Cannot inject a Provider that has no type parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectRawMembersInjector() {
|
||||||
|
return addMessage("Cannot inject a MembersInjector that has no type parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
|
||||||
|
return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotInjectRawTypeLiteral() {
|
||||||
|
return addMessage("Cannot inject a TypeLiteral that has no type parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors cannotSatisfyCircularDependency(Class<?> expectedType) {
|
||||||
|
return addMessage(
|
||||||
|
"Tried proxying %s to support a circular dependency, but it is not an interface.",
|
||||||
|
expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors circularProxiesDisabled(Class<?> expectedType) {
|
||||||
|
return addMessage(
|
||||||
|
"Tried proxying %s to support a circular dependency, but circular proxies are disabled.",
|
||||||
|
expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwCreationExceptionIfErrorsExist() {
|
||||||
|
if (!hasErrors()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CreationException(getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwConfigurationExceptionIfErrorsExist() {
|
||||||
|
if (!hasErrors()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ConfigurationException(getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwProvisionExceptionIfErrorsExist() {
|
||||||
|
if (!hasErrors()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ProvisionException(getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Message merge(Message message) {
|
||||||
|
List<Object> sources = Lists.newArrayList();
|
||||||
|
sources.addAll(getSources());
|
||||||
|
sources.addAll(message.getSources());
|
||||||
|
return new Message(sources, message.getMessage(), message.getCause());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors merge(Collection<Message> messages) {
|
||||||
|
for (Message message : messages) {
|
||||||
|
addMessage(merge(message));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors merge(Errors moreErrors) {
|
||||||
|
if (moreErrors.root == root || moreErrors.root.errors == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(moreErrors.root.errors);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getSources() {
|
||||||
|
List<Object> sources = Lists.newArrayList();
|
||||||
|
for (Errors e = this; e != null; e = e.parent) {
|
||||||
|
if (e.source != SourceProvider.UNKNOWN_SOURCE) {
|
||||||
|
sources.add(0, e.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwIfNewErrors(int expectedSize) throws ErrorsException {
|
||||||
|
if (size() == expectedSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw toException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorsException toException() {
|
||||||
|
return new ErrorsException(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasErrors() {
|
||||||
|
return root.errors != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors addMessage(String messageFormat, Object... arguments) {
|
||||||
|
return addMessage(null, messageFormat, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
|
||||||
|
String message = format(messageFormat, arguments);
|
||||||
|
addMessage(new Message(getSources(), message, cause));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors addMessage(Message message) {
|
||||||
|
if (root.errors == null) {
|
||||||
|
root.errors = Lists.newArrayList();
|
||||||
|
}
|
||||||
|
root.errors.add(message);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Message> getMessages() {
|
||||||
|
if (root.errors == null) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Ordering<Message>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Message a, Message b) {
|
||||||
|
return a.getSource().compareTo(b.getSource());
|
||||||
|
}
|
||||||
|
}.sortedCopy(root.errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code value} if it is non-null allowed to be null. Otherwise a message is added and
|
||||||
|
* an {@code ErrorsException} is thrown.
|
||||||
|
*/
|
||||||
|
public <T> T checkForNull(T value, Object source, Dependency<?> dependency)
|
||||||
|
throws ErrorsException {
|
||||||
|
if (value != null || dependency.isNullable()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
|
||||||
|
if (dependency.getInjectionPoint().getMember() instanceof Method) {
|
||||||
|
Method annotated = (Method) dependency.getInjectionPoint().getMember();
|
||||||
|
if (annotated.isAnnotationPresent(Provides.class)) {
|
||||||
|
switch (InternalFlags.getNullableProvidesOption()) {
|
||||||
|
case ERROR:
|
||||||
|
break; // break out & let the below exception happen
|
||||||
|
case IGNORE:
|
||||||
|
return value; // user doesn't care about injecting nulls to non-@Nullables.
|
||||||
|
case WARN:
|
||||||
|
// Warn only once, otherwise we spam logs too much.
|
||||||
|
if (!warnedDependencies.add(dependency)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/*logger.log(Level.WARNING,
|
||||||
|
"Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
|
||||||
|
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
|
||||||
|
+ " error.",
|
||||||
|
new Object[]{
|
||||||
|
dependency.getParameterIndex(),
|
||||||
|
convert(dependency.getInjectionPoint().getMember()),
|
||||||
|
convert(dependency.getKey())});*/
|
||||||
|
return null; // log & exit.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int parameterIndex = dependency.getParameterIndex();
|
||||||
|
String parameterName = (parameterIndex != -1)
|
||||||
|
? "parameter " + parameterIndex + " of "
|
||||||
|
: "";
|
||||||
|
addMessage("null returned by binding at %s%n but %s%s is not @Nullable",
|
||||||
|
source, parameterName, dependency.getInjectionPoint().getMember());
|
||||||
|
|
||||||
|
throw toException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return root.errors == null ? 0 : root.errors.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static abstract class Converter<T> {
|
||||||
|
|
||||||
|
final Class<T> type;
|
||||||
|
|
||||||
|
Converter(Class<T> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean appliesTo(Object o) {
|
||||||
|
return o != null && type.isAssignableFrom(o.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
String convert(Object o) {
|
||||||
|
return toString(type.cast(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract String toString(T t);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that a result could not be returned while preparing or resolving a binding. The caller
|
||||||
|
* should {@link Errors#merge(Errors) merge} the errors from this exception with their existing
|
||||||
|
* errors.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class ErrorsException extends Exception {
|
||||||
|
|
||||||
|
private final Errors errors;
|
||||||
|
|
||||||
|
public ErrorsException(Errors errors) {
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Errors getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
46
src/main/java/com/google/inject/internal/Exceptions.java
Normal file
46
src/main/java/com/google/inject/internal/Exceptions.java
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rethrows user-code exceptions in wrapped exceptions so that Errors can target the correct
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
class Exceptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rethrows the exception (or it's cause, if it has one) directly if possible.
|
||||||
|
* If it was a checked exception, this wraps the exception in a stack trace
|
||||||
|
* with no frames, so that the exception is shown immediately with no frames
|
||||||
|
* above it.
|
||||||
|
*/
|
||||||
|
public static RuntimeException rethrowCause(Throwable throwable) {
|
||||||
|
Throwable cause = throwable;
|
||||||
|
if (cause.getCause() != null) {
|
||||||
|
cause = cause.getCause();
|
||||||
|
}
|
||||||
|
return rethrow(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rethrows the exception.
|
||||||
|
*/
|
||||||
|
public static RuntimeException rethrow(Throwable throwable) {
|
||||||
|
if (throwable instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) throwable;
|
||||||
|
} else if (throwable instanceof Error) {
|
||||||
|
throw (Error) throwable;
|
||||||
|
} else {
|
||||||
|
throw new UnhandledCheckedUserException(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A marker exception class that we look for in order to unwrap the exception
|
||||||
|
* into the user exception, to provide a cleaner stack trace.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
static class UnhandledCheckedUserException extends RuntimeException {
|
||||||
|
public UnhandledCheckedUserException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.ExposedBinding;
|
||||||
|
import com.google.inject.spi.PrivateElements;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class ExposedBindingImpl<T> extends BindingImpl<T> implements ExposedBinding<T> {
|
||||||
|
|
||||||
|
private final PrivateElements privateElements;
|
||||||
|
|
||||||
|
public ExposedBindingImpl(InjectorImpl injector, Object source, Key<T> key,
|
||||||
|
InternalFactory<T> factory, PrivateElements privateElements) {
|
||||||
|
super(injector, key, source, factory, Scoping.UNSCOPED);
|
||||||
|
this.privateElements = privateElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrivateElements getPrivateElements() {
|
||||||
|
return privateElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(ExposedBinding.class)
|
||||||
|
.add("key", getKey())
|
||||||
|
.add("source", getSource())
|
||||||
|
.add("privateElements", privateElements)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTo(Binder binder) {
|
||||||
|
throw new UnsupportedOperationException("This element represents a synthetic binding.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purposely does not override equals/hashcode, because exposed bindings are only equal to
|
||||||
|
// themselves right now -- that is, there cannot be "duplicate" exposed bindings.
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.PrivateElements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This factory exists in a parent injector. When invoked, it retrieves its value from a child
|
||||||
|
* injector.
|
||||||
|
*/
|
||||||
|
final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener {
|
||||||
|
private final Key<T> key;
|
||||||
|
private final PrivateElements privateElements;
|
||||||
|
private BindingImpl<T> delegate;
|
||||||
|
|
||||||
|
ExposedKeyFactory(Key<T> key, PrivateElements privateElements) {
|
||||||
|
this.key = key;
|
||||||
|
this.privateElements = privateElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notify(Errors errors) {
|
||||||
|
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
||||||
|
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
|
||||||
|
|
||||||
|
// validate that the child injector has its own factory. If the getInternalFactory() returns
|
||||||
|
// this, then that child injector doesn't have a factory (and getExplicitBinding has returned
|
||||||
|
// its parent's binding instead
|
||||||
|
if (explicitBinding.getInternalFactory() == this) {
|
||||||
|
errors.withSource(explicitBinding.getSource()).exposedButNotBound(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delegate = explicitBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
return delegate.getInternalFactory().get(errors, context, dependency, linked);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.binder.AnnotatedElementBuilder;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For private binder's expose() method.
|
||||||
|
*/
|
||||||
|
public class ExposureBuilder<T> implements AnnotatedElementBuilder {
|
||||||
|
private final Binder binder;
|
||||||
|
private final Object source;
|
||||||
|
private Key<T> key;
|
||||||
|
|
||||||
|
public ExposureBuilder(Binder binder, Object source, Key<T> key) {
|
||||||
|
this.binder = binder;
|
||||||
|
this.source = source;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkNotAnnotated() {
|
||||||
|
if (key.getAnnotationType() != null) {
|
||||||
|
binder.addError(AbstractBindingBuilder.ANNOTATION_ALREADY_SPECIFIED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
|
Preconditions.checkNotNull(annotationType, "annotationType");
|
||||||
|
checkNotAnnotated();
|
||||||
|
key = Key.get(key.getTypeLiteral(), annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void annotatedWith(Annotation annotation) {
|
||||||
|
Preconditions.checkNotNull(annotation, "annotation");
|
||||||
|
checkNotAnnotated();
|
||||||
|
key = Key.get(key.getTypeLiteral(), annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key<?> getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AnnotatedElementBuilder";
|
||||||
|
}
|
||||||
|
}
|
53
src/main/java/com/google/inject/internal/FactoryProxy.java
Normal file
53
src/main/java/com/google/inject/internal/FactoryProxy.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.internal.InjectorImpl.JitLimitation;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder which enables us to swap in the real factory once the injector is created.
|
||||||
|
* Used for a linked binding, so that getting the linked binding returns the link's factory.
|
||||||
|
*/
|
||||||
|
final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
||||||
|
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private final Key<T> key;
|
||||||
|
private final Key<? extends T> targetKey;
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
private InternalFactory<? extends T> targetFactory;
|
||||||
|
|
||||||
|
FactoryProxy(InjectorImpl injector, Key<T> key, Key<? extends T> targetKey, Object source) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.key = key;
|
||||||
|
this.targetKey = targetKey;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notify(final Errors errors) {
|
||||||
|
try {
|
||||||
|
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
context.pushState(targetKey, source);
|
||||||
|
try {
|
||||||
|
return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
|
||||||
|
} finally {
|
||||||
|
context.popState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(FactoryProxy.class)
|
||||||
|
.add("key", key)
|
||||||
|
.add("provider", targetFactory)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
44
src/main/java/com/google/inject/internal/FailableCache.java
Normal file
44
src/main/java/com/google/inject/internal/FailableCache.java
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
|
||||||
|
* exception is thrown on retrieval.
|
||||||
|
*/
|
||||||
|
public abstract class FailableCache<K, V> {
|
||||||
|
|
||||||
|
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
|
||||||
|
new CacheLoader<K, Object>() {
|
||||||
|
public Object load(K key) {
|
||||||
|
Errors errors = new Errors();
|
||||||
|
V result = null;
|
||||||
|
try {
|
||||||
|
result = FailableCache.this.create(key, errors);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
return errors.hasErrors() ? errors : result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
protected abstract V create(K key, Errors errors) throws ErrorsException;
|
||||||
|
|
||||||
|
public V get(K key, Errors errors) throws ErrorsException {
|
||||||
|
Object resultOrError = delegate.getUnchecked(key);
|
||||||
|
if (resultOrError instanceof Errors) {
|
||||||
|
errors.merge((Errors) resultOrError);
|
||||||
|
throw errors.toException();
|
||||||
|
} else {
|
||||||
|
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
|
||||||
|
V result = (V) resultOrError;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean remove(K key) {
|
||||||
|
return delegate.asMap().remove(key) != null;
|
||||||
|
}
|
||||||
|
}
|
160
src/main/java/com/google/inject/internal/InheritingState.java
Normal file
160
src/main/java/com/google/inject/internal/InheritingState.java
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||||
|
import com.google.inject.spi.ProvisionListenerBinding;
|
||||||
|
import com.google.inject.spi.ScopeBinding;
|
||||||
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
final class InheritingState implements State {
|
||||||
|
|
||||||
|
private final State parent;
|
||||||
|
|
||||||
|
// Must be a linked hashmap in order to preserve order of bindings in Modules.
|
||||||
|
private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
|
||||||
|
private final Map<Key<?>, Binding<?>> explicitBindings
|
||||||
|
= Collections.unmodifiableMap(explicitBindingsMutable);
|
||||||
|
private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
|
||||||
|
private final List<TypeConverterBinding> converters = Lists.newArrayList();
|
||||||
|
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
|
||||||
|
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
|
||||||
|
private final List<ModuleAnnotatedMethodScannerBinding> scannerBindings = Lists.newArrayList();
|
||||||
|
private final WeakKeySet blacklistedKeys;
|
||||||
|
private final Object lock;
|
||||||
|
|
||||||
|
InheritingState(State parent) {
|
||||||
|
this.parent = checkNotNull(parent, "parent");
|
||||||
|
this.lock = (parent == State.NONE) ? this : parent.lock();
|
||||||
|
this.blacklistedKeys = new WeakKeySet(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public State parent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
|
||||||
|
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
|
||||||
|
Binding<?> binding = explicitBindings.get(key);
|
||||||
|
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
|
||||||
|
return explicitBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBinding(Key<?> key, BindingImpl<?> binding) {
|
||||||
|
explicitBindingsMutable.put(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
|
||||||
|
ScopeBinding scopeBinding = scopes.get(annotationType);
|
||||||
|
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
|
||||||
|
scopes.put(annotationType, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addConverter(TypeConverterBinding typeConverterBinding) {
|
||||||
|
converters.add(typeConverterBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeConverterBinding getConverter(
|
||||||
|
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
|
||||||
|
TypeConverterBinding matchingConverter = null;
|
||||||
|
for (State s = this; s != State.NONE; s = s.parent()) {
|
||||||
|
for (TypeConverterBinding converter : s.getConvertersThisLevel()) {
|
||||||
|
if (converter.getTypeMatcher().matches(type)) {
|
||||||
|
if (matchingConverter != null) {
|
||||||
|
errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
|
||||||
|
}
|
||||||
|
matchingConverter = converter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchingConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTypeListener(TypeListenerBinding listenerBinding) {
|
||||||
|
typeListenerBindings.add(listenerBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TypeListenerBinding> getTypeListenerBindings() {
|
||||||
|
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
|
||||||
|
List<TypeListenerBinding> result =
|
||||||
|
Lists.newArrayListWithCapacity(parentBindings.size() + typeListenerBindings.size());
|
||||||
|
result.addAll(parentBindings);
|
||||||
|
result.addAll(typeListenerBindings);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
|
||||||
|
provisionListenerBindings.add(listenerBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
|
||||||
|
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
|
||||||
|
List<ProvisionListenerBinding> result =
|
||||||
|
Lists.newArrayListWithCapacity(parentBindings.size() + provisionListenerBindings.size());
|
||||||
|
result.addAll(parentBindings);
|
||||||
|
result.addAll(provisionListenerBindings);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
|
||||||
|
scannerBindings.add(scanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
|
||||||
|
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
|
||||||
|
List<ModuleAnnotatedMethodScannerBinding> result =
|
||||||
|
Lists.newArrayListWithCapacity(parentBindings.size() + scannerBindings.size());
|
||||||
|
result.addAll(parentBindings);
|
||||||
|
result.addAll(scannerBindings);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void blacklist(Key<?> key, State state, Object source) {
|
||||||
|
parent.blacklist(key, state, source);
|
||||||
|
blacklistedKeys.add(key, state, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlacklisted(Key<?> key) {
|
||||||
|
return blacklistedKeys.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
|
||||||
|
return blacklistedKeys.getSources(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object lock() {
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Class<? extends Annotation>, Scope> getScopes() {
|
||||||
|
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
|
||||||
|
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {
|
||||||
|
builder.put(entry.getKey(), entry.getValue().getScope());
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
12
src/main/java/com/google/inject/internal/Initializable.java
Normal file
12
src/main/java/com/google/inject/internal/Initializable.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a reference that requires initialization to be performed before it can be used.
|
||||||
|
*/
|
||||||
|
interface Initializable<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures the reference is initialized, then returns it.
|
||||||
|
*/
|
||||||
|
T get(Errors errors) throws ErrorsException;
|
||||||
|
}
|
20
src/main/java/com/google/inject/internal/Initializables.java
Normal file
20
src/main/java/com/google/inject/internal/Initializables.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
final class Initializables {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an initializable for an instance that requires no initialization.
|
||||||
|
*/
|
||||||
|
static <T> Initializable<T> of(final T instance) {
|
||||||
|
return new Initializable<T>() {
|
||||||
|
public T get(Errors errors) throws ErrorsException {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(instance);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
181
src/main/java/com/google/inject/internal/Initializer.java
Normal file
181
src/main/java/com/google/inject/internal/Initializer.java
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Stage;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages and injects instances at injector-creation time. This is made more complicated by
|
||||||
|
* instances that request other instances while they're being injected. We overcome this by using
|
||||||
|
* {@link Initializable}, which attempts to perform injection before use.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class Initializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the only thread that we'll use to inject members.
|
||||||
|
*/
|
||||||
|
private final Thread creatingThread = Thread.currentThread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zero means everything is injected.
|
||||||
|
*/
|
||||||
|
private final CountDownLatch ready = new CountDownLatch(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps from instances that need injection to the MembersInjector that will inject them.
|
||||||
|
*/
|
||||||
|
private final Map<Object, MembersInjectorImpl<?>> pendingMembersInjectors =
|
||||||
|
Maps.newIdentityHashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps instances that need injection to a source that registered them
|
||||||
|
*/
|
||||||
|
private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an instance for member injection when that step is performed.
|
||||||
|
*
|
||||||
|
* @param instance an instance that optionally has members to be injected (each annotated with
|
||||||
|
* @param binding the binding that caused this initializable to be created, if it exists.
|
||||||
|
* @param source the source location that this injection was requested
|
||||||
|
* @Inject).
|
||||||
|
*/
|
||||||
|
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
|
||||||
|
Object source, Set<InjectionPoint> injectionPoints) {
|
||||||
|
checkNotNull(source);
|
||||||
|
|
||||||
|
ProvisionListenerStackCallback<T> provisionCallback =
|
||||||
|
binding == null ? null : injector.provisionListenerStore.get(binding);
|
||||||
|
|
||||||
|
// short circuit if the object has no injections or listeners.
|
||||||
|
if (instance == null || (injectionPoints.isEmpty()
|
||||||
|
&& !injector.membersInjectorStore.hasTypeListeners()
|
||||||
|
&& (provisionCallback == null || !provisionCallback.hasListeners()))) {
|
||||||
|
return Initializables.of(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
InjectableReference<T> initializable = new InjectableReference<T>(
|
||||||
|
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
|
||||||
|
pendingInjection.put(instance, initializable);
|
||||||
|
return initializable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares member injectors for all injected instances. This prompts Guice to do static analysis
|
||||||
|
* on the injected instances.
|
||||||
|
*/
|
||||||
|
void validateOustandingInjections(Errors errors) {
|
||||||
|
for (InjectableReference<?> reference : pendingInjection.values()) {
|
||||||
|
try {
|
||||||
|
pendingMembersInjectors.put(reference.instance, reference.validate(errors));
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs creation-time injections on all objects that require it. Whenever fulfilling an
|
||||||
|
* injection depends on another object that requires injection, we inject it first. If the two
|
||||||
|
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
||||||
|
*/
|
||||||
|
void injectAll(final Errors errors) {
|
||||||
|
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
|
||||||
|
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
|
||||||
|
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
|
||||||
|
try {
|
||||||
|
reference.get(errors);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pendingInjection.isEmpty()) {
|
||||||
|
throw new AssertionError("Failed to satisfy " + pendingInjection);
|
||||||
|
}
|
||||||
|
|
||||||
|
ready.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InjectableReference<T> implements Initializable<T> {
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private final T instance;
|
||||||
|
private final Object source;
|
||||||
|
private final Key<T> key;
|
||||||
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
|
||||||
|
public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
|
||||||
|
ProvisionListenerStackCallback<T> provisionCallback, Object source) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.key = key; // possibly null!
|
||||||
|
this.provisionCallback = provisionCallback; // possibly null!
|
||||||
|
this.instance = checkNotNull(instance, "instance");
|
||||||
|
this.source = checkNotNull(source, "source");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MembersInjectorImpl<T> validate(Errors errors) throws ErrorsException {
|
||||||
|
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
|
||||||
|
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||||
|
return injector.membersInjectorStore.get(type, errors.withSource(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this
|
||||||
|
* method will ensure that all its members have been injected before returning.
|
||||||
|
*/
|
||||||
|
public T get(Errors errors) throws ErrorsException {
|
||||||
|
if (ready.getCount() == 0) {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just wait for everything to be injected by another thread
|
||||||
|
if (Thread.currentThread() != creatingThread) {
|
||||||
|
try {
|
||||||
|
ready.await();
|
||||||
|
return instance;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Give up, since we don't know if our injection is ready
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toInject needs injection, do it right away. we only do this once, even if it fails
|
||||||
|
if (pendingInjection.remove(instance) != null) {
|
||||||
|
// safe because we only insert a members injector for the appropriate instance
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
MembersInjectorImpl<T> membersInjector =
|
||||||
|
(MembersInjectorImpl<T>) pendingMembersInjectors.remove(instance);
|
||||||
|
Preconditions.checkState(membersInjector != null,
|
||||||
|
"No membersInjector available for instance: %s, from key: %s", instance, key);
|
||||||
|
|
||||||
|
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
||||||
|
// (otherwise we'll inject all of them)
|
||||||
|
membersInjector.injectAndNotify(instance,
|
||||||
|
errors.withSource(source),
|
||||||
|
key,
|
||||||
|
provisionCallback,
|
||||||
|
source,
|
||||||
|
injector.options.stage == Stage.TOOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return instance.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.ConfigurationException;
|
||||||
|
import com.google.inject.Stage;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.InjectionRequest;
|
||||||
|
import com.google.inject.spi.StaticInjectionRequest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@code Binder.requestInjection} and {@code Binder.requestStaticInjection} commands.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class InjectionRequestProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
private final List<StaticInjection> staticInjections = Lists.newArrayList();
|
||||||
|
private final Initializer initializer;
|
||||||
|
|
||||||
|
InjectionRequestProcessor(Errors errors, Initializer initializer) {
|
||||||
|
super(errors);
|
||||||
|
this.initializer = initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(StaticInjectionRequest request) {
|
||||||
|
staticInjections.add(new StaticInjection(injector, request));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(InjectionRequest<?> request) {
|
||||||
|
Set<InjectionPoint> injectionPoints;
|
||||||
|
try {
|
||||||
|
injectionPoints = request.getInjectionPoints();
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
errors.merge(e.getErrorMessages());
|
||||||
|
injectionPoints = e.getPartialValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
initializer.requestInjection(
|
||||||
|
injector, request.getInstance(), null, request.getSource(), injectionPoints);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate() {
|
||||||
|
for (StaticInjection staticInjection : staticInjections) {
|
||||||
|
staticInjection.validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void injectMembers() {
|
||||||
|
/*
|
||||||
|
* TODO: If you request both a parent class and one of its
|
||||||
|
* subclasses, the parent class's static members will be
|
||||||
|
* injected twice.
|
||||||
|
*/
|
||||||
|
for (StaticInjection staticInjection : staticInjections) {
|
||||||
|
staticInjection.injectMembers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A requested static injection.
|
||||||
|
*/
|
||||||
|
private class StaticInjection {
|
||||||
|
final InjectorImpl injector;
|
||||||
|
final Object source;
|
||||||
|
final StaticInjectionRequest request;
|
||||||
|
ImmutableList<SingleMemberInjector> memberInjectors;
|
||||||
|
|
||||||
|
public StaticInjection(InjectorImpl injector, StaticInjectionRequest request) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.source = request.getSource();
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate() {
|
||||||
|
Errors errorsForMember = errors.withSource(source);
|
||||||
|
Set<InjectionPoint> injectionPoints;
|
||||||
|
try {
|
||||||
|
injectionPoints = request.getInjectionPoints();
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
errorsForMember.merge(e.getErrorMessages());
|
||||||
|
injectionPoints = e.getPartialValue();
|
||||||
|
}
|
||||||
|
if (injectionPoints != null) {
|
||||||
|
memberInjectors = injector.membersInjectorStore.getInjectors(
|
||||||
|
injectionPoints, errorsForMember);
|
||||||
|
} else {
|
||||||
|
memberInjectors = ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.merge(errorsForMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
void injectMembers() {
|
||||||
|
try {
|
||||||
|
injector.callInContext(new ContextualCallable<Void>() {
|
||||||
|
public Void call(InternalContext context) {
|
||||||
|
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||||
|
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
|
||||||
|
// or if we are in tool stage and the injection point is toolable.
|
||||||
|
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
|
||||||
|
memberInjector.inject(errors, context, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1142
src/main/java/com/google/inject/internal/InjectorImpl.java
Normal file
1142
src/main/java/com/google/inject/internal/InjectorImpl.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,71 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Stage;
|
||||||
|
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
||||||
|
import com.google.inject.spi.DisableCircularProxiesOption;
|
||||||
|
import com.google.inject.spi.RequireAtInjectOnConstructorsOption;
|
||||||
|
import com.google.inject.spi.RequireExactBindingAnnotationsOption;
|
||||||
|
import com.google.inject.spi.RequireExplicitBindingsOption;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A processor to gather injector options.
|
||||||
|
*/
|
||||||
|
class InjectorOptionsProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
private boolean disableCircularProxies = false;
|
||||||
|
private boolean jitDisabled = false;
|
||||||
|
private boolean atInjectRequired = false;
|
||||||
|
private boolean exactBindingAnnotationsRequired = false;
|
||||||
|
|
||||||
|
InjectorOptionsProcessor(Errors errors) {
|
||||||
|
super(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(DisableCircularProxiesOption option) {
|
||||||
|
disableCircularProxies = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(RequireExplicitBindingsOption option) {
|
||||||
|
jitDisabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(RequireAtInjectOnConstructorsOption option) {
|
||||||
|
atInjectRequired = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(RequireExactBindingAnnotationsOption option) {
|
||||||
|
exactBindingAnnotationsRequired = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InjectorOptions getOptions(Stage stage, InjectorOptions parentOptions) {
|
||||||
|
checkNotNull(stage, "stage must be set");
|
||||||
|
if (parentOptions == null) {
|
||||||
|
return new InjectorOptions(
|
||||||
|
stage,
|
||||||
|
jitDisabled,
|
||||||
|
disableCircularProxies,
|
||||||
|
atInjectRequired,
|
||||||
|
exactBindingAnnotationsRequired);
|
||||||
|
} else {
|
||||||
|
checkState(stage == parentOptions.stage, "child & parent stage don't match");
|
||||||
|
return new InjectorOptions(
|
||||||
|
stage,
|
||||||
|
jitDisabled || parentOptions.jitDisabled,
|
||||||
|
disableCircularProxies || parentOptions.disableCircularProxies,
|
||||||
|
atInjectRequired || parentOptions.atInjectRequired,
|
||||||
|
exactBindingAnnotationsRequired || parentOptions.exactBindingAnnotationsRequired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
296
src/main/java/com/google/inject/internal/InjectorShell.java
Normal file
296
src/main/java/com/google/inject/internal/InjectorShell.java
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import com.google.inject.Stage;
|
||||||
|
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
||||||
|
import com.google.inject.internal.util.SourceProvider;
|
||||||
|
import com.google.inject.internal.util.Stopwatch;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.Elements;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||||
|
import com.google.inject.spi.PrivateElements;
|
||||||
|
import com.google.inject.spi.ProvisionListenerBinding;
|
||||||
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.inject.Scopes.SINGLETON;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A partially-initialized injector. See {@link InternalInjectorCreator}, which
|
||||||
|
* uses this to build a tree of injectors in batch.
|
||||||
|
*/
|
||||||
|
final class InjectorShell {
|
||||||
|
|
||||||
|
private final List<Element> elements;
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
|
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
|
||||||
|
this.elements = elements;
|
||||||
|
this.injector = injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Injector is a special case because we allow both parent and child injectors to both have
|
||||||
|
* a binding for that key.
|
||||||
|
*/
|
||||||
|
private static void bindInjector(InjectorImpl injector) {
|
||||||
|
Key<Injector> key = Key.get(Injector.class);
|
||||||
|
InjectorFactory injectorFactory = new InjectorFactory(injector);
|
||||||
|
injector.state.putBinding(key,
|
||||||
|
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
|
||||||
|
injectorFactory, Scoping.UNSCOPED, injectorFactory,
|
||||||
|
ImmutableSet.<InjectionPoint>of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Logger is a special case because it knows the injection point of the injected member. It's
|
||||||
|
* the only binding that does this.
|
||||||
|
*/
|
||||||
|
/*private static void bindLogger(InjectorImpl injector) {
|
||||||
|
Key<Logger> key = Key.get(Logger.class);
|
||||||
|
LoggerFactory loggerFactory = new LoggerFactory();
|
||||||
|
injector.state.putBinding(key,
|
||||||
|
new ProviderInstanceBindingImpl<Logger>(injector, key,
|
||||||
|
SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
|
||||||
|
loggerFactory, ImmutableSet.<InjectionPoint>of()));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private static void bindStage(InjectorImpl injector, Stage stage) {
|
||||||
|
Key<Stage> key = Key.get(Stage.class);
|
||||||
|
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
SourceProvider.UNKNOWN_SOURCE,
|
||||||
|
new ConstantFactory<Stage>(Initializables.of(stage)),
|
||||||
|
ImmutableSet.<InjectionPoint>of(),
|
||||||
|
stage);
|
||||||
|
injector.state.putBinding(key, stageBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
InjectorImpl getInjector() {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Element> getElements() {
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Builder {
|
||||||
|
private final List<Element> elements = Lists.newArrayList();
|
||||||
|
private final List<Module> modules = Lists.newArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lazily constructed
|
||||||
|
*/
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
private InjectorImpl parent;
|
||||||
|
private InjectorOptions options;
|
||||||
|
private Stage stage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* null unless this exists in a {@link Binder#newPrivateBinder private environment}
|
||||||
|
*/
|
||||||
|
private PrivateElementsImpl privateElements;
|
||||||
|
|
||||||
|
Builder stage(Stage stage) {
|
||||||
|
this.stage = stage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder parent(InjectorImpl parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.state = new InheritingState(parent.state);
|
||||||
|
this.options = parent.options;
|
||||||
|
this.stage = options.stage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder privateElements(PrivateElements privateElements) {
|
||||||
|
this.privateElements = (PrivateElementsImpl) privateElements;
|
||||||
|
this.elements.addAll(privateElements.getElements());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addModules(Iterable<? extends Module> modules) {
|
||||||
|
if (modules != null) {
|
||||||
|
for (Module module : modules) {
|
||||||
|
this.modules.add(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stage getStage() {
|
||||||
|
return options.stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize on this before calling {@link #build}.
|
||||||
|
*/
|
||||||
|
Object lock() {
|
||||||
|
return getState().lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns the injector shells for the current modules. Multiple shells will be
|
||||||
|
* returned if any modules contain {@link Binder#newPrivateBinder private environments}. The
|
||||||
|
* primary injector will be first in the returned list.
|
||||||
|
*/
|
||||||
|
List<InjectorShell> build(
|
||||||
|
Initializer initializer,
|
||||||
|
ProcessedBindingData bindingData,
|
||||||
|
Stopwatch stopwatch,
|
||||||
|
Errors errors) {
|
||||||
|
checkState(stage != null, "Stage not initialized");
|
||||||
|
checkState(privateElements == null || parent != null, "PrivateElements with no parent");
|
||||||
|
checkState(state != null, "no state. Did you remember to lock() ?");
|
||||||
|
|
||||||
|
// bind Singleton if this is a top-level injector
|
||||||
|
if (parent == null) {
|
||||||
|
modules.add(0, new RootModule());
|
||||||
|
} else {
|
||||||
|
modules.add(0, new InheritedScannersModule(parent.state));
|
||||||
|
}
|
||||||
|
elements.addAll(Elements.getElements(stage, modules));
|
||||||
|
|
||||||
|
// Look for injector-changing options
|
||||||
|
InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
|
||||||
|
optionsProcessor.process(null, elements);
|
||||||
|
options = optionsProcessor.getOptions(stage, options);
|
||||||
|
|
||||||
|
InjectorImpl injector = new InjectorImpl(parent, state, options);
|
||||||
|
if (privateElements != null) {
|
||||||
|
privateElements.initInjector(injector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add default type converters if this is a top-level injector
|
||||||
|
if (parent == null) {
|
||||||
|
TypeConverterBindingProcessor.prepareBuiltInConverters(injector);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopwatch.resetAndLog("Module execution");
|
||||||
|
|
||||||
|
new MessageProcessor(errors).process(injector, elements);
|
||||||
|
|
||||||
|
new ListenerBindingProcessor(errors).process(injector, elements);
|
||||||
|
List<TypeListenerBinding> typeListenerBindings = injector.state.getTypeListenerBindings();
|
||||||
|
injector.membersInjectorStore = new MembersInjectorStore(injector, typeListenerBindings);
|
||||||
|
List<ProvisionListenerBinding> provisionListenerBindings =
|
||||||
|
injector.state.getProvisionListenerBindings();
|
||||||
|
injector.provisionListenerStore =
|
||||||
|
new ProvisionListenerCallbackStore(provisionListenerBindings);
|
||||||
|
stopwatch.resetAndLog("TypeListeners & ProvisionListener creation");
|
||||||
|
|
||||||
|
new ScopeBindingProcessor(errors).process(injector, elements);
|
||||||
|
stopwatch.resetAndLog("Scopes creation");
|
||||||
|
|
||||||
|
new TypeConverterBindingProcessor(errors).process(injector, elements);
|
||||||
|
stopwatch.resetAndLog("Converters creation");
|
||||||
|
|
||||||
|
bindStage(injector, stage);
|
||||||
|
bindInjector(injector);
|
||||||
|
//bindLogger(injector);
|
||||||
|
|
||||||
|
// Process all normal bindings, then UntargettedBindings.
|
||||||
|
// This is necessary because UntargettedBindings can create JIT bindings
|
||||||
|
// and need all their other dependencies set up ahead of time.
|
||||||
|
new BindingProcessor(errors, initializer, bindingData).process(injector, elements);
|
||||||
|
new UntargettedBindingProcessor(errors, bindingData).process(injector, elements);
|
||||||
|
stopwatch.resetAndLog("Binding creation");
|
||||||
|
|
||||||
|
new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements);
|
||||||
|
stopwatch.resetAndLog("Module annotated method scanners creation");
|
||||||
|
|
||||||
|
List<InjectorShell> injectorShells = Lists.newArrayList();
|
||||||
|
injectorShells.add(new InjectorShell(this, elements, injector));
|
||||||
|
|
||||||
|
// recursively build child shells
|
||||||
|
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
||||||
|
processor.process(injector, elements);
|
||||||
|
for (Builder builder : processor.getInjectorShellBuilders()) {
|
||||||
|
injectorShells.addAll(builder.build(initializer, bindingData, stopwatch, errors));
|
||||||
|
}
|
||||||
|
stopwatch.resetAndLog("Private environment creation");
|
||||||
|
|
||||||
|
return injectorShells;
|
||||||
|
}
|
||||||
|
|
||||||
|
private State getState() {
|
||||||
|
if (state == null) {
|
||||||
|
state = new InheritingState(State.NONE);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
|
||||||
|
private final Injector injector;
|
||||||
|
|
||||||
|
private InjectorFactory(Injector injector) {
|
||||||
|
this.injector = injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector get() {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Provider<Injector>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
|
||||||
|
public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
|
||||||
|
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
||||||
|
return injectionPoint == null
|
||||||
|
? Logger.getAnonymousLogger()
|
||||||
|
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Logger get() {
|
||||||
|
return Logger.getAnonymousLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Provider<Logger>";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private static class RootModule implements Module {
|
||||||
|
public void configure(Binder binder) {
|
||||||
|
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
||||||
|
binder.bindScope(Singleton.class, SINGLETON);
|
||||||
|
binder.bindScope(javax.inject.Singleton.class, SINGLETON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InheritedScannersModule implements Module {
|
||||||
|
private final State state;
|
||||||
|
|
||||||
|
InheritedScannersModule(State state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configure(Binder binder) {
|
||||||
|
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
|
||||||
|
binding.applyTo(binder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.HasDependencies;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.InstanceBinding;
|
||||||
|
import com.google.inject.util.Providers;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
||||||
|
|
||||||
|
final T instance;
|
||||||
|
final Provider<T> provider;
|
||||||
|
final ImmutableSet<InjectionPoint> injectionPoints;
|
||||||
|
|
||||||
|
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
InternalFactory<? extends T> internalFactory, Set<InjectionPoint> injectionPoints,
|
||||||
|
T instance) {
|
||||||
|
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
|
||||||
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
|
this.instance = instance;
|
||||||
|
this.provider = Providers.of(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
||||||
|
Set<InjectionPoint> injectionPoints, T instance) {
|
||||||
|
super(source, key, scoping);
|
||||||
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
|
this.instance = instance;
|
||||||
|
this.provider = Providers.of(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Provider<T> getProvider() {
|
||||||
|
return this.provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<InjectionPoint> getInjectionPoints() {
|
||||||
|
return injectionPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return instance instanceof HasDependencies
|
||||||
|
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
||||||
|
: Dependency.forInjectionPoints(injectionPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
|
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
|
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTo(Binder binder) {
|
||||||
|
// instance bindings aren't scoped
|
||||||
|
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(InstanceBinding.class)
|
||||||
|
.add("key", getKey())
|
||||||
|
.add("source", getSource())
|
||||||
|
.add("instance", instance)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof InstanceBindingImpl) {
|
||||||
|
InstanceBindingImpl<?> o = (InstanceBindingImpl<?>) obj;
|
||||||
|
return getKey().equals(o.getKey())
|
||||||
|
&& getScoping().equals(o.getScoping())
|
||||||
|
&& Objects.equal(instance, o.instance);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getKey(), getScoping());
|
||||||
|
}
|
||||||
|
}
|
136
src/main/java/com/google/inject/internal/InternalContext.java
Normal file
136
src/main/java/com/google/inject/internal/InternalContext.java
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.DependencyAndSource;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal context. Used to coordinate injections and support circular
|
||||||
|
* dependencies.
|
||||||
|
*/
|
||||||
|
final class InternalContext {
|
||||||
|
|
||||||
|
private final InjectorOptions options;
|
||||||
|
/**
|
||||||
|
* Keeps track of the hierarchy of types needed during injection.
|
||||||
|
*/
|
||||||
|
private final DependencyStack state = new DependencyStack();
|
||||||
|
private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
|
||||||
|
/**
|
||||||
|
* Keeps track of the type that is currently being requested for injection.
|
||||||
|
*/
|
||||||
|
private Dependency<?> dependency;
|
||||||
|
|
||||||
|
InternalContext(InjectorOptions options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InjectorOptions getInjectorOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> ConstructionContext<T> getConstructionContext(Object key) {
|
||||||
|
ConstructionContext<T> constructionContext
|
||||||
|
= (ConstructionContext<T>) constructionContexts.get(key);
|
||||||
|
if (constructionContext == null) {
|
||||||
|
constructionContext = new ConstructionContext<T>();
|
||||||
|
constructionContexts.put(key, constructionContext);
|
||||||
|
}
|
||||||
|
return constructionContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dependency<?> getDependency() {
|
||||||
|
return dependency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the new current dependency & adds it to the state.
|
||||||
|
*/
|
||||||
|
public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
|
||||||
|
Dependency<?> previous = this.dependency;
|
||||||
|
this.dependency = dependency;
|
||||||
|
state.add(dependency, source);
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops the current state & sets the new dependency.
|
||||||
|
*/
|
||||||
|
public void popStateAndSetDependency(Dependency<?> newDependency) {
|
||||||
|
state.pop();
|
||||||
|
this.dependency = newDependency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds to the state without setting the dependency.
|
||||||
|
*/
|
||||||
|
public void pushState(Key<?> key, Object source) {
|
||||||
|
state.add(key, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops from the state without setting a dependency.
|
||||||
|
*/
|
||||||
|
public void popState() {
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current dependency chain (all the state).
|
||||||
|
*/
|
||||||
|
public List<DependencyAndSource> getDependencyChain() {
|
||||||
|
ImmutableList.Builder<DependencyAndSource> builder = ImmutableList.builder();
|
||||||
|
for (int i = 0; i < state.size(); i += 2) {
|
||||||
|
Object evenEntry = state.get(i);
|
||||||
|
Dependency<?> dependency;
|
||||||
|
if (evenEntry instanceof Key) {
|
||||||
|
dependency = Dependency.get((Key<?>) evenEntry);
|
||||||
|
} else {
|
||||||
|
dependency = (Dependency<?>) evenEntry;
|
||||||
|
}
|
||||||
|
builder.add(new DependencyAndSource(dependency, state.get(i + 1)));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of the hierarchy of types needed during injection.
|
||||||
|
*
|
||||||
|
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
|
||||||
|
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
|
||||||
|
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
|
||||||
|
*/
|
||||||
|
private static final class DependencyStack {
|
||||||
|
private Object[] elements = new Object[16];
|
||||||
|
private int size = 0;
|
||||||
|
|
||||||
|
public void add(Object dependencyOrKey, Object source) {
|
||||||
|
if (elements.length < size + 2) {
|
||||||
|
elements = Arrays.copyOf(elements, (elements.length * 3) / 2 + 2);
|
||||||
|
}
|
||||||
|
elements[size++] = dependencyOrKey;
|
||||||
|
elements[size++] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pop() {
|
||||||
|
elements[--size] = null;
|
||||||
|
elements[--size] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(int i) {
|
||||||
|
return elements[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates objects which will be injected.
|
||||||
|
*/
|
||||||
|
interface InternalFactory<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object to be injected.
|
||||||
|
*
|
||||||
|
* @param context of this injection
|
||||||
|
* @param linked true if getting as a result of a linked binding
|
||||||
|
* @return instance to be injected
|
||||||
|
* @throws com.google.inject.internal.ErrorsException if a value cannot be provided
|
||||||
|
*/
|
||||||
|
T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts {@link ProviderInstanceBinding} providers, ensuring circular proxies
|
||||||
|
* fail (or proxy) properly.
|
||||||
|
*/
|
||||||
|
final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFactory<T> {
|
||||||
|
|
||||||
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
private final Initializable<? extends javax.inject.Provider<? extends T>> initializable;
|
||||||
|
|
||||||
|
public InternalFactoryToInitializableAdapter(
|
||||||
|
Initializable<? extends javax.inject.Provider<? extends T>> initializable,
|
||||||
|
Object source, ProvisionListenerStackCallback<T> provisionCallback) {
|
||||||
|
super(source);
|
||||||
|
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
|
||||||
|
this.initializable = checkNotNull(initializable, "provider");
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
return circularGet(initializable.get(errors), errors, context, dependency,
|
||||||
|
provisionCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
|
||||||
|
Dependency<?> dependency, ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||||
|
try {
|
||||||
|
return super.provision(provider, errors, dependency, constructionContext);
|
||||||
|
} catch (RuntimeException userException) {
|
||||||
|
throw errors.withSource(source).errorInProvider(userException).toException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return initializable.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
final class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> {
|
||||||
|
|
||||||
|
private final Provider<? extends T> provider;
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
public InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
|
||||||
|
this.provider = checkNotNull(provider, "provider");
|
||||||
|
this.source = checkNotNull(source, "source");
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws ErrorsException {
|
||||||
|
// TODO(sameb): Does this need to push state into the context?
|
||||||
|
try {
|
||||||
|
return errors.checkForNull(provider.get(), source, dependency);
|
||||||
|
} catch (RuntimeException userException) {
|
||||||
|
throw errors.withSource(source).errorInProvider(userException).toException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return provider.toString();
|
||||||
|
}
|
||||||
|
}
|
132
src/main/java/com/google/inject/internal/InternalFlags.java
Normal file
132
src/main/java/com/google/inject/internal/InternalFlags.java
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains flags for Guice.
|
||||||
|
*/
|
||||||
|
public class InternalFlags {
|
||||||
|
|
||||||
|
private static final IncludeStackTraceOption INCLUDE_STACK_TRACES
|
||||||
|
= parseIncludeStackTraceOption();
|
||||||
|
|
||||||
|
private static final CustomClassLoadingOption CUSTOM_CLASS_LOADING
|
||||||
|
= parseCustomClassLoadingOption();
|
||||||
|
|
||||||
|
private static final NullableProvidesOption NULLABLE_PROVIDES
|
||||||
|
= parseNullableProvidesOption(NullableProvidesOption.ERROR);
|
||||||
|
|
||||||
|
public static IncludeStackTraceOption getIncludeStackTraceOption() {
|
||||||
|
return INCLUDE_STACK_TRACES;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CustomClassLoadingOption getCustomClassLoadingOption() {
|
||||||
|
return CUSTOM_CLASS_LOADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NullableProvidesOption getNullableProvidesOption() {
|
||||||
|
return NULLABLE_PROVIDES;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IncludeStackTraceOption parseIncludeStackTraceOption() {
|
||||||
|
return getSystemOption("guice_include_stack_traces",
|
||||||
|
IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassLoadingOption parseCustomClassLoadingOption() {
|
||||||
|
return getSystemOption("guice_custom_class_loading",
|
||||||
|
CustomClassLoadingOption.BRIDGE, CustomClassLoadingOption.OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NullableProvidesOption parseNullableProvidesOption(
|
||||||
|
NullableProvidesOption defaultValue) {
|
||||||
|
return getSystemOption("guice_check_nullable_provides_params", defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the system option indicated by the specified key; runs as a privileged action.
|
||||||
|
*
|
||||||
|
* @param name of the system option
|
||||||
|
* @param defaultValue if the option is not set
|
||||||
|
* @return value of the option, defaultValue if not set
|
||||||
|
*/
|
||||||
|
private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue) {
|
||||||
|
return getSystemOption(name, defaultValue, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the system option indicated by the specified key; runs as a privileged action.
|
||||||
|
*
|
||||||
|
* @param name of the system option
|
||||||
|
* @param defaultValue if the option is not set
|
||||||
|
* @param secureValue if the security manager disallows access to the option
|
||||||
|
* @return value of the option, defaultValue if not set, secureValue if no access
|
||||||
|
*/
|
||||||
|
private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue,
|
||||||
|
T secureValue) {
|
||||||
|
Class<T> enumType = defaultValue.getDeclaringClass();
|
||||||
|
String value = null;
|
||||||
|
try {
|
||||||
|
value = AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||||
|
public String run() {
|
||||||
|
return System.getProperty(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue;
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return secureValue;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
//logger.warning(value + " is not a valid flag value for " + name + ". "
|
||||||
|
// + " Values must be one of " + Arrays.asList(enumType.getEnumConstants()));
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options for Guice stack trace collection.
|
||||||
|
*/
|
||||||
|
public enum IncludeStackTraceOption {
|
||||||
|
/**
|
||||||
|
* No stack trace collection
|
||||||
|
*/
|
||||||
|
OFF,
|
||||||
|
/**
|
||||||
|
* Minimum stack trace collection (Default)
|
||||||
|
*/
|
||||||
|
ONLY_FOR_DECLARING_SOURCE,
|
||||||
|
/**
|
||||||
|
* Full stack trace for everything
|
||||||
|
*/
|
||||||
|
COMPLETE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options for Guice custom class loading.
|
||||||
|
*/
|
||||||
|
public enum CustomClassLoadingOption {
|
||||||
|
/**
|
||||||
|
* No custom class loading
|
||||||
|
*/
|
||||||
|
OFF,
|
||||||
|
/**
|
||||||
|
* Automatically bridge between class loaders (Default)
|
||||||
|
*/
|
||||||
|
BRIDGE
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NullableProvidesOption {
|
||||||
|
/**
|
||||||
|
* Ignore null parameters to @Provides methods.
|
||||||
|
*/
|
||||||
|
IGNORE,
|
||||||
|
/**
|
||||||
|
* Warn if null parameters are passed to non-@Nullable parameters of provides methods.
|
||||||
|
*/
|
||||||
|
WARN,
|
||||||
|
/**
|
||||||
|
* Error if null parameters are passed to non-@Nullable parameters of provides parameters
|
||||||
|
*/
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,308 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
import com.google.inject.Stage;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.util.Stopwatch;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a tree of injectors. This is a primary injector, plus child injectors needed for each
|
||||||
|
* {@code Binder.newPrivateBinder() private environment}. The primary injector is not necessarily a
|
||||||
|
* top-level injector.
|
||||||
|
*
|
||||||
|
* <p>Injector construction happens in two phases.
|
||||||
|
* <ol>
|
||||||
|
* <li>Static building. In this phase, we interpret commands, create bindings, and inspect
|
||||||
|
* dependencies. During this phase, we hold a lock to ensure consistency with parent injectors.
|
||||||
|
* No user code is executed in this phase.</li>
|
||||||
|
* <li>Dynamic injection. In this phase, we call user code. We inject members that requested
|
||||||
|
* injection. This may require user's objects be created and their providers be called. And we
|
||||||
|
* create eager singletons. In this phase, user code may have started other threads. This phase
|
||||||
|
* is not executed for injectors created using {@link Stage#TOOL the tool stage}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class InternalInjectorCreator {
|
||||||
|
|
||||||
|
private final Stopwatch stopwatch = new Stopwatch();
|
||||||
|
private final Errors errors = new Errors();
|
||||||
|
|
||||||
|
private final Initializer initializer = new Initializer();
|
||||||
|
private final ProcessedBindingData bindingData;
|
||||||
|
private final InjectionRequestProcessor injectionRequestProcessor;
|
||||||
|
|
||||||
|
private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
|
||||||
|
private List<InjectorShell> shells;
|
||||||
|
|
||||||
|
public InternalInjectorCreator() {
|
||||||
|
injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
|
||||||
|
bindingData = new ProcessedBindingData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalInjectorCreator stage(Stage stage) {
|
||||||
|
shellBuilder.stage(stage);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
|
||||||
|
* stage to the stage of {@code parent}.
|
||||||
|
*/
|
||||||
|
public InternalInjectorCreator parentInjector(InjectorImpl parent) {
|
||||||
|
shellBuilder.parent(parent);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalInjectorCreator addModules(Iterable<? extends Module> modules) {
|
||||||
|
shellBuilder.addModules(modules);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector build() {
|
||||||
|
|
||||||
|
// Synchronize while we're building up the bindings and other injector state. This ensures that
|
||||||
|
// the JIT bindings in the parent injector don't change while we're being built
|
||||||
|
synchronized (shellBuilder.lock()) {
|
||||||
|
shells = shellBuilder.build(initializer, bindingData, stopwatch, errors);
|
||||||
|
stopwatch.resetAndLog("Injector construction");
|
||||||
|
|
||||||
|
initializeStatically();
|
||||||
|
}
|
||||||
|
|
||||||
|
injectDynamically();
|
||||||
|
|
||||||
|
if (shellBuilder.getStage() == Stage.TOOL) {
|
||||||
|
// wrap the primaryInjector in a ToolStageInjector
|
||||||
|
// to prevent non-tool-friendy methods from being called.
|
||||||
|
return new ToolStageInjector(primaryInjector());
|
||||||
|
} else {
|
||||||
|
return primaryInjector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and validate everything.
|
||||||
|
*/
|
||||||
|
private void initializeStatically() {
|
||||||
|
bindingData.initializeBindings();
|
||||||
|
stopwatch.resetAndLog("Binding initialization");
|
||||||
|
|
||||||
|
for (InjectorShell shell : shells) {
|
||||||
|
shell.getInjector().index();
|
||||||
|
}
|
||||||
|
stopwatch.resetAndLog("Binding indexing");
|
||||||
|
|
||||||
|
injectionRequestProcessor.process(shells);
|
||||||
|
stopwatch.resetAndLog("Collecting injection requests");
|
||||||
|
|
||||||
|
bindingData.runCreationListeners(errors);
|
||||||
|
stopwatch.resetAndLog("Binding validation");
|
||||||
|
|
||||||
|
injectionRequestProcessor.validate();
|
||||||
|
stopwatch.resetAndLog("Static validation");
|
||||||
|
|
||||||
|
initializer.validateOustandingInjections(errors);
|
||||||
|
stopwatch.resetAndLog("Instance member validation");
|
||||||
|
|
||||||
|
new LookupProcessor(errors).process(shells);
|
||||||
|
for (InjectorShell shell : shells) {
|
||||||
|
((DeferredLookups) shell.getInjector().lookups).initialize(errors);
|
||||||
|
}
|
||||||
|
stopwatch.resetAndLog("Provider verification");
|
||||||
|
|
||||||
|
for (InjectorShell shell : shells) {
|
||||||
|
if (!shell.getElements().isEmpty()) {
|
||||||
|
throw new AssertionError("Failed to execute " + shell.getElements());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.throwCreationExceptionIfErrorsExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the injector being constructed. This is not necessarily the root injector.
|
||||||
|
*/
|
||||||
|
private Injector primaryInjector() {
|
||||||
|
return shells.get(0).getInjector();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject everything that can be injected. This method is intentionally not synchronized. If we
|
||||||
|
* locked while injecting members (ie. running user code), things would deadlock should the user
|
||||||
|
* code build a just-in-time binding from another thread.
|
||||||
|
*/
|
||||||
|
private void injectDynamically() {
|
||||||
|
injectionRequestProcessor.injectMembers();
|
||||||
|
stopwatch.resetAndLog("Static member injection");
|
||||||
|
|
||||||
|
initializer.injectAll(errors);
|
||||||
|
stopwatch.resetAndLog("Instance injection");
|
||||||
|
errors.throwCreationExceptionIfErrorsExist();
|
||||||
|
|
||||||
|
if (shellBuilder.getStage() != Stage.TOOL) {
|
||||||
|
for (InjectorShell shell : shells) {
|
||||||
|
loadEagerSingletons(shell.getInjector(), shellBuilder.getStage(), errors);
|
||||||
|
}
|
||||||
|
stopwatch.resetAndLog("Preloading singletons");
|
||||||
|
}
|
||||||
|
errors.throwCreationExceptionIfErrorsExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads eager singletons, or all singletons if we're in Stage.PRODUCTION. Bindings discovered
|
||||||
|
* while we're binding these singletons are not be eager.
|
||||||
|
*/
|
||||||
|
void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
|
||||||
|
@SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
|
||||||
|
Iterable<BindingImpl<?>> candidateBindings = ImmutableList.copyOf(Iterables.concat(
|
||||||
|
(Collection) injector.state.getExplicitBindingsThisLevel().values(),
|
||||||
|
injector.jitBindings.values()));
|
||||||
|
for (final BindingImpl<?> binding : candidateBindings) {
|
||||||
|
if (isEagerSingleton(injector, binding, stage)) {
|
||||||
|
try {
|
||||||
|
injector.callInContext(new ContextualCallable<Void>() {
|
||||||
|
Dependency<?> dependency = Dependency.get(binding.getKey());
|
||||||
|
|
||||||
|
public Void call(InternalContext context) {
|
||||||
|
Dependency previous = context.pushDependency(dependency, binding.getSource());
|
||||||
|
Errors errorsForBinding = errors.withSource(dependency);
|
||||||
|
try {
|
||||||
|
binding.getInternalFactory().get(errorsForBinding, context, dependency, false);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errorsForBinding.merge(e.getErrors());
|
||||||
|
} finally {
|
||||||
|
context.popStateAndSetDependency(previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEagerSingleton(InjectorImpl injector, BindingImpl<?> binding, Stage stage) {
|
||||||
|
if (binding.getScoping().isEagerSingleton(stage)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle a corner case where a child injector links to a binding in a parent injector, and
|
||||||
|
// that binding is singleton. We won't catch this otherwise because we only iterate the child's
|
||||||
|
// bindings.
|
||||||
|
if (binding instanceof LinkedBindingImpl) {
|
||||||
|
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
|
||||||
|
return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Injector} exposed to users in {@link Stage#TOOL}.
|
||||||
|
*/
|
||||||
|
static class ToolStageInjector implements Injector {
|
||||||
|
private final Injector delegateInjector;
|
||||||
|
|
||||||
|
ToolStageInjector(Injector delegateInjector) {
|
||||||
|
this.delegateInjector = delegateInjector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectMembers(Object o) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.injectMembers(Object) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Key<?>, Binding<?>> getBindings() {
|
||||||
|
return this.delegateInjector.getBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Key<?>, Binding<?>> getAllBindings() {
|
||||||
|
return this.delegateInjector.getAllBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Binding<T> getBinding(Key<T> key) {
|
||||||
|
return this.delegateInjector.getBinding(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Binding<T> getBinding(Class<T> type) {
|
||||||
|
return this.delegateInjector.getBinding(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Binding<T> getExistingBinding(Key<T> key) {
|
||||||
|
return this.delegateInjector.getExistingBinding(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
|
||||||
|
return this.delegateInjector.findBindingsByType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector getParent() {
|
||||||
|
return delegateInjector.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector createChildInjector(Iterable<? extends Module> modules) {
|
||||||
|
return delegateInjector.createChildInjector(modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector createChildInjector(Module... modules) {
|
||||||
|
return delegateInjector.createChildInjector(modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
|
||||||
|
return delegateInjector.getScopeBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TypeConverterBinding> getTypeConverterBindings() {
|
||||||
|
return delegateInjector.getTypeConverterBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Provider<T> getProvider(Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getInstance(Key<T> key) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getInstance(Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.HasDependencies;
|
||||||
|
import com.google.inject.spi.LinkedKeyBinding;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class LinkedBindingImpl<T> extends BindingImpl<T> implements LinkedKeyBinding<T>, HasDependencies {
|
||||||
|
|
||||||
|
final Key<? extends T> targetKey;
|
||||||
|
|
||||||
|
public LinkedBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
|
Key<? extends T> targetKey) {
|
||||||
|
super(injector, key, source, internalFactory, scoping);
|
||||||
|
this.targetKey = targetKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedBindingImpl(Object source, Key<T> key, Scoping scoping, Key<? extends T> targetKey) {
|
||||||
|
super(source, key, scoping);
|
||||||
|
this.targetKey = targetKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key<? extends T> getLinkedKey() {
|
||||||
|
return targetKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(targetKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
|
return new LinkedBindingImpl<T>(getSource(), getKey(), scoping, targetKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
|
return new LinkedBindingImpl<T>(getSource(), key, getScoping(), targetKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTo(Binder binder) {
|
||||||
|
getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).to(getLinkedKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(LinkedKeyBinding.class)
|
||||||
|
.add("key", getKey())
|
||||||
|
.add("source", getSource())
|
||||||
|
.add("scope", getScoping())
|
||||||
|
.add("target", targetKey)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof LinkedBindingImpl) {
|
||||||
|
LinkedBindingImpl<?> o = (LinkedBindingImpl<?>) obj;
|
||||||
|
return getKey().equals(o.getKey())
|
||||||
|
&& getScoping().equals(o.getScoping())
|
||||||
|
&& Objects.equal(targetKey, o.targetKey);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getKey(), getScoping(), targetKey);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.HasDependencies;
|
||||||
|
import com.google.inject.spi.ProviderKeyBinding;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class LinkedProviderBindingImpl<T>
|
||||||
|
extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize {
|
||||||
|
|
||||||
|
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||||
|
final DelayedInitialize delayedInitializer;
|
||||||
|
|
||||||
|
private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey,
|
||||||
|
DelayedInitialize delayedInitializer) {
|
||||||
|
super(injector, key, source, internalFactory, scoping);
|
||||||
|
this.providerKey = providerKey;
|
||||||
|
this.delayedInitializer = delayedInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||||
|
InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||||
|
this(injector, key, source, internalFactory, scoping, providerKey, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedProviderBindingImpl(Object source, Key<T> key, Scoping scoping,
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||||
|
super(source, key, scoping);
|
||||||
|
this.providerKey = providerKey;
|
||||||
|
this.delayedInitializer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> LinkedProviderBindingImpl<T> createWithInitializer(InjectorImpl injector, Key<T> key,
|
||||||
|
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
|
||||||
|
Key<? extends javax.inject.Provider<? extends T>> providerKey,
|
||||||
|
DelayedInitialize delayedInitializer) {
|
||||||
|
return new LinkedProviderBindingImpl<T>(injector, key, source, internalFactory, scoping,
|
||||||
|
providerKey, delayedInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
|
||||||
|
return providerKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
|
if (delayedInitializer != null) {
|
||||||
|
delayedInitializer.initialize(injector, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
|
return new LinkedProviderBindingImpl<T>(getSource(), getKey(), scoping, providerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
|
return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTo(Binder binder) {
|
||||||
|
getScoping().applyTo(binder.withSource(getSource())
|
||||||
|
.bind(getKey()).toProvider(getProviderKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(ProviderKeyBinding.class)
|
||||||
|
.add("key", getKey())
|
||||||
|
.add("source", getSource())
|
||||||
|
.add("scope", getScoping())
|
||||||
|
.add("provider", providerKey)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof LinkedProviderBindingImpl) {
|
||||||
|
LinkedProviderBindingImpl<?> o = (LinkedProviderBindingImpl<?>) obj;
|
||||||
|
return getKey().equals(o.getKey())
|
||||||
|
&& getScoping().equals(o.getScoping())
|
||||||
|
&& Objects.equal(providerKey, o.providerKey);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getKey(), getScoping(), providerKey);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.ProvisionListenerBinding;
|
||||||
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@code Binder#bindListener} commands.
|
||||||
|
*/
|
||||||
|
final class ListenerBindingProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
ListenerBindingProcessor(Errors errors) {
|
||||||
|
super(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(TypeListenerBinding binding) {
|
||||||
|
injector.state.addTypeListener(binding);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ProvisionListenerBinding binding) {
|
||||||
|
injector.state.addProvisionListener(binding);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.spi.MembersInjectorLookup;
|
||||||
|
import com.google.inject.spi.ProviderLookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@code Binder.getProvider} and {@code Binder.getMembersInjector(TypeLiteral)} commands.
|
||||||
|
*/
|
||||||
|
final class LookupProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
LookupProcessor(Errors errors) {
|
||||||
|
super(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Boolean visit(MembersInjectorLookup<T> lookup) {
|
||||||
|
try {
|
||||||
|
MembersInjector<T> membersInjector
|
||||||
|
= injector.membersInjectorStore.get(lookup.getType(), errors);
|
||||||
|
lookup.initializeDelegate(membersInjector);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors()); // TODO: source
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Boolean visit(ProviderLookup<T> lookup) {
|
||||||
|
// ensure the provider can be created
|
||||||
|
try {
|
||||||
|
Provider<T> provider = injector.getProviderOrThrow(lookup.getDependency(), errors);
|
||||||
|
lookup.initializeDelegate(provider);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors()); // TODO: source
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
17
src/main/java/com/google/inject/internal/Lookups.java
Normal file
17
src/main/java/com/google/inject/internal/Lookups.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessors for providers and members injectors. The returned values will not be functional until
|
||||||
|
* the injector has been created.
|
||||||
|
*/
|
||||||
|
interface Lookups {
|
||||||
|
|
||||||
|
<T> Provider<T> getProvider(Key<T> key);
|
||||||
|
|
||||||
|
<T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type);
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.MembersInjector;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||||
|
import com.google.inject.spi.InjectionListener;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects members of instances of a given type.
|
||||||
|
*/
|
||||||
|
final class MembersInjectorImpl<T> implements MembersInjector<T> {
|
||||||
|
private final TypeLiteral<T> typeLiteral;
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private final ImmutableList<SingleMemberInjector> memberInjectors;
|
||||||
|
private final ImmutableSet<MembersInjector<? super T>> userMembersInjectors;
|
||||||
|
private final ImmutableSet<InjectionListener<? super T>> injectionListeners;
|
||||||
|
|
||||||
|
MembersInjectorImpl(InjectorImpl injector, TypeLiteral<T> typeLiteral,
|
||||||
|
EncounterImpl<T> encounter, ImmutableList<SingleMemberInjector> memberInjectors) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.typeLiteral = typeLiteral;
|
||||||
|
this.memberInjectors = memberInjectors;
|
||||||
|
this.userMembersInjectors = encounter.getMembersInjectors();
|
||||||
|
this.injectionListeners = encounter.getInjectionListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<SingleMemberInjector> getMemberInjectors() {
|
||||||
|
return memberInjectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectMembers(T instance) {
|
||||||
|
Errors errors = new Errors(typeLiteral);
|
||||||
|
try {
|
||||||
|
injectAndNotify(instance, errors, null, null, typeLiteral, false);
|
||||||
|
} catch (ErrorsException e) {
|
||||||
|
errors.merge(e.getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.throwProvisionExceptionIfErrorsExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
void injectAndNotify(final T instance,
|
||||||
|
final Errors errors,
|
||||||
|
final Key<T> key, // possibly null!
|
||||||
|
final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
|
||||||
|
final Object source,
|
||||||
|
final boolean toolableOnly) throws ErrorsException {
|
||||||
|
if (instance == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
injector.callInContext(new ContextualCallable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call(final InternalContext context) throws ErrorsException {
|
||||||
|
context.pushState(key, source);
|
||||||
|
try {
|
||||||
|
if (provisionCallback != null && provisionCallback.hasListeners()) {
|
||||||
|
provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
||||||
|
@Override
|
||||||
|
public T call() {
|
||||||
|
injectMembers(instance, errors, context, toolableOnly);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
injectMembers(instance, errors, context, toolableOnly);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
context.popState();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: We *could* notify listeners too here,
|
||||||
|
// but it's not clear if we want to. There's no way to know
|
||||||
|
// if a MembersInjector from the usersMemberInjector list wants
|
||||||
|
// toolable injections, so do we really want to notify
|
||||||
|
// about injection? (We could take a strategy of only notifying
|
||||||
|
// if atleast one InjectionPoint was toolable, in which case
|
||||||
|
// the above callInContext could return 'true' if it injected
|
||||||
|
// anything.)
|
||||||
|
if (!toolableOnly) {
|
||||||
|
notifyListeners(instance, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyListeners(T instance, Errors errors) throws ErrorsException {
|
||||||
|
int numErrorsBefore = errors.size();
|
||||||
|
for (InjectionListener<? super T> injectionListener : injectionListeners) {
|
||||||
|
try {
|
||||||
|
injectionListener.afterInjection(instance);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
errors.errorNotifyingInjectionListener(injectionListener, typeLiteral, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
void injectMembers(T t, Errors errors, InternalContext context, boolean toolableOnly) {
|
||||||
|
// optimization: use manual for/each to save allocating an iterator here
|
||||||
|
for (int i = 0, size = memberInjectors.size(); i < size; i++) {
|
||||||
|
SingleMemberInjector injector = memberInjectors.get(i);
|
||||||
|
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
|
||||||
|
injector.inject(errors, context, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: There's no way to know if a user's MembersInjector wants toolable injections.
|
||||||
|
if (!toolableOnly) {
|
||||||
|
for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) {
|
||||||
|
try {
|
||||||
|
userMembersInjector.injectMembers(t);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
errors.errorInUserInjector(userMembersInjector, typeLiteral, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MembersInjector<" + typeLiteral + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableSet<InjectionPoint> getInjectionPoints() {
|
||||||
|
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||||
|
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||||
|
builder.add(memberInjector.getInjectionPoint());
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.inject.ConfigurationException;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
import com.google.inject.spi.TypeListener;
|
||||||
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Members injectors by type.
|
||||||
|
*/
|
||||||
|
final class MembersInjectorStore {
|
||||||
|
private final InjectorImpl injector;
|
||||||
|
private final ImmutableList<TypeListenerBinding> typeListenerBindings;
|
||||||
|
|
||||||
|
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache
|
||||||
|
= new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() {
|
||||||
|
@Override
|
||||||
|
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
|
return createWithListeners(type, errors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MembersInjectorStore(InjectorImpl injector,
|
||||||
|
List<TypeListenerBinding> typeListenerBindings) {
|
||||||
|
this.injector = injector;
|
||||||
|
this.typeListenerBindings = ImmutableList.copyOf(typeListenerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if any type listeners are installed. Other code may take shortcuts when there
|
||||||
|
* aren't any type listeners.
|
||||||
|
*/
|
||||||
|
public boolean hasTypeListeners() {
|
||||||
|
return !typeListenerBindings.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new complete members injector with injection listeners registered.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked") // the MembersInjector type always agrees with the passed type
|
||||||
|
public <T> MembersInjectorImpl<T> get(TypeLiteral<T> key, Errors errors) throws ErrorsException {
|
||||||
|
return (MembersInjectorImpl<T>) cache.get(key, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purges a type literal from the cache. Use this only if the type is not actually valid for
|
||||||
|
* binding and needs to be purged. (See issue 319 and
|
||||||
|
* ImplicitBindingTest#testCircularJitBindingsLeaveNoResidue and
|
||||||
|
* #testInstancesRequestingProvidersForThemselvesWithChildInjectors for examples of when this is
|
||||||
|
* necessary.)
|
||||||
|
*
|
||||||
|
* Returns true if the type was stored in the cache, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean remove(TypeLiteral<?> type) {
|
||||||
|
return cache.remove(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new members injector and attaches both injection listeners and method aspects.
|
||||||
|
*/
|
||||||
|
private <T> MembersInjectorImpl<T> createWithListeners(TypeLiteral<T> type, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
|
int numErrorsBefore = errors.size();
|
||||||
|
|
||||||
|
Set<InjectionPoint> injectionPoints;
|
||||||
|
try {
|
||||||
|
injectionPoints = InjectionPoint.forInstanceMethodsAndFields(type);
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
errors.merge(e.getErrorMessages());
|
||||||
|
injectionPoints = e.getPartialValue();
|
||||||
|
}
|
||||||
|
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
|
||||||
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
|
||||||
|
EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups);
|
||||||
|
Set<TypeListener> alreadySeenListeners = Sets.newHashSet();
|
||||||
|
for (TypeListenerBinding binding : typeListenerBindings) {
|
||||||
|
TypeListener typeListener = binding.getListener();
|
||||||
|
if (!alreadySeenListeners.contains(typeListener) && binding.getTypeMatcher().matches(type)) {
|
||||||
|
alreadySeenListeners.add(typeListener);
|
||||||
|
try {
|
||||||
|
typeListener.hear(type, encounter);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
errors.errorNotifyingTypeListener(binding, type, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encounter.invalidate();
|
||||||
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
|
|
||||||
|
return new MembersInjectorImpl<T>(injector, type, encounter, injectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the injectors for the specified injection points.
|
||||||
|
*/
|
||||||
|
ImmutableList<SingleMemberInjector> getInjectors(
|
||||||
|
Set<InjectionPoint> injectionPoints, Errors errors) {
|
||||||
|
List<SingleMemberInjector> injectors = Lists.newArrayList();
|
||||||
|
for (InjectionPoint injectionPoint : injectionPoints) {
|
||||||
|
try {
|
||||||
|
Errors errorsForMember = injectionPoint.isOptional()
|
||||||
|
? new Errors(injectionPoint)
|
||||||
|
: errors.withSource(injectionPoint);
|
||||||
|
SingleMemberInjector injector = injectionPoint.getMember() instanceof Field
|
||||||
|
? new SingleFieldInjector(this.injector, injectionPoint, errorsForMember)
|
||||||
|
: new SingleMethodInjector(this.injector, injectionPoint, errorsForMember);
|
||||||
|
injectors.add(injector);
|
||||||
|
} catch (ErrorsException ignoredForNow) {
|
||||||
|
// ignored for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ImmutableList.copyOf(injectors);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@code Binder.addError} commands.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class MessageProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
MessageProcessor(Errors errors) {
|
||||||
|
super(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRootMessage(Throwable t) {
|
||||||
|
Throwable cause = t.getCause();
|
||||||
|
return cause == null ? t.toString() : getRootMessage(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(Message message) {
|
||||||
|
if (message.getCause() != null) {
|
||||||
|
String rootMessage = getRootMessage(message.getCause());
|
||||||
|
/*logger.log(Level.INFO,
|
||||||
|
"An exception was caught and reported. Message: " + rootMessage,
|
||||||
|
message.getCause());*/
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.addMessage(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@code Binder.scanModulesForAnnotatedMethods} commands.
|
||||||
|
*/
|
||||||
|
final class ModuleAnnotatedMethodScannerProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
ModuleAnnotatedMethodScannerProcessor(Errors errors) {
|
||||||
|
super(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(ModuleAnnotatedMethodScannerBinding command) {
|
||||||
|
injector.state.addScanner(command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
535
src/main/java/com/google/inject/internal/MoreTypes.java
Normal file
535
src/main/java/com/google/inject/internal/MoreTypes.java
Normal file
|
@ -0,0 +1,535 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.inject.ConfigurationException;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.util.Types;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.GenericArrayType;
|
||||||
|
import java.lang.reflect.GenericDeclaration;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.lang.reflect.TypeVariable;
|
||||||
|
import java.lang.reflect.WildcardType;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static methods for working with types that we aren't publishing in the
|
||||||
|
* public {@code Types} API.
|
||||||
|
*/
|
||||||
|
public class MoreTypes {
|
||||||
|
|
||||||
|
public static final Type[] EMPTY_TYPE_ARRAY = new Type[]{};
|
||||||
|
|
||||||
|
private MoreTypes() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<TypeLiteral<?>, TypeLiteral<?>> PRIMITIVE_TO_WRAPPER
|
||||||
|
= new ImmutableMap.Builder<TypeLiteral<?>, TypeLiteral<?>>()
|
||||||
|
.put(TypeLiteral.get(boolean.class), TypeLiteral.get(Boolean.class))
|
||||||
|
.put(TypeLiteral.get(byte.class), TypeLiteral.get(Byte.class))
|
||||||
|
.put(TypeLiteral.get(short.class), TypeLiteral.get(Short.class))
|
||||||
|
.put(TypeLiteral.get(int.class), TypeLiteral.get(Integer.class))
|
||||||
|
.put(TypeLiteral.get(long.class), TypeLiteral.get(Long.class))
|
||||||
|
.put(TypeLiteral.get(float.class), TypeLiteral.get(Float.class))
|
||||||
|
.put(TypeLiteral.get(double.class), TypeLiteral.get(Double.class))
|
||||||
|
.put(TypeLiteral.get(char.class), TypeLiteral.get(Character.class))
|
||||||
|
.put(TypeLiteral.get(void.class), TypeLiteral.get(Void.class))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a key that doesn't hold any references to parent classes.
|
||||||
|
* This is necessary for anonymous keys, so ensure we don't hold a ref
|
||||||
|
* to the containing module (or class) forever.
|
||||||
|
*/
|
||||||
|
public static <T> Key<T> canonicalizeKey(Key<T> key) {
|
||||||
|
// If we know this isn't a subclass, return as-is.
|
||||||
|
// Otherwise, recreate the key to avoid the subclass
|
||||||
|
if (key.getClass() == Key.class) {
|
||||||
|
return key;
|
||||||
|
} else if (key.getAnnotation() != null) {
|
||||||
|
return Key.get(key.getTypeLiteral(), key.getAnnotation());
|
||||||
|
} else if (key.getAnnotationType() != null) {
|
||||||
|
return Key.get(key.getTypeLiteral(), key.getAnnotationType());
|
||||||
|
} else {
|
||||||
|
return Key.get(key.getTypeLiteral());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an type that's appropriate for use in a key.
|
||||||
|
*
|
||||||
|
* <p>If the raw type of {@code typeLiteral} is a {@code javax.inject.Provider}, this returns a
|
||||||
|
* {@code com.google.inject.Provider} with the same type parameters.
|
||||||
|
*
|
||||||
|
* <p>If the type is a primitive, the corresponding wrapper type will be returned.
|
||||||
|
*
|
||||||
|
* @throws ConfigurationException if {@code type} contains a type variable
|
||||||
|
*/
|
||||||
|
public static <T> TypeLiteral<T> canonicalizeForKey(TypeLiteral<T> typeLiteral) {
|
||||||
|
Type type = typeLiteral.getType();
|
||||||
|
if (!isFullySpecified(type)) {
|
||||||
|
Errors errors = new Errors().keyNotFullySpecified(typeLiteral);
|
||||||
|
throw new ConfigurationException(errors.getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeLiteral.getRawType() == javax.inject.Provider.class) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||||
|
|
||||||
|
// the following casts are generally unsafe, but com.google.inject.Provider extends
|
||||||
|
// javax.inject.Provider and is covariant
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeLiteral<T> guiceProviderType = (TypeLiteral<T>) TypeLiteral.get(
|
||||||
|
Types.providerOf(parameterizedType.getActualTypeArguments()[0]));
|
||||||
|
return guiceProviderType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeLiteral<T> wrappedPrimitives = (TypeLiteral<T>) PRIMITIVE_TO_WRAPPER.get(typeLiteral);
|
||||||
|
if (wrappedPrimitives != null) {
|
||||||
|
return wrappedPrimitives;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we know this isn't a subclass, return as-is.
|
||||||
|
if (typeLiteral.getClass() == TypeLiteral.class) {
|
||||||
|
return typeLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recreate the TypeLiteral to avoid anonymous TypeLiterals from holding refs to their
|
||||||
|
// surrounding classes.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeLiteral<T> recreated = (TypeLiteral<T>) TypeLiteral.get(typeLiteral.getType());
|
||||||
|
return recreated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if {@code type} is free from type variables.
|
||||||
|
*/
|
||||||
|
private static boolean isFullySpecified(Type type) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (type instanceof CompositeType) {
|
||||||
|
return ((CompositeType) type).isFullySpecified();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return !(type instanceof TypeVariable) && ((CompositeType) canonicalize(type)).isFullySpecified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a type that is functionally equal but not necessarily equal
|
||||||
|
* according to {@link Object#equals(Object) Object.equals()}.
|
||||||
|
*/
|
||||||
|
public static Type canonicalize(Type type) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
Class<?> c = (Class<?>) type;
|
||||||
|
return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
|
||||||
|
|
||||||
|
} else if (type instanceof CompositeType) {
|
||||||
|
return type;
|
||||||
|
|
||||||
|
} else if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType p = (ParameterizedType) type;
|
||||||
|
return new ParameterizedTypeImpl(p.getOwnerType(),
|
||||||
|
p.getRawType(), p.getActualTypeArguments());
|
||||||
|
|
||||||
|
} else if (type instanceof GenericArrayType) {
|
||||||
|
GenericArrayType g = (GenericArrayType) type;
|
||||||
|
return new GenericArrayTypeImpl(g.getGenericComponentType());
|
||||||
|
|
||||||
|
} else if (type instanceof WildcardType) {
|
||||||
|
WildcardType w = (WildcardType) type;
|
||||||
|
return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> getRawType(Type type) {
|
||||||
|
if (type instanceof Class<?>) {
|
||||||
|
// type is a normal class.
|
||||||
|
return (Class<?>) type;
|
||||||
|
|
||||||
|
} else if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||||
|
|
||||||
|
// I'm not exactly sure why getRawType() returns Type instead of Class.
|
||||||
|
// Neal isn't either but suspects some pathological case related
|
||||||
|
// to nested classes exists.
|
||||||
|
Type rawType = parameterizedType.getRawType();
|
||||||
|
checkArgument(rawType instanceof Class,
|
||||||
|
"Expected a Class, but <%s> is of type %s", type, type.getClass().getName());
|
||||||
|
return (Class<?>) rawType;
|
||||||
|
|
||||||
|
} else if (type instanceof GenericArrayType) {
|
||||||
|
Type componentType = ((GenericArrayType) type).getGenericComponentType();
|
||||||
|
return Array.newInstance(getRawType(componentType), 0).getClass();
|
||||||
|
|
||||||
|
} else if (type instanceof TypeVariable) {
|
||||||
|
// we could use the variable's bounds, but that'll won't work if there are multiple.
|
||||||
|
// having a raw type that's more general than necessary is okay
|
||||||
|
return Object.class;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
|
||||||
|
+ "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if {@code a} and {@code b} are equal.
|
||||||
|
*/
|
||||||
|
public static boolean equals(Type a, Type b) {
|
||||||
|
if (a == b) {
|
||||||
|
// also handles (a == null && b == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (a instanceof Class) {
|
||||||
|
// Class already specifies equals().
|
||||||
|
return a.equals(b);
|
||||||
|
|
||||||
|
} else if (a instanceof ParameterizedType) {
|
||||||
|
if (!(b instanceof ParameterizedType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: save a .clone() call
|
||||||
|
ParameterizedType pa = (ParameterizedType) a;
|
||||||
|
ParameterizedType pb = (ParameterizedType) b;
|
||||||
|
return Objects.equal(pa.getOwnerType(), pb.getOwnerType())
|
||||||
|
&& pa.getRawType().equals(pb.getRawType())
|
||||||
|
&& Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
|
||||||
|
|
||||||
|
} else if (a instanceof GenericArrayType) {
|
||||||
|
if (!(b instanceof GenericArrayType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericArrayType ga = (GenericArrayType) a;
|
||||||
|
GenericArrayType gb = (GenericArrayType) b;
|
||||||
|
return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
|
||||||
|
|
||||||
|
} else if (a instanceof WildcardType) {
|
||||||
|
if (!(b instanceof WildcardType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WildcardType wa = (WildcardType) a;
|
||||||
|
WildcardType wb = (WildcardType) b;
|
||||||
|
return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
|
||||||
|
&& Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
|
||||||
|
|
||||||
|
} else if (a instanceof TypeVariable) {
|
||||||
|
if (!(b instanceof TypeVariable)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TypeVariable<?> va = (TypeVariable) a;
|
||||||
|
TypeVariable<?> vb = (TypeVariable) b;
|
||||||
|
return va.getGenericDeclaration().equals(vb.getGenericDeclaration())
|
||||||
|
&& va.getName().equals(vb.getName());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// This isn't a type we support. Could be a generic array type, wildcard type, etc.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int hashCodeOrZero(Object o) {
|
||||||
|
return o != null ? o.hashCode() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String typeToString(Type type) {
|
||||||
|
return type instanceof Class ? ((Class) type).getName() : type.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the generic supertype for {@code type}. For example, given a class {@code IntegerSet},
|
||||||
|
* the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the result
|
||||||
|
* when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
|
||||||
|
*/
|
||||||
|
public static Type getGenericSupertype(Type type, Class<?> rawType, Class<?> toResolve) {
|
||||||
|
if (toResolve == rawType) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we skip searching through interfaces if unknown is an interface
|
||||||
|
if (toResolve.isInterface()) {
|
||||||
|
Class[] interfaces = rawType.getInterfaces();
|
||||||
|
for (int i = 0, length = interfaces.length; i < length; i++) {
|
||||||
|
if (interfaces[i] == toResolve) {
|
||||||
|
return rawType.getGenericInterfaces()[i];
|
||||||
|
} else if (toResolve.isAssignableFrom(interfaces[i])) {
|
||||||
|
return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check our supertypes
|
||||||
|
if (!rawType.isInterface()) {
|
||||||
|
while (rawType != Object.class) {
|
||||||
|
Class<?> rawSupertype = rawType.getSuperclass();
|
||||||
|
if (rawSupertype == toResolve) {
|
||||||
|
return rawType.getGenericSuperclass();
|
||||||
|
} else if (toResolve.isAssignableFrom(rawSupertype)) {
|
||||||
|
return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
|
||||||
|
}
|
||||||
|
rawType = rawSupertype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can't resolve this further
|
||||||
|
return toResolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) {
|
||||||
|
Class<?> declaredByRaw = declaringClassOf(unknown);
|
||||||
|
|
||||||
|
// we can't reduce this further
|
||||||
|
if (declaredByRaw == null) {
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type declaredBy = getGenericSupertype(type, rawType, declaredByRaw);
|
||||||
|
if (declaredBy instanceof ParameterizedType) {
|
||||||
|
int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
|
||||||
|
return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int indexOf(Object[] array, Object toFind) {
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
if (toFind.equals(array[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
|
||||||
|
* a class.
|
||||||
|
*/
|
||||||
|
private static Class<?> declaringClassOf(TypeVariable typeVariable) {
|
||||||
|
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
|
||||||
|
return genericDeclaration instanceof Class
|
||||||
|
? (Class<?>) genericDeclaration
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkNotPrimitive(Type type, String use) {
|
||||||
|
checkArgument(!(type instanceof Class<?>) || !((Class) type).isPrimitive(),
|
||||||
|
"Primitive types are not allowed in %s: %s", use, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type formed from other types, such as arrays, parameterized types or wildcard types
|
||||||
|
*/
|
||||||
|
private interface CompositeType {
|
||||||
|
/**
|
||||||
|
* Returns true if there are no type variables in this type.
|
||||||
|
*/
|
||||||
|
boolean isFullySpecified();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ParameterizedTypeImpl
|
||||||
|
implements ParameterizedType, CompositeType {
|
||||||
|
private final Type ownerType;
|
||||||
|
private final Type rawType;
|
||||||
|
private final Type[] typeArguments;
|
||||||
|
|
||||||
|
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
|
||||||
|
// require an owner type if the raw type needs it
|
||||||
|
ensureOwnerType(ownerType, rawType);
|
||||||
|
|
||||||
|
this.ownerType = ownerType == null ? null : canonicalize(ownerType);
|
||||||
|
this.rawType = canonicalize(rawType);
|
||||||
|
this.typeArguments = typeArguments.clone();
|
||||||
|
for (int t = 0; t < this.typeArguments.length; t++) {
|
||||||
|
checkNotNull(this.typeArguments[t], "type parameter");
|
||||||
|
checkNotPrimitive(this.typeArguments[t], "type parameters");
|
||||||
|
this.typeArguments[t] = canonicalize(this.typeArguments[t]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureOwnerType(Type ownerType, Type rawType) {
|
||||||
|
if (rawType instanceof Class<?>) {
|
||||||
|
Class rawTypeAsClass = (Class) rawType;
|
||||||
|
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
|
||||||
|
"No owner type for enclosed %s", rawType);
|
||||||
|
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
|
||||||
|
"Owner type for unenclosed %s", rawType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] getActualTypeArguments() {
|
||||||
|
return typeArguments.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getRawType() {
|
||||||
|
return rawType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getOwnerType() {
|
||||||
|
return ownerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFullySpecified() {
|
||||||
|
if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MoreTypes.isFullySpecified(rawType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Type type : typeArguments) {
|
||||||
|
if (!MoreTypes.isFullySpecified(type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return other instanceof ParameterizedType
|
||||||
|
&& MoreTypes.equals(this, (ParameterizedType) other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(typeArguments)
|
||||||
|
^ rawType.hashCode()
|
||||||
|
^ hashCodeOrZero(ownerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
|
||||||
|
stringBuilder.append(typeToString(rawType));
|
||||||
|
|
||||||
|
if (typeArguments.length == 0) {
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
stringBuilder.append("<").append(typeToString(typeArguments[0]));
|
||||||
|
for (int i = 1; i < typeArguments.length; i++) {
|
||||||
|
stringBuilder.append(", ").append(typeToString(typeArguments[i]));
|
||||||
|
}
|
||||||
|
return stringBuilder.append(">").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GenericArrayTypeImpl
|
||||||
|
implements GenericArrayType, CompositeType {
|
||||||
|
private final Type componentType;
|
||||||
|
|
||||||
|
public GenericArrayTypeImpl(Type componentType) {
|
||||||
|
this.componentType = canonicalize(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getGenericComponentType() {
|
||||||
|
return componentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFullySpecified() {
|
||||||
|
return MoreTypes.isFullySpecified(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof GenericArrayType
|
||||||
|
&& MoreTypes.equals(this, (GenericArrayType) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return componentType.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return typeToString(componentType) + "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The WildcardType interface supports multiple upper bounds and multiple
|
||||||
|
* lower bounds. We only support what the Java 6 language needs - at most one
|
||||||
|
* bound. If a lower bound is set, the upper bound must be Object.class.
|
||||||
|
*/
|
||||||
|
public static class WildcardTypeImpl
|
||||||
|
implements WildcardType, CompositeType {
|
||||||
|
private final Type upperBound;
|
||||||
|
private final Type lowerBound;
|
||||||
|
|
||||||
|
public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
|
||||||
|
checkArgument(lowerBounds.length <= 1, "Must have at most one lower bound.");
|
||||||
|
checkArgument(upperBounds.length == 1, "Must have exactly one upper bound.");
|
||||||
|
|
||||||
|
if (lowerBounds.length == 1) {
|
||||||
|
checkNotNull(lowerBounds[0], "lowerBound");
|
||||||
|
checkNotPrimitive(lowerBounds[0], "wildcard bounds");
|
||||||
|
checkArgument(upperBounds[0] == Object.class, "bounded both ways");
|
||||||
|
this.lowerBound = canonicalize(lowerBounds[0]);
|
||||||
|
this.upperBound = Object.class;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
checkNotNull(upperBounds[0], "upperBound");
|
||||||
|
checkNotPrimitive(upperBounds[0], "wildcard bounds");
|
||||||
|
this.lowerBound = null;
|
||||||
|
this.upperBound = canonicalize(upperBounds[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] getUpperBounds() {
|
||||||
|
return new Type[]{upperBound};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] getLowerBounds() {
|
||||||
|
return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFullySpecified() {
|
||||||
|
return MoreTypes.isFullySpecified(upperBound)
|
||||||
|
&& (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return other instanceof WildcardType
|
||||||
|
&& MoreTypes.equals(this, (WildcardType) other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
|
||||||
|
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
|
||||||
|
^ (31 + upperBound.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (lowerBound != null) {
|
||||||
|
return "? super " + typeToString(lowerBound);
|
||||||
|
} else if (upperBound == Object.class) {
|
||||||
|
return "?";
|
||||||
|
} else {
|
||||||
|
return "? extends " + typeToString(upperBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/main/java/com/google/inject/internal/Nullability.java
Normal file
32
src/main/java/com/google/inject/internal/Nullability.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a member supports null values injected.
|
||||||
|
*
|
||||||
|
* <p>Support for {@code Nullable} annotations in Guice is loose.
|
||||||
|
* Any annotation type whose simplename is "Nullable" is sufficient to indicate
|
||||||
|
* support for null values injected.
|
||||||
|
*
|
||||||
|
* <p>This allows support for JSR-305's
|
||||||
|
* <a href="http://groups.google.com/group/jsr-305/web/proposed-annotations">
|
||||||
|
* javax.annotation.meta.Nullable</a> annotation and IntelliJ IDEA's
|
||||||
|
* <a href="http://www.jetbrains.com/idea/documentation/howto.html">
|
||||||
|
* org.jetbrains.annotations.Nullable</a>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Nullability {
|
||||||
|
private Nullability() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean allowsNull(Annotation[] annotations) {
|
||||||
|
for (Annotation a : annotations) {
|
||||||
|
Class<? extends Annotation> type = a.annotationType();
|
||||||
|
if ("Nullable".equals(type.getSimpleName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.spi.PrivateElements;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles {@code Binder.newPrivateBinder()} elements.
|
||||||
|
*/
|
||||||
|
final class PrivateElementProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
private final List<InjectorShell.Builder> injectorShellBuilders = Lists.newArrayList();
|
||||||
|
|
||||||
|
PrivateElementProcessor(Errors errors) {
|
||||||
|
super(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean visit(PrivateElements privateElements) {
|
||||||
|
InjectorShell.Builder builder = new InjectorShell.Builder()
|
||||||
|
.parent(injector)
|
||||||
|
.privateElements(privateElements);
|
||||||
|
injectorShellBuilders.add(builder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<InjectorShell.Builder> getInjectorShellBuilders() {
|
||||||
|
return injectorShellBuilders;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.PrivateBinder;
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.ElementVisitor;
|
||||||
|
import com.google.inject.spi.PrivateElements;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
public final class PrivateElementsImpl implements PrivateElements {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class acts as both a value object and as a builder. When getElements() is called, an
|
||||||
|
* immutable collection of elements is constructed and the original mutable list is nulled out.
|
||||||
|
* Similarly, the exposed keys are made immutable on access.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
private List<Element> elementsMutable = Lists.newArrayList();
|
||||||
|
private List<ExposureBuilder<?>> exposureBuilders = Lists.newArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lazily instantiated
|
||||||
|
*/
|
||||||
|
private ImmutableList<Element> elements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lazily instantiated
|
||||||
|
*/
|
||||||
|
private ImmutableMap<Key<?>, Object> exposedKeysToSources;
|
||||||
|
private Injector injector;
|
||||||
|
|
||||||
|
public PrivateElementsImpl(Object source) {
|
||||||
|
this.source = checkNotNull(source, "source");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Element> getElements() {
|
||||||
|
if (elements == null) {
|
||||||
|
elements = ImmutableList.copyOf(elementsMutable);
|
||||||
|
elementsMutable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector getInjector() {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initInjector(Injector injector) {
|
||||||
|
checkState(this.injector == null, "injector already initialized");
|
||||||
|
this.injector = checkNotNull(injector, "injector");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Key<?>> getExposedKeys() {
|
||||||
|
if (exposedKeysToSources == null) {
|
||||||
|
Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap();
|
||||||
|
for (ExposureBuilder<?> exposureBuilder : exposureBuilders) {
|
||||||
|
exposedKeysToSourcesMutable.put(exposureBuilder.getKey(), exposureBuilder.getSource());
|
||||||
|
}
|
||||||
|
exposedKeysToSources = ImmutableMap.copyOf(exposedKeysToSourcesMutable);
|
||||||
|
exposureBuilders = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exposedKeysToSources.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Element> getElementsMutable() {
|
||||||
|
return elementsMutable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addExposureBuilder(ExposureBuilder<?> exposureBuilder) {
|
||||||
|
exposureBuilders.add(exposureBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTo(Binder binder) {
|
||||||
|
PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder();
|
||||||
|
|
||||||
|
for (Element element : getElements()) {
|
||||||
|
element.applyTo(privateBinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
getExposedKeys(); // ensure exposedKeysToSources is populated
|
||||||
|
for (Map.Entry<Key<?>, Object> entry : exposedKeysToSources.entrySet()) {
|
||||||
|
privateBinder.withSource(entry.getValue()).expose(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getExposedSource(Key<?> key) {
|
||||||
|
getExposedKeys(); // ensure exposedKeysToSources is populated
|
||||||
|
Object source = exposedKeysToSources.get(key);
|
||||||
|
checkArgument(source != null, "%s not exposed by %s.", key, this);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(PrivateElements.class)
|
||||||
|
.add("exposedKeys", getExposedKeys())
|
||||||
|
.add("source", getSource())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue