Spring get managed beans during a method invocation - java

i'm new to spring, and i have a question that i couldn't find an answer for it -
during a method execution, i need to create new instances of a managed bean (scope == prototype) in a pre-defined quantity:
#Component
class SomeClass
{
#Resource
private ConnectionFactory conFactory;
private Set <Client> clients;
#Value ("${clientsNum}")
private int clientsNum;
public void init ()
{
Client client = null; //an interface, that the managed bean implements.
for (int i = 0; i < clientsNum; i++)
{
client = ... //how to get a new client instance?
clients.add (client);
client.doSomething ();
}
}
public void shutdown ()
{
for (Client client : clients)
client.shutdown ();
conFactory.shutdown ();
}
}
how do i do that?
i know i can use the init-method \ #PostConstruct annotation (and the matching destroy method), but i don't know how to get the instances according to the required amount.
i searched for the issue in the past few days, and read about service locator, lookup method and factory bean. they all use CGLIB (which i prefer NOT to use) or spring's ApplicationContext (which creates a dependency in spring's implementation), and above all - they don't handle the creation of beans during a method invocation (or at least - i didn't understand how to use them during the invocation).
please assist.
thanks.
EDIT:
eventually i realized that i have to use either CGLIB or Spring's application context, and i chose to use the service locator option.

I'm afraid you have to use CGLIB or ApplicationContext since you wanted a managed bean.
Lookup method injection
Add a lookup method to your SomeClass:
#Lookup
private Client createClient() {
return null; // Never mind, this method will be overridden
}
Then in init(), you can just call it:
client = createClient();
Note that #Lookup annotation was introduced in Spring 4.1.0. You need XML config if you're using older versions.
Using ApplicationContext
Declare an auto wired property:
#Autowired
private ApplicationContext applicationContext;
And in init():
client = applicationContext.getBean(Client.class);

I guess some type of factory would be a better choice for you, you may define a factory method which takes clientsNum as parameter and returns a set to you. This way you can hide all the details from your service class.
If you still want to do this using dependency injection you can try method injection. Have a look at following code to get an idea.
#Component
class SomeClass
{
#Resource
private ConnectionFactory conFactory;
protected abstract Client createClient();
private Set <Client> clients;
#Value ("${clientsNum}")
private int clientsNum;
public void init ()
{
Client client = null; //an interface, that the managed bean implements.
for (int i = 0; i < clientsNum; i++)
{
client = createClient();
clients.add (client);
client.doSomething ();
}
}
public void shutdown ()
{
for (Client client : clients)
client.shutdown ();
conFactory.shutdown ();
}
}
You can find a complete example for method injection with simple google search. But i recommend going with the first solution, its more clean.

i don't know how to get a new instance of the managed bean, without using injection, but during a method invocation.
Inject the Spring ApplicationContextand then use ApplicationContext.getBean(...)
#Autowired
private ApplicationContext applicationContext;
public void init ()
{
Client client = null; //an interface, that the managed bean implements.
for (int i = 0; i < clientsNum; i++)
{
/*>>>>*/ client = applicationContext.getBean(Client.class);
clients.add (client);
client.doSomething ();
}
}

Related

How to use CDI to inject into an object instantiated by 3rd party

I have a class that is instantiated by 3rd party code (it uses reflection to create the object.) I provide the implementation of their interface and they create the object. In my implementation I want to use CDI to inject a service that performs logic. What is the correct way to do this?
public interface ThirdPartyInterface {
public void DoSomething();
}
public class InjectedService {
public void DoSomeLogic() { ... }
}
public class MyImplementation implements ThirdPartyInterface {
#Inject InjectedService service;
#Override
public void DoSomething() {
service.DoSomeLogic();
}
}
I originally thought this would work through the magic of CDI, but found my service object to be null.
The only thing I've come up with so far is to inject the service manually in the constructor
public MyImplementation() {
CDI<Object> cdi = CDI.current();
service = cdi.select(InjectedService.class).get();
}
Is this the correct/only/best way of obtaining the instance? I am using Weld for my CDI implementation.
I have also found this to work in the constructor:
public MyImplementation() {
CDI<Object> cdi = CDI.current();
BeanManager bm = cdi.getBeanManager();
AnnotatedType<MyImplementation> myType = bm.createAnnotatedType(MyImplementation.class);
Set<Bean<?>> beans = bm.getBeans(MyImplementation.class);
Bean<?> bean = bm.resolve(beans);
#SuppressWarnings("unchecked")
CreationalContext<MyImplementation> cc = (CreationalContext<MyImplementation>)bm.createCreationalContext(bean);
bm.createInjectionTarget(myType).inject(this, cc);
}
So long as someone creates the object manually, CDI will not, by default, inject anything into it.
You approach with the constructor injection is probably going to work, unless you get into EARs and such where CDI.current() may not do what you would expect.
There is a way to make CDI inject into manually created objects - the 3rd party would have to take this step to make it work. You need BeanManager and an instance you want to inject into:
BeanManager beanManager = ...; // get hold of bean manager, can be injected
CreationalContext<Object> ctx = beanManager.createCreationalContext(null);
#SuppressWarnings("unchecked")
InjectionTarget<MyImplementation> injectionTarget = (InjectionTarget<MyImplementation>) beanManager
.getInjectionTargetFactory(beanManager.createAnnotatedType(myImplementationInstance.getClass())).createInjectionTarget(null);
injectionTarget.inject(myImplementationInstance, ctx);
Note - by doing this you take responsibility to clean up after the object once you no longer need it. Store the CreationContext somewhere and call release() method on it in order to dispose of it properly (with all possible pre destroy calls and such).
Alternatively, since you are already using CDI, why doesn't 3rd party simply #Inject the bean you provide?

Dropwizard HK2 injection

im pretty new in working with dropwizard. Currently I'm trying to implement the HK2 dependency injection. That works pretty fine inside a resource but it doesn't work outside of a resource. Here is what I'm doing:
Client client = new JerseyClientBuilder(environment).using(configuration.getJerseyClientConfiguration()).build("contentmoduleservice");
//DAOs
ContentModuleDAO contentModuleDAO = new ContentModuleDAO(hibernate.getSessionFactory());
ModuleServedDAO moduleServedDAO = new ModuleServedDAO(hibernate.getSessionFactory());
//Manager
ContentModuleManager moduleManager = new ContentModuleManager();
EntityTagManager eTagManager = new EntityTagManager();
ProposalManager proposalManager = new ProposalManager(client, configuration);
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
bind(eTagManager).to(EntityTagManager.class);
bind(contentModuleDAO).to(ContentModuleDAO.class);
bind(moduleServedDAO).to(ModuleServedDAO.class);
bind(proposalManager).to(ProposalManager.class);
bind(moduleManager).to(ContentModuleManager.class);
}
});
I create Instance of the classes I want to be injectible and bind them.
Inside my resource the injection works:
#Api
#Path("/api/contentmodule")
public class ContentModuleResource {
static final Logger LOG = LoggerFactory.getLogger(ContentModuleResource.class);
static final int MAX_PROPOSALS_PER_MODULE = 10;
#Inject
private ContentModuleDAO contentModuleDAO;
#Inject
private EntityTagManager eTagManager;
#Inject
private ProposalManager proposalManager;
#Inject
private ContentModuleManager contentModuleManager;
All these variables are filled with an Instance of the right class.
The problem is: The ContentModuleManager should also get some of these classes via injection:
public class ContentModuleManager {
#Inject
private ContentModuleDAO contentModuleDAO;
#Inject
private ProposalManager proposalManager;
#Inject
private ModuleServedDAO moduleServedDAO;
But those are null. Can somebody explain a dropwizard noob why this happens and how I can fix this? :D
Thanks!
If you are going to instantiate the service yourself then it is not going to go through the DI lifecycle and will never be injected. You can let the container create the service if you just register the services as a class
bind(ContentModuleManager.class)
.to(ContentModuleManager.class)
.in(Singleton.class);
On the other hand, if you are creating all the services yourself and you have all of them available, why don't you just not use the DI container at all? Just pass all the services through the constructor. Whether you are using constructor injection1 or manually passing through the constructor, getting the service through the constructor is good practice anyway, as it allows for easier testing of the service.
1 - Constructor injection
private Service service;
#Inject
public OtherService(Service service) {
this.service = service;
}

Guice FactoryModuleBuilder an instance with constructor parameters

I´m using Guice to initalize a class with some arguments from a config file
#Provides
#Singleton
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
This class is injected in another class which is singleton as well.
class A{
#Inject private RetryServiceCaller retryServiceCaller;
}
But now the problem is that since this new class A is singleton, I need to clone the retryServiceCaller every time that somebody use this class A.
I´ve been investigating FactoryModuleBuilder to use it and create a factory for this class. But since the class has parameters from the config file I could not find the way to make it works.
Something like this
class A{
#Inject private RetryServiceCaller.Factory retryServiceCallerFactory;
}
Then in my RetryServiceCaller implement this
public interface Factory {
#Inject
RetryServiceCaller create();
}
#Inject
public RetryServiceCaller(int maxRetryAttempts, int upperBoundary, int lowerBoundary, long multiplicationFactor, int timeout, int incrementTimeout) {
this.maxRetryAttempts = maxRetryAttempts;
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
this.multiplicationFactor = multiplicationFactor;
this.timeout = timeout;
this.incrementTimeout = incrementTimeout;
}
But guice throw me errors saying
No implementation for com.proxy.handlers.RetryServiceCaller$Factory was bound
Guice can automatically provide a zero-argument factory: Instead of injecting Foo, you can always inject Provider<Foo>. This allows you to call fooProvider.get() to create an instance whenever and wherever you'd like. You don't have to bind to a Provider or use a Provides method to get access to this; you can inject Foo or Provider<Foo> whether you use a bind(...).to(...) type binding, a toProvider binding, a toInstance binding, a #Provides method, or anything else, and Guice will call get or return an internal Provider automatically.
(The returned Provider will also respect scopes, so you'll need to drop your #Singleton scope in order to get more than one instance, and be aware that toInstance bindings will always return the same instance.)
This is not a job for FactoryModuleBuilder; only use FactoryModuleBuilder when you need to mix injected and non-injected constructor parameters in the same type.
Your finished binding should look like this:
#Provides
/* NOT #Singleton */
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
And in your class:
#Inject public YourCallerConsumer(Provider<RetryServiceCaller> callerProvider) {
this.callerProvider = callerProvider;
}
public void doAction() {
RetryServiceCaller newCaller = callerProvider.get();
// interact with caller
}
Your first approach should work just fine. If you don't want the RetryServiceCaller to be a singleton, remove the #Singleton annotation from the provider method, and a new instance will be created for every injection point.
Assisted inject could work here too, but it's overkill. If you want to go that route:
interface RetryServiceCallerFactory {
RetryServiceCaller create(String configParam1, String configParam2);
}
public class RetryServiceCaller {
#AssistedInject
public RetryServiceCaller(String configParam1, String configParam2) {}
}
then, in your module
install(new FactoryModuleBuilder().build(Factory.class);
and in your injection points
#Inject RetryServiceCallerFactory factory;
RetryServiceCaller create(JsonObject config) {
return factory.create(config.getFirstParam(), config.getSecondParam());
}
You can refer to the documentation for more extensive examples.

Guice FactoryBuilderModule NullPointerException On Generated Factory

I am new to Guice and have done a lot of reading on this but I have not hand any success with this. I am basically creating a DAO and want to use Guice and the AssistedInjection. Effectively the end goal is create the Injected factory in other classes throughout the application.
Intended use in a actual class that would have the injection of the factory to then get classes from it
public class TestAllModelBootstrap {
#Inject private DAOFactory factory;
public TestAllModelBootstrap() {
}
#Test
public void testGettingDAO() {
Injector injector = Guice.createInjector(new HibernateDAOModule());
Token oToken = new OperTokenV1();
AccountingDAO accountingDAO = factory.create(oToken);
}
}
This is based on Guice-based code of:
public interface DAOFactory {
public AccountingDAO create(Token oTicket);
}
The concrete class has a constructor annoated
#Inject
public HibernateAccountingDAO(#Assisted Token oTicket) {
this.oTicket = oTicket;
}
And the actual Module:
#Override
protected void configure() {
install(new FactoryModuleBuilder()
.implement(AccountingDAO.class, HibernateAccountingDAO.class)
.build(DAOFactory.class));
bind(SessionFactoryInterface.class)
.toProvider(HibernateSessionProvider.class);
}
Each time I try to run this:
java.lang.NullPointerException -> indicating that the:
factory.create(oToken);
has factory as a null. In reading up on the problem I was lead to believe that the injection will not work like I am using it in the "test" class. It needs to be put in an "injected" class itself. But this doesn't work either - if I wrapper the Factory injection in another class and then try to use it, it doesn't work.
Any help would be appreciated...
TestAllModelBootstrap did not come from an Injector—JUnit created it instead—so Guice hasn't had a chance to inject it yet. Guice can only inject into objects that it creates via getInstance (and those objects' dependencies, recursively), or objects passed into injectMembers, or existing instances as requested using requestInjection.
You can manually get a factory instance:
factory = injector.getInstance(DAOFactory.class);
Or ask Guice to inject your members using injectMembers:
injector.injectMembers(this); // sets the #Inject factory field
Or use a tool like Guiceberry to inject your test cases across your app.

How can I select Spring bean instance at runtime

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Categories

Resources