Spring源码深度解析(郝佳)-学习-jdk代理-cglib代理

        在java中目前主要使用的是jdk代理和cglib代理,这两种代理是Spring AOP的精髓所在,不过在理解Spring AOP之前,先来看看这两种代理的使用。

1. jdk代理使用示例

创建业务接口,业务对外提供了接口,包含着业务可以对象提供的功能。

public interface UserService {
    //  目标方法
    public abstract void add();
}

创建业务接口实现类

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("----------------add------------------");
    }
}

创建自定义InvocationHandler,用于对接口提供的方法进行增强。

public class MyInvocationHandler implements InvocationHandler {
    // 目标方法
    private Object target;

	//构造方法
    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }

	//执行目标对象的方法
    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
    	//在目标对象的方法执行之前简单的打印一下
        System.out.println("--------------before-----------------------");
        //执行目标对象的方法
        Object result = method.invoke(target, args);
        //在目标对方法执行之后简单的打印一下
        System.out.println("--------------after---------------------");
        return result;
    }

	//获取目标对象的代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),this);
    }
}
public class ProxyTest {

    public static void main(String[] args) {
        // 实例化目标对象
        UserService userService = new UserServiceImpl();
        // 实例化 invocationhandler
        MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
        // 根据目标对象生成代理对象
        UserService proxy = (UserService) invocationHandler.getProxy();
        // 调用代理对象方法
        proxy.add();
    }
}

执行结果如下:
--------------before-----------------------
----------------add------------------
--------------after---------------------
        用起来很简单,其实这个基本上就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行增强,Spring的AOP实现也是用了Proxy和InvocationHandler这个东西的。
        我们再来回顾一下使用JDK代理的方式,在整个创建过程中,对于InvocationHandler是创建的最为核心,在自定义InvocationHandler中需要重写3个函数

  • 构造函数,将代理的对象传入。
  • invoke方法,此方法中实现了AOP增强的所有逻辑。
  • getProxy方法,此方法中千篇一律,但是必不可少。
            那么,我们再来看看Spring中JDK代理是如何实现的呢?到JDKDynamicAopProxy的getProxy。
@Override
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);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

在这里插入图片描述

        通过之前的示例我们知道,JDKProxy的使用关键是创建自定义的InvocationHandler,而InvocationHandler中包含了需要覆盖的函数getProxy,而当前的方法正是完成了这个操作,再次确认一下JDKDynamicAopProxy也确实实现了InvocationHandler接口,那么我们就可以推断出,在JDKDynamicAopProxy会把AOP的核心逻辑写在其中,查看代码,果然有这样一个函数。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Class<?> targetClass = null;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            //equals方法处理
            return equals(args[0]);
        }
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            //has方法处理
            return hashCode();
        }
        //Class类的isAssignableFrom(Class cls)方法:
        //如果调用这个方法的class或接口与参与cls表示的类或接口相同
        //形象的:自身类.class.isAssignableFrom(自身类或子类.class)返回true
        //例如:
        //	System.out.println(ArrayList.class.isAssignableFrom(Object.class))
        //	//false
        //	System.out.println(Object.class.isAssignableFrom(ArrayList.class))
        // 	true
        if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        if (this.advised.exposeProxy) {
            //有个时候目标对象内部自我调用将无法实施切面中增强则需要通过此属性暴露代理
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }

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

        if (chain.isEmpty()) {
            //如果没有发现拦截器链那么直接调用切点方法
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
            //将拦截器封装在ReflectiveMethodInvocation,以便于使用proceed进行链接使用拦截器
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            //执行拦截器链
            retVal = invocation.proceed();
        }

        //返回结果
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target && 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);
        }
    }
}

        上面的函数最主要的工作就是创建一个拦截器链,并使用ReflectiveMethodInvocation类进行封装,而在ReflectiveMethodInvocation类的proceed方法中实现了拦截器的逐一调用,那么我们继续来研究,在proceed方法中是怎样实现前置增强在目标方法中前调用后置增强在目标方法后调用逻辑呢?

public Object proceed() throws Throwable {
    //执行完所有增强后执行切点方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

	//获取下一个要执行的拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        //动态匹配
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            //不匹配则不执行拦截器
            return proceed();
        }
    }
    else {
         //普通拦截器直接调用拦截器,如
         //ExposeInvocationInterceptor
         //DelegatePerTargetObjectIntroductionInterceptor,
         //MethodBeforeAdviceInterceptor
         //AspectJAroundAdvice,
         //AspectJAfterAdvice
         //将this作为参数传递以保证当前实例中调用链的执行
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

        在proceed方法中,或许代码逻辑并没有我们想象中的那样复杂,ReflectiveMethodInvocation中的主要职责是维护了链接调用计算器,记录着当前调用链接的位置,以便链接可以有序的进行下去,那么在这 个方法中并没有我们之前设想维护各种增强的顺序,而是将此工作委托给了各个增强器,使各个增强器在内部进行逻辑实现。
        来看看mybatis中JDKProxy代理的使用。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null)
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

        从上述源码中来看,在Mybatis中获取Mapper实际上获取到的Mapper接口的代理类,毋庸置疑,肯定有invoke方法,下面来看看mybatis在哪里实现方法调用。

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  	//如果是Object中的方法,直接反射调用,否则调用mapperMethod的execute()方法
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
}

        是不是觉得看懂了Spring源码后,再来看其他构架的源码很容易了。

2.CGLIB使用示例

        CGLIB是一个强大的高性能的代码生成包,它广泛地被许多AOP的框架使用,例如Spring AOP和dynaop,为它们提供方法的Interception(拦截),最流行的OR Mapping工具Hibernate也使用了CGLIB来代理单端single-ended(多对一和一对一)关联(对集合中延迟爬取是采用其他机制实现的),EasyMock和JMock是通过使用模仿(moke)对象来测试Java代码包,它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
        CGLIB包的底层通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类,除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码,当然不鼓励直接使用ASM,因为他要求你必需对JVM内部结构包括class文件的格式和指令集都很熟悉。

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class EnhancerDemo1 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(EnhancerDemo1.class);
        enhancer.setCallback(new MethodInterceptorImpl( new EnhancerDemo1()));
        EnhancerDemo1 demo = (EnhancerDemo1) enhancer.create();
        demo.test();
        System.out.println(demo);
    }


    public void test() {
        System.out.println("EnhancerDemo test ");
    }


    private static class MethodInterceptorImpl implements MethodInterceptor {
        private Object target ;

        private MethodInterceptorImpl(Object target){
            super();
            this.target = target;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("before invoke :" + method);
            //Object result = methodProxy.invokeSuper(o, args);
            Object result = methodProxy.invoke(target, args);
            System.out.println("after invoke " + method);
            return result;
        }
    }
}

运行结果如下:
before invoke :public void com.spring_1_100.test_61_70.test67_cglib_proxy.EnhancerDemo1.test()
EnhancerDemo test
after invoke public void com.spring_1_100.test_61_70.test67_cglib_proxy.EnhancerDemo1.test()
before invoke :public java.lang.String java.lang.Object.toString()
after invoke public java.lang.String java.lang.Object.toString()
com.spring_1_100.test_61_70.test67_cglib_proxy.EnhancerDemo1@617faa95

        可以看到System.out.println(demo),demo首先调用了toString()方法,然后又调用了hashCode,生成的对象为EnHancerDemo
$$EnhancerByCGLIB$$617faa95的实例,这个类是运行时由CGLIB产生的。
        完成了CGLIB代理的类是委托给Cglib2AopProxy类去实现的,我们进入这个类一探空间,就是说Cglib2AopProxy类的getProxy方法中实现了Enhancer的创建及接口封装。

public Object getProxy(ClassLoader classLoader) {
  if (logger.isDebugEnabled()) {
    logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
  }

  try {
    Class<?> rootClass = this.advised.getTargetClass();
    Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

    Class<?> proxySuperClass = rootClass;
    if (ClassUtils.isCglibProxyClass(rootClass)) {
      proxySuperClass = rootClass.getSuperclass();
      Class<?>[] additionalInterfaces = rootClass.getInterfaces();
      for (Class<?> additionalInterface : additionalInterfaces) {
        this.advised.addInterface(additionalInterface);
      }
    }

    //验证class
    validateClassIfNecessary(proxySuperClass, classLoader);

    // 创建及配置 CGLIB Enhancer...
    Enhancer enhancer = createEnhancer();
    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 UndeclaredThrowableStrategy(UndeclaredThrowableException.class));

	//设置拦截器
    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);

    //生成代理以及创建代理
    return createProxyClassAndInstance(enhancer, callbacks);
  }
  catch (CodeGenerationException ex) {
    throw new AopConfigException("Could not generate CGLIB subclass of class [" +
        this.advised.getTargetClass() + "]: " +
        "Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (IllegalArgumentException ex) {
    throw new AopConfigException("Could not generate CGLIB subclass of class [" +
        this.advised.getTargetClass() + "]: " +
        "Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (Exception ex) {
    // TargetSource.getTarget() failed
    throw new AopConfigException("Unexpected AOP exception", ex);
  }
}

protected Enhancer createEnhancer() {
    return new Enhancer();
}


protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
  enhancer.setInterceptDuringConstruction(false);
  enhancer.setCallbacks(callbacks);
  return (this.constructorArgs != null ?
      enhancer.create(this.constructorArgTypes, this.constructorArgs) :
      enhancer.create());
}

        以上函数完整的阐述了一个创建Spring中的EnHancer的过程,读者可以参考Enhancer的文档查看每个步骤的含义,这里最重要的是通过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);

  // Choose a "straight to target" interceptor. (used for calls that are
  // unadvised but can return this). May be required to expose the proxy.
  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());
  }

  // Choose a "direct to target" dispatcher (used for
  // unadvised calls to static targets that cannot return this).
  Callback targetDispatcher = isStatic ?
      new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();

  Callback[] mainCallbacks = new Callback[]{
  	//将拦截器加入到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 the target is a static one and the advice chain is frozen,
  // then we can make some optimisations by sending the AOP calls
  // direct to the target using the fixed chain for that method.
  if (isStatic && isFrozen) {
    Method[] methods = rootClass.getMethods();
    Callback[] fixedCallbacks = new Callback[methods.length];
    this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);

    // TODO: small memory optimisation here (can skip creation for methods with no advice)
    for (int x = 0; x < methods.length; x++) {
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
      fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
          chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
      this.fixedInterceptorMap.put(methods[x].toString(), x);
    }

    // Now copy both the callbacks from mainCallbacks
    // and fixedCallbacks into the callbacks array.
    callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
    System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
    System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
    this.fixedInterceptorOffset = mainCallbacks.length;
  }
  else {
    callbacks = mainCallbacks;
  }
  return callbacks;
}

在这里插入图片描述
        在getCallback中Spring考虑了很多情况,但是对于我们来说,只需要理解最常用的就可以了,比如将advised属性封装在DynamicAdvisedInterceptor并加入到callbacks中,这么做的目的是什么呢?如何调用呢?在前面的示例中,我们了解到了CGLIB中对于方法的拦截是通过将自定义拦截器(实现MethodInterceptor接口)加入Callback中并在调用代理时直接激活拦截器中的Interceptor方法来实现的,那么在getCallback中正是实现了这样一个目的,DynamicAdvisedInterceptor继承自MethodInterceptor,加入Callback中后,在再次调用代理时会直接调用DynamicAdvisedInterceptor中的intercept方法,由此推断,对于CGLIB方式实现的代理,其核心逻辑必然在DynamicAdvisedInterceptor中的intercept中。

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  Object oldProxy = null;
  boolean setProxyContext = false;
  Class<?> targetClass = null;
  Object target = null;
  try {
    if (this.advised.exposeProxy) {
      // Make invocation available if necessary.
      oldProxy = AopContext.setCurrentProxy(proxy);
      setProxyContext = true;
    }
    // May be null. Get as late as possible to minimize the time we
    // "own" the target, in case it comes from a pool...
    target = getTarget();
    if (target != null) {
      targetClass = target.getClass();
    }
    //获取拦截器链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    Object retVal;
    // Check whether we only have one InvokerInterceptor: that is,
    // no real advice, but just reflective invocation of the target.
    if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
      //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.
      retVal = methodProxy.invoke(target, args);
    }
    else {
      // We need to create a method invocation...
      retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    }
    retVal = processReturnType(proxy, target, method, retVal);
    return retVal;
  }
  finally {
    if (target != null) {
      releaseTarget(target);
    }
    if (setProxyContext) {
      // Restore old proxy.
      AopContext.setCurrentProxy(oldProxy);
    }
  }
}

        上述的实现与JDK方式实现代理中的invoke方法大同小异,都是首先构造链,然后封装此链进行串联调用,稍有区别就是在JDK中直接构建ReflectiveMethodInvocation,而在cglib中使用CglibMethodInvocation,CglibMethodInvocation继承自ReflectiveMethodInvocation,但是proceed方法并没有重写。
在这里插入图片描述

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

  private final MethodProxy methodProxy;

  private final boolean publicMethod;

  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 = methodProxy;
    this.publicMethod = Modifier.isPublic(method.getModifiers());
  }

  //调用公共方法时,与使用反射来调用目标相比,性能有所提高。
  @Override
  protected Object invokeJoinpoint() throws Throwable {
    if (this.publicMethod) {
      return this.methodProxy.invoke(this.target, this.arguments);
    }
    else {
      //反射调用方法
      return super.invokeJoinpoint();
    }
  }
}

        看了Spring中CGLIB的使用,我们再来看看MyBatis中CGLIB的使用场景。

1.创建UserMapper
public interface UserMapper {
    User findUserById(Long id );
}
2.创建Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.spring_101_200.test_131_140.test_138_mybatis_lazyloadtriggermethods.UserMapper" >


    <resultMap id="userBillResult" type="com.spring_101_200.test_131_140.test_138_mybatis_lazyloadtriggermethods.User"  >
        <id property="id" column="id" />
        <id property="realName" column="real_name"></id>
        <collection property="billList"  column="id" select="selectUserBill"  ></collection>
    </resultMap>

    <select id="selectUserBill" parameterType="long" resultType="com.spring_101_200.test_131_140.test_138_mybatis_lazyloadtriggermethods.UserBill">
       select * from lz_user_bill where user_id = #{id}
    </select>

    <!-- 根据Id查询用户,用于测试延迟加载 -->
    <select id="findUserById" parameterType="java.lang.Long"  resultMap="userBillResult" >
        select * from lz_user lu   where lu.id =#{id}
    </select>
    
</mapper>
3.延迟加载配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="spring_101_200/config_131_140/spring138_mybatis_lazyloadtriggermethods/db.properties"></properties>

    <settings>
        <setting name="cacheEnabled" value="false"/>
        <setting name="useGeneratedKeys" value="true"/>
        <setting name="mapUnderscoreToCamelCase" value="true" />
        <!--打开延迟加载的开关,全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。默认值 false  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--将积极加载改为消极加载及按需加载,当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载,默认值 true,,,
          如果aggressiveLazyLoading=true,只要触发到对象任何的方法,就会立即加载所有属性的加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--当逻辑触发lazyLoadTriggerMethods 对应的方法(equals,clone,hashCode,toString)则执行延迟加载  -->
        <setting name="lazyLoadTriggerMethods" value="hashCode"/>
    </settings>
    <typeAliases>
      <package name="com.spring_101_200.test_131_140.test_138_mybatis_lazyloadtriggermethods"/>
    </typeAliases>

    <plugins>
        <plugin interceptor="com.spring_101_200.test_131_140.test_138_mybatis_lazyloadtriggermethods.DataScopeInterceptor">
            <property name="someProperty" value="100"/>
        </plugin>
    </plugins>


    <environments default="development">
        <environment id="development">
            <transactionManager type="jdbc"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"></property>
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.pwd}"></property>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="spring_101_200/config_131_140/spring138_mybatis_lazyloadtriggermethods/UserMapper.xml"></mapper>
    </mappers>
</configuration>
3.开始测试
public class Test138 {

    static SqlSessionFactory sqlSessionFactory = null;

    static {
        sqlSessionFactory = MyBatisUtil.getSqlSEssionFactory();
    }


    @Test
    public void findUserBillLazyLoading() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(456l);
        System.out.println("-------------equals方法执行---------");
        System.out.println("equals result  : " + user.equals(new User()));
        System.out.println("-------------hashCode方法执行---------");
        System.out.println("hashCode: " + user.hashCode());
    }

}

在这里插入图片描述

如果没有配置延迟加载执行效果:
select * from lz_user lu where lu.id =?
select * from lz_user_bill where user_id = ?
-------------equals方法执行---------
equals result : false
-------------hashCode方法执行---------
hashCode: 1500309127

配置了延迟加载执行结果:

select * from lz_user lu where lu.id =?
-------------equals方法执行---------
equals result : false
-------------hashCode方法执行---------
select * from lz_user_bill where user_id = ?
hashCode: 1500309127

        从上述执行效果来看,如果没有配置延迟加载,一次性会查询lz_user表和lz_user_bill表数据,但是配置了延迟加载,只有在执行hashCode方法时,才从数据库中获取lz_user_bill数据。
        那我们来看看MyBatis中,是如何利用CGLIB来实现延迟加载的呢?

DefaultResultSetHandler
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
  final List<Object> constructorArgs = new ArrayList<Object>();
  final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149
        return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      }
    }
  }
  return resultObject;
}

        MyBatis在创建结果集对象时,我们的配置是允许延迟加载,因此返回的是一个代理对象。

CglibProxyFactory.java
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
EnhancedResultObjectProxyImpl.java
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  final Class<?> type = target.getClass();
  EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
  Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
  PropertyCopier.copyBeanProperties(type, target, enhanced);
  return enhanced;
}
CglibProxyFactory.java
private static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  Enhancer enhancer = new Enhancer();
  enhancer.setCallback(callback);
  enhancer.setSuperclass(type);
  try {
    type.getDeclaredMethod(WRITE_REPLACE_METHOD);
    // ObjectOutputStream will call writeReplace of objects returned by writeReplace
    log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
  } catch (NoSuchMethodException e) {
    enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
  } catch (SecurityException e) {
    // nothing to do here
  }
  Object enhanced = null;
  if (constructorArgTypes.isEmpty()) {
    enhanced = enhancer.create();
  } else {
    Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
    Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
    enhanced = enhancer.create(typesArray, valuesArray);
  }
  return enhanced;
}

在这里插入图片描述

        显然EnhancedResultObjectProxyImpl继承了MethodInterceptor,其必然实现了intercept方法,方法intercept如下

public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  final String methodName = method.getName();
  try {
    synchronized (lazyLoader) {
      if (WRITE_REPLACE_METHOD.equals(methodName)) {
        Object original = null;
        if (constructorArgTypes.isEmpty()) {
          original = objectFactory.create(type);
        } else {
          original = objectFactory.create(type, constructorArgTypes, constructorArgs);
        }
        PropertyCopier.copyBeanProperties(type, enhanced, original);
        if (lazyLoader.size() > 0) {
          return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
        } else {
          return original;
        }
      } else {
        if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
          if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
            lazyLoader.loadAll();
          } else if (PropertyNamer.isProperty(methodName)) {
            final String property = PropertyNamer.methodToProperty(methodName);
            if (lazyLoader.hasLoader(property)) {
              lazyLoader.load(property);
            }
          }
        }
      }
    }
    //调用目标方法,如getBillList或setBillList方法
    return methodProxy.invokeSuper(enhanced, args);
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        init();
        FastClassInfo fci = fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}

public void setBillList(List billList) {
    this.billList = billList;
}

        上述代码一大堆,可能有人会想,我知道MyBatis的懒加载是用CGLIB代理实现的,那什么时候执行lz_user_bill表的数据查询的呢?
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
        调用query方法对数据进行查询。
在这里插入图片描述
        上述过程只是做了截图,没有做深入研究,如果要深入研究,可能也不是一篇博客能解决的了,通过上述举例,我相信大家对JDK代理和CGLIB代理有了一定的认识,下面来总结一下。
下面是JDK与Cglib方式的总结

  • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
  • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  • 如果目标对象没有实现接口,必须采用CGLIB库,Spring会自动的在JDK动态代理和CGLIB之间进行转换。

如何强制使用CGLIB实现AOP呢?

  • 添加CGLIB库,Spring_HOME/cglib/*.jar
  • 在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true” />

JDK动态代理和CGLIB字节码生成的区别?

  • JDK动态代理只能实现了接口类生成的代理,而不能针对类
  • CGLIB是针对类实现的代理,主要是对指定类生成一个类,覆盖其中的方法,因为是继承,所以yyow类方法最好不要声明为final。

项目地址
https://github.com/quyixiao/spring_tiny

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值