Binding <annotationField> to advice body getting inconsistent binding [duplicate] - java

I am trying to apply a #before aspect on two different methods in two different paths
class Service1{
public Object applyX(X x){
//code
}
}
class Service2{
public OtherObject applyY(Y y){
//code
}
}
and I have my aspect class:
#Aspect
#Component
public class MyProcessor {
#Before("execution(* com.a.b.c.Service1.applyX"
+ " (com.xp.X)) "
+ "&& args(engineEvaluationRequest) || "
+ "execution(* com.a.b.d.Service2.applyY"
+ " (com.yp.Y))"
+ "&& args(y)")
public void process(X x ,Y y){
//code
}
}
I am getting an error org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'objectMapperConfigurer' defined in class path resource [springfox/documentation/spring/web/SpringfoxWebMvcConfiguration.class]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 inconsistent binding
and I don't understand what went wrong. can I get help?
Thanks!

The error message inconsistent binding already says it: Your variable binding with args() is inconsistent insofar as it is ambiguous due to the || (logical or) operator. Either X is found and can be bound or Y, but the other one would be undefined. You might have assumed that if a variable is not bound it defaults to null, but this assumption is wrong. AspectJ does not work like that. Your pointcut must bind variables unambiguously to the corresponding advice parameters.
Edit: Because || is a logical OR and thus non-exclusive (unlike XOR), it might even happen that two OR branches match at the same time. Then which matching argument or annotation should be bound? This really is ambiguous.
So how can you fix it? Just use two pointcut/advice pairs instead of just one. If the advice is complex and contains a lot of code you can still factor out that code into a helper method taking a JoinPoint parameter or so.

Related

Spring bean with #Autowire in superclass

I have a subclass like below:-
#Component
public class Subclass extends Superclass {
//few inherited methods implementation
}
Superclass is like below:-
#Component
public class Superclass implements InterfaceA {
#Autowired
#Qualifier("envBean")
private EnvironmentBean envBean;
private DateTime effective_date = envBean.getProperty("effective.date");
}
Now while deploying the application, I am getting below errors
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name "Subclass"
Constructor threw exception; nested exception is java.lang.NullPointerException
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [Subclass]:Constructor threw exception; nested exception is java.lang.NullPointerException.
and finally I saw -
Caused by: java.lang.NullPointerException: null
at Superclass <init> (SuperClass.java:{lineNumber}
which is at the below line :-
**envBean.getProperty("effective.date");**
I have tried using constructor injection of EnvironmentBean property from the subclass itself
Tried configuring it in xml and to instantiate Superclass bean with constructor injection.
Does someone have any idea how to resolve it?
Maybe you can try interface -> InitializingBean, and override method 'afterPropertiesSet', then you can assign effective_date value. just like:
#Override
public void afterPropertiesSet() {
effective_date = envBean.getProperty("effective.date");
}
It seems that this is because Spring has to first create an instance of the class Superclass and only then inject EnvironmentBean. That is, when the class Superclass is instantiated, Java will try to instantiate the field DateTime effective_date even before Spring tries to inject the dependency #Autowired #Qualifier("envBean") private EnvironmentBean envBean;. At this time, envBean refers to null. Hence, this will surely throw an NPE. (My view.)
So, not sure if this is really related to the class hierarchy itself.
There must be a class called EnvironmentBean It must be annotated with any one of Spring stereotype shown in doc https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/package-summary.html
Component - Indicates that an annotated class is a "component".
Controller - Indicates that an annotated class is a "Controller"
Indexed - Indicate that the annotated element represents a stereotype for the index.
Repository - Indicates that an annotated class is a "Repository", originally defined by Domain-Driven Design (Evans, 2003) as "a mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects".
Service - Indicates that an annotated class is a "Service", originally defined by Domain-Driven Design (Evans, 2003) as "an operation offered as an interface that stands alone in the model, with no encapsulated state."

Spring: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)

I could not find a solution on this site to the following problem when I use Spring with annotations (not XML).
Any suggestion what I am doing wrong?
I have the following simple class. I do not extend it somewhere else.
#Component
#Scope("prototype")
public class Matryoshka
{
private Matryoshka[] matryoshki;
public Matryoshka(Matryoshka[] matryoshki)
{
this.matryoshki = matryoshki;
}
}
Then I try to create instances of this class by Spring's <T> T BeanFactory.getBean(Class<T> var1, Object... var2)
From within a Spring scanned class I do something like:
Matryoshka matryoshka = new Matryoshka(new Matryoshka[] {});
matryoshka = applicationContext.getBean(matryoshka.getClass(), new Matryoshka[] {matryoshka, matryoshka});
//matryoshka = (Matryoshka) applicationContext.getBean("matryoshka", new Matryoshka[] {matryoshka, matryoshka}); //also gives same error
Error message is:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'matryoshka' defined in file [/home/...../Matryoshka.class]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
UPDATE:
It looks like BeanFactory.getBean(Class<T> var1, Object... var2) works when the constructor of the bean I try to instantiate has a fixed number of parameters. E.g.
public Matryoshka(Matryoshka matryoshki1, Matryoshka matryoshki2),
but not public Matryoshka(Matryoshka[] matryoshki)
or public Matryoshka(Matryoshka... matryoshki).
Is it possible to make Spring work with constructors with indefinite number of parameters?
I have seen something similar (not a constructor though) in the following answer
A function I want to Spring-inject takes varargs. Should I provide an overload that takes a list instead?
but they used XML configuration.

#ConfigurationProperties and unresolvable properties

Suppose the following configuration bean:
#ConfigurationProperties(prefix="foo")
private class FooProperties {
private String mystring;
private int myint;
}
and the following application.properties:
foo.mystring = ${bar.mystring}
foo.myint = ${bar.myint}
Notice the two properties are unresolvable since no properties starting with bar are defined. Here is what will happen:
foo.mystring is set to the string "${bar.mystring}" (without resolution)
foo.myint will cause a conversion error since the string "${bar.myint}" cannot be converted into a valid integer.
I would instead expect a kind of Unresolvable Property exception being thrown in this case. Just like what would happen if I had use #Value("${foo.mystring}").
Is that behavior expected?
Is there a way to make SpringBoot throw such exception in this case?
I would instead expect a kind of Unresolvable Property exception being
thrown in this case.
foo.myint is not throwing Unresolvable Property exception as it is getting resolved with ${bar.myint} whereas it fails at next step when Spring try to typecast String to int as every thing you store inside a Properties file is a String value unless you explicitly specified it's type, as:
foo.myint = (java.lang.Integer)${bar.myint}
Is that behaviour expected?
Probably Yes.
Is there a way to make SpringBoot throw such exception in this case?
Yes, for Global level you can use #ControllerAdvice and if you want to have it at Controller level then you can go with #ExceptionHandler

Guava ImmutableBiMap becomes LinkedHashMap and cause Spring autowiring mistake

I have ImmutableBiMap filled with 2 simple Spring beans.
OS: Manjaro Linux
JDK version: 1.8.0.102 Oracle
Spring version: 4.3.4.RELEASE from
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Athens-SR1</version>
Creating context throws:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Illegal arguments for constructor; nested exception is java.lang.IllegalArgumentException: argument type mismatch
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
As following screen show, when exception is throw by Spring's BeanUtil argument is a LinkedHashMap instead of BiMap.
Minimal, Complete, and Verifiable example:
#Component
#Slf4j
public class TestControl {
private final BiMap<String, Integer> automatons;
#Autowired
public TestControl(BiMap<String, Integer> automatons) {
this.automatons = automatons;
log.info("automatons={}", automatons.keySet());
}
}
#Configuration
public class TextContext {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(
TextContext.class,
TestControl.class
);
BiMap bean = context.getBean(BiMap.class);
}
#Bean
BiMap<String, Integer> automatons() {
return ImmutableBiMap.of(
"Cellular Automaton", cellularAutomaton(),
"Monte Carlo Automaton", monteCarloAutomaton());
}
#Bean
Integer cellularAutomaton() {
return 6;
}
#Bean
Integer monteCarloAutomaton() {
return 5;
}
}
This is a side effect of how Spring handles some container types.
Even typed Maps can be autowired as long as the expected key type is
String. The Map values will contain all beans of the expected type,
and the keys will contain the corresponding bean names: [...]
A BiMap is a Map.
Spring isn't trying to inject your automatons bean into the TestControl. Instead, it's trying to find all beans of type Integer as the values, collecting them into a Map (LinkedHashMap as implementation of choice), and associating them with their bean name as the key.
In this case, it fails because the constructor expects a BiMap.
One solution is to inject by name.
#Autowired()
public TestControl(#Qualifier(value = "automatons") BiMap<String, Integer> automatons) {
this.automatons = automatons;
}
By specifying a qualifier with a name, Spring will instead try to find a bean (with the appropriate type) that's named automatons.
If you're not too attached to the final instance field, you could also inject the field with #Resource
#Resource(name = "automatons") // if you don't specify the name element, Spring will try to use the field name
private BiMap<String, Integer> automatons;
For reasons, this will only work 4.3+.
For beans that are themselves defined as a collection/map or array
type, #Resource is a fine solution, referring to the specific
collection or array bean by unique name. That said, as of 4.3,
collection/map and array types can be matched through Spring’s
#Autowired type matching algorithm as well, as long as the element
type information is preserved in #Bean return type signatures or
collection inheritance hierarchies. In this case, qualifier values can
be used to select among same-typed collections, as outlined in the
previous paragraph.
I would be OK with the behavior you're seeing in pre-4.3, but this does seem like a bug for Map. (The correct behavior occurs for List and array types.)
I've opened SPR-15117 to track it, which has now been resolved (2 day turnover, wow!).
Unless there is a giant bug in Spring (which I doubt) this must be a human/editor error.
I have re-created a somewhat simpler example, same basics I have just used String, Integer, Long, and Boolean since I didn't have your types - this simple example it works.
LinkedHashMap is not a BiMap, so it would be a bug if it is chosen as an autowire candidate. It almost sounds like the source and compiled code is out-of-sync, have you tried to delete the build folder and rebuild?
If rebuilding does not help, the only way to solve this is good old fashioned debugging.
Put a breakpoint inside LinkedHashMaps constructor and see where it is constructed, does it have anything to do with your beans?
Set a conditional breakpoint (so you only stop if beanName.equals( "automatonTypeSettingsControl") in org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor, and step through the method so you can see how spring finds the autowire candidate;
Make the simplest standalone example which fails, put it on Github and post a link, then some one else may be able to help you debug.
Observation: I have read a lot of StackOverflow post during the last month, and it looks like the average developer is not very good a debugging thirdparty code. You can actually learn a lot from debugging other peoples code, especially the spring framework code, which I find quite easy to read, considering the problem it is solving.
Edit This turned out to be a limitation in Spring as described in another answer. That said I ended up reproducing the error and reading trough the Spring code to find the exact code for this behavior in about 1 hour. I feel that many developers overlook debugging as a software discipline. For me it is one of the most important disciplins, since you probably spend most of your time working with code you did not write yourself.

Find the class from where the exception has generated. without using stacktrace and with #ControllerAdvice in AOP way

I am centralizing the exception handling in my app. But I want the messages to be internationalized and should be kept in a properties file. For this purpose I am planning to keep the keys with fully qualified name of the controller appended with .message. So my #ControllerAdvice marked class will handle the exception and fetch the message from the properties file based on the "fully qualified name of exception.ControllerName" and the problem is I am not getting the controller name and the method signatures from where the exception occurred. Is there a way to do so??
AFAIK there is no direct way to achieve this.
So you need to have an Around advice for your #ExceptionHandler annotated methods such as below
#Aspect
public class ExceptionLogging {
#Around(execution("#annotation(ExceptionHandler)"))
public Object logException(ProceddingJoinPoint pjp){
String name = pjp.getSignature().getName(); // tweak this as per your requirement
logger.info("Controller where Exception was raised - " + name);
Object obj = pjp.proceed();
return obj;
}
}

Categories