为其他对象提供 一种代理以控制对这个对象的访问
    image.png
    代理类和被代理类实现相同的接口(或继承同一个抽象类)

    1. public interface ISubject {
    2. void a();
    3. void b();
    4. }
    5. public class ConcreteSubject implements ISubject{
    6. @Override
    7. public void a() {
    8. }
    9. @Override
    10. public void b() {
    11. }
    12. }
    13. public class Proxy implements ISubject{
    14. private ISubject iSubject;
    15. public Proxy(ISubject iSubject) {
    16. this.iSubject = iSubject;
    17. }
    18. @Override
    19. public void a() {
    20. this.before();
    21. iSubject.a();
    22. this.after();
    23. }
    24. @Override
    25. public void b() {
    26. this.before();
    27. iSubject.b();
    28. this.after();
    29. }
    30. private void before(){
    31. }
    32. private void after(){
    33. }
    34. }

    强制代理:从真实对象访问代理对象

    动态代理:通过反射生成代理类
    静态代理需要手动创建代理类和手动实现代理方法,而动态代理可以动态的自动生成代理类和实现代理方法

    1. jdk动态代理,实现invocationHandler接口
    2. cglib动态代理

    动态代理和静态代理
    (1)静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。
    (2)动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
    (3)若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。


    jdk动态代理

    1. public class ProxyInvocationHandler implements InvocationHandler {
    2. private Object target;
    3. public Object getTarget() {
    4. return target;
    5. }
    6. public void setTarget(Object target) {
    7. this.target = target;
    8. }
    9. //动态生成代理类
    10. Object getProxy(){
    11. return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    12. }
    13. //动态调用代理方法
    14. @Override
    15. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    16. before();
    17. //jdk序列化是通过method对象反射调用的
    18. Object result = method.invoke(target,args);
    19. after();
    20. return result;
    21. }
    22. private void before(){
    23. System.out.println("before");
    24. }
    25. private void after(){
    26. System.out.println("after");
    27. }
    28. }
    29. public class Client {
    30. public static void main(String[] args){
    31. ISubject subject = new ConcreteSubject();
    32. ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
    33. //设置被代理对象
    34. proxyInvocationHandler.setTarget(subject);
    35. //动态生成代理对象
    36. ISubject proxy = (ISubject) proxyInvocationHandler.getProxy();
    37. //调用方法
    38. proxy.a();
    39. }
    40. }

    jdk动态代理采用字节码重组,重新生成对象代替原始对象。
    (1)获取被代理对象的引用,并且获取它的所有接口,反射获取。
    (2)JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。
    (3)动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现)。
    (4)编译新生成的Java代码.class文件。
    (5)重新加载到JVM中运行。
    ClassPath以$开头的.class文件是自动生成的,jdk动态代理生成的代理类需要实现被代理类的所有接口(也就是说被代理的类至少需要实现一个接口,没有实现接口的类不能进行jdk动态代理)

    proxyclass在第一次生成代理对象的时候由ProxyClassFactory构建,然后放入缓存中,后续生成代理对象的时候直接从缓存获取


    cglib动态代理
    cglib动态代理通过动态继承目标对象实现动态代理,覆盖父类方法(也就是说cglib不能代理final的方法)
    MethodInterceptor

    jdk cglib
    实现方法 读取接口信息并实现 继承,覆盖父类方法
    对被代理类的要求 被代理类必须要实现至少一个接口 可以是任意一个普通类
    效率 生成字节码效率高,执行效率较低,每次都要通过Method反射机制调用 生成FastClass效率低,执行效率较高,生成FastClass后不需要通过Method反射调用
    final 只可以代理接口的方法 不可以代理final方法
    1. import org.springframework.cglib.proxy.MethodProxy;
    2. import java.lang.reflect.Method;
    3. public class CGLibInterceptor implements MethodInterceptor {
    4. public Object getProxy(Class<?> clazz){
    5. Enhancer enhancer = new Enhancer();
    6. enhancer.setSuperclass(clazz);
    7. enhancer.setCallback(this);
    8. return enhancer.create();
    9. }
    10. @Override
    11. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    12. before();
    13. Object object = methodProxy.invokeSuper(o,objects);
    14. after();
    15. return object;
    16. }
    17. private void before(){
    18. System.out.println("before");
    19. }
    20. private void after(){
    21. System.out.println("after");
    22. }
    23. }

    CGlib生成三个.class,其中fastClass会在第一次调用invoke()或者invokeSuper()的时候通过反射创建,然后放入缓存中

    • 代理类
    • 代理类的fastClass
    • 被代理类的fastClass

    fastClass为每一个代理类/被代理类的方法分配一个index,调用invoke的时候,根据index直接定位到要调用的方法并进行调用,不需要经过反射。也就是说,jdk动态代理的每次invoke都需过Method对象反射调用,而cglib动态代理的invoke方法有一个switch(index) case的操作

    1. cglib中的.class文件
    2. public Object invoke(int i,Object obj,Object[] args){
    3. siwtch(i){
    4. case 0:
    5. goto _L1;break;
    6. case 1:
    7. goto _L2;break;
    8. ...
    9. _L1:
    10. a();
    11. return null;
    12. _L1:
    13. b();
    14. return null;
    15. }
    16. }

    存储fastClass的缓存
    image.png

    1. package org.springframework.cglib.proxy;
    2. import java.lang.reflect.InvocationTargetException;
    3. import java.lang.reflect.Method;
    4. import org.springframework.cglib.core.AbstractClassGenerator;
    5. import org.springframework.cglib.core.CodeGenerationException;
    6. import org.springframework.cglib.core.GeneratorStrategy;
    7. import org.springframework.cglib.core.NamingPolicy;
    8. import org.springframework.cglib.core.Signature;
    9. import org.springframework.cglib.reflect.FastClass;
    10. /**
    11. * Classes generated by {@link Enhancer} pass this object to the
    12. * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can
    13. * be used to either invoke the original method, or call the same method on a different
    14. * object of the same type.
    15. * @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $
    16. */
    17. @SuppressWarnings({"rawtypes", "unchecked"})
    18. public class MethodProxy {
    19. private Signature sig1;
    20. private Signature sig2;
    21. private CreateInfo createInfo;
    22. private final Object initLock = new Object();
    23. private volatile FastClassInfo fastClassInfo;
    24. /**
    25. * For internal use by {@link Enhancer} only; see the {@link org.springframework.cglib.reflect.FastMethod} class
    26. * for similar functionality.
    27. */
    28. public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    29. MethodProxy proxy = new MethodProxy();
    30. proxy.sig1 = new Signature(name1, desc);
    31. proxy.sig2 = new Signature(name2, desc);
    32. proxy.createInfo = new CreateInfo(c1, c2);
    33. return proxy;
    34. }
    35. private void init() {
    36. /*
    37. * Using a volatile invariant allows us to initialize the FastClass and
    38. * method index pairs atomically.
    39. *
    40. * Double-checked locking is safe with volatile in Java 5. Before 1.5 this
    41. * code could allow fastClassInfo to be instantiated more than once, which
    42. * appears to be benign.
    43. */
    44. //用dcl保证只在init的时候创建,并且只创建一次,volatile禁用指令重排序,不会使fastClassInfo没进行初始化就被使用
    45. if (fastClassInfo == null) {
    46. synchronized (initLock) {
    47. if (fastClassInfo == null) {
    48. CreateInfo ci = createInfo;
    49. FastClassInfo fci = new FastClassInfo();
    50. fci.f1 = helper(ci, ci.c1);
    51. fci.f2 = helper(ci, ci.c2);
    52. fci.i1 = fci.f1.getIndex(sig1);
    53. fci.i2 = fci.f2.getIndex(sig2);
    54. fastClassInfo = fci;
    55. createInfo = null;
    56. }
    57. }
    58. }
    59. }
    60. private static class FastClassInfo {
    61. FastClass f1;
    62. FastClass f2;
    63. int i1;
    64. int i2;
    65. }
    66. //创建fastclass
    67. private static FastClass helper(CreateInfo ci, Class type) {
    68. FastClass.Generator g = new FastClass.Generator();
    69. g.setType(type);
    70. // SPRING PATCH BEGIN
    71. g.setContextClass(type);
    72. // SPRING PATCH END
    73. g.setClassLoader(ci.c2.getClassLoader());
    74. g.setNamingPolicy(ci.namingPolicy);
    75. g.setStrategy(ci.strategy);
    76. g.setAttemptLoad(ci.attemptLoad);
    77. return g.create();
    78. }
    79. private MethodProxy() {
    80. }
    81. /**
    82. * Invoke the original method, on a different object of the same type.
    83. * @param obj the compatible object; recursion will result if you use the object passed as the first
    84. * argument to the MethodInterceptor (usually not what you want)
    85. * @param args the arguments passed to the intercepted method; you may substitute a different
    86. * argument array as long as the types are compatible
    87. * @throws Throwable the bare exceptions thrown by the called method are passed through
    88. * without wrapping in an <code>InvocationTargetException</code>
    89. * @see MethodInterceptor#intercept
    90. */
    91. public Object invoke(Object obj, Object[] args) throws Throwable {
    92. try {
    93. init();
    94. FastClassInfo fci = fastClassInfo;
    95. return fci.f1.invoke(fci.i1, obj, args);
    96. }
    97. catch (InvocationTargetException ex) {
    98. throw ex.getTargetException();
    99. }
    100. catch (IllegalArgumentException ex) {
    101. if (fastClassInfo.i1 < 0)
    102. throw new IllegalArgumentException("Protected method: " + sig1);
    103. throw ex;
    104. }
    105. }
    106. /**
    107. * Invoke the original (super) method on the specified object.
    108. * @param obj the enhanced object, must be the object passed as the first
    109. * argument to the MethodInterceptor
    110. * @param args the arguments passed to the intercepted method; you may substitute a different
    111. * argument array as long as the types are compatible
    112. * @throws Throwable the bare exceptions thrown by the called method are passed through
    113. * without wrapping in an <code>InvocationTargetException</code>
    114. * @see MethodInterceptor#intercept
    115. */
    116. public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    117. try {
    118. init();
    119. FastClassInfo fci = fastClassInfo;
    120. return fci.f2.invoke(fci.i2, obj, args);
    121. }
    122. catch (InvocationTargetException e) {
    123. throw e.getTargetException();
    124. }
    125. }
    126. }

    框架:
    spring aop
    JdkDynamicAopProxy类和CglibAopProxy类

    • 当bean实现了接口,使用jdk动态代理
    • bean没有实现接口,使用cglib动态代理
    • 使用可以强制使用cglib动态代理

    Mybatis的MapperProxyFactory

    MapperProxyFactory生成MapperProxy和代理对象,代理对象(Mapper)调用方法,从缓存中获取MapperMethod对象,MapperMethod调用excute方法,SqlSession的成员变量SqlCommand记录了sql的类型,MedthodSignature解析将参数值转换为Map<Key,Value>的映射,Key是方法的参数名称,Value是参数值。Configuration保存sql语句。

    代理模式的优点
    (1)代理模式能将代理对象与真实被调用目标对象分离。
    (2)在一定程度上降低了系统的耦合性,扩展性好。
    (3)可以起到保护目标对象的作用。
    (4)可以增强目标对象的功能。

    代理模式的缺点
    (1)代理模式会造成系统设计中类的数量增加。
    (2)在客户端和目标对象中增加一个代理对象,会导致处理请求的速度变慢。
    (3)增加了系统的复杂度。