Looking for solution to intercept baseclass inherited method using Spring AOP - java

I am looking for solution to intercept parent class inherited method which is called from child another method.
Parent class LoggerException class having handleException method and I am calling this method from its child class SubLoggerException's method getException,
trying to intercept inherited method handleException from aspect programming
public class LoggerException{
public String handleException(Exception genericException) {
System.out.println("enter in LoggerException ");
return "success";
}
}
public class SubLoggerException extends LoggerException{
public void getException(){
handleException(null);
}
}
#Aspect
public class ErrorNotificationLogger {
private Logger logger = Logger.getLogger(ErrorNotificationLogger.class);
#Around("setterMethod(o)")
public Object markedMethodsAdvice(ProceedingJoinPoint joinPoint, Object o) throws Throwable {
System.out.println(" ****** Around Advice called ***************** ");
return null;
}
//#Pointcut("execution(* com.aop.LoggerException+.handleException(..)) && target(com.aop.SubLoggerException)")
//#Pointcut("execution(* com.aop.LoggerException+.handleException(..)) && this(o)")
#Pointcut("execution(* com.aop.LoggerException.handleException(..)) && this(o)")
public void setterMethod(Object o) {}
}
public class App extends AbstractService{
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] { "Spring-Customer.xml" });
SubLoggerException cust = (SubLoggerException)appContext.getBean("subLoggerExceptionBean");
System.out.println("*************************");
cust.getException();
System.out.println("*************************");
try {
} catch (Exception e) {
}
}
}

When calling another instance method from a Spring AOP proxy, these calls are not intercepted. The reason is that the actual execution of an intercepted method always ocurrs at the original bean (the proxy's target bean). As a consequence the other method will neven be called on the proxy object, but always on the target bean itself. What you need is a way to access the proxy from inside the target bean and then call the method on the proxy. You can get the proxy this way:
AopContext.currentProxy()
So, what you have to do is:
public void getException(){
((LoggerException)AopContext.currentProxy).handleException(null);
}
But consider that the proxy must be accessible if this shall work. This can be configured in your appContext.xml:
<aop:aspectj-autoproxy expose-proxy="true"/>
Hope it helps!

Related

How to audit methods in Java Spring Boot

I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();

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.

Using MethodInterceptor in Spring

I have the following configuration to intercept a method and apply advice after returning from method, But, the following configuration does not work. Could you suggest on what I am missing?
#Service("txnEventSubscriber")
EventSubscriberImpl
...
#Resource(name="txnEventSubscriber")
private EventSubscriberImpl subscriber;
#Bean
public Advice myAdvice() {
return new AfterReturningAdvice() {
#Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
{
System.out.println("inside advice");
}
};
}
#Bean
public ProxyFactoryBean myProxyFactoryBean() {
return new ProxyFactoryBean() {
private static final long serialVersionUID = 6296720408391985671L;
#PostConstruct
public void afterPropertiesSet() throws ClassNotFoundException {
setTarget(subscriber);
setInterceptorNames(new String[] {"myAdvice"});
}
};
}
I have the EventSubscriber which when invoked and when method is returned, I need to intercept the method call and do something... in this case, print "inside advice".
I am not seeing any exceptions, just method advice is not getting called.
First of all, I see you have the class name as EventSubscriberImpl and you are injecting the same type of class. Meaning, you are not programming to interfaces. In this case, you would want to setProxyTargetClass(true); for your ProxyFactoryBean bean and put CGLIB in your project's classpath.
Secondly, you would need something like this
#Resource(name="myProxyFactoryBean")
private EventSubscriberImpl subscriber;
whenever you want to use the proxied version of your EventSubscriberImpl. Meaning, you need to explicitly get that proxied bean by its proxied bean name.
Thirdly, I'd use something like this, to avoid getting the bean by its proxied name:
#Resource(name="txnEventSubscriber")
private EventSubscriberImpl subscriber;
#Bean
public Advice myAdvice() {
return new AfterReturningAdvice() {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
{
System.out.println("inside advice");
}
};
}
#Bean
public Advisor myAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(public * com.foo.bar.EventSubscriberImpl.*(..))");
return new DefaultPointcutAdvisor(pointcut, myAdvice());
}

How to inject callback classes in Spring context?

Is it possible to use callbacks with Spring to that they are managed by application context?
My problem is when a service is used from outer by #Autowired, but within that service there is a callback defined using new operator.
The following example executes a method that is worth retrying. Spring offers a RetryCallback for this case (I know this could be acchieved differently, but just to illustrate my callback problem).
#Service
class MyService {
//main method invoked
void run(DataVO dataVO) {
//new operator not usable in spring context
RetryCallback<Object> retryCallback = new RetryCallback<Object>() {
#Override
public Object doWithRetry(RetryContext context) throws Exception {
return createBooking(dataVO);
}
};
}
private Object createBooking(DataVO dataVO) {
//creates the booking, worth retry on specific failures
//uses further injected/autowired services here
}
}
Is it possible to refactor this snippet so that the callback is managed by spring/injected/autowired?
Make your service implement the callback interface :
#Service
class MyService implements RetryCallback<Object> {
//main method invoked
void run(DataVO dataVO) {
}
#Override
public Object doWithRetry(RetryContext context) throws Exception {
return createBooking(dataVO);
}
private Object createBooking(DataVO dataVO) {
//creates the booking, worth retry on specific failures
//uses further injected/autowired services here
}
}

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