Spring 4 Join point to get method argument names and values - spring

I am using Spring 4.3. Is it possible to get method parameter names and values passed to it? I believe this can be done using AOP (before advice) if possible could you please give me a source code.

The following works as expected (Java 8 + Spring 5.0.4 + AspectJ 1.8.13):
#Aspect
#Component
public class SomeAspect {
#Around("#annotation(SomeAnnotation)")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
System.out.println("First parameter's name: " + codeSignature.getParameterNames()[0]);
System.out.println("First argument's value: " + joinPoint.getArgs()[0]);
return joinPoint.proceed();
}
}

CodeSignature methodSignature = (CodeSignature) joinPoint.getSignature();
String[] sigParamNames = methodSignature.getParameterNames();
You can get method signature arguments names.

Unfortunately, you can't do this. It is a well-known limitation of bytecode - argument names can't be obtained using reflection, as they are not always stored in bytecode.
As workaround, you can add additional annotations like #ParamName(name = "paramName").
So that, you can get params names in the following way:
MethodSignature.getMethod().getParameterAnnotations()
UPDATE
Since Java 8 you can do this
You can obtain the names of the formal parameters of any method or constructor with the method java.lang.reflect.Executable.getParameters. (The classes Method and Constructor extend the class Executable and therefore inherit the method Executable.getParameters.) However, .class files do not store formal parameter names by default. This is because many tools that produce and consume class files may not expect the larger static and dynamic footprint of .class files that contain parameter names. In particular, these tools would have to handle larger .class files, and the Java Virtual Machine (JVM) would use more memory. In addition, some parameter names, such as secret or password, may expose information about security-sensitive methods.
To store formal parameter names in a particular .class file, and thus
enable the Reflection API to retrieve formal parameter names, compile
the source file with the -parameters option to the javac compiler.
https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html

In your AOP advice you can use methods of the JoinPoint to get access to methods and their parameters. There are multiple examples online and at stackoverflow.
Get method arguments using spring aop?
For getting arguments: https://docs.jboss.org/jbossaop/docs/2.0.0.GA/docs/aspect-framework/apidocs/org/jboss/aop/joinpoint/MethodInvocation.html#getArguments()
For getting method details: https://docs.jboss.org/jbossaop/docs/2.0.0.GA/docs/aspect-framework/apidocs/org/jboss/aop/joinpoint/MethodInvocation.html#getMethod%28%29

Related

List<List<String>> mapped to List<String>

I'm learning how to use Mapstruct in a Spring Boot and Kotlin project.
I've got a generated DTO (ThessaurusDTO) that has a List and I need this mapped into a List on my model (Vocab).
It makes sense that MapStruct can't map this automatically, but I know for a fact that the first list will always be size = 1. I have no control on the API the DTO model belongs to.
I found on the documentation that I can create define a default method implementation within the interface, which would loosely translate to a normal function in Kotlin
My mapper interface:
#Mapper
interface VocabMapper {
#Mappings(
// ...
)
fun thessaurusToVocab(thessaurusDTO: ThessaurusDTO): Vocab
fun metaSyns(nestedList: List<List<String>>): List<String>
= nestedList.flatten()
}
When I try to do a build I get the following error:
VocabMapper.java:16: error: Can't map collection element "java.util.List<java.lang.String>" to "java.lang.String ". Consider to declare/implement a mapping method: "java.lang.String map(java.util.List<java.lang.String> value)".
It looks like mapStruct is still trying to automatically do the mapping while ignoring my custom implementation. Am I missing something trivial here?
I found on the documentation that I can create define a default method implementation within the interface, which would loosely translate to a normal function in Kotlin
From my understand of what I found online, Kotlin does not properly translate an interface function into a default method in Java, but actually generates a class that implements the interface.
If that's the problem, you can annotate metaSyns with #JvmDefault:
Specifies that a JVM default method should be generated for non-abstract Kotlin interface member.
Usages of this annotation require an explicit compilation argument to be specified: either -Xjvm-default=enable or -Xjvm-default=compatibility.
See the link for the difference, but you probably need -Xjvm-default=enable.
I've seen to have fixed this by relying on an abstract based implementation, instead of using an interface.
From my understand of what I found online, Kotlin does not properly translate an interface function into a default method in Java, but actually generates a class that implements the interface.
https://github.com/mapstruct/mapstruct/issues/1577

How Spring gets parameter names without debug information

#RequestMapping("/form")
public String form(Model model, Integer id)
For example ,spring can know parameter id's name is id and bind request param's value to it in runtime
This is a feature of javac introduced in JDK 8. You need to include -parameters javac command line option to activate it. Then you will be able to get parameter names like this:
String name = String.class.getMethod("substring", int.class).getParameters()[0].getName()
System.out.println(name);
From Spring documentation 3.3 Java 8 (as well as 6 and 7)
You can also use Java 8’s parameter name discovery (based on the
-parameters compiler flag) as an alternative to compiling your code with debug information enabled.
As specified in the javadoc for ParameterNameDiscoverer class:
Parameter name discovery is not always possible, but various
strategies are available to try, such as looking for debug information
that may have been emitted at compile time, and looking for argname
annotation values optionally accompanying AspectJ annotated methods.
This is the link to related JIRA item in Spring project Java 8 parameter name discovery for methods and constructors
JDK Enhancement Proposal JEP 118: Access to Parameter Names at Runtime
It's the WebDataBinder who does it for you.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/WebDataBinder.html
As you can see on the api, WebDataBinder is for data binding from web request parameters to JavaBean objects.
WebDataBinder is also responsible for validation and conversion, so that's why if you have customized editor or validator, you have to add them to your webDataBinder in the controller like below.
#Controller
public class HelloController {
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(yourValidator);
binder.registerCustomEditor(the clazz to conver to.class, "the name of the parameter", yourEditor);
}
...
for more information, you have to check the document
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html

Faker for Grails gives variable not defined error

I have a Grails 2.4.4 application configured with spring-security-core. I want to generate fake users in the BootStrap using the faker plugin. However when I instantiate the bean fakerService in BootStrap and try using it ie. fakerService.firstname(), I get an error :
ERROR context.GrailsContextLoaderListener - Error initializing the application: Cannot invoke method firstName() on null object
Message: Cannot invoke method firstName() on null object
I'm just a beginner in Grails. Am I doing the Dependency Injection wrong?
http://pasteboard.co/rvbihRU.png
Yes you are :)
A little background. When you add a class-scope variable (a field) in a Groovy class without an explicit scope modifier (e.g. public, private, protected) it defaults to public just like classes and methods. But it is considered a property in the JavaBean sense, so the Groovy compiler creates a getter and a setter for you based on the name. So if you declare def foo and String bar (it doesn't matter whether you specify the type) you'll get Object getFoo(), void setFoo(Object), String getBar(), and void setBar(String) methods (you should decompile a POGO class with a decompiler and see this for yourself - it's pretty cool stuff - I recommend JD-GUI, but use whatever you prefer). If you had declared any of them already Groovy would skip that one and not overwrite yours. This is cool because you can treat the variable like a simple public field, but at any time add getter and/or setter logic and not affect calling clients (Groovy or Java, since the Java classes would have been calling the getter and setter all along, and Groovy calls the getter and setter for you when you read or write a property).
So why am I babbling on about this? Dependency injection is done by Spring - you're injecting Spring beans. There are various ways to do this, but the default in Grails is to use autoinject-by-name. So for any bean registered in the ApplicationContext and special classes like BootStrap, integration tests, etc., Spring scans the methods looking for setters. It strips off "set" and lowercases the next letter, and that's the "property" name of the setter. If there's a bean with that name in the ApplicationContext, Spring will call that setter, passing the bean with that name, and if the types are in sync, your class will have a reference to that bean.
You added a local variable. Nothing special happens to local variables, and Spring doesn't see them, and they're not candidates for dependency injection. Move the declaration to class scope, before the init closure, e.g.
class BootStrap {
def fakerService
def init = {
...
}
}
and the Groovy compiler will add a getFakerService method that isn't of much interest, but also a setFakerService method that Spring will see. It will determine that its property name is "fakerService", see that there is a bean with that name, and call the setter. This all happens before Grails calls the init closure, so at that point the value will be a non-null FakerService eagerly awaiting your calls.

jersey - How to use a Resource Method with multiple #FormParam of custom type

I use jersey and have a method in my Resource class which has multiple parameters. These parameters are filled using #FormParam but the problem is, the type of the parameters are custom java types, not some primitives or String. I want to convert the value of parameters from json to custom java types. If I use #Cosume(MediaType.APPLICATION_JSON), then I cannot use multiple parameters and if I remove it, parameters cannot be converted from json to their java instances.
#POST #Path("/add")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public String add(#FormParam("source") BookEntity source, #FormParam("author") AuthorEntity a) throws JsonGenerationException, JsonMappingException, IOException, TransformationException
{
...
}
If I change the parameter types to String and then use Jackson deserialization, I can deserialize json parameters to java instances but I want to do it for other methods too and get it done automatically.
I tried to use the approach used in Custom Java type for consuming request parameters but I cannot make it work.
You can use a custom type mapper.
See this answer
Anyway by default Jersey tries to map received json object representation using JAXB. Obiously you must annotate your objects.

Spring MVC request / command objects

Is there a convenient way to turn something like this..
#RequestMapping("/some/action/{q}/...")
public void doSomething(#PathVariable("q"), oh, my, god, a, billion, annotated parameters) { .. }
Into something like this..
#RequestMapping("/some/action/{q}/...")
public void doSomething(NiceEncapsulatingRequetObject request) { .. }
With Spring MVC?
After checking the docs, it doesn't seem like it is supported out of the box. You could try to create your own HandlerMethodArgumentResolver which gives you this feature. You might run into some issues since you'll need a circular reference between your implementation and the HandlerMethodArgumentResolverComposite instance. Nevertheless I think it should be possible.
Yes spring supports this out of the box, it is usualy refered to as bean binding.
Basicly you create an object with paramaters with the same name,
so if you have a paramater "q", your object should contain a private string q with both getter and setter present. It's also prefered not to use any constructors.
Spring will just fill in the paramaters it has in your object and pass it via the method's paramater.
You can create you own object like NiceEncapsulatingRequetObject and it's attributes should be String oh, Integer my etc. If you send the request with the exact names it will work

Resources