spring学习三-AOP之面向切面编程

目录

 

1 AOP的使用

1.1 没有异常通知

1.2 有异常通知

2 AOP源码简析

2.1 从EnableAspectJAutoProxy注解说起

2.2 导入的AspectJAutoProxyRegistrar类

2.3 AnnotationAwareAspectJAutoProxyCreator类的作用

2.4 创建代理的方法


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动态代理的具体实现方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章