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 {}
Related
I'm using Spring Boot to set up a REST API. I'll be making a bunch of #RestControllers and want to set a pointcut on those methods that return a subtype of a specific abstract class I call Model. These controllers look something like this:
#RestController
public class UserController {
#RequestMapping(...)
public Person getAllPeople() {
...
}
}
Where my Person class would look something like this:
public class Person extends Model {
...
}
So would it be possible to write advice that looks something like this:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(<T extends mypackages.Model> T mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
Of course that won't work because the advice is not valid syntactically. In the reference documentation, I have only found information about generic parameters, not return types (Spring AOP reference). What I have now is this, but I think something like the example above would be a lot more efficient:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api.*.*(..))",
returning = "model")
public void doSomething(Object model) {
if (model instanceof Model)
doSomethingWithModel((Model) model);
}
}
My next question would be, is the same possible for those methods that return a Collection of suptypes of Model? Because the reference states that parameter types cannot be generic Collections.
Have you tried using + after your interface?
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(mypackages.Model+ mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
You could try do not specify the return type. Based on the documentation it will be resolved by the type of the parameter used at the returning clause:
A returning clause also restricts matching to only those method
executions that return a value of the specified type ( Object in this
case, which will match any return value).
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
Have a look to the below link. It answers also your second question, about generic collections.
Aspectj Matching Return Type
Just for curiosity I have created a project for testing this and it started working for me straight forward. I can only think the path your pointcut is pointing to is wrong. Try with:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api..*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
You can have a look to my project at: spring-aspectj-interfaces
There you will see different values for the pointcut (only one not commented, of course), all of them valid.
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) { ... }
...
}
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
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.
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.