Java代理模式

大纲

代理模式(结构型设计模式)通过代理类去访问实现类中的方法,使用场景比如:已有接口和实现类的情况下,想要在已实现的方法基础上扩展更多的功能的场景。

代理模式里的主要类:

  1. 接口

  1. 实现类,需实现接口,用来完成真正的业务。(网上资料也叫做委托类、被代理类)

  1. 代理类,也需要实现接口,调用实现类的方法,本身不处理业务。

使用代理模式的好处:可以扩展实现类的功能,在实现类基本功能基础上增加一些额外的操作。

静态代理模式

图示:

要想做到在原来实现方法的基础上多增加功能,代理类同样也需要实现接口,并且需要持有一个实现类,在自己实现的方法中扩展功能,并且调用实现类的方法去处理真正的业务:

public class TV {
    String name;
    String model;

    public String getModel() {
        return model;
    }

    public String getName() {
        return name;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TV(String name,String model){
        this.name = name;
        this.model = model;
    }

    @Override
    public String toString() {
        return "TV{" +
            "name='" + name + '\'' +
            ", model='" + model + '\'' +
            '}';
    }
}

实体类TV.java

public interface ICompany {
    TV produceTV();
}

接口ICompany.java

实现类需要实现ICompany接口:

/**
* 实现类
*/
public class CompanyFactory implements ICompany{
    @Override
    public TV produceTV() {
        System.out.println("生产商生产一台电视");
        return new TV("TCL","40寸");
    }
}

代理类也需要实现ICompany接口:

/**
* 代理类
* 代理类和实现类都要实现接口
*/
public class TVProxy implements ICompany {
    ICompany company;

    @Override
    public TV produceTV() {
        System.out.println("代理收到一笔订单");
        if (Objects.isNull(company)) {
            company = new CompanyFactory();
        }
        return company.produceTV();
    }
}

CompanyFactory.java

运行时构建代理类,调用代理类的方法同时也调用了实体类的实现方法:

public class MainClass {
    public static void main(String[] args) {
        ICompany company = new TVProxy();
        TV tv = company.produceTV();
        System.out.println(tv.toString());
    }
}

总结:

静态代理通过创建代理类去实现接口,在实现方法里添加额外功能,从而实现不改变原有实现方法下做到功能扩展。

动态代理模式

静态代理有什么缺点?(先别看下面内容思考一下)

静态代理的局限在于,如果接口新增了方法,则代理类必须实现该方法,而使用动态JDK代理,代理类不用实现接口。

实体类TV不变,接口增加维修方法,如果是静态代理,接口新增方法,则对应的实现类和代理类都要实现这个方法,而动态代理则在代码编写层面不用实现接口的所有方法:

图示:

接口新增repairTV方法:

public interface ICompany {
    TV produceTV();

    void repairTV(String name); //新增维修电视的方法
}

实现类需要实现这个方法:

public class CompanyFactory implements ICompany {
    @Override
    public TV produceTV() {
        System.out.println("生产商生产一台电视");
        return new TV("TCL", "40寸");
    }

    @Override
    public void repairTV(String name) {
        System.out.println(String.format("生产商维修%s电视",name));
    }
}

这里是动态代理类,可以看到,这里并没有实现ICompany接口了

public class TVDynamicProxy {
    Object factory;

    public TVDynamicProxy(Object factory) {
        this.factory = factory;
    }

    
    public <T> T getProxy(Class<T> clazz) {
        //newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的:
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //回调
                        System.out.println("动态代理执行:接收到一笔订单");
                        Object tv = method.invoke(factory, args); //这里通过反射调用了factory里实现的方法
                        System.out.println("动态代理结束");
                        return tv;
                }
        });
    }

}

TVDynamicProxy.java

上面方法中newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的

public class MainClassB {
    public static void main(String[] args) {
        ICompany company = new CompanyFactory();
        //将生成的代理类写到本地:
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); //将内存中的$Proxy0.class写到本地
        ICompany company1 = new TVDynamicProxy(company).getProxy(ICompany.class);
        TV tv = company1.produceTV();
        company1.repairTV(tv.name);
    }
}

代码运行查看结果

运行后断点可以看到,这里实际上返回了$Proxy0

$Proxy0实现了接口,并实现了接口里定义的方法。

所以先前我们说,是在代码编写层面上不用去实现接口,实际是JDK帮我们自动实现了。

public final class $Proxy0 extends Proxy implements ICompany {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    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 TV produceTV() throws  {
        try {
            return (TV)super.h.invoke(this, m3, (Object[])null); //这里回调了InvocationHandler的invoke方法
        } 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 void repairTV(String var1) throws  {
        try {
            super.h.invoke(this, m4, 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);
        }
    }
    ...
}

$Proxy0.class

这个$Proxy0生成到了我们的根目录下面

总结:

动态代理在运行后会在内存中创建一个实现了接口的代理类,通过调用代理类的实现方法,回调到InvocationHandler接口的invoke方法,通过反射拿到实现类的方法,并进行调用。

思考:

如果我新建一个工厂类,不实现ICompany接口,那是否可以代理成功?

public class FactoryB {
    public TV produceTVB() {
        System.out.println("B工厂生产电视");
        return new TV("华为电视机", "南京");
    }
}

上面问题可以使用Cglib代理解决。有兴趣的同学可以看给出的参考链接自行拓展。

参考资料:

https://segmentfault.com/a/1190000040407024

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java代理模式是一种结构型设计模式,其目的是为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间充当中介,以便于客户端访问目标对象时,可以在不改变目标对象的情况下添加一些额外的功能,比如安全性、远程访问、缓存等。 在Java中,代理模式可以通过两种方式实现:静态代理和动态代理。静态代理需要手动编写代理类,而动态代理可以在运行时通过反射机制动态生成代理类,更加灵活。 举个例子,假设我们有一个接口`Subject`,其中定义了一些方法。我们希望在调用这些方法时,增加一些额外的日志记录功能。我们可以编写一个代理类`SubjectProxy`,在代理类中实现接口方法并调用目标对象的方法,同时在方法前后添加日志记录的代码。客户端则通过代理类访问目标对象。 静态代理示例代码如下: ```java public interface Subject { void doSomething(); } public class RealSubject implements Subject { @Override public void doSomething() { System.out.println("RealSubject do something."); } } public class SubjectProxy implements Subject { private Subject realSubject; public SubjectProxy(Subject realSubject) { this.realSubject = realSubject; } @Override public void doSomething() { System.out.println("Before do something."); realSubject.doSomething(); System.out.println("After do something."); } } public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); Subject subjectProxy = new SubjectProxy(realSubject); subjectProxy.doSomething(); } } ``` 动态代理示例代码如下: ```java public class SubjectHandler implements InvocationHandler { private Object target; public SubjectHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before " + method.getName()); Object result = method.invoke(target, args); System.out.println("After " + method.getName()); return result; } } public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new SubjectHandler(realSubject); Subject subjectProxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subjectProxy.doSomething(); } } ``` 无论是静态代理还是动态代理,代理模式都可以在不改变目标对象的情况下,为其添加额外的功能,提高代码的可复用性和灵活性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值