Is there any way to have spring call a factory with a runtime parameter of the type of the parent class of a variable it is trying to autowire?
For example, let's say I have something like this:
interface IConfig {
}
interface IConfigProvider {
IConfig getConfig(Class<?> type)
}
class MyClass {
#Autowired
private IConfig _config;
}
Is there anyway to have spring, when autowiring MyClass._config to essentially call IConfigProvider.getConfig(MyClass.class) (well the concrete version that is in the context) at runtime to wire the variable?
I know I could autowire the factory and call it myself, I could even "hide" it in a base class but I am trying to avoid this.
NOTE: I am very new to Spring so if I am asking something really stupid/not using the right terminology, I apologise.
You would need to create a FactoryBean for that. Something like this should do the trick.
class IConfigFactoryBean implements FactoryBean<IConfig> {
#Autowired
private IConfigProvider configProvider;
private IConfig config;
#PostConstruct
public void initialize() {
config = configProvider.getConfig(...);
}
#Override
public IConfig getObject() throws Exception {
return config;
}
#Override
public Class<?> getObjectType() {
return ...;
}
#Override
public boolean isSingleton() {
return true;
}
}
I am not sure what the argument would be. Note that if you have several instances of IConfig you will need to qualify them as Spring won't be able to know which one it has to inject based on a simple #Autowired annotation. Check the javadoc of #Qualifier for more information.
Related
I have one interface and two implementations for this interface
Interface definition:
public interface DoSomething {}
Two implementations:
public ImplementationOne implements DoSomething{}
public ImplementationTwo implements DoSomething{}
Then inside another class, I want to get a different implementaion (either ImplementationOne or ImplementationTwo) based on the condition, how can I do that using Spring?
Something like..
Public ServiceManager {
Private DoSomething doSomething = null;
Public void do() {
If (condition1) {
doSomething = new ImplementationOne();
} else {
doSomething = new ImplementationTwo();
}
}
}
You should definitely auto wire ApplicationContext type using #Autowire annotation. Then if you did it like this:
#Autowire
ApplicationContext context
Then you should get your desired bean like this:
context.getBean(yourDesiredType.class)
Like that you can get any bean you want to be placed under any matching type according to your example.
Another option to consider is have a configuration bean - for example -
#Configuration
public class EntityRepositoryConfiguration {
private Map<Entity, EntityRepository> entityEntityRepositoryMap = new HashMap<>();
protected EntityRepositoryConfiguration() {
entityEntityRepositoryMap.put(Entity.Book, new BookRepository());
}
#Bean
public EntityRepository getByEntityType(Entity entity) {
return entityEntityRepositoryMap.get(entity);
}
}
And then inject the configuration bean to your other beans and use the getEntityType method (for example) to get beans injected.
Assume I have a following code in Java EE / EJB / JAX-RS:
#POST
#Path("some/path")
#MyAnnotation
public MyResponse createActivation(MyRequest request, CustomValue value) {
// ...
}
How do I check for the presence of custom #MyAnnotation annotation and populate CustomValue value method parameter based on some request context parameters in case the annotation is present?
Note: I already have this code in Spring using HandlerInterceptorAdapter and HandlerMethodArgumentResolver. Now I need to do the same without Spring. I have already discovered the ContainerRequestFilter and I use it to check for the annotation, but now I am struggling with injecting the method parameter.
Custom method parameter injection is handled a little differently from normal (i.e. field, constructor) injection. With Jersey, this requires the implementation of a ValueFactoryProvider. For your case it would look something like
public class MyAnnotationParamValueProvider implements ValueFactoryProvider {
#Inject
private ServiceLocator locator;
#Override
public Factory<?> getValueFactory(Parameter parameter) {
if (parameter.getAnnotation(MyAnnotation.class) != null
&& parameter.getRawType() == CustomValue.class) {
final Factory<CustomValue> factory
= new AbstractContainerRequestValueFactory<CustomValue>() {
#Override
public CustomValue provide() {
final ContainerRequest request = getContainerRequest();
final String value = request.getHeaderString("X-Value");
return new CustomValue(value);
}
};
locator.inject(factory);
return factory;
}
return null;
}
#Override
public PriorityType getPriority() {
return Priority.NORMAL;
}
}
Then you need to register it with the ResourceConfig
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bind(MyAnnotationParamValueProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
}
});
}
}
See a complete example in this Gist
See also:
Custom Method Parameter Injection with Jersey. It shows another way to do this, where you don't need to explicitly inject, and also you will be able to inject the value in all three areas (field, constructor, and method param).
I have a class that is annotated #Component that was then #Autowired into another class. However, I need to remove this #Component annotation and instead, create it with an #Bean annotated method in the class where its was previously autowired.
Where previously the classes looked like:
#Component
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Autowired
private IMyClass myClass;
private void methodUsingMyClass()
{
myClass.doStuff();
}
}
So now I have removed the #Component annotation and written a #Bean annotated method like this:
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Bean
public IMyClass getMyClass()
{
return new MyClass();
}
....
}
My question is around replacing the previous call of myClass.doStuff() to use the new bean. Do I now pass in a parameter of type MyClass to the private method:
private void methodUsingMyClass(final MyClass myClass)
{
myClass.doStuff();
}
... or do I call this method directly (doesn't seem the correct way to me):
private void methodUsingMyClass()
{
getMyClass().doStuff();
}
... or are neither of these correct?
I think you misunderstand the #Bean annotation. It can be used to create a Bean. So basically spring will scan all classes, will find your #Bean and create a Bean, not more. You can now use this bean, like if you would use one created with <bean></bean>. To actually use the bean you need to either get it from ApplicationContext or #Autowire it. Of course you can still use that function like any other function in your code, to create a new instance of that object, but that would contradict to what you want to achieve with beans
Using Annotations that solutions
public class MyClass implements IMyClass{
private OtherClassInjection otherClassInjection;
private OtherClassInjection2 otherClassInjection2;
MyClass(OtherClassInjection otherClassInjection, OtherClassInjection2 otherClassInjection2){
this.otherClassInjection=otherClassInjection;
this.otherClassInjection2=otherClassInjection2;
}
public void useObject(){
otherClassInjection.user();
}
}
#Bean(name = "myClass")
#Autowired
#Scope("prototype") //Define scope as needed
public MyClass getMyClass(#Qualifier("otherClassInjection") OtherClassInjection otherClassInjection,
OtherClassInjection2 otherClassInjection2) throws Exception
{
return new MyClass(otherClassInjection, otherClassInjection2);
}
that logical, it's work injection #Autowired when create a Bean if context are know that bean, that you will to want inject.
I'm use that way.
I am in the process of refactoring an old module, by adding CDI.
I end with
public interface ApiFactory {
...
}
public class ApiFactorySp
implements ApiFactory {
#Inject
UrlProducer urlProducer; // <-- Does not get injected
...
}
and
public interface UrlProducer {
public String getUrl();
}
#Alternative
public class UrlProducerTest
implements UrlProducer {
#Override
public String getUrl() {
return "https://myTestEnv.mydomain/myWebApp";
}
}
For testing, I create a beans.xml file in META-INF:
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<alternatives>
<class>myOrg.myProject.myPackage.UrlProducerTest</class>
</alternatives>
</beans>
To test it, I am doing like shown in this blog
public class WeldContext {
public static final WeldContext INSTANCE = new WeldContext();
private final Weld weld;
private final WeldContainer container;
private WeldContext() {
this.weld = new Weld();
this.container = weld.initialize();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
weld.shutdown();
}
});
}
public <T> T getBean(Class<T> type) {
return container.instance().select(type).get();
}
}
and
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
public WeldJUnit4Runner(Class<Object> clazz) throws InitializationError {
super(clazz);
}
#Override
protected Object createTest() {
final Class<?> test = getTestClass().getJavaClass();
return WeldContext.INSTANCE.getBean(test);
}
}
Now, when I try to test the logic, I do
#RunWith(WeldJUnit4Runner.class)
public class MyTest {
#Inject
UrlProducer urlProducer;
#Inject
ApiFactory apiFactory;
#Test
public void test() {
apiFactory.doSomethingThatRequiresUrlProducer();
}
}
When I run this, both of the test attributes are inject, but I get NPE because the urlProducer attribute inside of the apiFactory instance has not been assigned a value.
Why is Weld not recognizing the #Inject attribute inside ApiFactory?
JDK 7, Weld 2.2.10, Junit 4.12
UPDATE: After posting the question, started trying with a simpler, brand new project (with just two interfaces and three classes). Using Weld "standalone" did not solve the issue, using CDI-Unit did solve it.
Then I modified my original project to use CDI-Unit, but it did not improve anything. After that I change the injection of UrlProducerTest in ApiFactory from field to constructor (i.e., defining the #Inject ApiFactory(UrlProducer urlProducer) constructor) solved it. I still have not tried this solution with "standalone" Weld (that is for tomorrow), but nonetheless I am still interested in know why field injection is not working.
If UrlProducerTest is an alternative and you want to inject this bean this class should be added to beans.xml into <alternatives> tag.
EDIT:
I believe if some bean can't be injected you get exception with 'unsatisfied/ambiguous dependencies' message. Null could be injected if you used CDI producer method that returned null but this is not your scenario.
So if there are no errors in console I have two assumptions:
Injection doesn't work at all and you get NPE because apiFactory is null
You use urlProducer before injection. For example, from constructor or initialization block (apiFactory.doSomethingThatRequiresUrlProducer() is not provided). So move this logic to some method and annotate it by #PostConstruct
Because ApiFactorySp isn't a CDI bean. You need to annotate the class with #Named to identify the class as a CDI bean for CDI to perform dependency injection.
I'm having problems trying to set the mock object in my wired bean in my testcase.
Here's my simplified problem:-
class SomeClassTest {
#Autowired
private SomeClass someClass;
#Test
public void testRun() {
Service service = mock(ServiceImpl.class);
when(service.doIt()).thenReturn("");
// this line fails with ClassCastException
((SomeClassImpl) someClass).setService(service);
assertEquals("bad", someClass.run());
}
}
interface SomeClass {
String run();
}
class SomeClassImpl implements SomeClass {
private Service service;
public void setService(Service service) {
this.service = service;
}
public String run() {
String value = service.doIt();
return StringUtils.isBlank(value) ? "bad" : "good";
}
}
interface Service {
String doIt();
}
class ServiceImpl implements Service {
public String doIt() {
return "bla";
}
}
In this example, I'm trying to test SomeClass by mocking out Service.doIt() so that I can test different conditions. The problem I'm facing is I'm not sure how exactly I should set the mock Service object in SomeClass. The only way I can think of is to downcast SomeClass into the concrete class to call setService(...), however, I'm getting a ClassCastException saying $Proxy incompatible with SomeClassImpl. I believe all my bean wirings are proxy-based because I'm using AOP to configure the transaction. I really do not want to expose setService(...) in SomeClass interface because it makes no sense to do so in my production code.
Is there a way for me to accomplish this?
Thanks.
You can use the #Resource annotation to get the implementation:
#Resource
private SomeClassImpl someClass;
...
someClass.setService(service);
...
Use additional interface for Service setter than.
or
Do not autowire Service but use 'new' operator in your test.