-
Notifications
You must be signed in to change notification settings - Fork 1
CustomScopes
Creating custom scopes is a multistep process:
- Define a scoping annotation
- Implementing the
Scopeinterface - Attaching the scope annotation to the implementation
- Triggering scope entry and exit
The scoping annotation identifies your scope. You'll use it to annotate Salta-constructed types, @Provides methods, and in the in() clause of a bind statement. Copy-and-customize this code to define your scoping annotation:
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import javax.inject.Scope;
@Target({ TYPE, METHOD }) @Retention(RUNTIME) @Scope
public @interface BatchScoped {}To avoid the need to write bytecode generation code, we will implement a ScopeHandler and wrap it in a ScopeImpl to get a Scope. SimpleScopeHandler is a decent starting point for a per-thread scope implementation. Copy this class into your project and tweak it to suit your needs.
import static com.google.common.base.Preconditions.checkState;
import java.util.Map;
import java.util.function.Supplier;
import com.github.ruediste.salta.core.Binding;
import com.github.ruediste.salta.core.CoreDependencyKey;
import com.github.ruediste.salta.standard.ScopeImpl.ScopeHandler;
import com.google.common.collect.Maps;
/**
* Scopes a single execution of a block of code. Apply this scope with a
* try/finally block:
*
* <pre>
* <code>
*
* scopeHandler.enter();
* try {
* // create and access scoped objects
* } finally {
* scopeHandler.exit();
* }
* </code>
* </pre>
*
* Register it with
*
* <pre>
* <code>
* bindScope(MyCustomScopeAnnotation.class, new ScopeImpl(scopeHandler));
* </code>
* </pre>
*
* @author Jesse Wilson
* @author Fedor Karpelevitch
* @author Ruedi Steinmann
*/
public class SimpleScopeHandler implements ScopeHandler {
private final ThreadLocal<Map<Binding, Object>> values = new ThreadLocal<>();
public void enter() {
checkState(values.get() == null,
"A scoping block is already in progress");
values.set(Maps.newHashMap());
}
public void exit() {
checkState(values.get() != null, "No scoping block in progress");
values.remove();
}
@Override
public Supplier<Object> scope(Supplier<Object> supplier, Binding binding,
CoreDependencyKey<?> requestedKey) {
return () -> {
Map<Binding, Object> scopedObjects = getScopedObjectMap(requestedKey);
if (!scopedObjects.containsKey(binding)) {
Object current = supplier.get();
scopedObjects.put(binding, current);
return current;
} else
return scopedObjects.get(binding);
};
}
private Map<Binding, Object> getScopedObjectMap(
CoreDependencyKey<?> requestedKey) {
Map<Binding, Object> scopedObjects = values.get();
if (scopedObjects == null) {
throw new RuntimeException("Cannot access " + requestedKey
+ " outside of a scoping block");
}
return scopedObjects;
}
}You must attach your scoping annotation to the corresponding scope implementation. Just like bindings, you can configure this in your module's configure() method. Usually you'll also bind the scope itself, so interceptors or filters can use it.
public class BatchScopeModule extends AbstractModule {
public void configure() {
SimpleScopeHandler handler = new SimpleScopeHandler();
// tell Salta about the scope handler
bindScope(BatchScoped.class, new ScopeImpl(handler));
// make our scope instance injectable
bind(SimpleScopeHandler.class)
.named("batchScopeHandler")
.toInstance(handler);
}
}SimpleScopeHandler requires that you manually enter and exit the scope. Usually this lives in some low-level infrastructure code, like a filter or interceptor. Be sure to call exit() in a finally clause, otherwise the scope will be left open when an exception is thrown.
@Inject @Named("batchScopeHandler") SimpleScopeHandler scopeHandler;
/**
* Runs {@code runnable} in batch scope.
*/
public void scopeRunnable(Runnable runnable) {
scopeHandler.enter();
try {
// create and access scoped objects
runnable.run();
} finally {
scopeHandler.exit();
}
}The Salta wiki. It was copied from the Guice Wiki and adapted to Salta.
- Home
- Why Dependency Injection?
- Getting Started
- Bindings
- Scopes
- Injections
- Injecting Providers
- AOP
- Speed
- Frequently Asked Questions
- Extending Salta
- Internals
- Best Practices
- Community