Skip to content

Commit

Permalink
Add support for jakarta.inject to core Guice.
Browse files Browse the repository at this point in the history
This does not add support for `toProvider(...)` to instances of `jakarta.inject.Provider` or classes/keys/typeLiterals of type `jakarta.inject.Provider`. See #1383 (comment) for details of the plans. The Guice 6.0 release will support `javax.inject` _and_ `jakarta.inject` (excluding `toProvider()` support for `jakarta.inject`), and the Guice 7.0+ release will remove the `javax.inject` references.

Further background on these issues:
 * #1630
 * #1679
 * #1490
 * #1383

PiperOrigin-RevId: 526763665
  • Loading branch information
sameb authored and Guice Team committed Apr 26, 2023
1 parent e17e8af commit c9c7d4e
Show file tree
Hide file tree
Showing 38 changed files with 1,246 additions and 85 deletions.
7 changes: 7 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ maven_install(
"com.google.guava:guava:31.0.1-jre",
"commons-logging:commons-logging:1.2",
"javax.inject:javax.inject:1",
"jakarta.inject:jakarta.inject-api:2.0.1",
"javax.persistence:javax.persistence-api:2.2",
"javax.servlet:servlet-api:2.5",
"org.apache.struts:struts2-core:2.5.30",
Expand Down Expand Up @@ -75,6 +76,12 @@ maven_install(
"1",
testonly = True,
),
maven.artifact(
"jakarta.inject",
"jakarta.inject-tck",
"2.0.1",
testonly = True,
),
maven.artifact(
"junit",
"junit",
Expand Down
19 changes: 19 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
Expand Down Expand Up @@ -90,6 +94,21 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<testExcludes>
<!--
Don't compile the jakarta TCK b/c it would clash w/ the javax.inject TCK.
We test it with the Bazel build for now, and basic jakarta functionality is tested
with the JakartaTest in the same package.
-->
<testExclude>**/GuiceJakartaTck.java</testExclude>
</testExcludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
Expand Down
1 change: 1 addition & 0 deletions core/src/com/google/inject/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ java_library(
"//third_party/java/error_prone:annotations",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//third_party/java/jakarta_inject",
],
)

Expand Down
26 changes: 13 additions & 13 deletions core/src/com/google/inject/Provider.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,24 @@
* 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.
* <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
* @author crazybob@google.com (Bob Lee)
*/
public interface Provider<T> extends javax.inject.Provider<T> {
public interface Provider<T> extends javax.inject.Provider<T>, jakarta.inject.Provider<T> {

/**
* Provides an instance of {@code T}.
Expand Down
3 changes: 2 additions & 1 deletion core/src/com/google/inject/Scopes.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public Boolean visitNoScoping() {
@Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return scopeAnnotation == Singleton.class
|| scopeAnnotation == javax.inject.Singleton.class;
|| scopeAnnotation == javax.inject.Singleton.class
|| scopeAnnotation == jakarta.inject.Singleton.class;
}

@Override
Expand Down
12 changes: 9 additions & 3 deletions core/src/com/google/inject/internal/Annotations.java
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,9 @@ boolean hasAnnotations(Class<? extends Annotation> annotated) {
}

private static final AnnotationChecker scopeChecker =
new AnnotationChecker(Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
new AnnotationChecker(
Arrays.asList(
ScopeAnnotation.class, javax.inject.Scope.class, jakarta.inject.Scope.class));

public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
return scopeChecker.hasAnnotations(annotationType);
Expand Down Expand Up @@ -402,7 +404,8 @@ public static Annotation findBindingAnnotation(
}

private static final AnnotationChecker bindingAnnotationChecker =
new AnnotationChecker(Arrays.asList(BindingAnnotation.class, Qualifier.class));
new AnnotationChecker(
Arrays.asList(BindingAnnotation.class, Qualifier.class, jakarta.inject.Qualifier.class));

/** Returns true if annotations of the specified type are binding annotations. */
public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) {
Expand All @@ -416,6 +419,8 @@ public static boolean isBindingAnnotation(Class<? extends Annotation> annotation
public static Annotation canonicalizeIfNamed(Annotation annotation) {
if (annotation instanceof javax.inject.Named) {
return Names.named(((javax.inject.Named) annotation).value());
} else if (annotation instanceof jakarta.inject.Named) {
return Names.named(((jakarta.inject.Named) annotation).value());
} else {
return annotation;
}
Expand All @@ -427,7 +432,8 @@ public static Annotation canonicalizeIfNamed(Annotation annotation) {
*/
public static Class<? extends Annotation> canonicalizeIfNamed(
Class<? extends Annotation> annotationType) {
if (annotationType == javax.inject.Named.class) {
if (annotationType == javax.inject.Named.class
|| annotationType == jakarta.inject.Named.class) {
return Named.class;
} else {
return annotationType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
+ " proxying is initialized to null."
+ " No methods can be called.");

// TODO: user -; ?
// TODO: method.setAccessible(true); ?
// this would fix visibility errors when we proxy a
// non-public interface.
return method.invoke(delegate, args);
Expand Down
1 change: 1 addition & 0 deletions core/src/com/google/inject/internal/InjectorShell.java
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ public void configure(Binder binder) {
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
binder.bindScope(Singleton.class, SINGLETON);
binder.bindScope(javax.inject.Singleton.class, SINGLETON);
binder.bindScope(jakarta.inject.Singleton.class, SINGLETON);
}
}

Expand Down
10 changes: 6 additions & 4 deletions core/src/com/google/inject/internal/MoreTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ public static <T> Key<T> canonicalizeKey(Key<T> key) {
/**
* 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 raw type of {@code typeLiteral} is a {@code javax.inject.Provider} or {@code
* jakarta.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.
*
Expand All @@ -91,11 +92,12 @@ public static <T> TypeLiteral<T> canonicalizeForKey(TypeLiteral<T> typeLiteral)
throw new ConfigurationException(errors.getMessages());
}

if (typeLiteral.getRawType() == javax.inject.Provider.class) {
if (typeLiteral.getRawType() == javax.inject.Provider.class
|| typeLiteral.getRawType() == jakarta.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
// javax.inject.Provider & jakarta.inject.Provider and is covariant
@SuppressWarnings("unchecked")
TypeLiteral<T> guiceProviderType =
(TypeLiteral<T>)
Expand Down
23 changes: 14 additions & 9 deletions core/src/com/google/inject/spi/InjectionPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int para

/** Returns the injected constructor, field, or method. */
public Member getMember() {
// TODO -).
// TODO: Don't expose the original member (which probably has setAccessible(true)).
return member;
}

Expand Down Expand Up @@ -300,7 +300,8 @@ public static InjectionPoint forConstructorOf(TypeLiteral<?> type, boolean atInj
.filter(
constructor ->
constructor.isAnnotationPresent(Inject.class)
|| constructor.isAnnotationPresent(javax.inject.Inject.class))
|| constructor.isAnnotationPresent(javax.inject.Inject.class)
|| constructor.isAnnotationPresent(jakarta.inject.Inject.class))
.collect(Collectors.toList());

Constructor<?> injectableConstructor = null;
Expand Down Expand Up @@ -477,20 +478,21 @@ private static boolean checkForMisplacedBindingAnnotations(Member member, Errors
abstract static class InjectableMember {
final TypeLiteral<?> declaringType;
final boolean optional;
final boolean jsr330;
final boolean specInject;
InjectableMember previous;
InjectableMember next;

InjectableMember(TypeLiteral<?> declaringType, Annotation atInject) {
this.declaringType = declaringType;

if (atInject.annotationType() == javax.inject.Inject.class) {
if (atInject.annotationType() == javax.inject.Inject.class
|| atInject.annotationType() == jakarta.inject.Inject.class) {
optional = false;
jsr330 = true;
specInject = true;
return;
}

jsr330 = false;
specInject = false;
optional = ((Inject) atInject).optional();
}

Expand Down Expand Up @@ -536,6 +538,9 @@ public boolean isFinal() {

static Annotation getAtInject(AnnotatedElement member) {
Annotation a = member.getAnnotation(javax.inject.Inject.class);
if (a == null) {
a = member.getAnnotation(jakarta.inject.Inject.class);
}
return a == null ? member.getAnnotation(Inject.class) : a;
}

Expand Down Expand Up @@ -646,7 +651,7 @@ boolean removeIfOverriddenBy(
InjectableMethod possiblyOverridden = iterator.next();
if (overrides(method, possiblyOverridden.method)) {
boolean wasGuiceInject =
!possiblyOverridden.jsr330 || possiblyOverridden.overrodeGuiceInject;
!possiblyOverridden.specInject || possiblyOverridden.overrodeGuiceInject;
if (injectableMethod != null) {
injectableMethod.overrodeGuiceInject = wasGuiceInject;
}
Expand Down Expand Up @@ -719,7 +724,7 @@ private static Set<InjectionPoint> getInjectionPoints(
Annotation atInject = getAtInject(field);
if (atInject != null) {
InjectableField injectableField = new InjectableField(current, field, atInject);
if (injectableField.jsr330 && Modifier.isFinal(field.getModifiers())) {
if (injectableField.specInject && Modifier.isFinal(field.getModifiers())) {
errors.cannotInjectFinalField(field);
}
injectableMembers.add(injectableField);
Expand Down Expand Up @@ -837,7 +842,7 @@ private static boolean isEligibleForInjection(Method method, boolean statics) {

private static boolean isValidMethod(InjectableMethod injectableMethod, Errors errors) {
boolean result = true;
if (injectableMethod.jsr330) {
if (injectableMethod.specInject) {
Method method = injectableMethod.method;
if (Modifier.isAbstract(method.getModifiers())) {
errors.cannotInjectAbstractMethod(method);
Expand Down
Loading

0 comments on commit c9c7d4e

Please sign in to comment.