目錄
2.1 從EnableAspectJAutoProxy註解說起
2.2 導入的AspectJAutoProxyRegistrar類
2.3 AnnotationAwareAspectJAutoProxyCreator類的作用
1 AOP的使用
AOP有如下幾種通知
@Before::前置通知,在方法執行之前執行
@After:後置通知,在方法執行之後執行
@AfterRunning:返回通知,在方法返回結果之後執行
@AfterThrowing:異常通知,在方法拋出異常之後
@Around:環繞通知,圍繞着方法執行
1.1 沒有異常通知
案例模型:用戶登錄
測試環境
LoggingAspect
//當有多個切面的時候,可以用Order註解來設置切面優先級。值越小,優先級越高。
@Order(1)
@Aspect
@Component
public class LoggingAspect {
/*聲明該方法是一個前置通知。這裏可以用通配符替代void、BaseServiceImpl、login,
匹配特定返回類型、特定類、特定類名的方法。
不管方法有沒有異常都會執行*/
@Before("execution(* com.hwl.aspectj.BaseServiceImpl.*())")
public void beforeMethod(JoinPoint point){
System.out.println("前置通知:" + point.getSignature().getName()+"方法執行前執行");
}
/*聲明方法是一個後置通知。
不管方法有沒有異常都會執行*/
@After("execution(* com.hwl.aspectj.BaseServiceImpl.*())")
public void afterMethod(JoinPoint point){
System.out.println("後置通知:" + point.getSignature().getName()+"方法執行後執行");
}
@AfterReturning ("execution(* com.hwl.aspectj.BaseServiceImpl.*())")
public void afterMethodRunning(JoinPoint point){
System.out.println("返回通知:" + point.getSignature().getName()+"方法返回結果後執行");
}
//環繞通知:需要攜帶ProceedingJoinPoint類型的參數,可以決定是否執行目標方法。
//環繞通知還需要捕獲異常。
@Around("execution(String com.hwl.aspectj.BaseServiceImpl.login())")
public void aroundMethod(ProceedingJoinPoint point){
//執行目標方法
try {
System.out.println("環繞通知:" + point.getSignature().getName()+"方法執行前執行");
point.proceed();
System.out.println("環繞通知:" + point.getSignature().getName()+"方法執行後執行");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("異常通知");
}
}
}
BaseServiceImpl:
@Component
public class BaseServiceImpl implements BaseService{
@Override
public String login() {
// TODO Auto-generated method stub
System.out.println("登錄……");
return "用戶";
}
}
測試代碼:
@Autowired
private BaseServiceInter baseService;
@Test
public void testLogin() {
baseService.login();
}
輸出:
環繞通知:login方法執行前執行
前置通知:login方法執行前執行
登錄……
環繞通知:login方法執行後執行
後置通知:login方法執行後執行
返回通知:login方法返回結果後執行
1.2 有異常通知
在上述基礎上
LoggingAspect類中增加異常通知:
/*
* 將 throwing 屬性添加到 @AfterThrowing 註解中, 也可以訪問連接點拋出的異常。
* Throwable 是所有錯誤和異常類的超類. 所以在異常通知方法可以捕獲到任何錯誤和異常.
* 如果只對某種特殊的異常類型感興趣, 可以將參數聲明爲其他異常的參數類型。
* 然後通知就只在拋出這個類型及其子類的異常時才被執行
*/
@AfterThrowing(value = "execution(void com.hwl.aspectj.BaseServiceImpl.testException())", throwing = "e")
public void afterThrowing(JoinPoint point ,Exception e){
System.out.println(point.getSignature().getName()+"方法發生" + e.getMessage() + "異常後執行");
}
BaseServiceImpl中增加異常測試代碼:
@Override
public void testException() {
System.out.println("方法發生異常");
int i = 9/0;
}
測試代碼:
@Autowired
private BaseService baseService;
@Test
public void testException() {
baseService.testException();
}
輸出:
前置通知:testException方法執行前執行
方法發生異常
後置通知:testException方法執行後執行
異常通知:testException方法發生/by zero異常後執行
由此可見前置通知和後置通知不管方法有沒有異常都會執行。返回通知在方法發生異常不執行。由於環繞通知會捕獲異常,故兩者一般不共用。
2 AOP源碼簡析
2.1 從EnableAspectJAutoProxy註解說起
首先看一下EnableAspectJAutoProxy註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* AOP的具體實現方式,該值默認是false,表示是jdk動態代理。如果爲true,表示用CGLIB基於子類繼承動態代理
*/
boolean proxyTargetClass() default false;
/**
* 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;
}
2.2 導入的AspectJAutoProxyRegistrar類
該類重實現了registerBeanDefinitions方法
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
重點是這行代碼
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
這行代碼主要目的是把AnnotationAwareAspectJAutoProxyCreator這個類註冊到spring容器中
2.3 AnnotationAwareAspectJAutoProxyCreator類的作用
下面我們在看看AnnotationAwareAspectJAutoProxyCreator這個類,它實現了一系列Aware的接口。我們看一看這個類的頂層抽象類AbstractAutoProxyCreator
該類父接口實現了BeanPostProcessor接口,實現了postProcessBeforeInstantiation方法
postProcessBeforeInstantiation方法中和代理相關的代碼如下
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
可以看到,這裏返回的就是代理對象而不是真正的對象了。
2.4 創建代理的方法
我們再看看createProxy方法,最終會執行
proxyFactory.getProxy(getProxyClassLoader());
接着往下走
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
先獲取AopProxy,然後創建代理對象
獲取AOPProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 如果代理對象是接口,則返回Jdk動態代理
return new JdkDynamicAopProxy(config);
}
// 否者返回cglib動態代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
創建代理對象的時候就是根據不同的代理方式創建了,有興趣的同學可以閱讀一下源碼,筆者之後講動態代理的時候會詳細講jdk動態代理和cglib動態代理的具體實現方式。