zoukankan      html  css  js  c++  java
  • Spring/Boot/Cloud系列知识(4)——代理模式(下)

    =============================================
    接上文《Spring/Boot/Cloud系列知识(3)——代理模式(中)》)

    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原理。

  • 相关阅读:
    从零搭建ES搜索服务(一)基本概念及环境搭建
    SpringBoot+Mybatis多模块(module)项目搭建教程
    Redis分布式锁实现方式(附有正解及错误示例)
    MySQL QA
    Netty handler处理类无法使用@Autowired注入bean的解决方法
    数组的全排列
    链表分段反转
    tomcat调优
    Spring Boot之JdbcTemplate多数据源配置与使用
    aPaaS
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744234.html
Copyright © 2011-2022 走看看