微热山丘,探索 IoC、AOP 实现原理(二) AOP 实现原理

栏目: Java · 发布时间: 8年前

内容简介:微热山丘,探索 IoC、AOP 实现原理(二) AOP 实现原理

AOP 实现原理

一、简介

AOP 是 Aspect Oriented Programming 的简写,面向切面编程。主要的作用是 以一种统一的方式对程序的逻辑进行修改、增强处理。 可以在编译时也可以在运行时实现。编译时处理一般是通过字节码处理技术,运行时进行的一般是通过动态代理技术实现。

二、AOP 核心概念

  • Concerns, 关注 :有两类,核心关注–是关于业务逻辑的;横切关注–是一些通用的逻辑,比如日志、缓存。
  • Joinpoint, 连接点 :是执行时的切入点。可以是字段访问也可以是被调用方法。基于动态代理技术实现的一般只支持方法调用的连接点。
  • Target, 目标 :是一个被切入的地方,一般是一个业务逻辑。比如是对一个业务逻辑的方法的调用。
  • Pointcut, 切入点 :并不是所有的连接点都需要切入,切入点用于指定哪些连接点需要切入。
  • Advice, 建议 :定义了 Aspect 的任务和什么时候执行它,是在核心关注之前还是之后。
  • Aspect, 方面 :Advice 和 pointcut 定义一个方面。Advice 定义 Aspect 的任务和什么时候执行它,而切入点 pointcut 定义在哪里具体地方切入。就是说 Aspect 定义了它是什么东西、什么时候切入和在哪里切入。
  • Weaving, 织入 :织入是一个把横切方面混合到业务目标对象的过程。可以是在编译时间,也可以是在运行时使用类加载机制,Spring AOP 是在运行时生成 bean 时处理。

下面是在 Sping 的 XML 里定义一个 AOP 的配置,注意其中个元素间的关系:

<!-- 一个 weaving 的定义 -->
<aop:config>
    <!-- 定义 aspect, ref 指向 任务的定义 -->
    <aop:aspect id="aspectCommonLogHandler" ref="commonLogHandler">
        <!-- 定义 pointcut -->
        <aop:pointcut id="commonLogPointcut" expression="execution( * net.coderbee.*.controller..*.*(..))" />

        <!-- 定义 advice, around 表示在目标的前/后执行 -->
        <aop:around method="inceptor" pointcut-ref="commonLogPointcut" />
    </aop:aspect>
</aop:config>

三、AOP 实现

1. 各组件定义配置

warnhill 的 AOP 实现没有采用 Spring 那样的配置方式,而是采用 bean 定义的形式组织起来:

<!-- 定义织入逻辑的实现 -->
<bean id="aspectJAutoProxyCreator" class="net.coderbee.warmhill.aop.AspectJAutoProxyCreator" />

<!-- 定义 pointcut -->
<bean id="pointcut" class="net.coderbee.warmhill.aop.AspectJExpressionPointcut" >
    <property name="expression" value="execution(* net.coderbee..*.*(..))" />
</bean>

<!-- 定义 任务 -->
<bean id="timerInterceptor" class="net.coderbee.warmhill.aop.TimerInterceptor" />
<bean id="anotherInterceptor" class="net.coderbee.warmhill.aop.AnotherInterceptor" />

<!-- 定义 aspect -->
<bean id="testAdvisor" class="net.coderbee.warmhill.aop.AspectJExpressionPointcutAdvisor">
    <property name="advice" ref="timerInterceptor" />
    <property name="pointcut" ref="pointcut" />
</bean>
<bean id="testAdvisor2" class="net.coderbee.warmhill.aop.AspectJExpressionPointcutAdvisor">
    <property name="advice" ref="anotherInterceptor" />
    <property name="pointcut" ref="pointcut" />
</bean>

2. 任务实现

任务的实现类必须实现 org.aopalliance.intercept.MethodInterceptor 接口,至于是在目标关注之前还是之后执行由任务实现类自行决定。

下面是个计算目标方法调用耗时的任务实现:

public class TimerInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("start invoke " + invocation.getMethod().getName());

        Object result = invocation.proceed();

        System.out.println("end invoke " + invocation.getMethod().getName() + " used time millils :" + (System.currentTimeMillis() - start));
        return result;
    }
}

3. 切入点实现

切入点只支持被调用方法,采用 aspectj 类库实现,用于匹配给定的 Class 是否满足该切入点。

具体实现见类: AspectJExpressionPointcut

4. 织入实现

warnhill 基于 IoC 容器的 BeanPostProcessor 机制对生成过程中的 bean 进行加工,织入相关的目标逻辑。

对于同一目标上的多个切面,是按声明的顺序生效的。对于代理机制,最后加入的代理反而是最先生效的。因此,对于多个切面,需要按声明的逆序进行切入,以便让调用时按声明顺序调用任务的逻辑。

对于框架基础设施类、任务类的实现是不能做织入的,避免循环地织入。

AspectJAutoProxyCreatorBeanPostProcessor 的一个实现类。下面是织入的核心:

public Object postProcessAfterInit(Object bean, String beanId) {
    if (Advice.class.isAssignableFrom(bean.getClass())
            || Advisor.class.isAssignableFrom(bean.getClass())
            || MethodInterceptor.class.isAssignableFrom(bean.getClass())
            || Pointcut.class.isAssignableFrom(bean.getClass())) {
        return bean;
    }

    Class<?> beanClass = bean.getClass();
    List<AspectJExpressionPointcutAdvisor> pointcutAdvisors = beanFactory.getBeans(AspectJExpressionPointcutAdvisor.class);
    Collections.reverse(pointcutAdvisors);

    for (AspectJExpressionPointcutAdvisor pointcutAdvisor : pointcutAdvisors) {
        if (pointcutAdvisor.getPointcut().getClassFilter().isMatch(beanClass)) {
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.setMethodMatcher(pointcutAdvisor.getPointcut().getMethodMatcher());
            proxyFactory.setInterceptor((MethodInterceptor) pointcutAdvisor.getAdvice());

            TargetSource targetSource = new TargetSource(bean, beanClass, beanClass.getInterfaces());
            proxyFactory.setTargetSource(targetSource);

            bean = proxyFactory.getProxy();
        }
    }

    return bean;
}

注意:上面的逻辑只是对 bean 的类型进行匹配,匹配则生成代理对象,对方法的判断是在调用时进行的。

CglibAopProxy 里面有对方法进行判断:

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

    CglibMethodInvocation cglibMethodInvocation = new CglibMethodInvocation(method, advisorSupport
            .getTargetSource().getTarget(), objects, methodProxy);
    if (advisorSupport.getMethodMatcher() != null && advisorSupport.getMethodMatcher().matches(method,
            advisorSupport.getTargetSource().getTargetType())){
        return advisorSupport.getInterceptor().invoke(cglibMethodInvocation);
    }

    return cglibMethodInvocation.proceed();
}

以上所述就是小编给大家介绍的《微热山丘,探索 IoC、AOP 实现原理(二) AOP 实现原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ordering Disorder

Ordering Disorder

Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99

The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具