Skip to content

Commit

Permalink
Recognize scoping annotations on concrete types also annotated with @…
Browse files Browse the repository at this point in the history
…ProvidedBy when using the @ProvidedBy as the provider.

Notably, this does *not* change things to allow scoping annotations on interfaces or abstract classes (even though theoretically we could, because the @ProvidedBy will be instantiating it). Scopes on abstract classes|interfaces will still throw an exception about the scope being misplaced.

Fixes #251 and fixes #1319.

PiperOrigin-RevId: 526009187
  • Loading branch information
sameb authored and Guice Team committed Apr 21, 2023
1 parent becce74 commit 2d78877
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
13 changes: 13 additions & 0 deletions core/src/com/google/inject/internal/InjectorImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.inject.internal;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.Annotations.findScopeAnnotation;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
Expand Down Expand Up @@ -783,6 +784,18 @@ <T> BindingImpl<T> createProvidedByBinding(
throw errors.recursiveProviderType().toException();
}

// if no scope is specified, look for a scoping annotation on the raw type
if (!scoping.isExplicitlyScoped()) {
int numErrorsBefore = errors.size();
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, rawType);
if (scopeAnnotation != null) {
scoping =
Scoping.makeInjectable(
Scoping.forAnnotation(scopeAnnotation), this, errors.withSource(rawType));
}
errors.throwIfNewErrors(numErrorsBefore);
}

// Assume the provider provides an appropriate type. We double check at runtime.
@SuppressWarnings("unchecked")
Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType);
Expand Down
2 changes: 2 additions & 0 deletions core/test/com/google/inject/BinderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,8 @@ protected void configure() {
assertNotNull(injector.getInstance(HasProvidedBy1.class));
assertNotNull(injector.getInstance(HasImplementedBy1.class));
assertNotSame(HasProvidedBy2.class, injector.getInstance(HasProvidedBy2.class).getClass());
assertNotSame(
injector.getInstance(HasProvidedBy2.class), injector.getInstance(HasProvidedBy2.class));
assertSame(
ExtendsHasImplementedBy2.class, injector.getInstance(HasImplementedBy2.class).getClass());
assertSame(JustAClass.class, injector.getInstance(JustAClass.class).getClass());
Expand Down
37 changes: 35 additions & 2 deletions core/test/com/google/inject/ScopesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ protected void configure() {
bind(NotASingleton.class);
bind(ImplementedBySingleton.class).in(Scopes.SINGLETON);
bind(ProvidedBySingleton.class).in(Scopes.SINGLETON);
bind(ProvidedByAnnotatedSingleton.class);
}
};

Expand All @@ -83,6 +84,7 @@ protected void setUp() throws Exception {
Implementation.nextInstanceId = 0;
ProvidedBySingleton.nextInstanceId = 0;
ThrowingSingleton.nextInstanceId = 0;
ProvidedByAnnotatedSingleton.nextInstanceId = 0;
}

public void testSingletons() {
Expand Down Expand Up @@ -115,6 +117,10 @@ public void testSingletons() {
assertSame(
injector.getInstance(ProvidedBySingleton.class),
injector.getInstance(ProvidedBySingleton.class));

assertSame(
injector.getInstance(ProvidedByAnnotatedSingleton.class),
injector.getInstance(ProvidedByAnnotatedSingleton.class));
}

public void testJustInTimeAnnotatedSingleton() {
Expand All @@ -123,12 +129,19 @@ public void testJustInTimeAnnotatedSingleton() {
assertSame(
injector.getInstance(AnnotatedSingleton.class),
injector.getInstance(AnnotatedSingleton.class));

assertSame(
injector.getInstance(ProvidedByAnnotatedSingleton.class),
injector.getInstance(ProvidedByAnnotatedSingleton.class));
}

public void testSingletonIsPerInjector() {
assertNotSame(
Guice.createInjector().getInstance(AnnotatedSingleton.class),
Guice.createInjector().getInstance(AnnotatedSingleton.class));
assertNotSame(
Guice.createInjector().getInstance(ProvidedByAnnotatedSingleton.class),
Guice.createInjector().getInstance(ProvidedByAnnotatedSingleton.class));
}

public void testOverriddingAnnotation() {
Expand All @@ -138,12 +151,17 @@ public void testOverriddingAnnotation() {
@Override
protected void configure() {
bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
bind(ProvidedByAnnotatedSingleton.class).in(Scopes.NO_SCOPE);
}
});

assertNotSame(
injector.getInstance(AnnotatedSingleton.class),
injector.getInstance(AnnotatedSingleton.class));

assertNotSame(
injector.getInstance(ProvidedByAnnotatedSingleton.class),
injector.getInstance(ProvidedByAnnotatedSingleton.class));
}

public void testScopingAnnotationsOnAbstractTypeViaBind() {
Expand Down Expand Up @@ -554,24 +572,39 @@ static class SingletonAndCustomScoped {}
@ImplementedBy(Implementation.class)
static interface ImplementedBySingleton {}

@ProvidedBy(ImplementationProvider.class)
@ProvidedBy(ProvidedByProvider.class)
static class ProvidedBySingleton {
static int nextInstanceId;
final int instanceId = nextInstanceId++;
}

@Singleton
@ProvidedBy(ProvidedByAnnotatedSingletonProvider.class)
static class ProvidedByAnnotatedSingleton {
static int nextInstanceId;
final int instanceId = nextInstanceId++;
}

static class Implementation implements ImplementedBySingleton {
static int nextInstanceId;
final int instanceId = nextInstanceId++;
}

static class ImplementationProvider implements Provider<ProvidedBySingleton> {
static class ProvidedByProvider implements Provider<ProvidedBySingleton> {
@Override
public ProvidedBySingleton get() {
return new ProvidedBySingleton();
}
}

static class ProvidedByAnnotatedSingletonProvider
implements Provider<ProvidedByAnnotatedSingleton> {
@Override
public ProvidedByAnnotatedSingleton get() {
return new ProvidedByAnnotatedSingleton();
}
}

public void testScopeThatGetsAnUnrelatedObject() {
Injector injector =
Guice.createInjector(
Expand Down

0 comments on commit 2d78877

Please sign in to comment.