I have service that has several overloaded methods, for example:
MyService.execute(Long id);
MyService.execute(Collection collection);
And i need to intercept only execution of 'MyService.execute(Long id)' via AOP like:
#Aspect
#Component
public class AopInterseptor{
#After("execution(* my.Service.MyService.execute(..))")
public void intercept(JoinPoint joinPoint) throws Exception {
// Do stuff
}
}
Is it possible to do so?
What about:
#Aspect
#Component
public class AopInterseptor{
#After("execution(* my.Service.MyService.execute(Long))")
public void intercept(JoinPoint joinPoint) throws Exception
{
// Do stuff
}
}
This Poincut designator matches only if there is only one param with type Long given in the method call.
Related
After adding Spring AOP to my Spring Boot project, the following aspect produces a NullPointerException on an autowired service component in my controllers:
#Aspect
#Component
#Slf4j
public class LogRequestAspect {
#Around("#annotation(org.springframework.web.bind.annotation.RequestMapping) && execution(public * *(..))")
public Object log(final ProceedingJoinPoint joinPoint) throws Throwable {
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes())
.getRequest();
final Object proceed = joinPoint.proceed();
log.info(
"{} {} from {},{}",
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr(),
request.getHeader("X-Forwarded-For"));
return proceed;
}
}
Example controller:
#RestController
public class AController {
#Autowired
AService aService;
#RequestMapping("/doSomething")
private List<Map<String, Object>> doSomething() {
return aService.doSomething();
}
}
Example service:
#Service
public class AService {
public List<Map<String, Object>> doSomething() {
List<Map<String, Object>> results = new ArrayList<>();
return results;
}
}
Example configuration:
#EnableAspectJAutoProxy
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... strings) {
}
}
As soon as i remove the aspect, everything works perfectly.
Any idea what i'm missing here?
Spring AOP, by default, works using proxies. IN this case a class based proxy is being used because no interface has been implemented. A class based proxy extends the actual class and overrides all the methods to apply the interceptors/aspects.
However a private method cannot be overriden in a subclass and as such your controller method will be invoked on the proxy instead of the proxied object. The proxy never has anything injected and hence the aService field is always null on there.
To fix make the method public or protected so that a subclass can override the method and eventually the method will be called on the proxied instance instead of the proxy.
I have an interceptor binding annotation :
#InterceptorBinding
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyBinding {
}
To this CDI interceptor :
#Interceptor
#MyBinding
public class MyInterceptor {
#AroundInvoke
public Object applyPolicy(InvocationContext ctx) throws Exception {
return blablabla;
}
}
And a class annotated, that mean every methods of this class will invoke MyInterceptor
#MyBinding
public class GlobalController {
public void methodA() {...}
public void methodB() {...}
}
All works fine, but I wish methodB not invoking my interceptor.
I tried both annotations #ExcludeClassInterceptors and #ExcludeDefaultInterceptors on my method but it doesn't works for me. I think these annotations are especially for exclude a method for EJB Interceptor, and not CDI Interceptor with Interceptor binding.
Not sure about these annotations but as a workaround you can add an annotation to the method you want to exclude. Get Method from InvocationContext in the interceptor and check whether the method has the annotation. In this case just delegate to the original method.
Try #MyBinding at method level:
public class GlobalController {
#MyBinding
public void methodA() {...}
public void methodB() {...}
}
I am new to Spring and AOP. I am trying this simple thing where I have created a custom annotation which when placed before any method should execute some code.
This is the annotation I created
// Declares a custom annotation that validates json
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface JsonSchemaAnnotation {
}
Next I created the Spring Aspect class which holds the logic
#Aspect
public class UpdateUIMetadataInterceptor {
#Pointcut("execution(public * com.fico.cardinal.cm.*.*(..))")
public void anyPublicMethod() {
System.out.println("Running");
}
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
}
And this is my simple test class
public class ValidationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/configuration.xml");
String jsondata = "{\"id\": \"EXPENSE_REPORT\",\"properties\": {\"transactionType\": \"EXPENSE_REPORT\"},\"sections\": []} ]}";
ValidationTest test = new ValidationTest();
test.jsonValidationTest("dummy", jsondata);
((AbstractApplicationContext) context).close();
}
#JsonSchemaAnnotation
public void jsonValidationTest(String dummy, String jsondata) {
System.out.println("Success");
}
The problem is my spring aop never gets triggered. I have included a bean in my configuration.xml
<aop:aspectj-autoproxy>
<aop:include name="UpdateUIMetadataInterceptor" />
</aop:aspectj-autoproxy>
<bean id="updateUI" class="com.fico.cardinal.cm.interceptor.UpdateUIMetadataInterceptor" />
Can anyone point out what I am missing?
You have several problems with your code:
You should create your ValidationTest object as a bean managed by Spring and not using new
<aop:include name="UpdateUIMetadataInterceptor" /> should be <aop:include name="updateUI"/>; you can actually just stick with <aop:aspectj-autoproxy/> for simplicity here
ProceedingJoinPoint is not supported for before aspects, so remove it; you can use JoinPoint instead if you need access to arguments
JsonSchemaAnnotation jsonSchemaAnnotation parameter should be present for validateJson method of your aspect, as pointed out by frant.hartm
I think you need either fully qualified name or a parameter in the method:
FQN:
#Before("anyPublicMethod() && #annotation(your.package.JsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
Parameter:
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp, JsonSchemaAnnotation jsonSchemaAnnotation ) throws Throwable {
System.out.println("Running");
}
Source: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts
(and you also need to use the bean, as Dmitry Kuskov pointed out
I want to use interceptors in a Java-SE application and I am using weld as CDI implementation and i'm testing this here:
The Main-Class:
public static void main(String[] args) {
WeldContainer weldContainer = new Weld().initialize();
Service service = weldContainer.instance().select(Service.class).get();
service.methodCall();
service.methodCallNumberTwo();
}
The Service-Class:
public class Service {
#TestAnnotation
public void methodCall(){
System.out.println("methodCall...!");
methodCallNumberTwo();
}
#TestAnnotation
public void methodCallNumberTwo(){
System.out.println("methodCallNumberTwo...!");
}
}
The Interceptor-Class:
#Interceptor
#TestAnnotation
public class TestInterceptor {
#AroundInvoke
public Object interceptorMethod(InvocationContext invocationContext) throws Exception {
System.out.println("I'm the TestInterceptor of "+invocationContext.getMethod());
return invocationContext.proceed();
}
}
Aaaand the output:
I'm the TestInterceptor of public void Service.methodCall()
methodCall...!
methodCallNumberTwo...!
I'm the TestInterceptor of public void Service.methodCallNumberTwo()
methodCallNumberTwo...!
My Questions
First: Why isn't the interceptor called in methodCall() when i'm calling methodCallNumberTwo()?
Second: Is there a way to change that?
I'm only studying the behavior of interceptors and want to understand. Thank you in advance!
The interceptor is not called because you are calling it on the same instance of the object. If you're familiar with EJBs it's the same as calling a method on the same object instead of through the EJB context.
If you debug through it you'll notic that the method call on the injected objects goes through a proxy. The method call from methodOne to methodTwo isn't proxied.
I was trying to create an Aspectj pointcut on method annotation but I failed all the time with different approaches. I'm using aspectj autoproxy (I have no other weaving configured in my spring context). My classes look like this:
public interface Intf
{
#SomeAnnotation
void method1() throws SomeExc;
}
public class Impl implements Intf
{
#Override
public void method1() throws SomeExc
{
//...
}
}
#Aspect
public class MyAspect
{
#AfterThrowing(
pointcut = "execution(* *(..)) && #annotation(SomeAnnotation)",
throwing = "error")
public void afterThrowing(JoinPoint jp, Throwable error)
{
System.err.println(error.getMessage());
}
}
#Component
public class Usage
{
#Autowired
Intf intf;
public void doStuff()
{
intf.method1();
}
}
So I'm wondering why the aspectj won't create the pointcut. I managed to make it work using execution(* *(..) throws SomeExc) which does the job for me but I still want to know what I did wrong.
Also since method1 is defined in an interface and I specify the annotation on implementing class, is there a way to make it work this way? Other proxying mechanisms like transaction management/security works this way in other parts of spring right? And if I'm using interface proxying would specifying the pointcut on implementing class create the pointcut? (I guess not since I'm not using cglib)
try to add #Component to MyAspect class
#Component
#Aspect
public class MyAspect {
...
simply mark your aspect method with
#After("#annotation(package.SomeAnnotation)")
Have a look at this for a step by step guide