狐言不胡言

导航

Tony老师带你来看Java设计模式:代理模式

定义

为其他对象提供一种代理来控制对这个对象的访问

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用

作用

不改变原类的代码,而增强原类对象的功能
可以选择前置、后置、环绕、异常处理增加

意图

为其他对象提供一种代理以控制对这个对象的访问

主要解决问题

在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层

优缺点

优点:

  • 职责比较清晰
  • 扩展性较高

缺点:

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂

与装饰者模式的区别

代理模式的用意是在代理中控制使用者对目标对象的访问,以及进行功能增强
装饰者模式的用意主要是在原有功能的基础上进行功能的增强

结构

在这里插入图片描述
涉及的角色:

  • 抽象主题(Subject)角色:声明了真实主题和代理主题的共同接口,这样的话,在任何可以使用真实主题的地方都可以使用代理主题
  • 代理主题(ProxySubject)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象,代理主题角色提供一个和真实主题角色相同的接口,以便在任何时候可以替代真实主题;控制对真实主题的引用,负责在需要的时候创建真实主题对象或者删除真实主题对象,代理角色通常在将客户端调用传递给真实的主题之前或者之后,都要执行某个操作,而不是单纯的将调用传递给真实主题对象
  • 真实主题(RealSubject)角色:定义了代理角色所代表的真实对象

源码如下:

public abstract class Subject {

    /** 声明一个抽象的请求方法 */
    abstract public void request();
}
public class RealSubject extends Subject {

    /** 构造函数 */
    public RealSubject() {
    }

    /** 实现请求的方法 */
    @Override
    public void request() {
        System.out.println("RealSubject实现请求");

    }
}
public class ProxySubject extends Subject {

    private RealSubject realSubject;

    /** 构造函数 */
    public ProxySubject() {
    }

    /** 实现请求的方法 */
    @Override
    public void request() {
        preRequest();
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
        postRequest();
    }

    /** 请求前的操作 */
    private void preRequest() {
        System.out.println("请求前的操作。。。");
    }

    /** 请求后的操作 */
    private void postRequest() {
        System.out.println("请求后的操作。。。");
    }
}
public class Client {
    public static void main(String[] args) {
        Subject subject = new ProxySubject();
        subject.request();
    }
}

从Tony老师来看实现方式

静态代理

由程序员创建或者由特定工具自动生成代理类源代码,再对其进行编译;在程序运行前,代理类的.class文件就已经存在了
在这里插入图片描述

public class TuHao {

    private double length;

    public TuHao(double length) {
        this.length = length;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public void dating(Girl girl) {
        girl.dating(length);
    }
}
public interface Girl {

    /** 约会 */
    boolean dating(double length);
}
public class TeacherCang implements Girl {
    @Override
    public boolean dating(double length) {
        if (length >= 1.7d) {
            System.out.println("身高可以,约一下!");
            return true;
        }
        System.out.println("身高不行,哥哥我们不约!");
        return false;
    }
}
public class Tony implements Girl {

    private Girl girl;

    public Girl getGirl() {
        return girl;
    }

    public void setGirl(Girl girl) {
        this.girl = girl;
    }

    @Override
    public boolean dating(double length) {
        doSomethingBefore();
        boolean result = this.girl.dating(length);
        doSomethingAfter();
        return result;
    }

    /** 前置增强 */
    private void doSomethingBefore() {
        System.out.println("老板,这个我试过了,很不错,推荐给你!");
    }

    /** 后置增强 */
    private void doSomethingAfter() {
        System.out.println("老板,你觉得怎样,欢迎下次再约!");
    }
}
public class Client {

    public static void main(String[] args) {
        TuHao tuHao = new TuHao(1.8d);
        Girl girl = new TeacherCang();
        Tony tony = new Tony();
        tony.setGirl(girl);
        tuHao.dating(tony);
    }
}

在这里插入图片描述
静态代理的缺点:

  • 扩展能力差
    • 横向扩展:代理更多的类
    • 纵向扩展:增加更多的方法
  • 可维护性差

动态代理

代理类在程序运行时,运用反射机制动态创建而成
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道

优点:
在运行时,动态为不同类的对象创建代理,增强功能,灵活扩展,易维护

两种实现方式:

  • JDK动态代理:只可对接口创建代理
  • CGLIB动态代理:可以对接口、类创建代理

JDK动态代理的实现

public class TonyCompany {

    public static Object proxy(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new MyInvationHandler(target));
    }

    /** 功能增加的实现 */
    private static class MyInvationHandler implements InvocationHandler {

        private Object target;

        public MyInvationHandler(Object target) {
            super();
            this.target = target;
        }

        public Object getTarget() {
            return target;
        }

        public void setTarget(Object target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            doSomethingBefore();
            /** 调用被代理对象的方法 */
            Object result = method.invoke(target, args);
            doSomethingAfter();
            return result;
        }

        /** 前置增强 */
        private void doSomethingBefore() {
            System.out.println("老板,这个我试过了,很不错,推荐给你!");
        }

        /** 后置增强 */
        private void doSomethingAfter() {
            System.out.println("老板,你觉得怎样,欢迎下次再约!");
        }
    }
}
public class Client {

    public static void main(String[] args) {
        TuHao th = new TuHao(1.8F);
        Girl tc = new TeacherCang();
        Girl girl = (Girl) TonyCompany.proxy(tc);
        th.dating(girl);
    }
}

在这里插入图片描述
下面可以通过一个工具类来看动态代理后所生成的.class文件:

public class ProxyUtils {

	/*
	 * 将根据类信息 动态生成的二进制字节码保存到硬盘中, 默认的是clazz目录下 params :clazz 需要生成动态代理类的类
	 * proxyName : 为动态生成的代理类的名称
	 */
	public static void generateClassFile(Class<?> clazz, String proxyName) {
		// 根据类信息和提供的代理类名称,生成字节码
		byte[] classFile = ProxyGenerator.generateProxyClass(proxyName,
				new Class[] { clazz });

		ProxyUtils.writeToFile(clazz, classFile, proxyName);

	}

	public static void writeToFile(Class<?> clazz, byte[] classFile,
			String proxyName) {
		String paths = clazz.getResource(".").getPath();
		System.out.println(paths);
		FileOutputStream out = null;

		try {
			// 保留到硬盘中
			out = new FileOutputStream(paths + proxyName + ".class");
			out.write(classFile);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}
public interface Boy {

    /** 约会 */
    boolean dating(char cup);

    /** 拍照 */
    void show();
}
public class TeacherChen implements Boy {
    @Override
    public boolean dating(char cup) {
        if (cup == 'E') {
            System.out.println("这个妹子不错可以约!");
            return true;
        }
        System.out.println("这个妹子不行,妹子我们不约!");
        return false;
    }

    @Override
    public void show() {
        System.out.println("开始进入拍照模式。。。");
    }
}
public class Client {
    public static void main(String[] args) {
        Boy boy = new TeacherChen();
        Boy boy1 = (Boy) TonyCompany.proxy(boy);
        boy1.dating('E');
        boy1.show();
        ProxyUtils.generateClassFile(Boy.class, boy1.getClass().getName());
    }
}

在这里插入图片描述
可以看到在项目中生成的文件:
在这里插入图片描述
点开上面所标识的文件即是动态生成的.class文件:

public final class $Proxy0 extends Proxy implements Boy {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void show() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean dating(char var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.example.demo.syms.proxy.p2.Boy").getMethod("show");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.demo.syms.proxy.p2.Boy").getMethod("dating", Character.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

cglib动态代理的实现

cglib是一个高层次的java字节码生成和转换的api库
在这里插入图片描述
主要用途:
在运行期为类、接口生成动态代理对象
以达到不改动原代码而实现功能增强的目的

常在哪里使用它:
AOP、test、orm框架中用来生成动态代理对象、拦截属性访问
在这里插入图片描述
代码示例:

public class CglibDemo {

    static class MyMethodInterceptor implements MethodInterceptor {

        private Object target;

        public MyMethodInterceptor(Object target) {
            this.target = target;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("*********" + method.getName());
            //前置增强
            doSomethingBefore();
            //返回值
            Object result = null;
            //调用父类的该方法,当是生成接口的代理时不可调用。
            //result = methodProxy.invokeSuper(o, objects);
            if (target != null) {
                //通过method来调用被代理对象的方法
                result = method.invoke(target, objects);
            }
            //后置增强
            doSomethingAfter();
            return result;
        }

        private void doSomethingBefore() {
            System.out.println("老板你好,这个我试过了,很不错,推荐给你!");
        }

        private void doSomethingAfter() {
            System.out.println("老板你觉得怎样? 欢迎下次.....");
        }
    }

    public static void main(String[] args) {
        //cglib的增强器,即代理人
        Enhancer e = new Enhancer();
        //被代理的对象
        TeacherCang c = new TeacherCang();
        //设置代理增加动作-回调
        e.setCallback(new MyMethodInterceptor(c));

        /** 接口代理 */
        //获得接口代理对象
        e.setInterfaces(new Class[]{Girl.class});
        Girl g = (Girl) e.create();
        g.dating(1.8D);

        System.out.println("------------------------------------------");

        /** 类代理 */
        //对类生成代理对象
        e.setSuperclass(TeacherCang.class);
        e.setInterfaces(null);

        TeacherCang tc = (TeacherCang) e.create();
        tc.dating(1.8D);

        //当有多个callback时,需要通过callbackFilter来指定被代理方法使用第几个callback
        //e.setCallbacks(new Callback[] {new MyMethodInterceptor(tc), new YouMethodInterceptor(tc)});
		/*e.setCallbackFilter(new CallbackFilter() {
			@Override
			public int accept(Method method) {
				// 返回多个callback中的第几个,索引值
				if("dating".equals(method.getName())) {
					return 1;
				}
				return 0;
			}
		});
		*/
    }
}

在这里插入图片描述

posted on 2021-04-17 11:00  狐言不胡言  阅读(123)  评论(0编辑  收藏  举报