spring基础(三)---AOP

简介

AOP全称: AspectOriented Programing面向切面编程
这里写图片描述

使用

启用@AspectJ支持

为了在Spring中配置@Aspect切面,必须启用Spring对@AspectJ切面配置的支持,spring ioc容器中启用AspectJ注解支持,只要在bean配置文件中定义一个xml元素即可:

<!--启用AspectJ对Annotation的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
当spring ioc容器检测到bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动与AspectJ切面匹配的bean创建代理

切面,通知,切入点

使用

启用@Aspect支持后,切面是带有@AspectJ注解的java类,此切面会被Spring自动识别并用于配置AOP.
定义了一个SecurityHandler 切面来捕捉通用的切入点表达式

package com.tgb.kwy;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * Description
 *
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-21 -27
 */
@Aspect
public class SecurityHandler {
    /*定义Pointcut,Pointcut的名称为pointCut(),此方法没有返回值和参数
    * 该方法就是一个标识,不进行调用*/
    @Pointcut("execution(* com.tgb.kwy.*.*(..))")
    private void pointCut(){};

    /*定义Advice,表示我们的advice应用到哪些pointcut订阅的joinPoint*/
    @Before("pointCut()")
    private void doBefore(){
        System.out.println("AOP Before Advice----doBefore");
    }
}

此时我的目录结构是这样的
这里写图片描述
applicationContext.xml的内容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--启用AspectJ对Annotation的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <bean id="userManager" class="com.tgb.kwy.UserManagerImpl"/>
    <bean id="SecurityHandler" class="com.tgb.kwy.SecurityHandler"></bean>
</beans>

UserManager

package com.tgb.kwy;

/**
 * Description
 *
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-20 -18
 */
public interface UserManager {
    public void addUser(String username, String password);
}

UserManagerImpl

package com.tgb.kwy;

/**
 * Description
 *
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-20 -28
 */
public class UserManagerImpl implements UserManager{
    @Override
    public void addUser(String username,String password){
        System.out.println("-------UserManagerImpl.add()---");
    }
}

此时我们来测试一下:

package com.tgb.kwy;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Description
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-21 -36
 */
public class Client {
    public static void main(String[] args) {
        BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");
       UserManager userManager=(UserManager) factory.getBean("userManager");
       userManager.addUser("zhangsan","123");

    }
}

输出内容:
这里写图片描述
这个就是一个非常简单的测试 可以简单看到aop的使用
而在上面的代码中,我们也看到了AspectJ切入点表达式,是通过方法的签名来匹配各种方法的:
execution(* com.tgb.kwy..(..))
第一个*, 代表任意修饰符及任意返回值
com.tgb.kwy.*代表匹配这个包下面所有的类
后面紧挨的.* ,代表的所有的方法
(..)代表匹配任意数量的参数

上面的测试,我们测的是before
不过在最上面的导图中,我们有看到,他有好几种通知的方式
后置通知,返回通知,异常通知

@Aspect
public class SecurityHandler {
    /*定义Pointcut,Pointcut的名称为pointCut(),此方法没有返回值和参数
    * 该方法就是一个标识,不进行调用*/
    @Pointcut("execution(* com.tgb.kwy.*.*(..))")
    private void pointCut(){};

    /*定义Advice,表示我们的advice应用到哪些pointcut订阅的joinPoint*/
    @Before("pointCut()")
    private void doBefore(){
        System.out.println("AOP Before Advice----doBefore");
    }

    @After("pointCut()")
    private void doAfter(){
        System.out.println("AOP After Advice----doAfter");
    }
    @AfterReturning(pointcut="pointCut()",returning="returnVal")
    public void afterReturn(JoinPoint joinPoint, Object returnVal){
        System.out.println("AOP AfterReturning Advice:" + returnVal);
    }

    @AfterThrowing(pointcut="pointCut()",throwing="error")
    public void afterThrowing(JoinPoint joinPoint,Throwable error){
        System.out.println("AOP AfterThrowing Advice..." + error);
        System.out.println("AfterThrowing...");
    }
}

运行结果
这里写图片描述

环绕通知

扫描二维码关注公众号,回复: 2260254 查看本文章
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;

/**
 * Description
 *
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-21 -27
 */
@Aspect
public class SecurityHandler {
    /*定义Pointcut,Pointcut的名称为pointCut(),此方法没有返回值和参数
    * 该方法就是一个标识,不进行调用*/
    @Pointcut("execution(* com.tgb.kwy.*.*(..))")
    private void pointCut(){};

/*环绕通知需要携带ProceedingJoinPoint类型的参数,它是joinPoint的子接口,允许控制何时执行,是否执行
*/
    @Around("pointCut()")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("AOP Around before...");
        String methodName = pjp.getSignature().getName();
       try {
            //前置通知,加入对调用方法前的处理逻辑
            System.out.println("\"The method \" + methodName + \" begins with \" + Arrays.asList(pjp.getArgs())");
            //正常调用目标对象的目标方法,执行被代理的方法,如果忘记写,就会导致通知被执行了,但是目标方法没有被执行
            pjp.proceed();
            //返回通知,加入对正常调用后的处理逻辑
            System.out.println("The method " + methodName + " ends with " + Arrays.asList(pjp.getArgs()));
        } catch (Throwable e) {
            //异常通知,目标对象的方法调用异常后的处理逻辑
            System.out.println("The method " + methodName + " occurs expection : " + e);
            e.printStackTrace();
        }
        //后置通知
        System.out.println("The method " + methodName + " ends");
        System.out.println("AOP Around after...");
    }
}

运行结果
这里写图片描述

这里追加一下环绕与其他的区别,其实在代码的注释里面,也都写了..
1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。

2) 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是环绕却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用

切面优先级
如果有多个aspect类, 默认无顺序,所以需要自己定义
可以通过实现Ordered接口或者@Order注解指定
使用@Order注解,序号出现在注解中;

@Order(1)
@Aspect
public class SecurityHandler {
    ......
}

源码分析

advice通知

拿before开刀
先看类的关系图:
这里写图片描述
MethodBeforeAdvice实现了一个回调函数,也就是说before方法的实现,会在advice中被配置到目标方法后,调用目标方法时被回调.

public interface Advice {  

}  

public interface BeforeAdvice extends Advice {  

}  
// 前置增强接口,使用这个前置接口需要实现一个回调函数  
    public interface MethodBeforeAdvice extends BeforeAdvice {  

        /** 
         * 作为回调函数,该方法的实现在Advice中被配置到目标方法后,会调用目标方法时被回调。 
         * @param method Method对象,是目标方法的反射对象 
         * @param args 对象数组,包含目标方法的输入参数 
         * @param target   
         * @throws Throwable 
         */  
        void before(Method method, Object[] args, Object target) throws Throwable;  

    }  

PointCut切点

public interface Pointcut {

    /**
     * 获取类过滤器
     * Return the ClassFilter for this pointcut.
     * @return the ClassFilter (never {@code null})
     */
    ClassFilter getClassFilter();

    /**
     * 获取匹配切入点的方法
     * Return the MethodMatcher for this pointcut.
     * @return the MethodMatcher (never {@code null})
     */
    MethodMatcher getMethodMatcher();


    /**
     * 总匹配的标准切入点实例
     * Canonical Pointcut instance that always matches.
     */
    Pointcut TRUE = TruePointcut.INSTANCE;

}

需要返回一个MethodMatcher,所以point的判断功能,具体是通过这个返回方法MethodMatcher来完成的.
我们找关于PointCut的类继承关系,找到了正则表达式切点: JdkRegexpMethodPointcut,那么就可以来了解一下它的实现了

public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {

    /**
     * 要编译的正则表达式模式
     */
    private Pattern[] compiledPatterns = new Pattern[0];

    /**
     * 编译时要排除的正则表达式模式
     * Compiled form of the exclusion patterns.
     */
    private Pattern[] compiledExclusionPatterns = new Pattern[0];


    /**
     * 将给定的模式字符串数组初始化为编译时的正则表达式模式
     * Initialize {@link Pattern Patterns} from the supplied {@code String[]}.
     */
    @Override
    protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException {
        this.compiledPatterns = compilePatterns(patterns);
    }

    /**
     * 使用正则表达式匹配给定的名称
     * Initialize exclusion {@link Pattern Patterns} from the supplied {@code String[]}.
     */
    @Override
    protected void initExcludedPatternRepresentation(String[] excludedPatterns) throws PatternSyntaxException {
        this.compiledExclusionPatterns = compilePatterns(excludedPatterns);
    }

    /**
     * 使用要排除的正则表达式匹配给定的名称
     * Returns {@code true} if the {@link Pattern} at index {@code patternIndex}
     * matches the supplied candidate {@code String}.
     */
    @Override
    protected boolean matches(String pattern, int patternIndex) {
        Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
        return matcher.matches();
    }

    /**
     * 使用要排除的正则表达匹配给定的名称
     * Returns {@code true} if the exclusion {@link Pattern} at index {@code patternIndex}
     * matches the supplied candidate {@code String}.
     */
    @Override
    protected boolean matchesExclusion(String candidate, int patternIndex) {
        Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);
        return matcher.matches();
    }


    /**
     * 将给定的字符串数组编译为正则表达式的模式 
     * Compiles the supplied {@code String[]} into an array of
     * {@link Pattern} objects and returns that array.
     */
    private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException {
        Pattern[] destination = new Pattern[source.length];
        for (int i = 0; i < source.length; i++) {
            destination[i] = Pattern.compile(source[i]);
        }
        return destination;
    }

}
所以pointcut切入点,基本功能就是根据正则表达式判断方法名是否匹配的

AOP xml配置

上面测试,我们是使用的注解方式,现在介绍一下,如果使用xml,应该如何配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
     <!--配置bean-->
    <bean id="userManager" class="com.tgb.kwy.UserManagerImpl"/>
    <!--配置切面bean-->
    <bean id="securityHandler" class="com.tgb.kwy.SecurityHandler"></bean>
    <!--配置AOP-->
    <aop:config>
        <!--配置切面及通知-->
        <aop:aspect id="securityHandler" ref="securityHandler" order="1">
            <!--以add开头的方法-->
            <!--<aop:pointcut id="doBefore" expression="execution(* add*(..))"/>-->
            <!--com.tgb.kwy包下所有的类所有的方法,参数不定-->
            <!--<aop:pointcut id="addAddMethod" expression="execution(* com.tgb.kwy.*.*(..))"></aop:pointcut>-->
            <!--配置切点表达式-->
            <aop:pointcut id="doBefore" expression="execution(* com.tgb.kwy.*.add*(..)) || execution(* com.tgb.kwy.*.del*(..))"></aop:pointcut>
            <!--前置通知-->
            <aop:before method="checkSecurity" pointcut-ref="doBefore"/>
        </aop:aspect>
    </aop:config>
</beans>

代理机制

  • spring aop部分使用jdk动态代理,或者CGLIB为目标对象创建代理
  • 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。
  • 如果希望强制使用CGLIB代理
<aop:config proxy-target-class="true">
      <!-- other beans defined here... -->
</aop:config>

代码实验

目录结构
这里写图片描述

SecurityHandler

package com.tgb.kwy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Description
 *动态代理
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-21 -27
 */
public class SecurityHandler implements InvocationHandler {
    private Object targetObject;
    public Object createProxyInstance(Object targetObject){
        this.targetObject=targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method,Object[] args) throws Throwable{
        checkSecurity();
        /*调用目标方法*/
        Object ret=method.invoke(targetObject,args);
        return ret;
    }
    private void checkSecurity(){
        System.out.println("----checkSecurity");
    }
}

UserManager

package com.tgb.kwy;

/**
 * Description
 *
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-20 -18
 */
public interface UserManager {
    public void addUser(String username,String password);
    public void delUser(int userId);
    public String findUserById(int userId);
    public void modifyUser(int userId,String username,String password);
}

UserManagerImpl

package com.tgb.kwy;

import javax.swing.plaf.PanelUI;

/**
 * Description
 *
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-20 -28
 */
public class UserManagerImpl implements UserManager{
    @Override
    public void addUser(String username,String password){
        System.out.println("-------UserManagerImpl.add()---");
    }
    @Override
    public void delUser(int userId){
        System.out.println("-------UserManagerImpl.add()---");
    }
    @Override
    public String findUserById(int userId){
        System.out.println("-------UserManagerImpl.findUserById()---");
        return "张三";
    }
    @Override
    public void modifyUser(int userId,String username,String password){
        System.out.println("--------UserManagerImpl.modifyUser()---");
    }
}

Client

package com.tgb.kwy;

/**
 * Description
 *动态代理
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-21 -36
 */
public class Client {
    public static void main(String[] args) {
        SecurityHandler handler=new SecurityHandler();
        UserManager userManager=(UserManager) handler.createProxyInstance(new UserManagerImpl());
        userManager.addUser("张三","123");

    }
}

测试结果
这里写图片描述

如果不用动态代理, 上面的SecurityHandler就可以不用了, 那么要改变的就是userManager的实现类了
就得这么写了:

package com.tgb.kwy;

/**
 * Description
 *静态代理
 * @author kongwy [email protected]
 * @version 1.0
 * @date 2018-06-04-21 -13
 */
public class UserManagerImplProxy implements UserManager{
    private UserManager userManager;
    @Override
    public void addUser(String username,String password){
        checkSecurity();
        userManager.addUser(username,password);
    }
    @Override
    public void delUser(int userId){
        checkSecurity();
        userManager.delUser(userId);
    }
    @Override
    public String findUserById(int userId){
        checkSecurity();
        return userManager.findUserById(userId);
    }
    @Override
    public void modifyUser(int userId,String username,String password){
        checkSecurity();
        userManager.modifyUser(userId,username,password);
    }
    private void checkSecurity(){
        System.out.println("-------checkSecurity-----");
    }
}

然后我没添加一个方法, 就得在这个方法里面写上checkSecurity. 是不是很麻烦.

源码

上面也有写类SecurityHandler 实现了InvocationHandler类. 所以动态代理的核心,就是这个类,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起.那么这个就是AOP的第二部分: 启动代理对象的拦截器来完成各种横切面的织入, 那么第一部分, 需要为目标对象建立代理对象,那么咱们先来聊聊,动态对象如何生成的

动态对象生成

ProxyFactoryBean ,spring应用是通过配置和调用它,来完成任务的,先看一下它的相关类继承关系
这里写图片描述

  • ProxyConfig,共同基类,可以看成有一个数据基类.
  • AdvisedSupport的实现中,封装了AOP对通知和通知器的相关操作;子类完成
  • ProxyCreatorSupport,创建AOP代理对象的一个辅助类;子类完成
  • AspectJProxyFactory,集成spring和AspectJ.适用于需要使用AspectJ 的AOP应用
  • ProxyFactoryBean,Spring的AOP应用, 可以在ioc容器中完成声明式配置
  • ProxyFactory: Sping的AOP应用,需要使用编程式使用

在ProxyFactoryBean中,1,为target目标对象生成Proxy代理对象.而想得到代理对象,得通过getproxy()方法
我们跳到ProxyFactoryBean类里面看这段代码:

/**
     * Return a proxy. Invoked when clients obtain beans from this factory bean.
     * Create an instance of the AOP proxy to be returned by this factory.
     * The instance will be cached for a singleton, and create on each call to
     * {@code getObject()} for a proxy.
     * @return a fresh AOP proxy reflecting the current state of this factory
     */
    @Override
    @Nullable
    public Object getObject() throws BeansException {
        initializeAdvisorChain();//初始化通知器
        if (isSingleton()) {
            return getSingletonInstance();//根据定义生成单例proxy
        }
        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(); //根据定义生成prototype的Proxy
        }
    }

我们继续看getSingletonInstance()

/**
     * Return the singleton instance of this class's proxy object,
     * lazily creating it if it hasn't been created already.
     * @return the shared singleton proxy
     */
    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();//这有是一个方法,这个方法就是返回创建代理时使用的目标对象。
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // Rely on AOP infrastructure to tell us what interfaces to proxy.
                //获取目标对象class
                Class<?> targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }

//Set the interfaces to be proxied. 设置代理对象接口
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.初始化共享单例 setFrozen是父类的方法             
            super.setFrozen(this.freezeProxy);
            //getProxy依旧是ProxyFactory的方法, 而createAopProxy则是ProxyCreatorSupport的方法
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }

我们跳进ProxyCreatorSupport方法看看

/**
     * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
     * create an AOP proxy with {@code this} as an argument.
     */
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }
/**
     * Return the AopProxyFactory that this ProxyConfig uses.
     */
    public AopProxyFactory getAopProxyFactory() {
        return this.aopProxyFactory;
    }
/**
     * Create a new ProxyCreatorSupport instance.
     */
    public ProxyCreatorSupport() {
        this.aopProxyFactory = new DefaultAopProxyFactory();
    }

继续看DefaultAopProxyFactory(),在这个类里面找: createAopProxy()

@Override
    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.");
//如果targetClass是接口类,使用jdk来生成Proxy
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            //如果不是接口类要生成Proxy,那么使用CGLIB来生成
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

到这里就能看出来,代理对象是jdk或者Cglib生成的

JDK生成AopProxy代理对象过程
上面我们知道了代理对象的生成,是通过Spring封装的JdkDynamicAopProxy类完成的,那么我们就跳到这个类里面,找它的getProxy()

@Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        //这里是调用jdk生成Proxy的地方
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
  • 总结一下,代理对象咋生成的?
    • 两种代理对象生成方法: 1.ProxyFactory 2.ProxyFactoryBean
    • 通过createAopProxy().getProxy() 来获取对应的代理对象, 上面讲的是 ProxyFactoryBean获得代理对象
    • 首先调用getSingletonInstance() 这个方法里面是引入了父类ProxyCreatorSupport的方法, 另外这个方法里面还包括了createAopProxy().
    • 继续往下走,有一个构造函数,定义了DefaultAopProxyFactory(). 这个类里面的createAopProxy(),默认是调用jdk动态

拦截器

原理:AopProxy封装对象target模板对象之后,ProxyFactoryBean的getObject方法得到的对象,就不在是一个普通的java对象了. 是一个代理对象.这是ProxyFactoryBean中配置的target对象,不会让应用直接调用其方法实现了.所以对target的方法调用时,会先被AopProxy代理对象拦截.jdk生成的代理, 使用的InvocationHandler的invoke回调入口
那么invoke是如何拦截的
还记得上面的这个吧:

@Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

最后一句话,我们跳进去看:
这里写图片描述
所以this,指的就是InvocationHandler对象了.InvocationHandler是jdk定义的反射的一个接口,这个接口定义了invoke方法(也就这么一个方法),这个invoke方法是作为jdk proxy代理对象进行拦截的回调入口出现的.
我们看JdkDynamicAopProxy的类图
这里写图片描述
因为JdkDynamicAopProxy实现了InvocationHandler,所以当Proxy对象的代理方法被调用时,JdkDynamicAopProxy的invoke方法,就会被作为Proxy对象的回调函数被触发
我们看一下invoke方法

/**
     * Implementation of {@code InvocationHandler.invoke}.
     * <p>Callers will see exactly the exception thrown by the target,
     * unless a hook method throws an exception.
     */
    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself. 如果目标对象没有实现Object类的基本方法:equals
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.如果目标对象没有实现Object类的基本方法:hashCode
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else 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;
            }

            // Get as late as possible to minimize the time we "own" the target,得到目标对象
            // in case it comes from a pool.
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // 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.如果没有设定拦截器,那么就直接用target的对应方法
            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.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // We need to create a method invocation...如果有拦截器的设定,那么需要调用拦截器之后才调用目标对象的相应方法,通过构造一个ReflectiveMethodInvocation来实现
                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 != Object.class && 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);
            }
        }
    }

上面代码我们看了,如果没有设置拦截器,会对目标对象直接进行调用.对于jdkDynamicAopProxy代理对象,这个对目标对象调用的方法,是通过AopUtils使用反射机制在AopUtils.invokeJoinpointUsingReflection方法中实现的,—上面代码有的,
我们来看一下这个方法

/**
     * Invoke the given target via reflection, as part of an AOP method invocation.
     * @param target the target object
     * @param method the method to invoke
     * @param args the arguments for the method
     * @return the invocation result, if any
     * @throws Throwable if thrown by the target method
     * @throws org.springframework.aop.AopInvocationException in case of a reflection error
     */
    @Nullable
    public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
            throws Throwable {

        // Use reflection to invoke the method.使用反射调用target对象方法的地方
        try {
            ReflectionUtils.makeAccessible(method);
            return method.invoke(target, args);
        }
        catch (InvocationTargetException ex) {
            // Invoked method threw a checked exception.
            // We must rethrow it. The client won't see the interceptor.抛出AOP异常,对异常进行转换
            throw ex.getTargetException();
        }
        catch (IllegalArgumentException ex) {
            throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
                    method + "] on target [" + target + "]", ex);
        }
        catch (IllegalAccessException ex) {
            throw new AopInvocationException("Could not access method [" + method + "]", ex);
        }
    }

拦截器链的调用

AOP实现核心部分
在上面的代码中,我们有看到,对拦截器链调用的是通过ReflectiveMethodInvocation中通过proceed方法实现的.在proceed方法中,我们跳进去看看

@Override
    @Nullable
    public Object proceed() throws Throwable {
        //  We start with an index of -1 and increment early.
        //从索引为-1的拦截器开始调用,并按顺序递增,如果拦截器链中的拦截器迭代调用完毕,这里开始调用target的函数,这个函数是通过反射机制完成的,具体实现在AopUtils.invokeJoinpointUsingReflection方法中
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }
        //这里沿着定义好的interceptorOrInterceptionAdvice链进行处理
        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.这里对拦截器进行动态匹配的判断,如果和前面定义的pointcut匹配,这个advice就会被执行.
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.匹配失败,proceed会被递归调用,直到所有拦截器都被运行过为止
                // Skip this interceptor and invoke the next in the chain.
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.如果是一个interceptor,直接调用这个interceptor对应的方法
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

猜你喜欢

转载自blog.csdn.net/kwy15732621629/article/details/80631648