為了更形象的說明清楚問題智什,結(jié)合下面的例子:
問題描述
定義了一個增強類烟阐,對IDemoService
類的所有方法進行增強
@Aspect
@Component
public class AspectDemo {
/**
* 定義切點:如果有此注解的地方
*/
@Pointcut("execution(public * com.yummon.braveglory.service.IDemoService.*())")
public void serviceAspect() {
}
@Before(value = "serviceAspect()")
public void before(){
System.out.println("===before===");
}
@After(value = "serviceAspect()")
public void after(){
System.out.println("===after===");
}
}
定義被增強類:
public interface IDemoService {
String demoHello();
String normalHello();
}
@Service
public class DemoService implements IDemoService {
public String demoHello(){
System.out.println("hello");
normalHello();
return "hello";
}
public String normalHello(){
System.out.println("normal");
return "normal";
}
}
測試代碼:
@EnableAspectJAutoProxy
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class PlayertoolApplication {
public static void main(String[] args) throws IOException {
final ConfigurableApplicationContext run = SpringApplication.run(PlayertoolApplication.class, args);
final IDemoService bean = run.getBean(IDemoService.class);
bean.demoHello();
System.in.read();
}
}
在 DemoService
類的demoHello
方法中調(diào)用本類的normalHello
方法,這就是標題所說的this調(diào)用本類方法瞭亮,但是最后的結(jié)果是normal
方法并沒有被增強痹换。
執(zhí)行結(jié)果如下:
===before===
hello
normal
===after===
解決辦法
思路:在SpringAop場景下,對于被代理類demoService
會基于相應(yīng)的代理類demoServiceProxy
精置,在上面的測試代碼中獲得的final IDemoService bean
就是代理對象计寇。所以調(diào)用代理對象的demoHello()
方法當然能得到增強,但是在demoHello
內(nèi)部調(diào)用normalHello
的時候已經(jīng)進去了原生的demoService
這個bean中脂倦,相當于直接調(diào)用demoService.normalHello
方法番宁,所以并不會增強。
基于上面的思路有兩種解決辦法
方法一:直接從BeanFactory中獲取再次代理Bean
示例代碼如下:
@Service
public class DemoService implements IDemoService {
@Autowired
private ApplicationContext context;
public String demoHello(){
System.out.println("hello");
final IDemoService bean = context.getBean(IDemoService.class);
bean.normalHello();
return "hello";
}
}
即繼續(xù)調(diào)用代理類的normalHello()
方法赖阻。
方法二:從AopContext中獲取代理Bean
SpringAop有一個配置參數(shù)exposeProxy
如果配置為true 蝶押,可以讓代理對象的方法被調(diào)用時把代理對象放入AopContext,這個配置的定義如下(ps:看看這個配置的注釋說明):
//org.springframework.context.annotation.EnableAspectJAutoProxy
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
所以代碼還可以這樣寫:
啟動類EnableAspectJAutoProxy
注解改為
@EnableAspectJAutoProxy(exposeProxy = true)
DemoService
類demoHello()
方法改為:
public String demoHello(){
System.out.println("hello");
final IDemoService bean = (IDemoService) AopContext.currentProxy();
bean.normalHello();
return "hello";
}
exposeProxy 屬性如何生效的
在這里拓展一點點火欧,關(guān)于Aop有兩個很重要的方法棋电, 代理類增強方法被調(diào)用時會進入這兩個個方法中的其中一個,基于代理方式:
CglibAopProxy.DynamicAdvisedInterceptor#intercept
//Cglib代理方式
JdkDynamicAopProxy#invoke
//JDK 代理方式
這兩個被代理類的方法在執(zhí)行的時候都有這么一段代碼:
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
因為進入了intercept
或者invoke
方法布隔,那么表示正在調(diào)用proxy
這個代理類的某個方法离陶,那么就把代理類放入Aop 上下文:
總結(jié)
- 本篇文章展示了Aop環(huán)境下 this 調(diào)用本類方法無法被增強的一個例子。
- 展示兩種解決這種問題的方法衅檀,其實本質(zhì)都是由this調(diào)用本類方法招刨,改為調(diào)用代理類的對應(yīng)增強方法。