一、设计模式

一、概念

代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
静态代理
动态代理【JDK动态代理、CGLIB动态代理】

二、代理模式关系图

image.png
从UML图中,可以看出代理类与真正实现的类都是继承了抽象的主题类,这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。

三、代理模式的角色

A、抽象的类或者接口—定义完成一件怎样的事情
B、代理对象—-完成这件事情的对象,直接面向用户的
C、被代理对象—完成事件背后的隐藏内容,用户看不到
image.png

二、JDk动态代理

动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成
image.png
示例:

接口

  1. public interface MyCalculator {
  2. //加法
  3. int jia(int num1, int num2);
  4. //减法
  5. int jian(int num1, int num2);
  6. //乘法
  7. int cheng(int num1, int num2);
  8. }

实现类

  1. package com.bjsxt.calculator.impl;
  2. import com.bjsxt.calculator.MyCalculator;
  3. import com.bjsxt.util.Log4JSxt;
  4. public class MyCalculatorImpl implements MyCalculator {
  5. @Override
  6. public int jia(int num1, int num2) {
  7. int z=num1+num2;
  8. return z;
  9. }
  10. @Override
  11. public int jian(int num1, int num2) {
  12. int z=num1-num2;
  13. return z;
  14. }
  15. @Override
  16. public int cheng(int num1, int num2) {
  17. int z=num1*num2;
  18. return z;
  19. }
  20. }

jdk代理类

  1. package com.bjsxt.proxy;
  2. import com.bjsxt.calculator.MyCalculator;
  3. import com.bjsxt.util.Log4JSxt;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Proxy;
  7. //JDK动态代理
  8. public class JDKProxy {
  9. //在虚拟机中动态的产生代理对象--中介
  10. public Object getProxy(Object o){
  11. //[1]调用getProxy接收new MyCalculatorImpl()这个对象,虚拟机创建了$proxy.class这个代理类对象(实现了MyCalculatorImpl的接口)
  12. return Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{MyCalculator.class}, new InvocationHandler() {
  13. /*[2]用户调用代理类proxy的方法时执行,InvocationHandler().invoke
  14. * 将proxy对象、其中包含的方法和参数传入invoke方法
  15. * 暴露给用户 方便用户进行功能上的扩展
  16. * proxy对象利用反射进行方法的调用*/
  17. //proxy:代理对象 method:需要执行方法对象 args:实参列表
  18. @Override
  19. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  20. //进行功能的扩展 例如:添加日志
  21. Log4JSxt.log(args[0],args[1],method.getName());
  22. //利用反射 调用目标函数中方法(房东对象)
  23. Object invoke = method.invoke(o, args);
  24. return invoke;
  25. }
  26. });
  27. }
  28. }

扩展的日志

package com.bjsxt.util;

public class Log4JSxt {

      public   static   void    log(Object num1,Object num2,String  method){

          System.out.println("操作数A:"+num1+",操作数B:"+num2+",做了:"+method+"操作");

      }

}

测试

package com.bjsxt.test;

import com.bjsxt.calculator.MyCalculator;
import com.bjsxt.calculator.impl.MyCalculatorImpl;
import com.bjsxt.proxy.JDKProxy;

public class TestB {

    public static void main(String[] args) {

        //返回的是代理对象
        MyCalculator proxy = (MyCalculator) new JDKProxy().getProxy(new MyCalculatorImpl());
        //进行方法的调用   ----代理对象的乘法
        int rs = proxy.cheng(10, 10);

        System.out.println("结果为:"+rs);
    }
}

三、CGLIB动态代理

1.JDK动态代理存在的问题

JDK代理的产生必须要实现对应的接口的,如果没有对应的接口,这个时候代理对象就没有办法产生。

2.解决方案

CGLIB动态代理

3.CGLIB动态代理

cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

实现类

package com.bjsxt.calculator.impl;

import com.bjsxt.calculator.MyCalculator;

public class MyCalculatorImpl2 {

    public int jia(int num1, int num2) {

        int  z=num1+num2;
        return z;
    }

    public int jian(int num1, int num2) {
        int z=num1-num2;
        return z;
    }


    public int cheng(int num1, int num2) {
        int z=num1*num2;
        return z;
    }
}

CGLIB代理类

package com.bjsxt.proxy;

import com.bjsxt.util.Log4JSxt;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy {
    public   static   Object  getPoexy(Object  target){

        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        /**
         * 参数一:代理对象
         * 参数二:目标方法反射对象
         * 参数三:目标方法参数
         * 参数四:代理方法反射对象
         */
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //添加扩展功能
                Log4JSxt.log(objects[0],objects[1],method.getName());

                //调用到目标函数
                Object invoke = method.invoke(target, objects);
                return invoke;
            }
        });

        //使上述操作生效 返回代理对象
        Object o = enhancer.create();
        return o;
    }
}

测试

package com.bjsxt.test;
import com.bjsxt.calculator.impl.MyCalculatorImpl2;
import com.bjsxt.proxy.CglibProxy;
public class TestC {

    public static void main(String[] args) {

        //返回的是代理对象
        MyCalculatorImpl2 poexy = (MyCalculatorImpl2) CglibProxy.getPoexy(new MyCalculatorImpl2());
        //进行方法的调用   ----代理对象的乘法
        int cheng = poexy.cheng(20, 30);
        System.out.println("结果为:"+cheng);

    }
}