Spring AOP实现原理

 

AOP的实现者

AOP工具的设计目标是把横切的问题(如性能监视、事务管理)模块化。使用类似于OOP的方式进行切面的编程工作。位于AOP工具核心的是连接点模型,它提供了一种机制,可以识别出在哪里发生了横切。

AspectJ

AspectJ是语言级的AOP实现,2001年由Xerox PARC的AOP小组发布。AspectJ扩展了Java语言,定义了AOP语法,能够在编译器提供横切代码的织入,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

AspectWerkz

基于Java的简单、动态、轻量级的AOP框架,2002年发布。支持运行期或类装载期织入横切代码,所以它拥有一个特殊的类装载器。现在已与AspectJ项目合并,合作的第一个版本是AspectJ 5,扩展AspectJ语言,以基于注解的方式支持类似AspectJ的代码风格。

JBoss AOP

2004作为JBoss应用服务器的扩展功能发布。

Spring AOP

Spring AOP 使用纯Java实现,它不需要专门的编译过程,不需要特殊的类装载器,它在运行期通过代理方式想目标类织入增强代码。Spring并不尝试提供最完整的AOP实现,相反,它侧重于提供一种和IoC容器整合的AOP实现,用以解决企业级开发中的常见问题。在Spring中,我们可以无缝地将Spring AOP、IoC和AspectJ整合在一起。

AOP联盟

AOP联盟(aopalliance.sourceforge.net)是众多开源AOP项目的联合组织,该组织的目的是为了制定一套规范描述AOP的标准,定义标准的AOP接口,以便各种遵守标准的具体实现可以相互调用。现以成为事实上的标准。

Spring对AOP的支持

Spring在2.0以后对AOP功能进行了重要的增强,主要表现在以下几个方面:

- 新增了基于Schema的配置支持,为AOP专门提供了aop命名空间。

- 新增了对AspectJ切点表达式语言支持。使用和@AspectJ相同风格的注解,并通过AspectJ提供的注解库和解析库处理切点。当然,由于Spring只支持方法级的切点,所以仅对@AspectJ提供了有限的支持。

- 可以无缝地继承AspectJ,AspectJ提供了语言级切面的实现。

我们所说的Spring AOP,它包括基于XML配置的AOP和基于@AspectJ注解的AOP,这两种方法虽然在配置切面时的表现方式不同,但底层都是采用动态代理技术(JDK代理或CGLib代理)。Spring可以集成AspectJ,但AspectJ本身并不属于Spring AOP的范畴。

在一般情况下,对于开发JAVAEE企业应用的开发者而言,Spring AOP已经可以满足使用的要求,虽然AspectJ提供对AOP更为细致的实现,但像实例化切面、属性访问切面、条件切面等功能在实际应用中并不常用。

如果是基于JDK5.0的项目,推荐使用Spring提供的@AspectJ配置方式,因为这种方式能以更简单的更直接的方式应用切面。

 

ProxyFactoryBean(aspectj方案则是AspectJProxyFactory)

 

public Object getObject() throws BeansException {  
        initializeAdvisorChain();  
        if (isSingleton()) {  
            return getSingletonInstance();  
        }  
        else {  
            if (this.targetName == null) {  
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +  
                        "Enable prototype proxies by setting the 'targetName' property.");  
            }  
            return newPrototypeInstance();  
        }  
    } 

 

 ProxyCreatorSupport

 

protected final synchronized AopProxy createAopProxy() {  
        if (!this.active) {  
            activate();  
        }  
        return getAopProxyFactory().createAopProxy(this);  
    } 

 

 DefaultAopProxyFactory

 

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()) {  
                return new JdkDynamicAopProxy(config);  
            }  
            return CglibProxyFactory.createCglibProxy(config);  
        }  
        else {  
            return new JdkDynamicAopProxy(config);  
        }  
    } 

 

我们回忆一下我们做过的jdk代理的例子:

 

UserMgr mgr = new UserMgrImpl();  
InvocationHandler h = new TransactionHandler(mgr);  
UserMgr u = (UserMgr) Proxy.newProxyInstance(UserMgr.class, h);

  

 生成的代理是比如$Proxy34,h是$Proxy34的成员变量,

 

public class Proxy implements java.io.Serializable {  
  /** prefix for all proxy class names */  
    private final static String proxyClassNamePrefix = "$Proxy";  
  /** 
     * the invocation handler for this proxy instance. 
     * @serial 
     */  
    protected InvocationHandler h;  
} 

 

 

在spring aop中正是JdkDynamicAopProxy。那么重点来了,我们就从JdkDynamicAopProxy的invoke方法看起:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    MethodInvocation invocation;  
    Object oldProxy = null;  
    boolean setProxyContext = false;  
  
    TargetSource targetSource = this.advised.targetSource;  
    Class<?> targetClass = null;  
    Object target = null;  
  
    try {  
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {  
            // The target does not implement the equals(Object) method itself.  
            return equals(args[0]);  
        }  
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {  
            // The target does not implement the hashCode() method itself.  
            return hashCode();  
        }  
        if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&  
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
            // Service invocations on ProxyConfig with the proxy config...  
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  
        }  
  
        Object retVal;  
  
        if (this.advised.exposeProxy) {  
            // Make invocation available if necessary.  
            oldProxy = AopContext.setCurrentProxy(proxy);  
            setProxyContext = true;  
        }  
  
        // May be null. Get as late as possible to minimize the time we "own" the target,  
        // in case it comes from a pool.  
        target = targetSource.getTarget();  
        if (target != null) {  
            targetClass = target.getClass();  
        }  
  
        // Get the interception chain for this method.  
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  
  
        // Check whether we have any advice. If we don't, we can fallback on direct  
        // reflective invocation of the target, and avoid creating a MethodInvocation.  
        if (chain.isEmpty()) {  
            // We can skip creating a MethodInvocation: just invoke the target directly  
            // Note that the final invoker must be an InvokerInterceptor so we know it does  
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.  
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
        }  
        else {  
            // We need to create a method invocation...  
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
            // Proceed to the joinpoint through the interceptor chain.  
            retVal = invocation.proceed();  
        }  
  
        // Massage return value if necessary.  
        Class<?> returnType = method.getReturnType();  
        if (retVal != null && retVal == target && returnType.isInstance(proxy) &&  
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
            // Special case: it returned "this" and the return type of the method  
            // is type-compatible. Note that we can't help if the target sets  
            // a reference to itself in another returned object.  
            retVal = proxy;  
        }  
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {  
            throw new AopInvocationException(  
                    "Null return value from advice does not match primitive return type for: " + method);  
        }  
        return retVal;  
    }  
    finally {  
        if (target != null && !targetSource.isStatic()) {  
            // Must have come from TargetSource.  
            targetSource.releaseTarget(target);  
        }  
        if (setProxyContext) {  
            // Restore old proxy.  
            AopContext.setCurrentProxy(oldProxy);  
        }  
    }  
} 

 

 ReflectiveMethodInvocation implements ProxyMethodInvocation extends MethodInvocation extends Invocation extends Joinpoint

public Object proceed() throws Throwable {  
    //  We start with an index of -1 and increment early.  
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {  
        return invokeJoinpoint();  
    }  
  
    Object interceptorOrInterceptionAdvice =  
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);  
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {  
        // Evaluate dynamic method matcher here: static part will already have  
        // been evaluated and found to match.  
        InterceptorAndDynamicMethodMatcher dm =  
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;  
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {  
            return dm.interceptor.invoke(this);  
        }  
        else {  
            // Dynamic matching failed.  
            // Skip this interceptor and invoke the next in the chain.  
            return proceed();  
        }  
    }  
    else { // eg. <span style="line-height: 25.2px; white-space: normal;">ExposeInvocationInterceptor</span>  
        // It's an interceptor, so we just invoke it: The pointcut will have  
        // been evaluated statically before this object was constructed.  
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  
    }  
} 

 

package org.aopalliance.intercept; // Aop联盟  
  
import java.lang.reflect.AccessibleObject;  
  
public interface Joinpoint {  
    Object proceed() throws Throwable;  
  
    Object getThis();  
  
    AccessibleObject getStaticPart();  
} 

 

 interceptor.invoker - eg. MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {  
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );  
    return mi.proceed(); // 又回归到了上面的调用  
} 

 

 值得注意的是,虽然切面可以只用到一个类的部分方法上,但我们调用其他方法时,仍然会经历上面的逻辑,此时拦截器链里只有一个interceptor - ExposeInvocationInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {  
    MethodInvocation oldInvocation = invocation.get();  
    invocation.set(mi);  
    try {  
        return mi.proceed();  
    }  
    finally {  
        invocation.set(oldInvocation);  
    }  
} 

 

 

 Aop应用参考:

一、分库分表http://wely.iteye.com/blog/2275725

二、方法性能监控

package com.itlong.bjxizhan.support.web.service.monitor;  
  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
  
/** 
 * Created by shenhongxi on 2016/8/10. 
 */  
@Aspect  
public class MonitorAspect {  
  
    private String tagPrefix;  
  
    @Around(  
            value = "execution(* *(..)) && @annotation(monitor)",  
            argNames = "pjp,monitor"  
    )  
    public Object doUmpLogging(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable {  
        // String tag = monitor.tag();  
        // boolean heart = monitor.heart();  
        long start = System.currentTimeMillis();  
        // record invocation (times)  
        Object obj = null;  
        try {  
            obj = pjp.proceed();  
        } catch (Exception e) {  
            // record error  
            throw e;  
        } finally {  
            long end = System.currentTimeMillis();  
            // record time -> end - start  
        }  
        return obj;  
    }  
  
    public String getTagPrefix() {  
        return tagPrefix;  
    }  
  
    public void setTagPrefix(String tagPrefix) {  
        this.tagPrefix = tagPrefix;  
    }  
}  
  
@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})  
public @interface Monitor {  
    String DEFAULT_TAG_NAME = "@@USE_METHOD_NAME";  
  
    String tag() default "@@USE_METHOD_NAME";  
  
    String message() default "";  
  
    boolean heart() default false;  
  
} 

 

 另外,性能监控拦截器可参考org.springframework.aop.interceptor.PerformanceMonitorInterceptor

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

武汉红喜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值