SpringAOP作成プロキシのソースコード分析

SpringAOP作成プロキシのソースコード分析

TSMYKJavaテクノロジープログラミング

序文

前回の記事では、Spring AOPアノテーションメソッドのソースコード分析により、対応するBeanのエンハンサーが取得されました。その後、対応するプロキシを作成できます。SpringAOPの最下層は、JDK動的プロキシとCGLIBプロキシです。どのような状況である必要がありますか。 JDKを使用する動的プロキシ。CGLIBプロキシを使用する場合は、ソースコードを見てみましょう。


// AbstractAutoProxyCreator.java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //......
    // 获取的增强器
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    //............
    return bean;
}

プロキシの作成createProxy


protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    // ....
    ProxyFactory proxyFactory = new ProxyFactory();
    //复制当前类的一些属性
    proxyFactory.copyFrom(this);
    // 如果在配置文件中配置的aop标签的属性proxy-target-class为false,
    if (!proxyFactory.isProxyTargetClass()) {
        // 是否需要代理当前类而不是代理接口,根据preserveTargetClass属性来判断Boolean.TRUE.equals(bd.getAttribute("preserveTargetClass")
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            // 如果代理的是接口,则添加代理接口
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    // 对增强器进行包装,有些增强是通过拦截器等方式来实现的,所以这里统一封装为 Advisor 进行处理
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 加入增强器
    proxyFactory.addAdvisors(advisors);
    // 设置要代理的类
    proxyFactory.setTargetSource(targetSource);
    // 用户自定义代理
    customizeProxyFactory(proxyFactory);
    // 该属性用来控制代理工厂被配置以后,是否还允许修改通知,默认为false
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    // 创建代理
    return proxyFactory.getProxy(getProxyClassLoader());
}

// 添加接口代理
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
    Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
    boolean hasReasonableProxyInterface = false;
    //....
    if (hasReasonableProxyInterface) {
        for (Class<?> ifc : targetInterfaces) {
            proxyFactory.addInterface(ifc);
        }
    }
    else {
        proxyFactory.setProxyTargetClass(true);
    }
}

カプセル化の機能強化:Springでは、一部の機能強化がインターセプターを介して実装されているため、buildAdvisors()メソッドに対応する処理用のAdvisorの統合カプセル化を次に示します。


protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
    //解析所有的 InterceptorName
    Advisor[] commonInterceptors = resolveInterceptorNames();

    List<Object> allInterceptors = new ArrayList<>();
    if (specificInterceptors != null) {
        // 添加参数传进来的,即我们自定义的增强
        allInterceptors.addAll(Arrays.asList(specificInterceptors));
        if (commonInterceptors.length > 0) {
            // 添加拦截器
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }
    //把拦截器包装为Advisor
    Advisor[] advisors = new Advisor[allInterceptors.size()];
    for (int i = 0; i < allInterceptors.size(); i++) {
        // wrap包装
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
}

//wrap包装
// 仅仅对 Advisor 和 Advice进行包装
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    // 如果本来就是 Advisor,则直接返回
    if (adviceObject instanceof Advisor) {
        return (Advisor) adviceObject;
    }
    // 类型不正确,异常
    if (!(adviceObject instanceof Advice)) {
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    if (advice instanceof MethodInterceptor) {
        // MethodInterceptor 类型使用 DefaultPointcutAdvisor 封装
        return new DefaultPointcutAdvisor(advice);
    }
    // 如果存在 Advisor 的适配器,也需要包装
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

プロキシgetProxyを作成する


return proxyFactory.getProxy(getProxyClassLoader());

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

createAopProxy():

ここでは、プロキシの作成方法がJDKの動的プロキシを使用するかCGLIBのプロキシを使用するかを判断します。


public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    //这里初步判断代理的创建方式,如果不满足则直接使用 JDK 动态代理,如果满足条件,则进一步在判断是否使用 JKD 动态代理还是 CGLIB 代理
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("......");
        }
        // 如果代理的是接口或者设置代理的类就是当前类,则使用 JDK 动态代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 否则使用 CGLIB 代理
        return new ObjenesisCglibAopProxy(config);
    }
    // 条件不满足CGBLIB的方式直接使用JDK动态代理
    else {
        return new JdkDynamicAopProxy(config);
    }
}
  1. ここには3つのif条件があります。

config.isOptimize():CGLIBによって作成されたプロキシが積極的な最適化戦略を使用するかどうかを制御するために使用されます。現在はCGLIBプロキシにのみ使用されます

  1. config.isProxyTargetClass():Spring AOPアノテーションのソースコード分析で、proxy-target-class属性が次のような構成ファイルでtrueに設定されている限り、SpringにプロキシにCGLIBを使用させることができることを学びました。 :<aop:aspectj- autoproxy Exposure-proxy = "true" proxy-target-class = "true" />、この属性が構成されている場合、CGLIBを使用してプロキシが作成されます

    1. hasNoUserSuppliedProxyInterfaces(config):プロキシインターフェイスがあるかどうか、プロキシインターフェイスがない場合は、プロキシにCGLIBを使用します

    これらの3つの条件のいずれかが満たされた場合、プロキシである必要があるクラスがインターフェイスであるか、現在のクラスをプロキシするように設定されているかどうかが再度判断されます。そうである場合は、JDK動的プロキシが使用されます。 CGLIBプロキシが使用されます。

1.プロキシクラスがインターフェイスを実装する場合、SpringはデフォルトでJDK動的プロキシを使用しますが、CGLIBプロキシの使用を強制するように設定できます。2。JDK
動的プロキシはインターフェイスのみをプロキシできますがクラスは使用できません
。3。CGLIBプロキシクラス、継承を通じて、クラスはサブクラスを生成し、プロキシを実装するためにメソッドを書き換えます。最終的なクラスまたはメソッドをプロキシすることはできません
。4。JDK動的プロキシおよびCGLIBプロキシの使用法については、SpringAOP関数の使用法の詳細を参照してください。

次に、JDK動的プロキシとCGLIBプロキシの作成プロセスを見てください。

JDK動的プロキシ


return new JdkDynamicAopProxy(config);

// JdkDynamicAopProxy.java
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
    this.advised = config;
}

JDK動的プロキシを介してプロキシメソッドgetProxy()を取得します。


public Object getProxy(ClassLoader classLoader) {
    // 获取代理类的接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // 处理 equals , hashcode 方法
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // 创建代理
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

ご覧のとおり、Springがプロキシを作成するためにJDKを使用することと、プロキシを作成するために使用するJDKに違いはありません。どちらもProxy.newProxyInstanceを使用して作成されます。JDK動的プロキシには、ターゲットを実行するためのinvokeメソッドがあることがわかっています。 JdkDynamicAopProxyはInvocationHandlerインターフェースを実装しているため、このメソッドを書き直し、このメソッドに拡張機能を組み込みます。


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 {
        // 如果接口没有定义 equals 方法且当前方法是 equals 方法,则不会增强,直接返回
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 如果接口没有定义 hashCode方法且当前方法是 hashCode方法,则不会增强,直接返回
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 如果方法所在的类和Advised是同一个类或者是父类子类关系,则直接执行
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        // 返回值
        Object retVal;

        // 这里对应的是expose-proxy属性的应用,把代理暴露处理
        // 目标方法内部的自我调用将无法实施切面中的增强,所以在这里需要把代理暴露出去
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 获取该方法的拦截器
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        //如果方法的拦截器为空,则直接执行目标方法,避免创建 MethodInvocation 对象
        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            // 执行目标方法:method.invoke(target, args)
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 把所有的拦截器封装在ReflectiveMethodInvocation中,以便于链式调用 
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 执行拦截器链
            retVal = invocation.proceed();
        }
        // ..........
        return retVal;
    }
    finally {
      // .            
    }
}

エンハンスメントメソッドは、実行インターセプターメソッドproceedで実行されます。たとえば、プリエンハンスメントはメソッドの前に実行され、ポストエンハンスメントはメソッドの後に実行されます。proceedメソッドは次のとおりです。


public Object proceed() throws Throwable {
    //当执行完所有增强方法后执行目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // method.invoke(target, args)
        return invokeJoinpoint();
    }
     // 获取下一个要执行的拦截器
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 动态匹配
        InterceptorAndDynamicMethodMatcher dm = interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 如果能够匹配,则执行拦截器的方法,
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            // 比如 @After @Before 对应的增强器(拦截器)的方法
            // 比如 @After 对应的增强器 AspectJAfterAdvice 的invoke方法为:MethodInvocation.proceed();
            return dm.interceptor.invoke(this);
        }
        else {
            // 如果动态匹配失败,则跳过该拦截器,执行下一个拦截器
            return proceed();
        }
    }
    else {
        // 普通拦截器,直接调用
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

このメソッドでは、拡張された埋め込みが完了します。主なロジックは、各メソッドにインターセプターチェーンがあり、これをAOPで拡張と呼び、すべてのインターセプターが実行されると、ループ内で各インターセプターチェーンを実行します。その後、ターゲットメソッド実行されます。たとえば、@ Afterに対応するエンハンサーAspectJAfterAdvice、@ Aroundに対応するエンハンサーAspectJAroundAdviceなどです。

上記は、SpringがJDK動的プロキシを介してAOPを実装するプロセスです。

CGLIBエージェント

ObjenesisCglibAopProxyはCglibAopProxyから継承します


return new ObjenesisCglibAopProxy(config)

public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
    this.advised = config;
    this.advisedDispatcher = new AdvisedDispatcher(this.advised);
}

CglibAopProxyのgetProxyメソッドは次のとおりです。


public Object getProxy(ClassLoader classLoader) {
    // 代理的目标类
    Class<?> rootClass = this.advised.getTargetClass();
    Class<?> proxySuperClass = rootClass;
    if (ClassUtils.isCglibProxyClass(rootClass)) {
        proxySuperClass = rootClass.getSuperclass();
        Class<?>[] additionalInterfaces = rootClass.getInterfaces();
        for (Class<?> additionalInterface : additionalInterfaces) {
            this.advised.addInterface(additionalInterface);
        }
    }
    // 创建并配置 CGLIB Enhancer
    Enhancer enhancer = createEnhancer();
    if (classLoader != null) {
        enhancer.setClassLoader(classLoader);
        if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isCla***eloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
        }
    }
    enhancer.setSuperclass(proxySuperClass);
    enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

    // 设置拦截器
    Callback[] callbacks = getCallbacks(rootClass);
    Class<?>[] types = new Class<?>[callbacks.length];
    for (int x = 0; x < types.length; x++) {
        types[x] = callbacks[x].getClass();
    }
    enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
    enhancer.setCallbackTypes(types);

    //生成代理类和创建代理
    return createProxyClassAndInstance(enhancer, callbacks);
}

// 生成代理类和创建代理
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    enhancer.setInterceptDuringConstruction(false);
    enhancer.setCallbacks(callbacks);
    return (this.constructorArgs != null && this.constructorArgTypes != null ?
            enhancer.create(this.constructorArgTypes, this.constructorArgs) :
            enhancer.create());
}

上記のメソッドから、SpingがCGLIBを使用して、使用するのと同じようにプロキシクラスとプロキシオブジェクトを作成していることがわかります。これらはEnhancer.create()を使用して作成されます。ここでの主なことは、インターセプターを設定することです。 getCallbacks()メソッド。次のとおりです。


private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    //expose-proxy 属性
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isFrozen = this.advised.isFrozen();
    boolean isStatic = this.advised.getTargetSource().isStatic();

    // 将拦截器封装在 DynamicAdvisedInterceptor 中
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

    //暴露代理
    Callback targetInterceptor;
    if (exposeProxy) {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
    }
    else {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
    }
    // 将拦截器 aopInterceptor 进入到 Callback 中 
    Callback[] mainCallbacks = new Callback[] {
            aopInterceptor,  // for normal advice
            targetInterceptor,  // invoke target without considering advice, if optimized
            new SerializableNoOp(),  // no override for methods mapped to this
            targetDispatcher, this.advisedDispatcher,
            new EqualsInterceptor(this.advised),
            new HashCodeInterceptor(this.advised)
    };
    // ..............
    return callbacks;
}

CGLIBを使用してプロキシ機能を実装する場合、プロキシを実行すると、JKD動的プロキシのinvokeメソッドと同様のインターセプトメソッドが呼び出されます。SpringのCGLIBのインターセプトメソッドは次のとおりです。メソッドはDynamicAdvisedInterceptorにあり、上記のプロキシから、インターセプターをカプセル化するために使用します。これはCglibAopProxyのサブクラスです。


public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy){
    Object oldProxy = null;
    boolean setProxyContext = false;
    Object target = null;
    // 目标类
    TargetSource targetSource = this.advised.getTargetSource();
    // 处理 expose-proxy 属性,暴露代理
    if (this.advised.exposeProxy) {
        oldProxy = AopContext.setCurrentProxy(proxy);
        setProxyContext = true;
    }
    target = targetSource.getTarget();
    Class<?> targetClass = (target != null ? target.getClass() : null);

    // 获取拦截器链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    // 返回值
    Object retVal;

    if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
        // 如果拦截器为空则直接执行目标方法
        retVal = methodProxy.invoke(target, argsToUse);
    }
    else {
        //封装拦截器链并执行
        retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    }
    // 处理返回值类型
    retVal = proce***eturnType(proxy, target, method, retVal);
    return retVal;
    // .....................
}

CGLIBは、CglibMethodInvocationを使用して、CglibAopProxyの内部クラスであるインターセプターチェーンをカプセル化します。


private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

    @Nullable
    private final MethodProxy methodProxy;

    public CglibMethodInvocation(Object proxy, Object target, Method method,Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {

        super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);

        this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
                method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
                !AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
                methodProxy : null);
    }

    // proceed 方法会调用该方法来执行
    @Override
    protected Object invokeJoinpoint() throws Throwable {
        if (this.methodProxy != null) {
            return this.methodProxy.invoke(this.target, this.arguments);
        }
        else {
            return super.invokeJoinpoint();
        }
    }
}

proceedメソッドが呼び出されると、処理はJDKの処理と同じですが、すべてのインターセプターが実行された後、実行ターゲットメソッドがCglibMethodInvocationのinvokeJoinpointを呼び出して実行する点が異なります。

CglibMethodInvocationはReflectiveMethodInvocationを継承し、JDKはReflectiveMethodInvocationを使用して実行するため、ReflectiveMethodInvocationのinvokeJoinpointメソッドは次のようになります。method.invoke(target、args)

上記は、SpringがJDK動的プロキシとCGLIBプロキシを使用してAOPを実装するという原則です。

おすすめ

転載: blog.51cto.com/15077536/2608544