一、代理模式 代理是一种结构设计模式,可让您为另一个对象提供替代品。 代理控制对原始对象的访问,允许您在请求到达原始对象之前或之后执行某些操作。
二、静态代理 (1)概念: 对目标对象的每个方法的增强都是手动完成。在编译时就已经将接口、被代理类、代理类等确定下来 。在程序运行之前,代理类的.class文件就已经生成。
(2)静态代理简单实现 : 这里借用班长给学生代交班费的例子 1.创建接口并确定接口具体行为
1 2 3 4 public interface Person { void giveMoney () ; }
2.被代理对象实现接口,完成具体的业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 public class Student implements Person { private String name; public Student (String name) { this .name = name; } @Override public void giveMoney () { System.out.println(name + "上交班费50元" ); } }
3.代理类实现接口对被代理类的执行进行控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class StudentsProxy implements Person { Student stu; public StudentsProxy (Person stu) { this .stu = (Student)stu; } @Override public void giveMoney () { System.out.println("交之前做的事" ); stu.giveMoney(); System.out.println("交之后做的事" ); } }
4.测试
1 2 3 4 5 6 7 8 9 10 11 12 13 public class StaticProxyTest { public static void main (String[] args) { Person zhangsan = new Student("张三" ); Person monitor = new StudentsProxy(zhangsan); monitor.giveMoney(); } }
交之前做的事 张三上交班费50元 交之后做的事
三、动态代理 (1)概念: 代理类在程序运行时创建的代理方式被成为动态代理。
我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 比如说,想要在每个代理的方法前都加上一个处理方法:
1 2 3 4 5 public void giveMoney () { beforeMethod(); stu.giveMoney(); }
这里只有一个giveMoney方法,就写一次beforeMethod方法,但是如果除了giveMonney还有很多其他的方法,那就需要写很多次beforeMethod方法,麻烦。所以建议使用动态代理实现。
(2)动态代理简单实现: 在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
1.创建接口并确定接口具体行为
1 2 3 4 public interface Person { void giveMoney () ; }
2.被代理对象实现接口,完成具体的业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 public class Student implements Person { private String name; public Student (String name) { this .name = name; } @Override public void giveMoney () { System.out.println(name + "上交班费50元" ); } }
3.自定义 InvocationHandler 并重写invoke方法,在 invoke方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class StuInvocationHandler implements InvocationHandler { private Object target; public StuInvocationHandler (Object target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理执行" +method.getName() + "方法" ); Object result = method.invoke(target, args); return result; } }
4.测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ProxyTest { public static void main (String[] args) { Person zhangsan = new Student("张三" ); InvocationHandler stuHandler = new StuInvocationHandler(zhangsan); Person stuProxy = (Person) Proxy.newProxyInstance(stu.getClass().getClassLoader(), stu.getClass().getInterfaces(), stuHandler); stuProxy.giveMoney(); } }
代理执行giveMoney方法 张三上交班费50元
四、CGLIB 动态代理 (1)概念: JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。
(2)简单实现: 1.定义一个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class NoInterfaceStudent { private String name; public NoInterfaceStudent () { } public NoInterfaceStudent (String name) { this .name = name; } public void giveMoney () { System.out.println(name + "上交班费50元" ); } }
2.自定义 MethodInterceptor并重写 intercept方法,intercept用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke方法类似;
1 2 3 4 5 6 7 8 public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("CGLIB代理执行" +method.getName() + "方法" ); return methodProxy.invokeSuper(o, objects); } }
3.通过 Enhancer类的create()创建代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class CglibProxyFactory { public static Object getProxy (Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setClassLoader(clazz.getClassLoader()); enhancer.setSuperclass(clazz); enhancer.setCallback(new MyMethodInterceptor()); return enhancer.create(); } }
4.测试
1 2 3 4 5 6 public class CglibTest { public static void main (String[] args) { NoInterfaceStudent proxy =(NoInterfaceStudent) CglibProxyFactory.getProxy(NoInterfaceStudent.class); proxy.giveMoney(); } }
CGLIB代理执行giveMoney方法 上交班费50元
五、静态代理和动态代理的对比 1、灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
2、JVM层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
六、JDK 动态代理和 CGLIB 动态代理对比 1、JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
2、就二者的效率来说,大部分情况都是 JDK 动态代理更优秀。