How do you provide a pointcut bound annotation to an advice method? - java

So I've defined pointcuts for all methods and all classes of a particular annotation... what I want to do is retrieve the annotation value on every method call. Here is what I have so far
#Aspect
public class MyAspect {
#Pointcut("execution(* my.stuff..*(..))")
private void allMethods(){}
#Pointcut("within(#my.stuff.MyAnnotation*)")
private void myAnnotations(){}
#Pointcut("allMethods() && myAnnotations()")
private void myAnnotatedClassMethods(){}
#Before("myAnnotatedClassMethods()")
private void beforeMyAnnotatedClassMethods(){
System.out.println("my annotated class method detected.");
// I'd like to be able to access my class level annotation's value here.
}
}

Yes, you can have Spring AOP supply the value of the annotation annotating your target object's class.
You'll have to use the binding forms documented in the specification and propagate an argument across your #Pointcut methods.
For example
#Pointcut("execution(* my.stuff..*(..))")
private void allMethods() {
}
#Pointcut("#within(myAnnotation)")
private void myAnnotations(MyAnnotation myAnnotation) {
}
#Pointcut("allMethods() && myAnnotations(myAnnotation)")
private void myAnnotatedClassMethods(MyAnnotation myAnnotation) {
}
#Before("myAnnotatedClassMethods(myAnnotation)")
private void beforeMyAnnotatedClassMethods(MyAnnotation myAnnotation){
System.out.println("my annotated class method detected: " + myAnnotation);
}
Spring, starting from the myAnnotations pointcut, will match the name given in #within with a method parameter and use that to determine the annotation type. That is then propagated down to the beforeMyAnnotatedClassMethods advice through the myAnnotatedClassMethods pointcut.
The Spring AOP stack will then lookup the annotation value before invoking your #Before method and pass it as an argument.
An alternative, if you don't like the solution above, is to simply provide a JoinPoint parameter in your advice method. You can use it to resolve the target instance with getTarget and use that value to get the class annotation. For example,
#Before("myAnnotatedClassMethods()")
private void beforeMyAnnotatedClassMethods(JoinPoint joinPoint) {
System.out.println("my annotated class method detected: " + joinPoint.getTarget().getClass().getAnnotation(MyAnnotation.class));
}
I'm unsure how this will behave if the target is further wrapped in other proxies. The annotation might be "hidden" behind proxy classes. Use it carefully.

Related

Java Spring #Valid on a method call [duplicate]

Hej,
I want to use the #Validated(group=Foo.class) annotation to validate an argument before executing a method like following:
public void doFoo(Foo #Validated(groups=Foo.class) foo){}
When i put this method in the Controller of my Spring application, the #Validated is executed and throws an error when the Foo object is not valid. However if I put the same thing in a method in the Service layer of my application, the validation is not executed and the method just runs even when the Foo object isn't valid.
Can't you use the #Validated annotation in the service layer ? Or do I have to do configure something extra to make it work ?
Update:
I have added the following two beans to my service.xml:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
and replaced the #Validate with #Null like so:
public void doFoo(Foo #Null(groups=Foo.class) foo){}
I know it is a pretty silly annotation to do but I wanted to check that if I call the method now and passing null it would throw an violation exception which it does. So why does it execute the #Null annotation and not the #Validate annotation ? I know one is from javax.validation and the other is from Spring but I do not think that has anything to do with it ?
In the eyes of a Spring MVC stack, there is no such thing as a service layer. The reason it works for #Controller class handler methods is that Spring uses a special HandlerMethodArgumentResolver called ModelAttributeMethodProcessor which performs validation before resolving the argument to use in your handler method.
The service layer, as we call it, is just a plain bean with no additional behavior added to it from the MVC (DispatcherServlet) stack. As such you cannot expect any validation from Spring. You need to roll your own, probably with AOP.
With MethodValidationPostProcessor, take a look at the javadoc
Applicable methods have JSR-303 constraint annotations on their
parameters and/or on their return value (in the latter case specified
at the method level, typically as inline annotation).
Validation groups can be specified through Spring's Validated
annotation at the type level of the containing target class, applying
to all public service methods of that class. By default, JSR-303 will
validate against its default group only.
The #Validated annotation is only used to specify a validation group, it doesn't itself force any validation. You need to use one of the javax.validation annotations like #Null or #Valid. Remember that you can use as many annotations as you would like on a method parameter.
As a side note on Spring Validation for methods:
Since Spring uses interceptors in its approach, the validation itself is only performed when you're talking to a Bean's method:
When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces, you'll be talking to the default Validator of the underlying ValidatorFactory. This is very convenient in that you don't have to perform yet another call on the factory, assuming that you will almost always use the default Validator anyway.
This is important because if you're trying to implement a validation in such a way for method calls within the class, it won't work. E.g.:
#Autowired
WannaValidate service;
//...
service.callMeOutside(new Form);
#Service
public class WannaValidate {
/* Spring Validation will work fine when executed from outside, as above */
#Validated
public void callMeOutside(#Valid Form form) {
AnotherForm anotherForm = new AnotherForm(form);
callMeInside(anotherForm);
}
/* Spring Validation won't work for AnotherForm if executed from inner method */
#Validated
public void callMeInside(#Valid AnotherForm form) {
// stuff
}
}
Hope someone finds this helpful. Tested with Spring 4.3, so things might be different for other versions.
#pgiecek You don't need to create a new Annotation. You can use:
#Validated
public class MyClass {
#Validated({Group1.class})
public myMethod1(#Valid Foo foo) { ... }
#Validated({Group2.class})
public myMethod2(#Valid Foo foo) { ... }
...
}
Be careful with rubensa's approach.
This only works when you declare #Valid as the only annotation. When you combine it with other annotations like #NotNull everything except the #Valid will be ignored.
The following will not work and the #NotNull will be ignored:
#Validated
public class MyClass {
#Validated(Group1.class)
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated(Group2.class)
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
In combination with other annotations you need to declare the javax.validation.groups.Default Group as well, like this:
#Validated
public class MyClass {
#Validated({ Default.class, Group1.class })
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated({ Default.class, Group2.class })
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
As stated above to specify validation groups is possible only through #Validated annotation at class level. However, it is not very convenient since sometimes you have a class containing several methods with the same entity as a parameter but each of which requiring different subset of properties to validate. It was also my case and below you can find several steps to take to solve it.
1) Implement custom annotation that enables to specify validation groups at method level in addition to groups specified through #Validated at class level.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ValidatedGroups {
Class<?>[] value() default {};
}
2) Extend MethodValidationInterceptor and override determineValidationGroups method as follows.
#Override
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
final Class<?>[] classLevelGroups = super.determineValidationGroups(invocation);
final ValidatedGroups validatedGroups = AnnotationUtils.findAnnotation(
invocation.getMethod(), ValidatedGroups.class);
final Class<?>[] methodLevelGroups = validatedGroups != null ? validatedGroups.value() : new Class<?>[0];
if (methodLevelGroups.length == 0) {
return classLevelGroups;
}
final int newLength = classLevelGroups.length + methodLevelGroups.length;
final Class<?>[] mergedGroups = Arrays.copyOf(classLevelGroups, newLength);
System.arraycopy(methodLevelGroups, 0, mergedGroups, classLevelGroups.length, methodLevelGroups.length);
return mergedGroups;
}
3) Implement your own MethodValidationPostProcessor (just copy the Spring one) and in the method afterPropertiesSet use validation interceptor implemented in step 2.
#Override
public void afterPropertiesSet() throws Exception {
Pointcut pointcut = new AnnotationMatchingPointcut(Validated.class, true);
Advice advice = (this.validator != null ? new ValidatedGroupsAwareMethodValidationInterceptor(this.validator) :
new ValidatedGroupsAwareMethodValidationInterceptor());
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
4) Register your validation post processor instead of Spring one.
<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/>
That's it. Now you can use it as follows.
#Validated(groups = Group1.class)
public class MyClass {
#ValidatedGroups(Group2.class)
public myMethod1(Foo foo) { ... }
public myMethod2(Foo foo) { ... }
...
}

Java annotation to determine whether the annotated method executes

In my methods, all my code is in an if block, testing certain condition.
public void myMethod() {
if (/* some condition */) {
//do something
}
}
I would like to do this by annotation - meaning the annotation will execute some code that will "decide" whether or not the method should be invoked.
#AllowInvokeMethod(/* some parameters to decide */)
public void myMethod() {
//do something (if annotation allows invokation)
}
Is this possible?
You can use Spring AOP to create an ASpect to advise methods that are annotated your custom annotation
For example create an FilteredExecution annotation to be specified on your methods
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface FilteredExecution{
Class<? extends ExecutionFilter> value();
}
ExecutionFilter is an interface to decide whether execution should occur
public interface ExecutionFilter{
boolean sholudExecute();
}
Then the aspect
#Aspect
#Component
public class FilteredExceutionAspect{
#Around("#annotion(filterAnnotation)")
public void filter(ProceedingJoinPoint pjp , FilteredExecution filterAnnotation){
boolean shouldExecute = checkShouldExecute(filterAnnotation);
if(shouldExecute){
pjp.proceed();
}
}
private boolean checkShouldExecute(FilteredExecution filterAnnotation){
//use reflection to invoke the ExecutionFilter specified on filterAnnotatoon
}
You need to setup your context so that your beans with the custom annotation are auto proxied by using #EnableAspectjAutoProxy on your configuration class
you can try this, documentation above metoh.
this annotation is show when the method is invoke, and see the document of
meothd
/**
* descripcion of the method
* #param value , any value
*/
public void myMethod(String value) {
//do something (if annotation allows invokation)
}
if you put this structure you can't see the documentation when you
call some method,,
//descripcion of the method
public void myMethod(String value) {
//do something (if annotation allows invokation)
}
in my case, it works, i hope this works for you

Invoke other method instead

I have two methods and one of them with an annotation, let's say:
#ReplacingMethod(bar)
public void foo() { ... }
public void bar { ... }
Is it possible to invoke bar instead of foo whenever foo is called, without jumping into the body of foo? I did some research on this and were not able to set a return value via reflections. Any suggestions?
You can achieve this using Aspect Oriented Programming, e.g. with Spring AOP. I don't think you can change method implementation in pure Java without AOP.
Let me give you an example how to achieve what you asked for with Spring AOP. First, define your annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReplacingMethod {
String value();
}
Then define an aspect that will do the actual replacing of method:
#Aspect // aspect is a module encapsulating your replacing functionality
public class ReplacingAspect {
// pointcut gives an expression selecting the "joint points" to be intercepted
#Pointcut("#annotation(example.annotation.ReplacingMethod)")
public void methodToBeReplaced() { }
// advice defining the code executed at joint points selected by given pointcut;
// in our case #Around is executed instead of the method call selected by pointcut methodToBeReplaced()
#Around("methodToBeReplaced()")
public void replaceMethodCall(ProceedingJoinPoint pjp) throws Throwable {
// get reference to the method to be replaced
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// extract the name of the method to be called from ReplacingMethod annotation
ReplacingMethod replacingMethodAnnotation = method.getAnnotation(ReplacingMethod.class);
String methodToCallName = replacingMethodAnnotation.value();
// use reflection to call the method
Method methodToCall = pjp.getTarget().getClass().getMethod(methodToCallName);
methodToCall.invoke(pjp.getTarget());
}
}
Now, assuming you have class TestClass where you have applied your #ReplacingMethod annotation,
public class TestClass {
#ReplacingMethod("bar")
public void foo() { System.out.println("foo"); }
public void bar() { System.out.println("bar"); }
}
the last missing piece is to get create your instance of TestClass with AOP enabled and your ReplacingAspect applied:
public class Main {
public static void main(String... args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // create Spring context that enables AOP under the hood
TestClass testObject = context.getBean(TestClass.class); // we get reference to TestClass instance from context; calling on a plain new instance wouldn't work
testObject.foo(); // prints "bar" !
}
#EnableAspectJAutoProxy // enables AOP support
#Configuration
public static class TestConfiguration {
#Bean public TestClass testClass() { return new TestClass(); }
#Bean public ReplacingAspect aspect() { return new ReplacingAspect(); } // enables our ReplacingAspect
}
}
You can check out the whole working example at GitHub.
Reflection cannot change the schema of a class and not its behaviour. It can only call (possibly hidden) features.
If you want to replace a method call by another try out a byte code library as asm or javassist. These tools allow you to change class definitions and behaviour (even at runtime with some restrictions).
The approach with AOP is easier, but it is not as flexible and its classpath footprint is larger.

Spring AOP for interface and for annotated methods within it

I have service code like this:
#Component
public class MyService implements com.xyz.WithSession {
public void someMethodWhichDoesNotNeedAutorization() {
// code S1
}
#com.xyz.WithAuthorization
public void someMethodWhichNeedAutorization() {
// code S2
}
}
and aspect like this:
#Aspect
public class MyAspect {
#Before("target(com.xyz.WithSession)")
public void adviceBeforeEveryMethodFromClassImplementingWithSession() {
// code A1
}
#Before("target(com.xyz.WithSession) && #annotation(com.xyz.WithAuthorization)")
public void adviceBeforeWithAuthorizationMethodFromClassImplementingWithSession() {
// code A2
}
Annotation looks like:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface WithAuthorization{
}
code A1 is called before code S1 -- OK
code A1 is called before code S2 -- OK
code A2 isn't called before code S2 -- NOT OK
What am I doing wrong?
Code is written in Java 7 with Spring 3.1.3.
Update
I've tried another way. I use 'Around' advice instead of 'Before' and 'After' to have access to ProceedingJoinPoint. In this advice I check with reflection whether method has annotation 'com.xyz.WithAuthorization' or not:
private boolean isAnnotated(ProceedingJoinPoint proceedingJoinPoint) {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
return signature.getMethod().isAnnotationPresent(com.xyz.WithAuthorization);
}
My annotation has '#Retention(RetentionPolicy.RUNTIME)' but I see in debugger that annotation is missing on runtime in the method signature. So the problem still exists.
In Spring Reference at this link
e.g. in Spring reference
#Before("com.xyz.lib.Pointcuts.anyPublicMethod() && #annotation(auditable)")
public void audit(Auditable auditable) {
AuditCode code = auditable.value();
// ...
}
the execution of any method defined by the AccountService interface:
execution(* com.xyz.service.AccountService.*(..))
any join point (method execution only in Spring AOP) where the executing method has an #Transactional annotation:
#annotation(org.springframework.transaction.annotation.Transactional)
I suggest you to use...
#Before("execution(* com.xyz.WithSession.*(..)) && #annotation(authorization)")
public void adviceBeforeWithAuthorizationMethodFromClassImplementingWithSession(WithAuthorization authorization) {
// code A2
}
Your second pointcut
#Before("target(com.xyz.WithSession) && #annotation(com.xyz.WithAuthorization)")
fails in two ways. First the
target(com.xyz.WithSession)
matches only classes, not methods. So like #Xstian pointed out you should use something along the lines of
execution(* com.whatever.MyService.*(..))
to match all methods inside the MyService class.
Second problem is the
#annotation(com.xyz.WithAuthorization)
where the argument should be name that matches the argument name in the advice. So you use #annotation(someArgumentName) and then have com.xyz.WithAuthorization someArgumentName as your advice methods argument.
Probably your annotation does not have runtime retention:
#Retention(RetentionPolicy.RUNTIME)
public #interface WithAuthorization {}

How to use CDI for method parameter injection?

Is it possible to use CDI to inject parameters into method calls? The expected behaviour would be similar to field injection. The preferred producer is looked up and the product is used.
What I would like to do is this:
public void foo(#Inject Bar bar){
//do stuff
}
or this (with less confusing sytax):
public void foo(){
#Inject
Bar bar;
//do stuff
}
This syntax is illegal in both cases. Is there an alternative? If no - would this be a bad idea for some reason if it were possible?
Thank you
EDIT - I may have made my requirements not clear enough - I would like to be able to call the method directly, leaving the initialization of the bar variable to the container. Jörn Horstmann's and Perception's answer suggest that it is not possible.
Injection points are processed for a bean when it is instantiated by the container, which does limit the number of uses cases for method level injection. The current version of the specification recognizes the following types of method injection:
Initializer method injection
public class MyBean {
private Processor processor;
#Inject
public void setProcessor(final Processor processor) {
this.processor = processor;
}
}
When an instance of MyBean is injected, the processor instance will also be injected, via it's setter method.
Event Observer Methods
public class MyEventHandler {
public void processSomeEvent(#Observes final SomeEvent event) {
}
}
The event instance is injected into the event handling method directly (though, not with the #Inject annotation)
Producer Methods
public class ProcessorFactory {
#Produces public Processor getProcessor(#Inject final Gateway gateway) {
// ...
}
}
Parameters to producer methods automatically get injected.
If what you REALLY want is not something as the parameter of the method (which should be provided by the caller), but a properly initialized instance of a CDI bean each time when the method is called, and fully constructed and injected, then check
javax.inject.Provider<T>
Basically, first inject a provider to the class
#Inject Provider<YourBean> yourBeanProvider;
then, in the method, obtain a new instance
YourBean bean = yourBeanProvider.get();
Hope this helps :)
This question came up when I originally did a search on this topic, and I have since learned that with the release of CDI 1.1 (included in the JavaEE 7 spec), there is now a way to actually do what the OP wanted, partially. You still cannot do
public void foo(#Inject Bar bar){
//do stuff
}
but you can "inject" a local variable, although you do not use #Inject but rather programmatically look up the injected instance like this:
public void foo() {
Instance<Bar> instance = CDI.current().select(Bar.class);
Bar bar = instance.get();
CDI.current().destroy(instance);
// do stuff with bar here
}
Note that the select() method optionally takes any qualifier annotations that you may need to provide. Good luck obtaining instances of java.lang.annotation.Annotation though. It may be easier to iterate through your Instance<Bar> to find the one you want.
I've been told you need to destroy the Instance<Bar> as I have done above, and can verify from experience that the above code works; however, I cannot swear that you need to destroy it.
That feature of CDI is called an "initializer method". The syntax differs from your code in that the whole method is annotated with #Inject, the method parameters can further be annotated by qualifiers to select a specific bean. Section 3.9 of JSR 299 shows the following example, with #Selected being a qualifier that can be omitted if there is only one bean implementation.
#Inject
void setProduct(#Selected Product product) {
this.product = product;
}
Please note that
The application may call initializer methods directly, but then no parameters will be passed to the method by the container.
You can use the BeanManager API in your method to get contextual references, or depending on your ultimate goal you could inject an
Instance<Bar>
outside of the method and use it in the method.
If your goal is to call the method via reflection, it is possible to create an InjectionPoint for each parameter.
Here's an example using CDI-SE:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
public class ParameterInjectionExample {
public static class Foo {
// this method will be called by reflection, all parameters will be resolved from the BeanManager
// calling this method will require 2 different Bar instances (which will be destroyed at the end of the invocation)
public void doSomething(Bar bar, Baz baz, Bar bar2) {
System.out.println("got " + bar);
System.out.println("got " + baz);
System.out.println("got " + bar2);
}
}
#Dependent
public static class Bar {
#PostConstruct
public void postConstruct() {
System.out.println("created " + this);
}
#PreDestroy
public void preDestroy() {
System.out.println("destroyed " + this);
}
}
#ApplicationScoped
public static class Baz {
#PostConstruct
public void postConstruct() {
System.out.println("created " + this);
}
#PreDestroy
public void preDestroy() {
System.out.println("destroyed " + this);
}
}
public static Object call(Object target, String methodName, BeanManager beanManager) throws Exception {
AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(target.getClass());
AnnotatedMethod<?> annotatedMethod = annotatedType.getMethods().stream()
.filter(m -> m.getJavaMember().getName().equals(methodName))
.findFirst() // we assume their is only one method with that name (no overloading)
.orElseThrow(NoSuchMethodException::new);
// this creationalContext will be valid for the duration of the method call (to prevent memory leaks for #Dependent beans)
CreationalContext<?> creationalContext = beanManager.createCreationalContext(null);
try {
Object[] args = annotatedMethod.getParameters().stream()
.map(beanManager::createInjectionPoint)
.map(ip -> beanManager.getInjectableReference(ip, creationalContext))
.toArray();
return annotatedMethod.getJavaMember().invoke(target, args);
} finally {
creationalContext.release();
}
}
public static void main(String[] args) throws Exception {
try (SeContainer container = SeContainerInitializer.newInstance().disableDiscovery().addBeanClasses(Bar.class, Baz.class).initialize()) {
System.out.println("beanManager initialized");
call(new Foo(), "doSomething", container.getBeanManager());
System.out.println("closing beanManager");
}
}
}

Categories