spring aop

一、何为aop

AOP(Aspect-Oriented Programming)面向方面编程,可以说是OOP(Object-Oriented Programing)面向对象编程的补充和完善。AOP(面向切面编程)与OOP(面向对象编程)的区别是什么。其实AOP与OOP可以理解为不在同一层面上的两个独立的定义。也就是说其实这两个东西没法去做一个对等的比较,OOP专注于对象,我们利用对象的属性,行为来解决现实中的问题,而AOP则用来在使用OOP解决问题的过程中增强解决问题的能力,实现更好的模块化。

AOP编程思想,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

二、编程中的aop

aop并非在spring首先引入的,而是由来已久,他是前人智慧的结晶,仔细寻找就能发现其踪迹。

第一次接触servlet 过滤器 filter乱码的解决

默认的解决乱码的思路是每个方法中写request.setCharacterEncoding("UTF-8");并且有其局限性,代码也不够简介明了,filter的出现打破了这个局限,在请求之前做响应的操作。

       <filter>
             <filter-name>EncodingFilter</filter-name>
             <filter-class>com.mingxungu.filter.EncodingFilter</filter-class>
       </filter>
 
       <filter-mapping>
             <filter-name>EncodingFilter</filter-name>
             <url-pattern>/*</url-pattern>
       </filter-mapping>
原理:通过过滤器拦截所有的请求,对中文乱码的解决通过一个拦截器类统一进行解决。

原理图:

第二次接触Java的动态代理

代码:

//JDK动态代理  
public class UserServiceProxyFactory implements InvocationHandler {  
    public UserServiceProxyFactory(UserService us) {  
        super();  
        this.us = us;  
    }  
    private UserService us;  
    public UserService getUserServiceProxy(){  
        //生成动态代理  
        UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),  
                                    UserServiceImpl.class.getInterfaces(),   
                                    this);  
        //返回  
        return usProxy;  
    }  
    @Override  
    public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {  
        //额外的责任  
        System.out.println("打开事务!");  
        //执行主体方法  
        Object invoke = method.invoke(us, arg2)  
       //额外的责任  
        System.out.println("提交事务!");return invoke;  
    }  
} 
原理:点击查看动态代理的原理

            invoke()主体方法,通过java的反射机制 Object invoke = method.invoke(us, arg2)获取执行主体方法,在前后加入与业务无关的额外的方法

原理图:

第三次接触struts拦截器Interceptor

我们发现在jsp中定义好name属性值,根据 对象名.属性名(user.name)的方式在Action中获取对象的时候其属性值已经被塞入其中。这不得不归功于Interceptor。

我们struts的一个重要功能之一就是参数赋值,我们就回顾一下其源码实现

@Override  
public String doIntercept(ActionInvocation invocation) throws Exception {  
    Object action = invocation.getAction();//获取当前执行的Action对象  
    if (!(action instanceof NoParameters)) {//判断Action是否实现了NoParameters接口,实现该接口表示该Action没有任何请求参数  
        ActionContext ac = invocation.getInvocationContext();//获取ActionContext对象  
        final Map<String, Object> parameters = retrieveParameters(ac);//获取请求参数Map  
        //省略...  
        if (parameters != null) {//如果请求参数不为null  
            Map<String, Object> contextMap = ac.getContextMap();//获取ActionContext内部的context Map,即OgnlContext对象  
            try {  
                //省略...  
                ValueStack stack = ac.getValueStack();//获取值栈  
                setParameters(action, stack, parameters);//为值栈设置参数  
            } finally {  
                //省略...  
            }  
        }  
    }  
    return invocation.invoke();//调用下一个拦截器  
} 
setParameters方法才是ParametersInterceptor拦截器的主要逻辑
protected void setParameters(Object action, ValueStack stack, final Map<String, Object> parameters) {  
    ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware)  
            ? (ParameterNameAware) action : null;//判断Action有无实现ParameterNameAware接口  
  
    Map<String, Object> params;  
    Map<String, Object> acceptableParameters;//合法参数集合  
    //判断参数设置是否有序,ordered默认为false,即无序  
    if (ordered) {  
        params = new TreeMap<String, Object>(getOrderedComparator());//如果有序则要获取比较器  
        acceptableParameters = new TreeMap<String, Object>(getOrderedComparator());  
        params.putAll(parameters);  
    } else {  
        params = new TreeMap<String, Object>(parameters);  
        acceptableParameters = new TreeMap<String, Object>();  
    }  
    //迭代请求参数  
    for (Map.Entry<String, Object> entry : params.entrySet()) {  
        String name = entry.getKey();  
        //判断参数是否合法,如果Action实现了ParameterNameAware则acceptableName(name)返回true且parameterNameAware.acceptableParameterName(name)  
        //也返回true该参数才是合法的;如果Action没有实现ParameterNameAware则参数是否合法由acceptableName(name)方法决定  
        boolean acceptableName = acceptableName(name)  && (parameterNameAware == null  || parameterNameAware.acceptableParameterName(name));  
        //如果参数合法  
        if (acceptableName) {  
            acceptableParameters.put(name, entry.getValue());//把合法参数添加到合法参数集合中  
        }  
    }  
  
    ValueStack newStack = valueStackFactory.createValueStack(stack);  
    //省略...  
    for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {//迭代合法参数  
        String name = entry.getKey();//参数名  
        Object value = entry.getValue();//参数值  
        try {  
            newStack.setValue(name, value);//将该参数设置到ValueStack中  
        } catch (RuntimeException e) {  
            //省略...  
        }  
    }  
    //省略...  
    //看该方法的名称是将合法参数添加到ActionContext中,但在该拦截器中,该方法为空实现,无任何代码  
    //该方法被声明为protected,即子类可以覆盖该方法以改变行为  
    addParametersToContext(A
原理:查看源码不难发现使用的是Interceptor拦截器获取request中的键值对和值栈中找有相应setter方法的对象进行赋值。

原理图:



三、spring  aop

首先了解其中几个名词

Pointcut (切入点):目标对象,已经增强的饭方法。

Joinpoint (连接点):目标对象中,所有可以增强的方法。

Advice(通知/增强):增强的代码。

Target (目标对象):被通知的对象。

Weaving(织入):将通知应用到切入点的过程。

Proxy (代理):将通知织入到目标对象之后,形成的代理对象。

Aspect(切面):切入点+通知


spring 的 aop的原理

spring使用动态代理来实现aop的。spring提供了两种方式来生成代理对象: JDK Proxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。下面我们来研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,相关代码:

/** 
    * <ol> 
    * <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false) 
    * <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口 
    * <li>调用Proxy.newProxyInstance创建代理对象 
    * </ol> 
    */  
   public Object getProxy(ClassLoader classLoader) {  
       if (logger.isDebugEnabled()) {  
           logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());  
       }  
       Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);  
       findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
       return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
}  
我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。
public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {  
       MethodInvocation invocation = null;  
       Object oldProxy = null;  
       boolean setProxyContext = false;  
   
       TargetSource targetSource = this.advised.targetSource;  
       Class targetClass = null;  
       Object target = null;  
   
       try {  
           //eqauls()方法,具目标对象未实现此方法  
           if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){  
                return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);  
           }  
   
           //hashCode()方法,具目标对象未实现此方法  
           if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){  
                return newInteger(hashCode());  
           }  
   
           //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知  
           if (!this.advised.opaque &&method.getDeclaringClass().isInterface()  
                    &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
                // Service invocations onProxyConfig with the proxy config...  
                return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);  
           }  
   
           Object retVal = null;  
   
           if (this.advised.exposeProxy) {  
                // Make invocation available ifnecessary.  
                oldProxy = AopContext.setCurrentProxy(proxy);  
                setProxyContext = true;  
           }  
   
           //获得目标对象的类  
           target = targetSource.getTarget();  
           if (target != null) {  
                targetClass = target.getClass();  
           }  
   
           //获取可以应用到此方法上的Interceptor列表  
           List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);  
   
           //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)  
           if (chain.isEmpty()) {  
                retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);  
           } else {  
                //创建MethodInvocation  
                invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
                retVal = invocation.proceed();  
           }  
   
           // Massage return value if necessary.  
           if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)  
                    &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
                // Special case: it returned"this" and the return type of the method  
                // is type-compatible. Notethat we can't help if the target sets  
                // a reference to itself inanother returned object.  
                retVal = proxy;  
           }  
           return retVal;  
       } finally {  
           if (target != null && !targetSource.isStatic()) {  
                // Must have come fromTargetSource.  
               targetSource.releaseTarget(target);  
           }  
           if (setProxyContext) {  
                // Restore old proxy.  
                AopContext.setCurrentProxy(oldProxy);  
           }  
       }  
    }  
获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。
        首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现: 
public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {  
                   MethodCacheKeycacheKey = new MethodCacheKey(method);  
                   List<Object>cached = this.methodCache.get(cacheKey);  
                   if(cached == null) {  
                            cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(  
                                               this,method, targetClass);  
                            this.methodCache.put(cacheKey,cached);  
                   }  
                   returncached;  
         }  

可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。

下面来分析下这个方法的实现:
/** 
    * 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor, 
    * 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断 
    * 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回. 
    */  
    publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {  
       // This is somewhat tricky... we have to process introductions first,  
       // but we need to preserve order in the ultimate list.  
       List interceptorList = new ArrayList(config.getAdvisors().length);  
   
       //查看是否包含IntroductionAdvisor  
       boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);  
   
       //这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor  
       AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();  
   
       Advisor[] advisors = config.getAdvisors();  
        for (int i = 0; i <advisors.length; i++) {  
           Advisor advisor = advisors[i];  
           if (advisor instanceof PointcutAdvisor) {  
                // Add it conditionally.  
                PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;  
                if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {  
                    //TODO: 这个地方这两个方法的位置可以互换下  
                    //将Advisor转化成Interceptor  
                    MethodInterceptor[]interceptors = registry.getInterceptors(advisor);  
   
                    //检查当前advisor的pointcut是否可以匹配当前方法  
                    MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();  
   
                    if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {  
                        if(mm.isRuntime()) {  
                            // Creating a newobject instance in the getInterceptors() method  
                            // isn't a problemas we normally cache created chains.  
                            for (intj = 0; j < interceptors.length; j++) {  
                               interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));  
                            }  
                        } else {  
                            interceptorList.addAll(Arrays.asList(interceptors));  
                        }  
                    }  
                }  
           } else if (advisor instanceof IntroductionAdvisor){  
                IntroductionAdvisor ia =(IntroductionAdvisor) advisor;  
                if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {  
                    Interceptor[] interceptors= registry.getInterceptors(advisor);  
                    interceptorList.addAll(Arrays.asList(interceptors));  
                }  
           } else {  
                Interceptor[] interceptors =registry.getInterceptors(advisor);  
                interceptorList.addAll(Arrays.asList(interceptors));  
           }  
       }  
       return interceptorList;  
}  
这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor.接下来我们再看下得到的拦截器链是怎么起作用的。
if (chain.isEmpty()) {  
                retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);  
            } else {  
                //创建MethodInvocation  
                invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
                retVal = invocation.proceed();  
            }  
从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,来看下具体代码
    public Object proceed() throws Throwable {  
           //  We start with an index of -1and increment early.  
           if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {  
               //如果Interceptor执行完了,则执行joinPoint  
               return invokeJoinpoint();  
           }  
       
           Object interceptorOrInterceptionAdvice =  
               this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);  
             
           //如果要动态匹配joinPoint  
           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)) {  
                    //执行当前Intercetpor  
                    returndm.interceptor.invoke(this);  
               }  
               else {  
                    //动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor  
                    return proceed();  
               }  
           }  
           else {  
               // It's an interceptor, so we just invoke it: The pointcutwill have  
               // been evaluated statically before this object was constructed.  
               //执行当前Intercetpor  
               return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  
           }  
    }  


部分代码参考:http://blog.csdn.net/moreevan/article/details/11977115/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章