Spring Aspects (AspectJ) does not seem to work - spring

Well I really don't know why this does not work:
Every jar needed is in the place. Including aspectjrt.
Basically I start with configuration class:
#Configuration
#ComponentScan(basePackages = { "some.path" })
#EnableAspectJAutoProxy
public class SomeConf { ... }
Then I have my Aspect:
#Component
#Aspect
public class ControllerLoggerAspect {
#Pointcut("execution(* some.path.ATest.*(..))")
private void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
}
Under some.path I have Atest class:
package some.path;
public class ATest {
public void dummyMethod(){
System.out.println("\n\n\nDummy static executed\n\n\n");
}
}
Imagine now that I have controller:
#Controller
#RequestMapping(value = "/mapping")
public class SomeController {
#RequestMapping(value = "/something")
public ResponseEntity<String> publish(#RequestParam("Id") Long[] ids) {
//aspect should be invoked here
new ATest().dummyMethod();
return new ResponseEntity<>("{ \"status\": \"stubbed\"}", HttpStatus.OK);
}
}
Everything is invoked properly except aspect method. No errors, no exceptions, nothing. Any ideas?

Spring AOP only works on Spring beans, i.e. if you want to intercept one of its methods, class ATest needs to be a #Component.

You need to configure the advice you want execute. The pointcut only helps in determining the join points. The advice will be executed, not the pointcut.
You could write a Advice like this using your pointcut "aspectTest()":
#Before("aspectTest()")
public void beforeAdvice(){
System.out.println("\n\n ASPECT WORKING \n\n");
}
Or you could just replace your Pointcut annotation in the example with an advice annotation like this:
#Before("execution(* some.path.ATest.*(..))")
public void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
Here you have a list of all adivces:
#Before("aspectTest()")
#After("aspectTest()")
#AfterReturning(pointcut = "aspectTest()", returning="retVal")
#AfterThrowing(pointcut = "aspectTest()", throwing="ex")
#Around("aspectTest()")

Related

advice is not executing if the annotations contains parameters

I'm trying to get some advice to execute.
When I use annotation without parameters its do execute but when the annotation includes parameters it's not.
#Aspect
class a{
#Pointcut("execution(#com.annotations.AnnotationName* *(..))")
void someMethod() {}
#Around("someMethod()")
public Object aroundSomeMethod(ProceedingJoinPoint pjp) throws Throwable
{
// some code
}
}
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface AnnotationName
{
public String someString();
public boolean someBoolean();
}
The use of the annotation:
#AnnotationName(
someString= "string",
someBoolean = false
)
private void mycode()
{//code }
Following aspect code would advice a target method annotated with #AnnotationName
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("#annotation(annotationName) && within(so.qn69016852..*)")
private void someMethod(AnnotationName annotationName) {}
#Around("someMethod(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someString());
System.out.println(annotationName.someBoolean());
return pjp.proceed();
}
}
Couple of corrections/observations .
Spring AOP cannot advice a private method of a Spring bean. The mycode() method should be in a bean and ideally public. ( Refer )
The Aspect should also be a spring bean. This can be achieved by annotating the aspect with #Component
Remember to limit the scope : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#writing-good-pointcuts
You may also go through this answer from #kriegaex to understand why an #annotation has a global scope.
Update :
The code shared by OP also works with modifying a typo ( a space between the AnnotationName and * in the pointcut expression ) . The observations shared earlier holds good here as well.
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("execution(#so.qn69016852.anno.AnnotationName * so.qn69016852..*.*(..))")
private void someMethod() {}
#Around("someMethod() && #annotation(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someBoolean());
System.out.println(annotationName.someString());
return pjp.proceed();
}
}

Spring AOP introductions not working with && this(IntroducedInterface)

I have following aspect
#Aspect
public class AspectClass{
#DeclareParents(value="com.mac.model.*",defaultImpl=Impl.class)
public IntroduceInterface inter;
#Pointcut("execution(* com.mac.Employee.display(..))")
public void empPointcut(){}
#Before("empPointCut() && this(introduceInterface)")
public void BeforeAdvice(JoinPoint jp,IntroduceInterface inf){
inf.introMethod();
}
}
and I am trying to replicate code from spring docs which is like below:
#Aspect
public class UsageTracking {
#DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
#Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}
but its not working its giving error :
IllegalArgumentException error at ::0 formal unbound in pointcut
Its a simple spring application .What could be reason its not working?
Name in here
#Before("empPointCut() && this(_name_in_here_)")
should be the same as in
public void BeforeAdvice(JoinPoint jp,IntroduceInterface _name_in_here_){
So this should work fine:
#Aspect
public class AspectClass{
#DeclareParents(value="com.mac.model.*",defaultImpl=Impl.class)
public IntroduceInterface inter;
#Pointcut("execution(* com.mac.Employee.display(..))")
public void empPointcut(){}
#Before("empPointCut() && this(inf)")
public void BeforeAdvice(JoinPoint jp,IntroduceInterface inf){
inf.introMethod();
}
}
Annotation object is available to the advice via the annotation parameter. So both names (method parameter name and annotation declared inside pointcut expression) should be same.
Please take a look at these two sections from spring documentation - Passing parameters to advice.
From Spring doc:
The proxy object ( this), target object ( target), and annotations ( #within, #target, #annotation, #args) can all be bound in a similar fashion. The following example shows how you could match the execution of methods annotated with an #Auditable annotation, and extract the audit code.
First the definition of the #Auditable annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Auditable {
AuditCode value();
}
And then the advice that matches the execution of #Auditable methods:
#Before("com.xyz.lib.Pointcuts.anyPublicMethod() && #annotation(**auditable**)")
public void audit(Auditable **auditable**) {
AuditCode code = auditable.value();
// ...
}

how to avoid using context.getbean in spring

There have been several arguments around not using ApplicationContext.getBean() to get a bean reference, of which most are based on logic that it violates the principles of Inversion of control.
Is there a way to get reference to prototype scoped bean without calling context.getBean() ?
Consider to use Spring Boot!
Than you can do something like this...
Runner:
#SpringBootApplication
public class Runner{
public static void main(String[] args) {
SpringApplication.run(Runner.class, args);
}
}
Some Controller:
#Controller
public class MyController {
// Spring Boot injecting beans through #Autowired annotation
#Autowired
#Qualifier("CoolFeature") // Use Qualifier annotation to mark a class, if for example
// you have more than one concreate class with differant implementations of some interface.
private CoolFeature myFeature;
public void testFeature(){
myFeature.doStuff();
}
}
Some cool feature:
#Component("CoolFeature") // To identify with Qualifier
public class CoolFeature{
#Autowired
private SomeOtherBean utilityBean;
public void doStuff(){
// use utilityBean in some way
}
}
No XML files to handle.
We can still access context for manual configurations if needed.
Suggested reading:
Spring Boot Reference
Pro Spring Boot
This type of problem can be solved using method injection, which is described in more detail here: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-method-injection
This is the most common approach to create prototype bean:
abstract class MyService {
void doSome() {
OtherService otherService = getOtherService();
}
abstract OtherService getOtherService();
}
#Configuration
class Config {
#Bean
public MyService myService() {
return new MyService() {
OtherService getOtherService() {
return otherService();
}
}
}
#Bean
#Scope("prototype")
public OtherService otherService() {
return new OtherService();
}
}

spring mvc + spring aop + aspectj

I struggle to use aspect in Spring MVC project.
Method that is a pointcut is running fine, but without advise.
Here is class, that starts whole spring boot and that is root of spring context:
#Lazy
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#Configuration
public class MainSpringBootClass{
public static void main(String[] args)
{
SpringApplication.run(MainSpringBootClass.class, args);
}
}
Here is class with method, that is pointcut.
#Component
#Log
#Aspect
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleClass
{
public void example()
{
System.out.println("example");
}
}
And here is my aspect:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyAspect implements MethodBeforeAdvice
{
#Pointcut("execution(* com.example.MyExampleClass.example())")
public void asd()
{
// pointcut
}
#Before("asd()")
public void login()
{
System.out.println("im am logging in");
}
#Before("execution(* com.example.MyExampleClass.example())")
public void login2()
{
System.out.println("im am logging in2");
}
#Override
public void before(Method method, Object[] args, Object target) throws Throwable
{
System.out.println("aop before");
}
}
And here is my controller:
#RestController
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleController
{
private final MyExampleClass myExampleClass;
#Inject
public AdController(MyExampleClass myExampleClass)
{
this.myExampleClass = myExampleClass;
}
#RequestMapping("/")
public String index()
{
myExampleClass.example();
return "x";
}
}
As You can see, I have been trying to bruteforce correct result with annotations.
I have also seen on some website, that I need specific dependencies, so here are mine (pasting only those, related to aspects):
compile 'org.springframework:spring-aop:+'
compile 'org.aspectj:aspectjrt:+'
compile 'org.aspectj:aspectjweaver:+'
compile 'cglib:cglib:+'
compile 'cglib:cglib-nodep:+'
All dependencies have been successfully downloaded, project compiles and runs fine.
When I hit localhost:8080 then I see returned value "x", and inside logs I see "example".
However, I do not see any advices from spring aop nor aspectj - what am I doing wrong?
I am just using this project as sandbox to learn aspects, so I would be eager to learn, how to do it with each of Spring AOP and AspectJ.
The most important thing for me is to do it all without XML.
EDIT:
I have added simple constructor to MyAspect with println to check, if it is created (as it is normal spring bean with #Component after all) and it does - it is correctly created by spring.
EDIT 2:
IntelliJ IDEA tells me about methods login and login2: "This advice advices no method", but at the same time, I am able to jump (with control-click) from string, that is value in annotations to correct implementations.
All you should need is something like this:
#Aspect
#Component
public class MyAspect {
#Before("execution(* com.example.MyExampleClass.example(..))")
public void logBefore(JoinPoint pjp) throws Throwable {
System.out.println("before...");
}
}
You might have to replace all of the aspectJ dependencies with spring-boot-starter-aop .
Here's an example project that works (see RestControllerAspect.java):
https://github.com/khoubyari/spring-boot-rest-example

AOP Spring Before Advice not working

The method DefaultProduitGeneriqueService.valider is not catched by the method traceWhenReturnedValueDoesntExistOrNotNecessary and I don't understand why?
package fr.generali.nova.atp.service.metier.impl;
public class DefaultProduitGeneriqueService extends DefaultService implements IProduitGeneriqueService, IBacAware {
...
#Override
#Traceable(value = ETraceableMessages.VALIDATION_PRODUIT_GENERIQUE, hasReturnedValue=Traceable.HAS_NOT_RETURNS_VALUE)
public void valider(ElementNiveauUn element) {
...
}
...
}
package fr.generali.nova.atp.logging.advisor;
#Aspect
public class TraceableAdvisor {
#Before(value = "execution(* fr.generali.nova.atp.service.metier.impl.*.*(..)) && #annotation(traceable) && args(element)", argNames = "element,traceable")
public void traceWhenReturnedValueDoesntExistOrNotNecessary(ElementNiveauUn element, Traceable traceable) {
...
}
}
Assuming that the service interfaces are in package fr.generali.nova.atp.service.metier.api:
package fr.generali.nova.atp.service.metier.api;
public interface IProduitGeneriqueService {
void valider(ElementNiveauUn element);
}
And the service implementations are in package fr.generali.nova.atp.service.metier.impl:
package fr.generali.nova.atp.service.metier.impl;
public class DefaultProduitGeneriqueServiceImpl implements IProduitGeneriqueService {
#Override
#Traceable(value = ETraceableMessages.VALIDATION_PRODUIT_GENERIQUE, hasReturnedValue=Traceable.HAS_NOT_RETURNS_VALUE)
public void valider(ElementNiveauUn element) {
// TODO: implement
}
}
Your aspect should look like this:
package fr.generali.nova.atp.logging.advisor;
#Aspect
public class TraceableAdvisor {
#Before(value = "execution(* fr.generali.nova.atp.service.metier.api.*.*(..)) && #annotation(traceable) && args(element)", argNames = "element,traceable")
public void traceWhenReturnedValueDoesntExistOrNotNecessary(ElementNiveauUn element, Traceable traceable) {
// TODO: implement
System.err.println("traced...");
}
}
The default proxy strategy for Spring AOP is JDK interface-based proxies, so Your pointcut expression should match the interface method execution, not the implementation method execution, and Your poincut expression may match either interface mothod execution or implementation method execution.
And remember to include an AspectJAutoProxyCreator in your configuration using for example <aspectj-autoproxy /> tag.
And here is a simple test to prove everyting is working:
public class TraceableAdvisorTest {
#Configuration
public static class TestConfiguration {
#Bean
public IProduitGeneriqueService produitGeneriqueService() {
return new DefaultProduitGeneriqueServiceImpl();
}
#Bean
public TraceableAdvisor traceableAdvisor() {
return new TraceableAdvisor();
}
#Bean
public AnnotationAwareAspectJAutoProxyCreator autoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
}
private AnnotationConfigApplicationContext testApplicationContext;
#Test
public void testTraceWhenReturnedValueDoesntExistOrNotNecessary() {
this.testApplicationContext = new AnnotationConfigApplicationContext();
this.testApplicationContext.register(TestConfiguration.class);
this.testApplicationContext.refresh();
IProduitGeneriqueService service = BeanFactoryUtils.beanOfType(this.testApplicationContext, IProduitGeneriqueService.class);
System.err.println("BEFORE");
service.valider(null);
System.err.println("AFTER");
}
}
The err output is:
BEFORE
traced...
AFTER
For all combinations:
fr.generali.nova.atp.service.metier.api.*.*(..)
fr.generali.nova.atp.service.metier.impl.*.*(..)
fr.generali.nova.atp.service.metier..*.*(..)
Make sure both beans are properly configured, either through annotations or in your appCtx.
It looks like your Aspect is definitely right, but how about the other class? Is it Spring enabled?
Also, if both classes are indeed configured correctly, are you sure that the instance being passed is a Spring bean and not a "new" instance from a constructor?

Resources