spring源碼 — 3、AOP代理生成

AOP代理生成

AOP就是面向切面編程,主要做用就是抽取公共代碼,無侵入的加強現有類的功能。從一個簡單的spring AOP配置開始:html

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <!-- 定義target -->
    <bean id="human" class="org.lep.springtest.aop.Human">
    </bean>
    <!-- 定義advice -->
    <bean id="sleepHlper" class="org.lep.springtest.aop.SleepHelper">
    </bean>

    <!-- 定義切點 -->
    <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
        <property name="pattern" value=".*sleep"></property>
    </bean>

    <!-- 定義advisor -->
    <bean id="sleepAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice" ref="sleepHlper"></property>
        <property name="pointcut" ref="sleepPointcut"></property>
    </bean>

    <!-- 定義代理 -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="human"></property>
        <property name="interceptorNames" value="sleepAdvisor"></property>
    </bean>
</beans>

上面的配置涉及到AOP幾個重要的概念:java

  • pointcut:切點,定義具體什麼地方須要加強
  • advice:通知,定義在切點處進行哪些加強,也就是在切點處乾的事
  • advisor:通知器,將pointcut和advice結合起來,也就組成了一個切面,定義了在什麼地方作什麼事
  • target:須要進行加強的目標類,定義了對誰進行加強
  • proxy:將切面應用在target上,說明對誰(target)作什麼事(advisor)

從上面能夠看出是proxy把這些東西結合起來,那麼proxy是怎麼實現的呢,好比:spring

ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
Sleep sleeper =(Sleep) context.getBean("proxy");
sleeper.sleep();

上面有兩個問題:編程

  1. 定義的proxy是ProxyFactoryBean類型的,爲何能夠強制轉化爲Sleep接口類型的
  2. 在調試的時候爲何 sleeper.sleep() 直接跳進了JdkDynamicAopProxy的invoke

針對第一個問題咱們分析getBean的過程,也就是ProxyFactoryBean的初始化spring-mvc

ProxyFactoryBean初始化

在spring IoC容器初始化以後咱們分析了bean的初始化,最後提到了FactoryBean和BeanFactory的區別,可是沒有詳細分析涉及到的factoryBean的初始化過程,具體以下:mvc

ProxyFactoryBean初始化

ProxyFactoryBean的初始化就是先當作普通的bean初始化,以後再獲取具有factory能力的bean,這裏分爲代理對象的生成和得到真正的代理ui

  • createAopProxy
  • getProxy
    上面圖中在ProxyFactoryBean的getSingletonInstance方法中
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 targetClass = getTargetClass();
            if (targetClass == null) {
                throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
            }
            setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
        }
        // Initialize the shared singleton instance.
        super.setFrozen(this.freezeProxy);
        // createAopProxy裏面會調用DefaultAopProxyFactory的createAopProxy方法來獲取AopProxy
        // getProxy會利用上面生成的AopProxy來生成具體的代理對象
        this.singletonInstance = getProxy(createAopProxy());
    }
    return this.singletonInstance;
}

DefaultAopProxyFactory的createAopProxy以下:this

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.");
        }
        // 若是目標類實現了接口則使用jdk生成proxy
        if (targetClass.isInterface()) {
            return new JdkDynamicAopProxy(config);
        }
        if (!cglibAvailable) {
            throw new AopConfigException(
                    "Cannot proxy target class because CGLIB2 is not available. " +
                    "Add CGLIB to the class path or specify proxy interfaces.");
        }
        // 若是沒有實現接口,則使用cglib來生成proxy,由於jdk的生成方法只支持實現了接口的類的proxy生成
        return CglibProxyFactory.createCglibProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

在ProxyFactoryBean的getProxy方法中會調用上面得到到的AopProxy來生成proxy,生成的proxy返回,也就是最後getBean得到到對象,這個對象是實現了Sleep接口的類,只不過這個類存在內存裏面,由JdkDynamicProxy動態生成的(也就是動態代理),因此能夠向上轉型爲Sleep
將動態代理類保存在本地,反編譯獲得:debug

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.lep.springtest.aop.Sleep;

public final class $Proxy0 extends Proxy implements Sleep {
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sleep() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("org.lep.springtest.aop.Sleep").getMethod("sleep", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

爲何調用到JdkDynamicProxy的invoke方法

上面已經說到了,由於是在內存中存在的動態代理,這個代理實現了Sleep接口,也就是說調試的時候應該跳轉到的是這個代理對象的sleep方法,在sleep方法中調用了InvocationHandler的invoke方法,而JdkDynamicProxy實現了InvocationHandler接口,在JdkDynamicProxy的getProxy方法中3d

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);
        // 把JdkDynamicProxy自身傳入,在proxy中調用的就是JdkDynamicProxy的invoke方法
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

可是IDE不知道內存中的代理類類,因此就直接跳轉到了JdkDynamicProxy的invoke方法

相關文章
相關標籤/搜索