代理模式简介及使用示例

文章目录

  • 代理模式简介
  • 代理模式使用
    • 静态代理
    • JDK动态代理
    • CGLib动态代理
  • 扩展联想学习

代理模式简介

功能:在不改变原有类的前提下对被代理类进行动态扩展

分类:

  1. 静态代理
  2. JDK动态代理
  3. CGLib动态代理

代理模式使用

静态代理

静态代理较为简单,使用代理对象将目标对象包裹起来,实际执行时相当于还是调用了目标对象的方法,只是在调用目标方法前或后添加了其他的代码,以达到扩展目的。

类图:
代理模式简介及使用示例_第1张图片代码:

/***
 * 被代理对象所实现的接口
 */
public interface TargetInterface {
    void method01();
    void method02();
}
/**
 * 被代理类
 */
public class Target implements TargetInterface{
    @Override
    public void method01() {
        System.out.println("目标对象 方法1 执行");
    }

    @Override
    public void method02() {
        System.out.println("目标对象 方法2 执行");
    }
}
/**
 * 目标对象代理对象
 */
public class TargetProxy implements TargetInterface {
    private TargetInterface target;

    public TargetProxy(TargetInterface target) {
        this.target = target;
    }

    @Override
    public void method01() {
        beforeMethod();
        target.method01();
    }

    @Override
    public void method02() {
        target.method02();
        afterMethod();
    }

    private void beforeMethod(){
        System.out.println("代理对象方法 before");
    }

    private void afterMethod(){
        System.out.println("代理对象方法 after");
    }
}
public class Client {
    public static void main(String[] args) {
        TargetInterface target = new Target();
        TargetInterface proxy = new TargetProxy(target);
        proxy.method01();
        System.out.println("-----------");
        proxy.method02();
    }
}

结果输出:

代理对象方法 before
目标对象 方法1 执行
-----------
目标对象 方法2 执行
代理对象方法 after

静态代理的缺点十分明显:目标对象和代理对象都需要实现相同的接口,并且代理对象编码过程中必须处理该接口中的全部方法。

实际使用代理模式的时候,并不是所有的类都会实现接口,又或者目标对象被封装的起来,无法得知想要扩展的方法来自哪个接口。也有些时候,仅想针对一个方法进行扩展,但是接口中有非常多的方法,这个时候使用静态代理就会产生特别多无用工作。

JDK动态代理

JDK动态代理为原生代理实现,要求扩展的方法同样来自目标对象实现的某个接口,但是不需要知道具体来自哪个接口。
JDK代理使用比较简单,不附类图,想要了解原理查看扩展学习内容

代码:
Target 和 TargetInterface 代码同上,不再重复

代理对象写法1:

/**
 * 目标对象代理对象
 */
public class TargetProxy1 implements InvocationHandler {
    private TargetInterface target;

    public TargetInterface getProxyInstance(TargetInterface target){
        this.target = target;
        Class<? extends TargetInterface> clazz = target.getClass();
        return (TargetInterface)Proxy.newProxyInstance(
											clazz.getClassLoader(), //目标对象类加载器
											clazz.getInterfaces(), //目标对象实现的接口
											this // 当前代理对象
										);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnValue;
        // 此处可对方法进行筛选,否则全部方法都会被扩展
        if("method01".equals(method.getName())){
            beforeMethod();
            returnValue = method.invoke(this.target, args);
            afterMethod();
        }else{
            returnValue = method.invoke(this.target, args);
        }
        return returnValue;
    }

    private void beforeMethod(){
        System.out.println("代理对象方法 before");
    }
    private void afterMethod(){
        System.out.println("代理对象方法 after");
    }
}

业务不太复杂时可以使用更简单的写法
代理对象写法2:

/**
 * 目标对象代理对象
 */
public class TargetProxy2{

    public TargetInterface getProxyInstance(TargetInterface target){
        Class<? extends TargetInterface> clazz = target.getClass();
        return (TargetInterface)Proxy.newProxyInstance(
        		clazz.getClassLoader(), clazz.getInterfaces(),
                new InvocationHandler() {
		            @Override
		            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		                Object returnValue;
		                // 此处可对方法进行筛选,否则全部方法都会被扩展
		                if("method1".equals(method.getName())){
		                    beforeMethod();
		                    returnValue = method.invoke(target, args);
		                    afterMethod();
		                }else{
		                    returnValue = method.invoke(target, args);
		                }
		                return returnValue;
		            }
		            private void beforeMethod(){
		                System.out.println("代理对象方法 before");
		            }
		            private void afterMethod(){
		                System.out.println("代理对象方法 after");
		            }
		        });
    }
}

Client:
更简单的写法可以直接在Client中使用匿名内部类

public class Client {
    public static void main(String[] args) {
        TargetInterface target = new Target();
        TargetInterface proxy1 = new TargetProxy1().getProxyInstance(target);
        TargetInterface proxy2 = new TargetProxy2().getProxyInstance(target);

        // 业务简单时,更简洁的形式,不需要单独写代理类
//      TargetInterface prox = (TargetInterface)Proxy.newProxyInstance(
//              target.getClass().getClassLoader(),
//              target.getClass().getInterfaces(),
//              (Object proxy, Method method, Object[] params)->{
//                System.out.println("代理对象方法 before");
//                Object returnValue = method.invoke(target, params);
//                System.out.println("代理对象方法 after");
//                return returnValue;
//        });

        proxy1.method01();
        System.out.println("-------------------");
        proxy1.method02();
        
//        proxy2.method01();
//        System.out.println("-------------------");
//        proxy2.method02();

    }
}

输出结果:

代理对象方法 before
目标对象 方法1 执行
代理对象方法 after
-------------------
目标对象 方法2 执行

CGLib动态代理

实际开发中,存在目标对象没有实现任何接口的情况,这时候想要进行动态代理就需要使用CGLib来实现。

使用CGLib代理需要引入CGLib相关的jar包
Spring核心包中已经包含CGLib功能,所以maven工程直接引入spring-core.jar即可,非maven工程需要引入 cglib-xxx.jar,asm-xxx.jar,xxx为版本,暂时没遇见过版本问题。

代码:
Target 代码同上,不再重复

/**
 * 目标对象代理对象
 */
public class TargetProxy implements MethodInterceptor {
    private Object target;

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

    public Object getProxyInstance(){
        Enhancer en = new Enhancer();
        en.setSuperclass(target.getClass());
        en.setCallback(this);
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeMethod();
        Object returnValue = method.invoke(target);
        afterMethod();
        return returnValue;
    }

    private void beforeMethod(){
        System.out.println("代理对象方法 before");
    }

    private void afterMethod(){
        System.out.println("代理对象方法 after");
    }
}
public class Client {
    public static void main(String[] args) {
        Target target = new Target();
        Target proxy = (Target)new TargetProxy(target).getProxyInstance();
        proxy.method01();
    }
}

结果输出:

代理对象方法 before
目标对象 方法1 执行
代理对象方法 after

扩展联想学习

JDK动态代理实现原理详解
CGLib动态代理实现原理详解
代理模式和装饰者模式有何区别?

其他设计模式

你可能感兴趣的:(设计模式)