GOF23 代理模式
动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类: 基于接口的动态代理,基于类的动态代理
- 基于接口 --
**JDK**
动态代理 - 基于类:
cglib
java
字节码实现:javasist
- 基于接口 --
需要了解两个类
Proxy
代理类
InvocationHandler
-
代理类具有以下属性:
- 代理类的不合格名称未指定。 然而,应该为代理类保留以字符串
"$Proxy"
开头的类名空间。 - 指定代理类的包和模块指定为below 。
- 代理类是最终的和非抽象的 。
- 代理类扩展为
java.lang.reflect.Proxy
。 - 代理类完全按照相同的顺序实现其创建时指定的接口。 调用
getInterfaces
其上Class
对象将返回包含接口的同一列表的阵列(在其创建时指定的顺序),调用getMethods
其上Class
对象将返回的数组方法
对象,其中包括所有的在这些接口的方法,以及调用getMethod
将会在代理接口中找到可以预期的方法。 - 代理类的
ProtectionDomain
与引导类加载器加载的系统类(例如java.lang.Object
,因为代理类的代码是由可信系统代码生成的。 这个保护域通常会被授予java.security.AllPermission
。 - 可以使用
Proxy.isProxyClass
方法来确定给定的类是否是代理类。
- 代理类的不合格名称未指定。 然而,应该为代理类保留以字符串
-
代理实例具有以下属性:
-
给定代理实例
proxy
和由其代理类实现的接口Foo
,以下表达式将返回true:proxy instanceof Foo
并且以下的投射操作将会成功(而不是抛出一个
ClassCastException
):(Foo) proxy
-
每个代理实例都有一个关联的调用处理程序,它被传递给它的构造函数。 静态
Proxy.getInvocationHandler
方法将返回与作为其参数传递的代理实例关联的调用处理程序。 -
代理实例上的接口方法调用将被编码并分派到调用处理程序的
invoke
方法,如该方法的文档所述。 -
的调用
hashCode
,equals
,或toString
中声明的方法java.lang.Object
上的代理实例将被编码并分派到调用处理程序的invoke
中相同的方式方法,接口方法调用进行编码和调度,如上所述。 传递给invoke
的方法
对象的声明类将为java.lang.Object
。 继承自java.lang.Object
的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用与java.lang.Object
实例java.lang.Object
。
-
基于接口的动态代理
- 出租接口
Rent
public interface Rent {
public void rent();
}
- 房东(代理类)
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
- 代理实例 实现接口
InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
/**
* 获取代理对象 rent的实现类
* @return
*/
public Rent getProxy(){
return (Rent) Proxy.newProxyInstance(rent.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行接口
seeHouse();
Object invoke = method.invoke(rent, args);
tseHouse();
pay();
return invoke ;
}
public void seeHouse(){
System.out.println("看房子!!");
}
public void pay(){
System.out.println("支付");
}
public void tseHouse(){
System.out.println("看房子!!!");
}
}
- 用户 客户端对象
public class Client {
public static void main(String[] args) {
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(new Host());
Rent proxy = pih.getProxy();
proxy.rent();
}
}
代理模式在实际开发中的应用
在使用分布式应用的时候可能会用到分库分表的操作 使用java操作时可能需要配置多个数据源,我们可以通过数据源路由来动态切换数据源。首先创建Order订单类.
静态代理
- 订单类
public class Order {
private Object orderInfo;
private Long createTime;
private String id;
@Override
public String toString() {
return "Order{" +
"orderInfo=" + orderInfo +
", createTime=" + createTime +
", id='" + id + '\'' +
'}';
}
public Object getOrderInfo() {
return orderInfo;
}
public void setOrderInfo(Object orderInfo) {
this.orderInfo = orderInfo;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
dao
层
public class OrderDao {
public int insert (Order order){
System.out.println("OrderDao创建order成功");
return 1;
}
}
- service层接口
public interface IOrderService {
int createOrder(Order order);
}
- service实现类
public class OrderService implements IOrderService {
private OrderDao orderDao;
public OrderService(){
orderDao=new OrderDao();
}
@Override
public int createOrder(Order order) {
System.out.println("service调用orderDao insert方法创建订单成功!!");
return orderDao.insert(order);
}
}
- 数据源切换类
/**
* @program: DesignPattern
* @description: 动态切换数据源
* @author: ZGrey
* @create: 2021-03-29 22:01
**/
public class DynamicDataSourceEntry {
public final static String DEFAULT_SOURCE= null;
//使用threadLocal进行数据源切换
private final static ThreadLocal<String> local = new ThreadLocal<>();
private DynamicDataSourceEntry(){}
//清空数据源
public static void clear(){
local.remove();
}
//获取当前正在使用的数据源名称
public static String get(){
return local.get();
}
//还原当前切换的数据源
public static void restore(){
local.set(DEFAULT_SOURCE);
}
//设置已知名字的数据源
public static void set(String source){
local.set(source);
}
public static void set(int year){
local.set("DB_"+year);
}
}
- 动态切换数据源代理类
- 使用静态代理主要完成的任务就是:根据订单创建时间自动按年份进行分表。根据开闭原则,我们修改原来已经写好的代码逻辑,通过代理来完成。创建数据源路由对象,使用
ThreadLocal
的单例实现DynamicDataSourceEntry
类
- 使用静态代理主要完成的任务就是:根据订单创建时间自动按年份进行分表。根据开闭原则,我们修改原来已经写好的代码逻辑,通过代理来完成。创建数据源路由对象,使用
/**
* @program: DesignPattern
* @description: 动态切换数据源代理类
* @author: ZGrey
* @create: 2021-03-29 22:06
**/
public class OrderServiceStaticProxy {
private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
private IOrderService orderService;
public OrderServiceStaticProxy(OrderService orderService){
this.orderService=orderService;
}
public int createOrder(Order order){
before();
Long createTime = order.getCreateTime();
Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(createTime)));
System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
DynamicDataSourceEntry.set(dbRouter);
orderService.createOrder(order);
after();
return 0;
}
private void before(){
System.out.println("proxy before method.");
}
private void after(){
System.out.println("proxy after method.");
}
}
- 测试类
public class Test02 {
public static void main(String[] args) {
try{
Order order = new Order();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
Date parse = simpleDateFormat.parse("2019/02/01");
order.setCreateTime(parse.getTime());
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy(new OrderService());
orderServiceStaticProxy.createOrder(order);
}catch (Exception e){
}
}
}
- 执行结果
动态代理
public class OrderServiceStaticProxy implements InvocationHandler {
private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
private Object target;
public Object getInstance(Object target){
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//参数0为order对象的引用
before(args[0]);
Object invoke = method.invoke(target, args);
after();
return invoke;
}
public void before(Object target){
try{
System.out.println("Proxy before method "+target);
Long time = (Long) target.getClass().getMethod("getCreateTime").invoke(target);
Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
System.out.println("静态代理自动分配到【DB_"+dbRouter+"】数据源处理数据");
DynamicDataSourceEntry.set(dbRouter);
}catch (Exception e){
}
}
public void after(){
System.out.println("Proxy after method!!");
}
}
- 测试
public class Test03 {
public static void main(String[] args) throws ParseException {
Order order = new Order();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
Date parse = simpleDateFormat.parse("2019/12/02");
order.setCreateTime(parse.getTime());
IOrderService orderService = (IOrderService) new OrderServiceStaticProxy()
.getInstance(new OrderService());
orderService.createOrder(order);
}
}
- 结果
从上面的代码可以看出,依然可以达到相同的效果,但是,使用动态代理实现之后,不仅能实现Order的数据源动态路由,还可以实现其他任何类的数据源路由,当然,有一个重要的约定就是必须实现getCreateTime()
,因为路由规则是根据时间来运算。可以通过接口规范达到约束的目的。