代理模式

151 阅读5分钟

代理简介

1.1概念

  • 代理就是帮别人做事情,如:工厂的中介,中介负责为工厂招收工人,那么中介就是工厂的代理;客户通过商家购买东西,商家向厂家购买货物,商家就是工厂的代理。

  • 在开发中存在a类需要调用c类的方法,完成某一个功能,但是c禁止a调用。这时,可以在a和c之间创建一个b类代理,a类访问b类,b类访问c类。例如:登陆的时候需要进行短信验证,这个时候代理就是中国移动的子公司来完成短信的发送功能。

  • 代理模式就是为其他对象提供一种代理来控制整个对象的访问,在某些情况下,一个对象不适合或不能直接引用另一个对象,二代理对象可以在客户类和目标对象直接起到中介的作用。

  • 功能增强:其中目标对象实现真正的功能,但是代理对象可以对目标对象的功能做进一步的扩充。

1.2 设计模式

设计模式代表了最佳的实践,通常被有经验的面向对象的软件开发者所采用,它是开发中面临的一般问题的解决方案,这些解决方案是众多的软件开发人员经过相当长的实践的经验和错误总结出来的。

1.3 举例

如果设计一个类,该类中含有加减乘除四个方法,现在需要给每一个方法添加测试方法执行时间的代码,如果不使用代理的话,就需要对每一个方法进行修改,需要修改多次。违背了开闭原则(OCP,对扩展开放,修改关闭)和单一职责(SRP)

1.4 作用

功能增强:在原有的功能上增加额外的功能 控制访问:代理类去访问目标类

静态代理

静态代理实在程序运行前就已经存在代理类的字节码文件,静态代理通常是对原有业务逻辑的扩充,通过代理类持有真实的对象,在代理类的源代码中调用被代理类的方法来添加我们需要的业务逻辑。例如:买车不去工厂而是去4S店。

  1. 创建一个接口:
interface Animal{
public abstract void show();
}
  1. 代理类
public class Zanglingyang {
Sheep sheep=new Sheep();
@Override
public void show(){
sheep.show();
System.out.println("我爱游泳!");
}
}

创建被代理的对象,在代理类的方法中调用被代理类的方法,在这个过程中可以实现对被代理类的功能的扩充。

  1. 被代理类:
public class Sheep implements Animal{
@Override
public void show(){
System.out.println("我爱吃青草");
}
}
  1. 测试类:
public class Test{
public static void main(String[] args){
Zanglingyang znglingyang=new Zanglingyang();
Zanglingyang.show();
}
}

静态代理的优缺点:

  • 优点:实现简单,容易理解
  • 缺点:当目标类增多了,代理类也需要增减。当接口中的方法增加或者修改的时候,很多类都需要修改。因为,目标类和代理类都实现了相同的接口。

动态代理(JDK代理,针对接口的代理)

动态代理是利用反射的机制动态的生成代理的对象。主要应用场景是实现AOP编程,面向切面编程。

JDK动态代理:使用java反射包中的类和接口实现动态代理的功能,反射包是 java.lang.reflect,里面的InvocationHandler接口,Method,Proxy类在jdk动态代理会被需要

创建一个接口

public interface Target{
    public void show();
}

被代理类

public class TargetClass implments Target{
    @Override
    public void show() {
        System.out.println("我是目标方法");
    }
}

实现InvocationHandler接口:表示代理要干什么,定义目标类要完成的功能

public class MyHandler implements InvocationHandler {

    private Object target=null;

    //动态代理:目标对象是活动的,不是固定的,需要传进来
    //传入的是谁,就给谁创建代理
    public MyHandler(Object target) {
        this.target = target;
    }

    //必须实现InvocationHandler接口,完成代理类需要的功能(调用目标方法,功能增强)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {


        System.out.println("完成功能前置增强");


        Object res = null;

        res = method.invoke(target, args);//执行目标方法


        System.out.println("完成功能后置增强");

        return res;




    }
}

InvocationHandler接口中的invoke()方法的参数信息: invoke(Object proxy,Method method,Object[] args) Object proxy:jdk创建的代理对象,不需要赋值 Method method:目标类中的目标方法,jdk提供method对象 Object[] args:目标类中的目标方法的参数

测试类

public class Test{
public static void main(String[] args){
//创建目标对象
Target target=new TargetClass();

//创建invocationHandler对象
InvocationHandler invocationHandler=new MyHandler(target);

//创建代理对象
Target proxy=(Target) Proxy.newProxyInstance(
                            target.getClass().getClassLoader(),
                            target.getClass().getInterfaces(),
                            invocationHandler
                            );
                            System.out.println(proxy.show());
 }
 }
                            

使用Proxy类的静态方法newProxyInstance(),创建代理对象,这个方法需要三个参数: ** public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)**

  • loader:一个ClassLoader对象,定义了对哪个目标类获得相应代理类的类加载对象

  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的方法。

  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,这个对象完成了该调用方法的实现。我个人感觉这里是动态代理的核心,因为,在这里根据动态代理对象调用方法的不同,会即使的动态实现调用方法。

从上面的例子中知道,当目标类实现的接口增加其接口中的方法的时候,此时,目标类需要修改,但是代理获得的对象不需要做任何的修改,因为代理对象可以动态的获得目标类的任何方法,至于是哪个方法,那就看代理对象需要调用目标类中的哪个方法。