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.
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.