A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

3.3 Proxy.newProxyInstance内部如何完成工作的

我们来看看org.mockito.cglib.proxy.Proxy.newProxyInstance这个方法内部的代码:

public class Proxy implements Serializable {
    ......

    // 这个常量BAD_OBJECT_METHOD_FILTER(代理选择器)的设定,等一下会用到
    // 实际上代码也清楚,一般情况下执行第0个代理器,如果是代理执行java.lang.Object中的方法
    // 且这些方法又不是hashCode()、equals()、toString(),则执行第1个代理器
    private static final CallbackFilter BAD_OBJECT_METHOD_FILTER = new CallbackFilter() {
        public int accept(Method method) {
            if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
                String name = method.getName();
                if (!(name.equals("hashCode") ||
                      name.equals("equals") ||
                      name.equals("toString"))) {
                    return 1;
                }
            }
            return 0;
        }
    };

    ......

    // private for security of isProxyClass
    private static class ProxyImpl extends Proxy {
        protected ProxyImpl(InvocationHandler h) {
            super(h);
        }
    }

    ......

    // 该方法中使用了Cglib中对ASM freamework的封装,动态创建一个class定义
    public static Class getProxyClass(ClassLoader loader, Class[] interfaces) {
        Enhancer e = new Enhancer();
        // 为这个class设置一个父类,这个父类名叫ProxyImpl,其中定义了一个构造函数
        // 那个构造函数需要传入个代理器对象
        e.setSuperclass(ProxyImpl.class);
        // 然后为这个class设置接口,请注意,可以设置多个接口哦
        e.setInterfaces(interfaces);
        // 为这个动态class设置代理器类型,设定了这个方法就应该使用CallbackFilter设定代理选择器(过滤器)
        e.setCallbackTypes(new Class[]{
            InvocationHandler.class,
            NoOp.class,
        });
        // 这个我们使用了Proxy中,在前文定义好的BAD_OBJECT_METHOD_FILTER常量,请参见
        //  
        e.setCallbackFilter(BAD_OBJECT_METHOD_FILTER);
        e.setUseFactory(false);
        // 最后创建这个动态class(注意是创建class,并不是这个class的实例)
        return e.createClass();
    }

    ......

    // newProxyInstance方法中实际上就是两句话,重要的代码都在getProxyClass这个方法中。
    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) {
        try {
            // 该方法用来动态创建一个class,请参考方法中的内容
            Class clazz = getProxyClass(loader, interfaces);
            // 很显然,通过以上方法我们拿到了一个动态class,这个class有三个重要特点,
            // 1、这个class有一个带有参数的构造函数,这个参数就是需要我们传入代理器接口的一个实现实例。
            // 2、这个class实现了我们需要它实现的一个或者多个接口——没有实现代码,但是有这样的类结构
            // 3、这个class设定了两个代理器,通常执行第0个代理器,就是我们传入的InvocationHandler h对象
            // 另外,如果是代理执行java.lang.Object中的方法
            // 且这些方法又不是hashCode()、equals()、toString(),则执行第1个代理器
            //
            // 接着,我们执行第二句代码,这句代码初始化一个这个动态类的示例,并传入代理器实例对象
            return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h });
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

    ......
}

要阅读本小节以上代码片段,请从上篇文章中newProxyInstance()方法开始看。

4 Cglib和JDK动态代理在Spring中的应用

4.1、再次说明JDK动态代理的实例

Spring中使用JDK动态代理的情况已将在前文中介绍过一次(请参见前文《Spring/Boot/Cloud系列知识(3)——代理模式(中)》),这里再举一个实际应用——Spring Data JPA组件。我们通过实现了Java JPA规范的Hibernate组件,定义了一个AgentRepository接口,注意这个接口没有任何实现,只有接口、接口方法和接口方法注解上的HQL/SQL操作语句(如果您的接口方法遵循了JPA规范标准定义,甚至没有操作语句的注解也成)。接着我们在某个Service中依赖注入这个AgentRepository接口对象。

以下是AgentRepository接口的定义。请注意,这个接口遵循JPA规范,其下并没有任何实现类:
@Repository("agentRepository")
public interface AgentRepository extends JpaRepository<AgentEntity, String>, JpaSpecificationExecutor<AgentEntity> {

  AgentEntity findByAccountAndStatus(String account , UseStatus status);

  AgentEntity findByAccount(String account);

  @Modifying
  @Query(value="insert into role_agent(agent_id , role_id) value (:agentId , '2bb')" , nativeQuery=true)
  void bindAgentRole(@Param("agentId") String agentId);
}

接着我们在其它代码中,注入这个对象,并通过
  • debug模式观察AgentRepository接口实例的对象引用情况(关于Spring-data-JPA这个组件本专题后续文章还会详细讲解):

4.2、Cglib代理在Spring中的应用(1)Cglib的封装

实际上Spring组件并没有直接使用Cglib的原生类/工具,而是通过Spring Core组件对Cglib组件进行了封装/重写,在Spring Core组件中的org.springframework.cglib包中:

在介绍Cglib在Spring生态中的具体应用前,我们先来看看在以上的代码示例中Cglib代理的应用位置:

通过MyService接口实例在Debug模式下显示的内容可以看到,myService是一个Spring通过Cglib组件动态生成的代理实例,这里有7个代理器对象,负责在不同的方法被调用时进行代理。

(2)Cglib的代理器原理

Spring AOP组件中,默认使用Cglib动态代理。它对Cglib动态代理的支持,主要由Spring-cor组件提供,而Spring AOP组件依赖于Spring Core组件。前者Spring AOP组件中的org.springframework.aop.framework.DefaultAopProxyFactory类将决定和生成具体的代理器——JdkDynamicAopProxy或者ObjenesisCglibAopProxy(这是CglibAopProxy类的子类)。我们来看看org.springframework.aop.framework.CglibAopProxy这个类中的一些重要代码——主要是其中如何组装各种代理器的:


public Object getProxy(ClassLoader classLoader) {

    ......

    // 这是Spring Core中封装的org.springframework.cglib.proxy.Enhancer类

    // 可见Spring aop组件中依赖于spring core组件。

    // Configure CGLIB Enhancer...

    Enhancer enhancer = createEnhancer();

    // ========以下代码用于动态生成被代理的proxySuperClass的代理类

    if (classLoader != null) {

        enhancer.setClassLoader(classLoader);

        if (classLoader instanceof SmartClassLoader &&

                ((SmartClassLoader) classLoader).isClassReloadable(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();

    }

    // fixedInterceptorMap only populated at this point, after getCallbacks call above

    enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));

    enhancer.setCallbackTypes(types);

    // Generate the proxy class and create a proxy instance.

    return createProxyClassAndInstance(enhancer, callbacks);

    ......

}

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {

    // Parameters used for optimisation choices...

    // advised是AOP配置信息的一个对象(AdvisedSupport extends ProxyConfig implements Advised)

    // 从内部代码看,exposeProxy属性和frozen属性来自于它的父类ProxyConfig,默认值都为false

    boolean exposeProxy = this.advised.isExposeProxy();

    boolean isFrozen = this.advised.isFrozen();

    // targetSource是org.springframework.aop.TargetSource接口的一个实现,

    // 它描述了被代理/被切面的实际对象。其下有很多实现,包括但不限于:PrototypeTargetSource、ThreadLocalTargetSource、

    // EmptyTargetSource、SimpleBeanTargetSource、CommonsPool2TargetSource等

    // 视不同的代理目标、环境配置,使用的实现类不一样。

    // 其中SingletonTargetSource类是TargetSourcede的默认实现,当被代理的目标是来自于Spring IOC容器的简单对象时,就使用此代理类实现代理

    // SingletonTargetSource类实例中的isStatic属性,默认返回true

    boolean isStatic = this.advised.getTargetSource().isStatic();

    ......

    // 这个数组是实现了Callback接口的多个对象,就是在上图中看到的7个代理器对象。

    // 其中aopInterceptor对象是DynamicAdvisedInterceptor代理器的实现

    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)

    };

    ......

    Callback[] callbacks;

    if(isStatic && isFrozen) {

        ......

    } eles {

        callbacks = mainCallbacks;

    }

    return callbacks;

}


4. 后文介绍


IOC容器原理、Spring EL表达式和Spring AOP原理。


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马