Google Guice bind by annotation and/or package - java

I have 3 beans in one package that I would like to be eager singletons.
public class Module1 implements Module {
#Override
public void configure(Binder binder) {
binder.bind(Bean1.class).asEagerSingleton();
binder.bind(Bean2.class).asEagerSingleton();
binder.bind(Bean3.class).asEagerSingleton();
}
}
How can I configure them all as eager singletons without exact writing class name using Google Guice?
I'm looking for something like marking Bean1, Bean2, Bean3 by some custom annotation or scanning by package name.

I would do something like this:
#Override
protected void configure() {
try {
for (ClassInfo classInfo:
ClassPath.from(getClass().getClassLoader()).getTopLevelClasses("my.package.name")) {
bind(classInfo.load()).asEagerSingleton();
}
} catch (IOException e) { // Do something
}
}
ClassPath is coming from the Guava library which Guice 4 depends. If you're using Guice 3 you will probably need to add this dependency.
There may also be 3rd party libraries that include an #EagerSingleton annotation, FWIW.

Related

Jersey 2.26: Defining Custom Injection Annotation

I'm migrating a JavaSE application from Jersey 2.x to 2.26. The application relies on HK2 for dependency injection.
Unfortunately some of the official documentation - custom injection, chapter 23 - is now incorrect and has not yet been updated. In his answer here, Paul explains how to migrate a HK2 Factory to a Supplier, which jersey now uses to set up a custom injection provider. Works great, but I'd like to ask for help for the rest of the chapter:
How do I setup a custom injection annotation?
Currently, my existing custom-injection-resolver classes (exactly as per the documentation) compile fine. I'm not sure if they should continue to implement org.glassfish.hk2.api.InjectionResolver direcly? In the javadocs, I find InjectionResolverWrapper, do I need to extend that instead?
The real problem is how to bind the injection resolver to the custom injection. This does not compile:
bind(SessionInjectResolver.class)
.to(new TypeLiteral<InjectionResolver<SessionInject>>(){})
.in(Singleton.class);
I'd very much appreciate an example how to make injection with custom annotations work on Jersey 2.26 again.
Thanks to Paul's comment about using GenericType, here is one solution that works for me on Jersey 2.26 again. It's using the org.glassfish.hk2.api.* classes.
AbstractBinder
....
#Override
protected void configure() {
/*
Adds binding for #CurrentUser.
By default, factories are being injected with PerLookup scope.
*/
bindFactory(CurrentUserSupplier.class)
.to(User.class)
.proxy(true)
.proxyForSameScope(false)
.in(RequestScoped.class);
bind(CurrentUserResolver.class)
.to(new GenericType<InjectionResolver<CurrentUser>>(){})
.in(Singleton.class);
}
....
CurrentUserSupplier
public class CurrentUserSupplier implements Supplier<User> {
// inject what is required
#Override
public User get() {
// do what is necessary to obtain User
// and return it
}
}
CurrentUserResolver
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
public class CurrentUserResolver implements InjectionResolver<CurrentUser> {
#Inject
#Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
InjectionResolver<Inject> systemInjectionResolver;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (User.class == injectee.getRequiredType()) {
return systemInjectionResolver.resolve(injectee, handle);
}
return null;
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}

Pass Parameters to constructor in Guice with no modifications to the Interface/Impl class

I would like to pass constructor argument when binding an interface with Impl in Guice.
There are a couple of solutions, but I don't have code access to these interfaces/Impl's.
They're developed by other team's and I've included them in My project.
#Named/#Assisted - Both needs a change in the source code (Constructor) of the Impl to include these annotations. (I don't have access to this code)
Implement Provider - which returns instance of that Impl by loading the required arguments. This Worked.
But the problem is I've 200+ such existing DI's and I'll have to write 200+ Providers.
Currently we're using Spring DI and are in the process of moving to Guice.
So I need to define something like
<bean name="clientWrapper" class="com.wrapper.client.ClientWrapper">
<constructor-arg index="0" value="${server.ip}" />
<constructor-arg index="1" value="${server.port}" />
</bean>
in Guice. But Pass those Constructor Args.
bind(IClientWrapper.class).to(ClientWrapper.class);
How to achieve this without using Providers?
Your best option I think is a mix of Provides methods and toConstructor binding.
Use the #Provides method binding when you have an object who has dependencies that can not be worked out by type alone.
public class ProvidesModule extends AbstractModule {
#Provides
IClientWrapper clientWrapper(#Named("server.ip") String ip,
#Named("server.port") int port) {
return new ClientWrapper(ip, port);
}
}
In overall code size this is not significantly more than Spring and is type safe.
When the constructor only has dependencies that can be worked out by type alone then use toConstructor binds
protected void configure() {
try {
bind(TransactionLog.class).toConstructor(
DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
} catch (NoSuchMethodException e) {
addError(e);
}
}
One last option:
Our Legacy thingie:
interface LegacyThing {
}
class LegacyThingImp implements LegacyThing {
public LegacyThingImp(String test) {
System.out.println(test);
}
}
Is my magic provider on GitHub. This takes an implementation class and the list of dependencies (as Keys) and then finds the right constructor by Magic (or reflection).
public class TestMagic {
public static void main(final String... arg) {
Guice.createInjector(
new AbstractModule() {
#Override
protected void configure() {
bind(String.class).annotatedWith(Names.named("testString")).toInstance("Guice!");
bind(LegacyThing.class).toProvider(new MagicLegacyProvider<>(LegacyThingImp.class, Key.get(String.class, Names.named("testString"))));
}
}).getInstance(LegacyThing.class);
}
}

Empty Multibinder/MapBinder in Guice

In the process of building a plugin architecture using Guice's MapBinder, using Guice 3.0, I've run into the issue that Guice throws a CreationException when stripped of all modules, which is a viable configuration in this application. Is there a way to get Guice to inject an empty Map? Or, by extension, an empty set with Multibinder?
For example:
interface PlugIn {
void doStuff();
}
class PlugInRegistry {
#Inject
public PlugInRegistry(Map<String, PlugIn> plugins) {
// Guice throws an exception if OptionalPlugIn is missing
}
}
class OptionalPlugIn implements PlugIn {
public void doStuff() {
// do optional stuff
}
}
class OptionalModule extends AbstractModule {
public void configure() {
MapBinder<String, PlugIn> mapbinder =
MapBinder.newMapBinder(binder(), String.class, PlugIn.class);
mapbinder.addBinding("Optional").to(OptionalPlugIn.class);
}
}
In the documentation for MapBinder, it says:
Contributing mapbindings from different modules is supported. For example, it is okay to have both CandyModule and ChipsModule both create their own MapBinder, and to each contribute bindings to the snacks map. When that map is injected, it will contain entries from both modules.
So, what you do is, don't even add the entry in your basic module. Do something like this:
private final class DefaultModule extends AbstractModule {
protected void configure() {
bind(PlugInRegistry.class);
MapBinder.newMapBinder(binder(), String.class, PlugIn.class);
// Nothing else here
}
}
interface PlugIn {
void doStuff();
}
Then, when you create your injector, if the additional modules exist, great! Add them. If they don't exist, then don't add them. In your class, do this:
class PlugInRegistry {
#Inject
public PlugInRegistry(Map<String, PlugIn> plugins) {
PlugIn optional = plugins.get("Optional");
if(optional == null) {
// do what you're supposed to do if the plugin doesn't exist
}
}
}
Note: You have to have the empty MapBinder, or the Map injection won't work if there are no optional modules present.

Can Guice instantiate my classes from class literals?

I am using Guice to build my application and I have a peculiar situation. I have a property file with a map of my interface and implementation classes such as -
interface = Implclass
I would like to bind the interface.class to my implclass.class
So that when I request injector.getInstance(MyInterface.class) Guice can return me an instance of my Impl class.
Is this possible ?
You could do something very simple like this:
class Module extends AbstractModule {
Properties properties;
Module(Properties properties) {
this.properties = properties;
}
#Override
protected void configure() {
for (Entry<Object, Object> entry: properties.entrySet()) {
try {
Class<?> abstractClass = Class.forName((String)entry.getKey());
Class implementation = Class.forName((String)entry.getValue());
bind(abstractClass).to(implementation);
} catch (ClassNotFoundException e) {
//Handle e
}
}
}
}
Note that the properties file will need to contain fully qualified class names for this to work. I noticed your question uses short class names. Have a look at this question to add support for that.
Spring has extensive support for XML based configuration, which might be a better option depending on what you're trying to do. Keeping your bindings in code is nice because they survive refactoring.
And if you're trying to allow clients to add functionality to your application SPI might be a better bet.
Google says yes:
public class BillingModule extends AbstractModule {
#Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
}
}
To get a Class given a String of the class name, use Class.forName.

How to specify default Enum instance for Guice?

I'd need something like
#DefaultInstance(Level.NORMAL)
enum Level {NORMAL, FANCY, DEBUGGING}
which would make Guice to return Level.NORMAL for the expression
injector.getInstance(Level.class)
There's no such thing like #DefaultInstance. As a workaround I've tried #ProvidedBy with a trivial Provider, but it doesn't work.
Maybe overriding modules could help you. A default level can be configured using AppLevel module:
public class AppModule extends AbstractModule {
#Override
public void configure() {
bind(Level.class).toInstance(Level.NORMAL);
// other bindings
}
}
and a specific one can be configured in a small overriding module:
public class FancyLevelModule extends AbstractModule {
#Override
public void configure() {
bind(Level.class).toInstance(Level.FANCY);
}
}
At the end just create an injector overriding the AppModule with a specific Level config:
public static void main(String[] args) {
Injector injector =
Guice.createInjector(
Modules.override(new AppModule()).with(new FancyLevelModule())
);
System.out.println("level = " + injector.getInstance(Level.class));
}
UPDATE
This problem can be solved in a bit different way. Let's say that Level is used in a class as an injected field:
class Some
{
#Injected(optional = true)
private Level level = Level.NORMAL;
}
A default level will be initialized as part of the creation of instances of Some. If some Guice config module declares some other level it will be optionally injected.
A solution, but unfortunatelly not using annotations, would be:
enum Level
{
NORMAL, FANCY, DEBUGGING;
static final Level defaultLevel = FANCY; //put your default here
}
then define module like this:
public class DefaultLevelModule extends AbstractModule
{
#Override public void configure()
{
bind(Level.class).toInstance(Level.defaultLevel);
}
}
It's the issue 295 and looks like a very trivial bug.
I've patched it for myself and maybe one day somebody there will fix this very old issue, too.

Categories

Resources