Subresouce and CDI Injection Issue - java

I am new to jax-rs and i am stuck with subresources. Take a look.
this is not working
#Path(..)
public class Test
{
#Path(...)
public SubResource getSub(){
return new SubResource();
}
}
public class SubResource {
#Inject
private MyBean myBean;
#GET
public String getStr(){
return myBean.getStr(); // myBean is null, injection didnt work properly
}
this works, but why????
#Path(..)
public class Test
{
#Context
private ResourceContext context;
#Path(...)
public SubResource getSub(){
return context.getResource(SubResource.class);
}
}
public class SubResource{
#Inject
private MyBean myBean;
#GET
public String getStr(){
return myBean.getStr(); // myBean is not null anymore, why?
}
Why CDI Injection works with ResoureContext?

This has nothing do to with subresources or JAX-RS. In principle, this is about how CDI injection works.
Firstly, your not working sample. Or to be precise, this bit:
#Path(...)
public SubResource getSub(){
return new SubResource();
}
You are creating the SubResource instance yourself via the new keyword. Therefore CDI has no clue about it existing and has absolutely zero control over such object. Therefore, CDI cannot inject anything into this object.
Now to the working sample:
#Context
private ResourceContext context;
#Path(...)
public SubResource getSub(){
return context.getResource(SubResource.class);
}
In this case, you injected a context (a CDI managed "object" already) and tell it to retrieve the resource for you. Therefore you let the CDI container handle the object creation and its lifecycle. And since it manages creation, it can also resolve injection points and inject MyBean.
Generally, when you want to use CDI, you barely ever create objects via new. The obvious exception are producers, but we are not talking those here.

Whenever you create an object like this:
return new SubResource();
then it's lifecycle belongs to you and no injection is performed on it.
In the second case you have allowed the JAX-RS container to create the SubResource:
return context.getResource(SubResource.class);
which gives it control of the object lifecycle, giving it the opportunity to perform injection and other lifecycle operations such as executing #PostConstruct annotated methods.

Related

Initiate object via constructor through #AutoWired during runtime

I was new to Springboot application using the #Autowired to perform the dependency injection. We can use #Autowired directly by initiate that class object for class that has none or default parameterless constructor. But what if a class has some parameter in its constructor, and I would like to initiate it during runtime conditionally and automatically, is it possible to do that?
For example
#Component
public class SomeContext {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Component
public class SomeBuilder {
private final SomeContext ctx;
#Autowired
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
public void run(SomeContext ctx) {
if (ctx != null) {
// I want someBuilder to be initiated here in someway with my input ctx
// but NOT doing through new with constructor like below
// someBuilder = new SomeBuilder(ctx);
someBuilder.test(); // ctx name: null, I would expect to see "ctx name: someUser", while ctx was injected into someBuilder in any possible way
}
}
}
#RestController
public class HelloWorldController
{
#Autowired
SomeService someService;
#RequestMapping("/")
public String hello() {
SomeContext someContext = new SomeContext();
someContext.setName("someUser");
someService.run(someContext);
return "Hello springboot";
}
}
I'm not sure I've got your question right, but from the code it looks like you really want to create a new instance of SomeBuilder every time you call the run method of SomeService.
If so, I think the first thing to understand is that in general the injection magic happens only if the class is managed by Spring by itself. Read, if spring creates the object of the class - it will inject stuff into it otherwise you're on your own here.
The next thing to understand is that, if you have a object of class SomeBuilder managed by spring and you want to inject SomeContext into it, this SomeContext instance has to be managed by spring as well.
Bottom line, spring can deal only with objects that it manages. These objects are stored in a 'global registry' of all the objects called ApplicationContext in spring.
Now Spring has a concept of prototype scope vs. singleton scope. By Default all the beans are singletons, however you can easily alter this behavior. This has two interesting consequences:
You Can create prototype objects being injected into the singleton upon each invocatino (of method run in your case, so the SomeBuilder can and I believe should be a prototype)
Prototype objects are not stored in the application contexts so the capabilities of injecting stuff in there during the runtime are rather limited
With all this in mind:
If you want to create SomeContext like you do in the controller, its not managed by spring, so you can't use Injection of spring as is into the builder.
The builder is a singleton, so if you inject it with a regular #Autowire into another singleton (SomeService in your case), you'll have to deal with the same instance of the builder object - think about concurrent access to the method run of SomeService and you'll understand that this solution is not really a good one.
So these are the "inaccuracies" in the presented solution.
Now, in terms of solution, you can:
Option 1
Don't manage builders in Spring, not everything should be managed by spring, in this case you'll keep your code as it is now.
Option 2
and this is a the solution, although pretty advanced one:
Use Java Configuration to create prototype beans in runtime with an ability to inject parameters into the bean.
Here is an example:
// Note, I've removed all the annotations, I'll use java configurations for that, read below...
public class SomeBuilder {
private final SomeContext ctx;
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
Now the class SomeService will also slightly change:
public class SomeService {
private Function<SomeContext, SomeBuilder> builderFactory;
public void run(SomeContext ctx) {
SomeBuilder someBuilder = builderFactory.apply(ctx);
someBuilder.test();
}
}
And now you should "glue" it to spring in an advanced way with Java Configurations:
#Configuration
public class MyConfiguration {
#Bean
public Function<SomeContext, SomeBuilder> builderFactory() {
return ctx -> someBuilder(ctx);
}
#Bean
#Scope(value = "prototype")
public SomeBuilder someBuilder(SomeContext ctx) {
return new SomeBuilder(ctx);
}
#Bean
public SomeService someService() {
return new SomeService(builderFactory());
}
}
For more details with a really similar example, see this tutorial

How do I lazy load a spring bean?

So, hypothetically speaking, if I have an object, lets say it's a exception response I want to send back from a service BUT only if an exception is thrown as I have a different animal I'm sending back if everything's fine. How do I get it from Spring... but only if I need it? In other words, how do I avoid injecting objects I don't yet need and can't be sure that I will? How do I do lazy loading using Spring?
There are two possibilities:
#Lazy on the bean definition + ObjectFactory or Provider or on the injection point
#Lazy on the bean definition + #Lazy on the injection point
example #2
#Service
#Lazy
class LazyService {
LazyService() {
System.out.println("service");
}
String bar() {
System.out.println("bar");
return "bar";
}
}
#RestController
class Controller {
private final LazyService service;
Controller(#Lazy LazyService service) {
this.service = service;
System.out.println("controller");
}
#GetMapping("/")
String foo() {
System.out.println("foo");
return service.bar();
}
}
LazyService is instantiated when method Controller.foo() is called for the first time.

Is it possible to #inject with guice the ctor and a member at the same time?

Here is my code:
public class RoutingRequestUrlRepository implements IRoutingRequestUrlRepository {
String fileName;
RandomAccessFile randomAccessFile;
int chunkSizeInLines;
private IFileHandler<String> fileHandler;
#Inject GlobalSettings globalSettings;
#Inject
public RoutingRequestUrlRepository(#Named("requests_file") String fileName,
#Named("request_sample_file") IFileHandler samplesFileHandler) {
.....
why does globalSettings isn't iniallized?
Is my syntax correct? Can I #Inject ctor and a class member at the same time?
I think it is not a good idea to inject both directly fields and by constructor. I recommend to always inject by constructor because doing that exposes clearly dependencies.
Anyway, injecting a field like you have done is correct for guice, and you can inject this field into constructor or directly.
You can use also method injection to instantiate your field as follow:
#Inject
public void setGlobalSettings(GlobalSettings settings) {
this.settings = settings;
}
You can also inject Injector class, and call explicitly injector.injectMembers( this );
All these possibilities are explained here: https://github.com/google/guice/wiki/Injections
As your syntax seems to be correct, you should verify the binding or the version of guice library used in your project if your are sure that this field is not really instantiated

Spring #Controller and RequestMapping calling different services depending on a given parameter

Let's assume I have this code:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private SomeService aService;
#RequestMapping("/doStuff")
public void doStuff(#RequestParam("type") String type) {
aService.doStuff();
}
}
In my application I need to call a specific service depending on the specified type. All services implements the same interface. If I understand correctly SomeService cannot be an interface. I could use a service factory and instantiate the service depending on the type every time a new request is done, but this doesn't look very efficient.
Alternatively I could use a different controller for each different type of service (and encode the type in the REST URI), but this would imply a lot of code duplication since all the services basically implements the same interface.
My question is, assuming the called service depends on the passed parameter, what is the best pattern to adopt for this scenario?
Similar to RC.'s answer, instead of using a Map and adding the values by you, just let the Spring BeanFactory handle this for you:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private BeanFactory beanFactory;
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
SomeService aService = (SomeService)beanFactory.getBean(type);
aService.doStuff();
}
}
You could use a map here, something along this:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private SomeService someService;
#Autowired
private SomeOtherService someOtherService;
// ...
private final Map<String, ServiceCommonInterface> serviceMap = new HashMap<>();
#PostConstruct
private void postConstruct() {
serviceMap.put(typeForSomeService, someService);
serviceMap.put(typeForSomeOtherService, someOtherService);
}
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
// TODO: ensure type is correct (an enum might be handy here)
serviceMap.get(type).doStuff();
}
}
Or better, as stated in comments you can leverage qualifiers:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private ApplicationContext applicationContext;
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
// TODO: ensure type is a correct bean name
applicationContext.getBean(type, ServiceCommonInterface.class).doStuff();
}
}
Depending on the number of types you wish to support there are two options I see.
1) Autowire in a factory as you mentioned and lazily create each service as needed. If the services are stateless you could keep a reference to the object after created so would only need to create once per type.
2) Autowire in a Spring Map with the key being your types and the value being the correct service to use. Then when your receive the type, can retrieve the correct service impl for your map.
For example for map: http://www.mkyong.com/spring/spring-collections-list-set-map-and-properties-example/ or see How to inject a Map<String, List> in java springs?
Both of these require you to create an interface for your service which is something you said is possible.

#Cacheable breaks DependencyInjection

I stumbled upon a case where the AOP proxy created by using #Cacheable breaks the dependency injection in Spring 3.1.1. Here is my scenario:
I have an interface and a class implementing this interface using #Cacheable at the implemented method.
Example interface:
public interface ImgService {
public byte[] getImage(String name);
}
Example implementation:
public class ImgServiceImpl implements ImgService {
#Cacheable(cacheName = "someCache")
public byte[] getImage(String name){//TODO};
protected String someOtherMethod(){//};
}
I also have to JUnit test classes - one which injects the interface and one the implementation:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceTest {
#Inject
private ImgService;
}
and
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {
#Inject
private ImgServiceImpl;
}
Dependency injection for the interface works fine. However, when I get to injecting the implementation in the second test class I get an "Injection of autowired dependencies failed". I was able to debug it and it appears that ClassUtils.isAssignableValue() wrongly compares the desired type to the proxy class. It is called by DefaultListableBeanFactory. What is even stranger is that if I remove the #Cacheable annotation from the implemented method and add it to some other protected/private method, dependency injection works fine again. Is this a bug and what would be the correct approach to handle this situation?
It's not a bug, it's an expected side-effect of using JDK dynamic proxies for AOP implementation.
Since all calls to the cacheable method of ImgServiceImpl should go through the dynamic proxy of type ImgService, there is no way to inject this dependency into a field of type ImgServiceImpl.
When you move #Cacheable to private or protected method, injection works because #Cacheable doesn't take effect in this case - only public methods can be adviced using proxy-based AOP.
So, you should either declare fields to be injected as ImgService, or configure Spring to use target class-based proxies instead using proxy-target-class = "true".
Yet another option is to configure Spring to use AspectJ-based AOP implementation (requires compile-time or load-time weaving).
It's applicable to all AOP-based features provided by Spring (transactions, security, async execution, cache, custom aspects, etc).
See also:
7.6 Proxying mechanisms
OK, so here is the solution I came up finally. I implemented a simple method that attempts to extract the target object from the proxy based on its implementation of the org.springframework.aop.framework.Advised class:
#SuppressWarnings({"unchecked"})
public static <T> T getTargetObject(Object proxy, Class<T> targetClass) {
if (AopUtils.isJdkDynamicProxy(proxy)) {
try {
return (T) ((Advised)proxy).getTargetSource().getTarget();
} catch (Exception e) {
return null;
}
} else {
return (T) proxy;
}
}
My implementation test class now looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {
#Inject
private ImgService imgService;
private ImgServiceImpl imgServiceImpl;
#PostConstruct
public void setUp() {
imgServiceImpl = getTargetObject(imgService, ImgServiceImpl.class);
}
}

Categories