目录
1 AOP的使用
1.1 没有异常通知
1.2 有异常通知
2 AOP源码简析
2.1 从EnableAspectJAutoProxy注解说起
2.2 导入的AspectJAutoProxyRegistrar类
2.3 AnnotationAwareAspectJAutoProxyCreator类的作用
2.4 创建代理的方法
AOP有如下几种通知
@Before::前置通知,在方法执行之前执行
@After:后置通知,在方法执行之后执行
@AfterRunning:返回通知,在方法返回结果之后执行
@AfterThrowing:异常通知,在方法抛出异常之后
@Around:环绕通知,围绕着方法执行
案例模型:用户登录
测试环境
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方法返回结果后执行
在上述基础上
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异常后执行
由此可见前置通知和后置通知不管方法有没有异常都会执行。返回通知在方法发生异常不执行。由于环绕通知会捕获异常,故两者一般不共用。
首先看一下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;
}
该类重实现了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容器中
下面我们在看看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;
可以看到,这里返回的就是代理对象而不是真正的对象了。
我们再看看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动态代理的具体实现方式。