Make object spring managed - java

How can I get an already existing object spring managed? I would like to hook it up to Springs AoP capabilities using aspectj. I know this to be a challenge since Spring AoP uses dynamic proxies which probably are created along with the object.
Why do I need this?
I have a third-party class which takes a constructor argument which is only known in runtime,
hence it seems I cannot add it to my applicationContext or use springs FactoryBean interface for construction. Is there any other way?
I've already tried the following without great success:
Obj obj = new ThirdPartyObj("runtime constructor arg");
appContext.getAutowireCapableBeanFactory().initializeBean(obj, "Obj");
It might be spring-managed, but I still cannot use it to trigger aspects.
[EDIT] axtavt pointed out the problem is that I don't use the object returned from initializeBean(..). Both mentioned approaches work, but only if:
Using interface ObjInterface obj = (ObjInterface) ac.getBean("obj", args); or we will get a:
java.lang.ClassCastException: $Proxy28 cannot be cast to com.company.Obj
Not using interface but enable CGLIB. This requires a non-private default constructor, or we will get a:
java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given

Why not create a new class that wraps the functionality of ThirdPartyObj, and make that Spring-managed. Dependencies can then be injected into its fields and method parameters, and passed on the the instantiated ThirdPartyObj.

You should be able to trigger aspects using this (note that you need to use returned object which can be a proxy):
Obj obj = new ThirdPartyObj("runtime constructor arg");
obj = appContext.getAutowireCapableBeanFactory().initializeBean(obj, "Obj");
Another option is to declare it as a regular bean and pass the constructor argument via getBean():
Obj obj = appContext.getBean("Obj", "runtime constructor arg");

How about annotating the domain object with #Configurable annotation? I myself haven't tried it but seems like it might helpful in your scenario. AspectJ and Spring would create a managed object with attributes defined in the bean. The then created object instance can be used.

Related

How does Mockito handling Suppliers?

On the first screenshot you can see my test class. This class is annotated with #ExtendWith({MockitoExtension.class}) and also the tested service is annotated with #InjectMocks. On the second screenshot you can see the tested service.
Why does Mockito uses the long supplier in both cases?
Mockito uses different strategies when injecting mocks in this order:
Constructor injection
Property setter injection
Field injection
Field injection will not work in your example, since the service's fields are declared final.
Since the fields are declared final and the code snippet you showed does not have a field initializer, I assume that you have a constructor with the Supplier args. E.g.
public SomeService(Supplier<String> stringSupplier, Supplier<Long> longTimeSupplier) {
this.stringSupplier = stringSupplier;
this.longTimeSupplier = longTimeSupplier;
}
Thus Mockito will try the constructor injection, find the constructor with the two Supplier parameters and tries to resolve the arguments.
Mockito then finds the two Supplier mocks in the test, but it can not see the generic type due to type erasure. Thus Mockito sees the constructor like this:
public SomeService(Supplier stringSupplier, Supplier longTimeSupplier)
Mockito can also not decide which Supplier to use based on the parameter name, because the normal Java reflection API does not provide that information. So the name of the mocks, will not be taken into account.
There are libraries like paranamer that read the bytecode and extract the debug information to read the parameter names, but Mockito doesn't use that libs.
Thus Mockito just injects the first matching mock which is Supplier<String> stringSupplier in your case. Even your issues is related to generics, Mockito would also act the same way when you have two parameters of the same type that are not generic.
I assumed that you have a constructor that takes the two Supplier. So you can just invoke it in your test's before.
#BeforeEach
public void setup() {
service = new SomeService(stringSupplier, longSupplier);
}
If you can not access the constructor, e.g. it has package scope, you need to invoke it using reflection and set the accessible property to true
#BeforeEach
public void setup() throws Exception {
Constructor<SomeService> constructor = SomeService.class.getConstructor(Supplier.class, Supplier.class);
constructor.setAccessible(true);
service = constructor.newInstance(stringSupplier, longSupplier);
}
PS If you want to remove the final, make sure that the mocks are either named after the fields in the service longTimeSupplier vs. longSupplier or you use #Mock(name = "longTimeSupplier").

How to ensure a Java CDI framework implementation will inject an instance at a custom injectionpoint of a custom bean?

Given that:
I am using Weld as my underlying CDI framework implementation.
I have a class ClassWithoutControl without a no-arg constructor (a.k.a. not a valid Bean).
I create a custom Bean for that class.
I add this bean via an Extension when a AfterBeanDiscovery event is triggered.
When create(CreationalContext<> ctx) is called on this bean I construct a new instance of class ClassWithoutControl.
I have created a set of InjectionPoints for that custom Bean.
One of those InjectionPoints is a field of ClassWithControl.
How do I ensure that CDI will inject an instance of the type required by the InjectionPoint?
In other words, how do I construct a custom bean for a class over which I have no control, in such a way that I can still inject an instance of a class over which I do have control into a particular field?
My current (non-functional) code looks like this:
void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager beanManager) {
final AnnotatedType<ClassWithoutControl> annotatedType = beanManager.createAnnotatedType(ClassWithoutControl.class);
AnnotatedField<ClassWithoutControl> annotatedField = null;
for (AnnotatedField<? super ClassWithoutControl> field : annotatedType.getFields()) {
if ("field".equals(field.getJavaMember().getName()) && ClassWithControl.class.equals(field.getJavaMember().getType())) {
annotatedField = (AnnotatedField<ClassWithoutControl>) field;
break;
}
}
final InjectionPoint injectionPoint = beanManager.createInjectionPoint(annotatedField);
final HashSet<InjectionPoint> injectionPoints = new HashSet<>();
injectionPoints.add(injectionPoint);
BiFunction<CreationalContext<ClassWithoutControl>, Bean<ClassWithoutControl>, ClassWithoutControl> creator = (creationalContext, bean) -> {
final InjectionTarget<ClassWithoutControl> injectionTarget = beanManager.createInjectionTarget(annotatedType);
ClassWithoutControl instance = new ClassWithoutControl(this.paramater1, this.parameter2);
injectionTarget.inject(instance, creationalContext);
injectionTarget.postConstruct(instance);
return instance;
};
customBeanBuilder.setInjectionPoints(injectionPoints).setCreator(creator);
final BeanAttributes<ClassWithoutControl> beanAttributes = beanManager.createBeanAttributes(annotatedType);
customBeanBuilder.setBeanAttributes(beanAttributes);
abd.addBean(customBeanBuilder.build());
}
CustomBeanBuilder is a class which creates an instance of CustomBean which extends Bean.
The creator BiFunction is called in the create(CreationalContext ctx) function of a CustomBean.
The creator's parameters are the CreationalContext as passed to create() and a CustomBean (this).
I know why the above does not work. A NonProducibleInjectionTarget is returned by Weld, since the AnnotatedType that is used by weld to create the InjectionTarget has no no-args constructor.
I am however looking for a way that I can do this, without having to depend on Weld's internal implementations.
I can't find a way to trick the CDI into accepting my instance of ClassWithoutControl while retraining the ability to inject another instance.
I have looked at https://docs.jboss.org/weld/reference/latest/en-US/html_single/#_wrapping_an_injectiontarget but I do not quite understand how to create such a wrapper. So any help in that direction would be appreciated as well.
I dove into Weld (my current CDI implementation) to see if I could find a way to resolve this.
Unfortunately I cannot provide the InjectionTarget due to the missing no-args constructor in the class I do not control.
BeforeBeanDiscovery reveals that the bean for the class is being added by the Extension. However due to the fact that it is missing the no-args constructor an InjectionTarget is never created.
I've attemped to resolve this by wrapping the AnnotatedType during ProcessAnnotatedType and inserting a AnnotatedConstructor and returning it with the constructors of the original AnnotatedType. This can be done, unfortunately AnnotatedConstructor has a getJavaMember() method which returns the original Constructor. In my case that Constructor (java-member) does not exist and seeing as you cannot instantiate a new instance of Constructor. This was a dead end as there are no other means of obtaining a custom instance of Constructor.
I'm now exploring byte-code manipulation libraries such as byte-buddy. These would enable me to add a no-args constructor at run-time. There would be no repercussions for me as the no-args constructor sole purpose is to ensure that CDI marks the class as a valid Bean.

How do you figure out whether a CLASS is a spring proxy?

In a nutshell
In the AopUtils, we have
/**
* Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
* <p>This method additionally checks if the given object is an instance
* of {#link SpringProxy}.
* #param object the object to check
* #see #isJdkDynamicProxy
* #see #isCglibProxy
*/
public static boolean isAopProxy(#Nullable Object object) {
return (object instanceof SpringProxy && (Proxy.isProxyClass(object.getClass()) ||
object.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)));
}
In now want to check whether a bean class is proxied without instantiating the bean (i.e. just with its class) in a BeanFactoryPostProcessor.
I thought I could just "translate" above method:
private fun <T> isAopProxyClass(candidate: Class<T>): Boolean {
return SpringProxy::class.java.isAssignableFrom(candidate)
&& (
Proxy.isProxyClass(candidate)
|| candidate.name.contains(CGLIB_CLASS_SEPARATOR)
)
}
But this does not detect proxies because SpringProxy::class.java.isAssignableFrom(candidate) is false even for obviously proxied classes.
How do I make this work?
Full picture
I'm in a BeanFactoryPostProcessor and I need the un-proxied bean classes to access certain annotated methods by reflection.
Access happens in a lambda function that will first use the ApplicationContext to retrieve the bean for the class. The bean must not be forcibly instantiated in this BeanFactoryPostProcessor (and in fact should throw an exception if it does because some beans are session-scoped).
Interesting question. 😀
The three classes highlighted in your screenshot are CGLIB proxies but not AOP proxies. Look at their class names: They are all Spring configuration classes. But that does not make them normal Spring proxies, especially not AOP proxies. For the difference between #Component and #Configuration, also with regard to proxying and self-invocation behaviour, please read my answer here.
Consequently, a Spring #Configuration class also does not implement SpringProxy like normal Spring proxies.
So basically your solution works just fine, no need to worry, as far as I can see.
P.S.: I am a Java guy, not a Kotlin person. So I re-implemented your code from the screenshot in Java, so I could debug into it and reproduce your situation. But even in Kotlin I would have had to re-type everything. Please next time publish the code as copyable text, not just as an image.
Update: If you check something like the content of
beanClasses.stream()
.filter(aClass ->
aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &&
aClass.getSuperclass().getAnnotation(Configuration.class) == null
)
.collect(Collectors.toList())
you should see an empty collection, whereas
beanClasses.stream()
.filter(aClass ->
aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &&
aClass.getSuperclass().getAnnotation(Configuration.class) != null
)
.collect(Collectors.toList())
should yield the same list of classes as simply
beanClasses.stream()
.filter(aClass -> aClass.getName().contains(CGLIB_CLASS_SEPARATOR))
.collect(Collectors.toList())
I.e. all remaining CGLIB proxies in beanClasses should in fact be configurations, not normal Spring proxies.
In now want to check whether a bean class is proxied without instantiating the bean (i.e. just with its class) in a BeanFactoryPostProcessor.
This task seems impossible to me since there are 100500 ways to proxy bean at runtime (bean post processor, advice, etc). Technically you can use random to decide whether to proxy some bean. You can have two instances of a given class (e.g. with different qualifiers), one proxied, other not.

How do I get the real annotation instead of a proxy from ConstraintDescriptor::getAnnotation? (javax.validation Apache Bval)

I'm trying to get a Class object reference for the annotation which caused a ConstraintViolation in the javax.validation package (Apache Bval implementation).
After getting some ConstraintViolations, I pass them into the following function:
private Class<?> getConstraintAnnotation(final ConstraintViolation<?> constraintViolation) {
return constraintViolation
.getConstraintDescriptor()
.getAnnotation()
.getClass();
}
And this returns a class object whose getName(), getCanonicalName(), and getTypeName() all return "java.lang.reflect.Proxy".
Weirdly enough, the toString() method of the Class object returns back
"class com.sun.proxy.$Proxy10".
Is there a way for me to get the real annotation classes and not these proxies? I would ideally like to map the built-in annotations to error codes (without having to overwrite the message every time I use it).
Java annotation is implemented by Proxy. The Proxy do be the really annotation. You should use Annotation.annotationType rather than Object.getClass to get the real annotation class.

Spring AOP advice is not getting called

My advice is not getting called for method getPOInvoice method from this method, but if I call it separately then advice is getting called and getPOInvoice and getPOInvoice is declared in same class.
public StreamingOutput getPDFStream(String invoiceId, String versionNumber) throws TugnavException {
final POInvoice poInv = getPOInvoice(invoiceId, versionNumber);
...
}
My advice is:
#AfterReturning(value="execution(* com.tugnav.service.facade.*.get*(..))", returning="doc")
public TugnavBaseDocument setupTimeCreatedString(TugnavBaseDocument doc){
...
}
If I call this method from another class then advice is getting called.
Why is it not getting called internally?
You need to read the documentation about proxies here.
Basically, because of the way proxies are generated, a instance method call within another instance method will not trigger an aspect (or proxy behavior).
Given:
class FooBar {
// advised method
public void foo() {
bar();
}
// would've been advised
public void bar() {
// ... do something
}
}
Say an instance of FooBar was proxied, a call to foo() will trigger the aspect because from outside you have a reference to the proxy object. When you call bar() internally, you have access to this (equivalent to this.bar()) which is a reference to the target instance, the actual object, so there is no added behavior.
There are a few ways to solve this problem, but probably not the way you want. See the answer to this question.
AOP would not normally work this way.
AOP is added as an aspect through Proxies, to compiled class, so it does not have any effect on the internal class invocations.
When it's an outer cal, you are actually referring to some Proxy which intercepts your call and triggers appropriate AOP calls.
When it's internal cal, inside the class, it is a direct call, without any Proxy involved.
As a solution you can do following:
Refactore service you are using, to exclude internal calls
Alter bytecode on Class loading, with your AOP functionality :)
If you want that your advice is called for getPOInvoice method when you are invoking from method getPDFStream(..), both in the same bean, you can't use a proxy-based AOP, like Spring use by default. Instead you should use load time weaving(LTW) with AspectJ.
http://static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/html/aop.html#aop-aj-ltw

Categories