代理模式有三种类型,静态代理,动态代理(JDK代理,接口代理)、Cglib代理(在内存中动态的创建目标对象的子类)
代理模式大致有三种角色:
Real Subject
:真实类,也就是被代理类、委托类。用来真正完成业务服务功能;Proxy
:代理类,将自身的请求用 Real Subject 对应的功能来实现,代理类对象并不真正的去实现其业务功能;Subject
:定义 RealSubject 和 Proxy 角色都应该实现的接口。某公司生产电视机,在当地销售需要找到一个代理销售商。那么客户需要购买电视机的时候,就直接通过代理商购买就可以。
package dev.rennen.designpattern.proxy.staticproxy;
public class TV {
private String name;//名称
private String address;//生产地
public TV(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "TV{" +
"name='" + name + '\\'' +
", address='" + address + '\\'' +
'}';
}
}
package dev.rennen.designpattern.proxy.staticproxy;
public interface TVCompany {
/**
* 生产电视机
* @return 电视机
*/
TV produceTV();
}
package dev.rennen.designpattern.proxy.staticproxy;
public class TVConsumer {
public static void main(String[] args) {
TVProxy tvProxy = new TVProxy();
TV tv = tvProxy.produceTV();
System.out.println(tv);
}
}
package dev.rennen.designpattern.proxy.staticproxy;
public class TVFactory implements TVCompany {
@Override
public TV produceTV() {
System.out.println("TV factory produce TV...");
return new TV("小米电视机","合肥");
}
}
package dev.rennen.designpattern.proxy.staticproxy;
import java.util.Objects;
public class TVProxy implements TVCompany{
private TVCompany tvCompany;
public TVProxy(){
}
@Override
public TV produceTV() {
System.out.println("TV proxy get order .... ");
System.out.println("TV proxy start produce .... ");
if(Objects.isNull(tvCompany)){
System.out.println("machine proxy find factory .... ");
tvCompany = new TVFactory();
}
return tvCompany.produceTV();
}
}
JDK动态代理对象不需要实现接口,只有目标对象需要实现接口。
实现基于接口的动态代理需要利用JDK中的API,在JVM内存中动态的构建 Proxy对象
。
需要使用到 java.lang.reflect.Proxy
,和其 newProxyInstance
方法,但是该方法需要接收三个参数。
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader
:指定当前目标对象使用类加载器,获取加载器的方法是固定的。Class<?>[] interfaces
:目标对象实现的接口的类型,使用泛型方式确认类型。InvocationHandler h
:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。动态代理的方式中,所有的函数调用最终都会经过 invoke 函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。
有一天公司增加了业务,出售的商品越来越多,售后也需要更上。但是公司发现原来的代理商,还要再培训才能完成全部的业务,于是就找了另外的动态代理商B 。 代理商B 承诺无缝对接公司所有的业务,不管新增什么业务,均不需要额外的培训即可完成。
代码示例:
package dev.rennen.springbootproject.proxy.dynamicproxy;
import dev.rennen.springbootproject.proxy.TV;
public interface TVCompany {
/**
* 生产电视机
* @return 电视机
*/
public TV produceTV();
/**
* 维修电视机
* @param tv 电视机
* @return 电视机
*/
public TV repair(TV tv);
}
package dev.rennen.springbootproject.proxy.dynamicproxy;
import dev.rennen.springbootproject.proxy.TV;
public class TVConsumer {
public static void main(String[] args) {
TVCompany target = new TVFactory();
TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();
TV tv = tvCompany.produceTV();
tvCompany.repair(tv);
}
}
package dev.rennen.springbootproject.proxy.dynamicproxy;
import dev.rennen.springbootproject.proxy.TV;
public class TVFactory implements TVCompany {
@Override
public TV produceTV() {
System.out.println("TV factory produce TV...");
return new TV("小米电视机","合肥");
}
@Override
public TV repair(TV tv) {
System.out.println("tv is repair finished...");
return new TV("小米电视机","合肥");
}
}
package dev.rennen.springbootproject.proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TVProxyFactory {
private final Object target;
public TVProxyFactory(Object o){
this.target = o;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("TV proxy find factory for tv.... ");
return method.invoke(target, args);
});
}
}
JDK 动态代理有一个最致命的问题是它只能代理实现了某个接口的实现类,并且代理类也只能代理接口中实现的方法,要是实现类中有自己私有的方法,而接口中没有的话,该方法不能进行代理调用。