基本介绍:

  1. 代理模式:为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是,可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能
  2. 被代理的对象可以是远程对象、创建开销打的对象、需要安全控制的对象
  3. 代理模式有不同的形式,主要有静态代理、动态代理(JDK代理、接口代理)、Cglib代理(可以在内存动态的创建对象而不需要实现接口,也属于动态代理的范畴)三种

代理模式类图:
image.png

静态代理

基本介绍:静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类

代码示例:

  1. public interface ITeacherDao {
  2. void teach();
  3. }
  4. public class TeacherDao implements ITeacherDao {
  5. @Override
  6. public void teach() {
  7. System.out.println("授课中。。。。");
  8. }
  9. }
  10. public class TeacherDaoProxy implements ITeacherDao {
  11. private TeacherDao teacherDao;
  12. public TeacherDaoProxy(TeacherDao teacherDao){
  13. this.teacherDao = teacherDao;
  14. }
  15. @Override
  16. public void teach() {
  17. System.out.println("--开始代理");
  18. teacherDao.teach();
  19. System.out.println("--代理完成");
  20. }
  21. }
  22. public class Client {
  23. public static void main(String[] args) {
  24. TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
  25. proxy.teach();
  26. }
  27. }

优缺点:

  1. 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
  2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
  3. 一旦接口增加方法,目标对象与代理对象都要维护

动态代理

基本介绍:

  1. 代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  2. 代理对象的生成,是利用JDK的API(JDK反射机制),动态的在内存中构建代理对象
  3. 动态代理也叫做JDK代理、接口代理

JDk中生成代理对象的API

  1. 代理类所在的包 java.lang.reflect.Proxy
  2. JDK实现代理只需要使用 newProxyInstance 方法,但是该方法需要接收三个参数,完整的是public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

代码示例:

  1. public interface ITeacherDao {
  2. void teach();
  3. }
  4. public class TeacherDao implements ITeacherDao {
  5. @Override
  6. public void teach() {
  7. System.out.println("授课中。。。。。");
  8. }
  9. }
  10. public class ProxyFactory {
  11. private Object target;
  12. public ProxyFactory(Object target){
  13. this.target = target;
  14. }
  15. public Object getProxyInstance(){
  16. return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
  17. @Override
  18. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  19. System.out.println("--JDK代理开始");
  20. Object invoke = method.invoke(target, args);
  21. return invoke;
  22. }
  23. });
  24. }
  25. }
  26. public class Client {
  27. public static void main(String[] args) {
  28. ITeacherDao target = new TeacherDao();
  29. ProxyFactory factory = new ProxyFactory(target);
  30. ITeacherDao proxyInstance =(ITeacherDao) factory.getProxyInstance();
  31. System.out.println("proxyInstance=" + proxyInstance.getClass());
  32. proxyInstance.teach();
  33. }
  34. }

Cglib代理

基本介绍:

  1. 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理
  2. Cglib代理也叫子类代理,它是内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理
  3. Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
  4. 在AOP编程中如何选择代理模式:
    1. 目标对象需要实现接口,用JDK代理
    2. 目标对象不需要实现接口,用Cglib代理
  5. Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

Cglib代理模式实现步骤:

  1. 引入Cglib的jar文件
    1. asm.jar
    2. asm-commons.jar
    3. asm-tree.jar
    4. cglib-2.2.jar
  2. 在内存中动态构建子类,注意代理的类不能为final,否则报错 java.lang.illegalArgumentException
  3. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

代码示例:

  1. public class TeacherDao {
  2. public void teach(){
  3. System.out.println("---授课中,Cglib代理,不需要实现接口");
  4. }
  5. }
  6. public class ProxyFactory implements MethodInterceptor {
  7. private Object target;
  8. public ProxyFactory(Object target){
  9. this.target = target;
  10. }
  11. public Object getProxyInstance(){
  12. //创建工具类
  13. Enhancer enhancer = new Enhancer();
  14. //设置父类
  15. enhancer.setSuperclass(target.getClass());
  16. //设置回调函数
  17. enhancer.setCallback(this);
  18. //创建子类对象,即代理对象
  19. return enhancer.create();
  20. }
  21. @Override
  22. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  23. System.out.println("----Cglib模式,开始··");
  24. Object invoke = method.invoke(target, objects);
  25. System.out.println("----Cglib模式,提交··");
  26. return invoke;
  27. }
  28. }
  29. public class Client {
  30. public static void main(String[] args) {
  31. TeacherDao target = new TeacherDao();
  32. TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();
  33. proxyInstance.teach();
  34. }
  35. }

代理模式的变体

几种常见的代理模式:

  1. 防火墙代理

内网通过代理穿透防火墙,实现对公网的访问

  1. 缓存代理

当请求图片文件等资源时,先缓存到代理处,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存

  1. 远程代理

远程对象的本地代理,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息

  1. 同步代理

主要使用在多线程编程中,完成多线程间同步工作