I am using AOP for the first time.
I have written the below AOP code which works fine when i use it to intercept a particular method.
Can somebody guide me - how I can set it up to intercept all methods in a certain package (com.test.model)?
Basically how to setup appcontext.xml.
Also, do i need to do something like below to call before calling each method?
AopClass aoptest = (AopClass) _applicationContext.getBean("AopClass");
aoptest.addCustomerAround("dummy");
Can somebody help?
Please let me if some more explanation is needed.
Below is my code:
Interface:
package com.test.model;
import org.springframework.beans.factory.annotation.Autowired;
public interface AopInterface {
#Autowired
void addCustomerAround(String name);
}
Class:
package com.test.model;
import com.test.model.AopInterface;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
#Component
public class AopClass implements AopInterface {
public void addCustomerAround(String name){
System.out.println("addCustomerAround() is running, args : " + name);
}
}
AOP:
package com.test.model;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class TestAdvice{
#Around("execution(* com.test.model.AopInterface.addCustomerAround(..))")
public void testAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("testAdvice() is running!");
}
}
appcontext:
<!-- Aspect -->
<aop:aspectj-autoproxy />
<bean id="AopClass" class="com.test.model.AopClass" />
<bean id="TestAdvice" class="com.test.model.TestAdvice" />
Just put:
#Around("execution(* com.test.model..*.*(..))")
The format of an execution expression is:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
where only ret-type-pattern, name-pattern and param-pattern are required, so at least we need an expression like:
execution(ret-type-pattern name-pattern(param-pattern))
ret-type-pattern matches the return type, * for any
name-pattern matches the method name, you can use * as wildcard and .. to indicate sub-package
param-pattern matches the method parameters, (..) for any number of parameters
You can find more information here: 10. Aspect Oriented Programming with Spring, there are some useful examples.
Related
If i keep #Pointcut("within(org.example.ShoppingCart.*)") in AuthenticationAspect.java then the authenticate method is NOT getting invoked BUT when i change to #Pointcut("within(org.example..*)") then it does get invoked.
Doesn't the 2nd PointCut below automatically include the first one?
#Pointcut("within(org.example.ShoppingCart.*)")
#Pointcut("within(org.example..*)")
i believe both of these should work.
Following is the code :
ShoppingCart.java
package org.example;
import org.springframework.stereotype.Component;
#Component
public class ShoppingCart {
public void checkout(String status)
{
System.out.println("Checkout method called");
}
}
AuthenticationAspect.java
package org.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class AuthenticationAspect {
#Pointcut("within(org.example.ShoppingCart.*)")
public void authenticationPointCut()
{
}
#Before("authenticationPointCut()")
public void authenticate()
{
System.out.println("Authentication is being performed");
}
}
Main.java
package org.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
ShoppingCart cart = context.getBean(ShoppingCart.class);
cart.checkout("CANCELLED");
}
}
BeanConfig.java
package org.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration // Indicates this is a configuration class
#ComponentScan(basePackages = "org.example") // Indicates where to find the components/beans
#EnableAspectJAutoProxy // Enables the AspectJ features in project
public class BeanConfig {
}
i am learning spring aspect and unsure why the pointcut expression is not working .
Since the within pointcut designator limits matching to join points of certain types, the problem is your pointcut expression is matching types within ShoppingCart because of .* in the end. If you remove those characters it should work.
#Pointcut("within(org.example.ShoppingCart)")
public void authenticationPointCut(){}
I have a rest controller contains many methods
#RestController
#RequestMapping("v1/test")
public class TestRestController {
...... 100 methods (GET, POST, PATCH, etc)
}
How can I know which method is being accessed without using print in each method?
Are there any ways to do that?
While you can use AOP tactics noted in the other answers to log the methods, there are better approaches if all you want to do is log all of the requests and responses for the endpoints those methods implement. There are a couple ways this can be done: Use Spring's CommonRequestLoggingFilter or write your own HandlerInterceptor. Here is a nice article on the first two options.
Using Aspect Oriented Programming with Spring:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Aspect
public class SpringAspect {
private static final Logger LOG = LoggerFactory.getLogger(SpringAspect.class);
#Before("execution(* sample.package.path.TestRestController.*(..))")
public void executedMethodsLogger(JoinPoint joinPoint) {
LOG.info("[ Executed method {} ]", joinPoint.toString());
}
}
You can use Spring AOP:
package com.example;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class ExampleAspect {
#Before("execution(* com.example.TestRestController(..)")
public void log(JoinPoint joinPoint) {
System.out.println("Executing :" + joinPoint.toString());
}
}
I working with java code in AEM.
We use maven and in pom.xml is exist all needed dependencies and aspectj-maven-plugin
I prepared aspect with pointcut:
package org.xxx.aop.aspects;
import javax.jcr.Node;
import org.apache.sling.api.SlingHttpServletRequest;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.slf4j.*;
#Aspect
public class NodeAspect {
private final Logger LOG = LoggerFactory.getLogger(NodeAspect.class);
#Pointcut("execution(public * javax.jcr.Node.*(..)")
public void jcrNodeAccess() {}
#Around("jcrNodeAccess()")
public Object jcrNodeMethodCall(ProceedingJoinPoint thisJoinPoint) throws Throwable {
LOG.info("--- LOG NodeAspect ---");
Object result = thisJoinPoint.proceed();
// some code
// ..
return result;
}
But when I installed my package in AEM, my aspect does not work.
Maybe my aspect I will need to activate?
Maybe this problem related with behavior of OSGI (felix) in AEM?
Please provide to me ideas how to fix it
I am still a beginner in Spring Framework so I tried to code a program about "introduction" in Spring AOP but I am facing an error while compiling. Please find below the classes in the package concert:
PerformanceImp.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class PerformanceImp implements Performance {
public void perform() {
System.out.println("This is the performance function");
}
}
Performance.java
package concert;
public interface Performance {
public void perform();
}
Encoreable.java
package concert;
public interface Encoreable {
void performEncore();
}
DefaultEncoreable.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class DefaultEncoreable implements Encoreable {
public void performEncore() {
System.out.println("This is the performEncore function");
}
}
EncoreableIntroducer.java
package concert;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class EncoreableIntroducer {
#DeclareParents(value="concert.Performance+",
defaultImpl=DefaultEncoreable.class)
public static Encoreable encoreable;
}
ConcertConfig.java
package concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("concert")
public class ConcertConfig {
}
And the main class:
Main.java
package concert;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConcertConfig.class);
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);
((Encoreable) pi).performEncore();
pi.perform();
}
}
I am getting the error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'concert.PerformanceImp' available
Any help please ?
You cannot access the implementation (PerformanceImp) by default, because you enabled AOP, which sets to target interfaces instead of implementation. If you would remove EnableAspectJAutoProxy, you would see the code would work fine.
To understand a bit more about how AOP targeting works, take a look at this Spring Documentation
Spring AOP can also use CGLIB proxies. This is necessary to proxy
classes rather than interfaces. CGLIB is used by default if a business
object does not implement an interface. As it is good practice to
program to interfaces rather than classes; business classes normally
will implement one or more business interfaces. It is possible to
force the use of CGLIB, in those (hopefully rare) cases where you need
to advise a method that is not declared on an interface, or where you
need to pass a proxied object to a method as a concrete type.
So you have two options:
Take the interface when trying to get the bean from the ApplicationContext.
Enable AOP to target concrete classes instead.
To do this point #2, modify your annotation as follows:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Try:
Performance pi = context.getBean("performanceImp", Performance.class);
instead of:
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);
package test.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
#Component
#Aspect
class LoggingMonitor {
#AfterThrowing(
pointcut = "execution(* test.aop..foo(..))",
throwing = "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
System.out.println(joinPoint);
}
}
#Service
class MyBean {
void foo() {
throw new RuntimeException("run error");
}
}
#SpringBootApplication
public class App {
#Autowired
MyBean myBean;
#Bean
CommandLineRunner runner() {
return r -> {
System.out.println("Run");
myBean.foo();
};
}
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
I don't understand why the code above not working, but when I changed my pointcut to "* org.springframework.boot.CommandLineRunner.run(..)", it works.
Spring AOP works on public methods only. Make your method public, or revert to AspectJ.
Due to the proxy-based nature of Spring’s AOP framework, protected
methods are by definition not intercepted, neither for JDK proxies
(where this isn’t applicable) nor for CGLIB proxies (where this is
technically possible but not recommendable for AOP purposes). As a
consequence, any given pointcut will be matched against public methods
only! If your interception needs include protected/private methods or
even constructors, consider the use of Spring-driven native AspectJ
weaving instead of Spring’s proxy-based AOP framework. This
constitutes a different mode of AOP usage with different
characteristics, so be sure to make yourself familiar with weaving
first before making a decision.
Source