码迷,mamicode.com
首页 > 编程语言 > 详细

Spring源码阅读:使用标准AOP的API模拟Spring AOP + AspectJ的设计与实现

时间:2014-07-31 16:16:06      阅读:437      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   java   使用   os   strong   

在上一篇博客中,提到了标准AOP与Spring AOP。这一篇就来把他们模拟出来。

在模拟之前,还需要提及的是,在Spring框架中,对于AOP的支持:

Spring 支持的AOP

 

AspectJ是另外一个有名的AOP框架,Spring也集成AspectJ,同时Spring AOP与AspectJ有一定程度的集成,这样一来Spring中就支持两种AOP:1)Spring AOP、2)AspectJ。而使用AOP的方式却又三种:

    1)完全使用Spring AOP

    2)完全使用AspectJ(分为注解方式、XML配置方式,这与Spring IOC有关)

    3)使用SpringAOP与AspectJ集成(其中AspectJ的配置分注解和XML两种)

 这些内容在Spring官方提供的参考文档中写的清清楚楚的。

 

采用AOP标准接口来模拟SpringAOP与AspectJ注解集成

 

注解也是自定义的,不是使用AspectJ的。

 

设计:

先看看标准AOP的API:

bubuko.com,布布扣

 

这是AOP的标准API,由AOP联盟制定,定义在aopalliance.jar中。

 

左侧:Advice代表建议,Interceptor就是一种Advice。Interceptor可以分为多种:构造拦截器,方法拦截器,字段拦截器。

右侧:JoinPoint代表连接点,其实就是执行的方法。Invocation就代表对target方法的调用,它把这个调用抽象成接口Invocation。Invocation与Interceptor呈对应关系。

 

既然AOP联盟定义了接口,我们就需要对其进行实现处理:

bubuko.com,布布扣

 

Advice的加入:可以在Interceptor的Invoke方法中加入,也可以在Invocation的proceed方法中加入。

处理方式可以参照J2EE的Filter和Struts2的ActionInvocation,因为它们都是采用这种机制。

@Before, @After 是两个自定义注解,模拟AspectJ注解。

 

 

接下来是使用JDK动态代理产生代理的设计:

bubuko.com,布布扣

 

代码实现:

1)AbstractMethodInterceptor,在AOP标准接口MethodInterceptor上扩展了两个方法。并实现了invoke方法。

bubuko.com,布布扣
package com.fjn.aop.core;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public abstract class AbstractMethodInterceptor implements MethodInterceptor{

    public final Object invoke(MethodInvocation invocation) throws Throwable {
        Object result=invocation.proceed();
        return result;
    }
    
    
    public abstract void beforeAdvice();
    
    public abstract void afterAdvice();
    
}
View Code

 

2)DefaultMethodInvocation,是对AOP标准接口MethodInvocation的实现。

bubuko.com,布布扣
package com.fjn.aop.core;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;

import org.aopalliance.intercept.MethodInvocation;

import com.fjn.aop.annotation.After;
import com.fjn.aop.annotation.Before;
import com.fjn.aop.bean.DefaultProxy;
/**
 * 方法调用的具体实现
 * @author fjn
 *
 */
public class DefaultMethodInvocation implements MethodInvocation{
    List<AbstractMethodInterceptor> interceptors;
    private DefaultProxy proxy;
    private Method method;
    private Object target;
    private Object[] args;
    int index=0;
    private boolean executed=false;
    
    public DefaultMethodInvocation(DefaultProxy proxy, Method method, Object target, Object[] args, List<AbstractMethodInterceptor> interceptorChain){
        this.interceptors=interceptorChain;
        this.method=method;
        this.target=target;
        this.args=args;
        this.proxy=proxy;
    }
    
    public Object proceed() throws Throwable {
        AbstractMethodInterceptor interceptor=null;
        Object result=null;
        if(interceptors.size()>0 && index<interceptors.size()){
            interceptor=interceptors.get(index++);
            if(new AdviceMatcher(interceptor, this).match(Before.class,"beforeAdvice")){
                interceptor.beforeAdvice();     //     执行前置建议
            }
            proceed();    //    执行下一个拦截器
        }
        // 执行真正的方法调用
        if(!executed){
            executed=true;
            try {
                result=method.invoke(target, args);
            } catch (RuntimeException e) {
                System.out.println(e.getMessage());
            }
        }
        if(index>0){
            interceptor=interceptors.get(--index);
            if(new AdviceMatcher(interceptor, this).match(After.class,"afterAdvice")){
                interceptor.afterAdvice();    //     执行后置建议
            }
        }
        
        return result;
    }

    public Object getThis() {
        return target;
    }

    public AccessibleObject getStaticPart() {
        return null;
    }

    public Method getMethod() {
        return method;
    }
    
    public DefaultProxy getProxy(){
        return proxy;
    }
    
    public Object[] getArguments() {
        return args;
    }
}
View Code

 

3)AdviceMatcher,在拦截器处理时,要判断要执行的方法是否可以被拦截器拦截。

bubuko.com,布布扣
package com.fjn.aop.core;

import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInvocation;

import com.fjn.aop.annotation.After;
import com.fjn.aop.annotation.Before;

/**
 * method 与 PointCut 匹配器
 * 支持的方法名表达式有:
 *  1)*xxx, 以*开头
 *  2)xxx*, 以*结尾
 *  3)*    所有
 *  4)没有*,指定方法名
 *  
 * @author fjn
 */
@SuppressWarnings("rawtypes")
public class AdviceMatcher {
    private AbstractMethodInterceptor interceptor;
    private MethodInvocation invocation;
    AdviceMatcher(AbstractMethodInterceptor interceptor, MethodInvocation invocation){
        this.interceptor=interceptor;
        this.invocation=invocation;
    }
    
    
    public boolean match(Class adviceAnnotationType, String joinPoint){
        // 要执行的方法
        String methodName=invocation.getMethod().getName();
        try {
            Method adviceMethod=interceptor.getClass().getDeclaredMethod(joinPoint, new Class[]{});
            if(adviceAnnotationType==Before.class){
                Before before=adviceMethod.getAnnotation(Before.class);
                if(before==null) return false;
                String pointcut=before.expression();
                return beforeOrAfterMatch(pointcut, methodName);
            }else
            if(adviceAnnotationType==After.class){
                After after=adviceMethod.getAnnotation(After.class);
                if(after==null) return false;
                String pointcut=after.expression();
                return beforeOrAfterMatch(pointcut, methodName);
            }
            
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 支持的方法名表达式有:
     *  1)*xxx, 以*开头
     *  2)xxx*, 以*结尾
     *  3)*    所有
     *  4)没有*,指定方法名
     *  
     * @param pointcut
     * @param methodName
     * @return
     */
    private boolean beforeOrAfterMatch(String pointcut, String methodName){
        int indexOfStar=pointcut.indexOf("*");
        if(indexOfStar!=-1){// 方法名中有*号
            if(indexOfStar==0){// 以*开头
                if("*".equals(pointcut)){// 只有*
                    return true;
                }
                else{
                    return methodName.endsWith(pointcut.substring(1));
                }
            }else {// 以*结尾,中间有*也算以*结尾
                return methodName.startsWith(pointcut.substring(0, indexOfStar));
            }
        }else{
            return methodName.equals(pointcut);
        }
    }
}
View Code

 

4)两个模拟AspectJ的注解:@Before,@After

bubuko.com,布布扣
package com.fjn.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value={ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface After{
    String expression() default "";
}

package com.fjn.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value={ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
    String expression() default "";
}
View Code

 

这两个注解,其实是借鉴了AspectJ的注解,但是又不想完全使用,因为与AspectJ的注解有:

    @Aspect:声明切面

    @PointCut:声明切点,设置表达式

    @Before、@After、@Around等,这些是Advice,是要引用@PointCut中的表达式的,所以为了简便,只是自定定义了两个注解,并且拥有的@PointCut定义匹配表单式的作用。

 

================================================================

 

这个分割线以上的是AOP,下面代码是通过代理相关的设计:

 

5)默认的代理DefaultProxy设计:

bubuko.com,布布扣
package com.fjn.aop.bean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import com.fjn.aop.core.AbstractMethodInterceptor;
import com.fjn.aop.core.DefaultMethodInvocation;

public class DefaultProxy implements InvocationHandler{
    Object target;
    List<AbstractMethodInterceptor> interceptorChain=new ArrayList<AbstractMethodInterceptor>();
    
    public DefaultProxy(Object target, List<AbstractMethodInterceptor> interceptorChain){
        this.target=target;
        if(interceptorChain!=null && interceptorChain.size()>0){
            this.interceptorChain.addAll(interceptorChain);
        }
    }
    
    public final Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        DefaultMethodInvocation methodInvocation=new DefaultMethodInvocation(this, method, target, args, interceptorChain);
        return methodInvocation.proceed();
    }
    
    public Object getProxy(){
        Object proxy=null;
        try {
            proxy=Proxy.newProxyInstance(DefaultProxy.class.getClassLoader(), target.getClass().getInterfaces(), this);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return proxy;
    }
    
    public Object getTarget(){
        return target;
    }
    
}
View Code

在invoke()内部会与AOP衔接起来。

 

6)模拟ProxyBeanFactory:

bubuko.com,布布扣
package com.fjn.aop.bean;

import java.util.List;

import com.fjn.aop.core.AbstractMethodInterceptor;
import com.fjn.aop.exception.NotFoundInterfaceException;
@SuppressWarnings("rawtypes")
/**
 * 单例的ProxyBeanFactory
 * @author Administrator
 *
 */
public class ProxyBeanFactory {
    private ProxyBeanFactory(){}
    
    private static ProxyBeanFactory beanFactory=null;
    
    public synchronized static ProxyBeanFactory getBeanFactory(){
        if (beanFactory==null) {
            beanFactory=new ProxyBeanFactory();
        }
        return beanFactory;
    }
    
    /**
     * 
     * @param targetClazz 创建指定类target实例
     * @param chain 系统的拦截器链
     * @return
     */
    public Object createProxy(Class targetClazz,List<AbstractMethodInterceptor> chain){
        Object proxyBean=null;
        if(targetClazz.getInterfaces().length==0){
            throw new NotFoundInterfaceException(targetClazz);
        }
        try {
            Object target=targetClazz.newInstance();
            proxyBean=new DefaultProxy(target, chain).getProxy();
    
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return proxyBean;
    }
    
    public Object createProxy(Object target,List<AbstractMethodInterceptor> chain){
        Object proxyBean=null;
        if(target.getClass().getInterfaces().length==0){
            throw new NotFoundInterfaceException(target.getClass());
        }
        proxyBean=new DefaultProxy(target, chain).getProxy();
        return proxyBean;
    }
}
View Code

 

可以根据Target.class,也可以根据target对象,来创建代理动态代理对象,返回值就是动态代理对象。

参数中的List< AbstractMethodInterceptor> 就相当于系统中配置好的那些拦截器。

 

==============================================================

 

测   试

有了这些以后,基本上就成型了,现在可以做测试了:

 

Target设计:

    因为使用的Java动态代理,所以必须得为Target设计接口。

bubuko.com,布布扣
package com.fjn.aop;

public interface TargetInterface {
    void helloAop(String str);
    void noUseAop(String str);
}

package com.fjn.aop;
import com.fjn.aop.TargetInterface;

/**
 * Target可以是Service层,也可以是DAO层
 * @author fjn
 */
public class Target implements TargetInterface{
    
    public void helloAop(String str) {
        System.out.println("hello, "+str+ ", welcome you use aop in your application");
    }
    
    public void noUseAop(String str){
        System.out.println("hello, "+str+ ", the method has‘t use AOP .");
    }
}
View Code

 

 

接下来设计两个拦截器,要作为系统拦截器配置:

LoggingInterceptor:

bubuko.com,布布扣
package com.fjn.aop.interceptor;

import com.fjn.aop.annotation.After;
import com.fjn.aop.annotation.Before;
import com.fjn.aop.core.AbstractMethodInterceptor;

public class LoggingInterceptor extends AbstractMethodInterceptor{

    @Before(expression="hello*")
    public void beforeAdvice(){
        System.out.println("Logging: before advice invoked ...");
    }

    @After(expression="*Aop")
    public void afterAdvice(){
        System.out.println("Logging: after advice invoked ...");
    }
    

}
View Code

这个类中的两个建议说明:

beforeAdvice,只能匹配到方法名是以hello开头的。

afterAdvice,只能匹配到方法名以Aop结尾的。

 

AnotherInterceptor:

bubuko.com,布布扣
package com.fjn.aop.interceptor;

import com.fjn.aop.annotation.After;
import com.fjn.aop.annotation.Before;
import com.fjn.aop.core.AbstractMethodInterceptor;

public class AnotherInterceptor extends AbstractMethodInterceptor{
    @Override
    @After(expression="*")
    public void afterAdvice() {
        System.out.println("another: after");
    }
    
    @Override
    @Before(expression="*")
    public void beforeAdvice() {
        System.out.println("another: before");
    }
}
View Code

*表示所有方法都匹配。所以系统中如果使用这个拦截器,所有的方法调用时,都要用到这 两个advice。

 

 

现在就可以写测试用例了:

package com.fjn.aop;

import java.util.ArrayList;
import java.util.List;

import com.fjn.aop.bean.ProxyBeanFactory;
import com.fjn.aop.core.AbstractMethodInterceptor;
import com.fjn.aop.interceptor.AnotherInterceptor;
import com.fjn.aop.interceptor.LoggingInterceptor;

public class Client {
    public static void main(String[] args) {
        // 初始化配置的拦截器链。
        // 这里【相当于】Spring的在XML文件中配置AOP或者在拦截器上指定@AspectJ
        List<AbstractMethodInterceptor> interceptorChain=new ArrayList<AbstractMethodInterceptor>();
        interceptorChain.add(new LoggingInterceptor());
        interceptorChain.add(new AnotherInterceptor());
        
        // 这里相当于Spring的通过IOC创建的代理Bean
        TargetInterface target=(TargetInterface)ProxyBeanFactory.getBeanFactory().createProxy(Target.class, interceptorChain);
        
        // 我们在使用Spring时,只需要写下面的代码就可以了:
        target.helloAop("zhang san");
        System.out.println("--------------------------");
        target.noUseAop("zhang san");
        System.out.println("--------------------------");
        target.helloAop("zhang san");
        
    }
}

 

上面的程序执行结果:

 

Logging: before advice invoked ...
another: before
hello, zhang san, welcome you use aop in your application
another: after
Logging: after advice invoked ...
--------------------------
another: before
hello, zhang san, the method has‘t use AOP .
another: after
Logging: after advice invoked ...
--------------------------
Logging: before advice invoked ...
another: before
hello, zhang san, welcome you use aop in your application
another: after
Logging: after advice invoked ...

 

 

程序执行的流程如下:

bubuko.com,布布扣

1-3 :获取target的代理对象,中间过程使用的是JDK的动态代理。

4-16:target.xxMethod执行其实就是在Proxy的invoke方法内部调用MethodInvocation.proceed()的过程。

而proceed执行就是一个拦截器连执行的过程。

 

以上,就是模拟的SpringAOP流程了。

 

Spring源码阅读:使用标准AOP的API模拟Spring AOP + AspectJ的设计与实现,布布扣,bubuko.com

Spring源码阅读:使用标准AOP的API模拟Spring AOP + AspectJ的设计与实现

标签:style   blog   http   color   java   使用   os   strong   

原文地址:http://www.cnblogs.com/f1194361820/p/3880768.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!