Search code examples
javaspringannotationsaopaspect

How to pass object as parameter to a spring aspect?


Let's say I need to enhance the shower() method with a @MusicAround advice to give me some music before and after executing the shower() method.

public class Me {
    @MusicAround
    public void shower() {
        // shower code omitted
    }
}

First I created the new annotation @MusicAround.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MusicAround {

Then bind it with an aspect MusicAspect.

@Aspect
public class MusicAspect {
    @Around("@annotation(MusicAround)")
    public Object musicAround(ProceedingJoinPoint joinPoint) throws Throwable {
        IPhone iphone = new IPhone();
        Iphone.music();
        joinPoint.proceed();
        iphone.music();
    }
}

Configure MusicAspect as a Bean. @EnableAspectJAutoProxy annotation leaves spring to encapsulate the aspect proxy for me.

@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfig {
    // ... other beans omitted

    @Bean 
    public MusicAspect musicAspect() {
        return new MusicAspect();
    }
}

In main method, get Me instance from context, and execute shower() method.

public static void main(String[] args) {
    try {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
        Me me = context.getBean(Me.class);
        me.shower();
        context.close();
    } catch (ApplicationContextException ace) {
        // handle exception
    }
}

Now I can enjoin the music during the shower.

<hey jude>
I'm showering
<don't be so serious>

The problem is that in this way MusicAspect class is coupled with IPhone class. I want to decouple them by injecting IPhone object as a parameter as below,

@Aspect
public class MusicAspect {
    @Around("@annotation(MusicAround)")
    public Object musicAround(ProceedingJoinPoint joinPoint, IPhone iphone) throws Throwable {
        iphone.music();
        joinPoint.proceed();
        iphone.music();
    }
}

Of cause a second parameter "iphone" in musicAround() method will not be allowed here. Is there any spring features that I can use to decouple IPhone and MusicAspect in this case?

!Note: thanks @kriegaex for the proofreading.


Solution

  • Problem solved with the help of @k-wasilewski and @kriegaex. Thanks bro.

    The answer is @Autowired annotation.

    Define IPhone as a field of MusicAspect class. Add @Autowired tag, which tells spring context to initialize the IPhone instance for us.

    @Aspect
    public class MusicAspect {
    
        @Autowired
        private IPhone iphone;
    
        @Around("@annotation(MusicAround)")
        public Object musicAround(ProceedingJoinPoint joinPoint) throws Throwable {
            Iphone.music();
            joinPoint.proceed();
            iphone.music();
        }
    
    }
    

    Don't forget to register IPhone bean in ApplicationConfig. The rest part remain the same.

    @Configuration
    @EnableAspectJAutoProxy
    public class ApplicationConfig {
        // ... other beans omitted
    
        @Bean 
        public IPhone iphone() {
            return new IPhone();
        }
    }
    

    The code passed unit-test on my laptop.