目标:
- 掌握代理的应用场景与实现原理
- 了解静态代理与动态代理的区别
- 了解CGLib与JDK Proxy的根本区别
- 手写实现自定义的JDK动态代理
代理模式:为其他对象提供一种代理,以控制对这个对象的访问。属于结构型设计模式
一、静态代理(显示声明被代理对象)
1、案例一:
顶层接口–Person—找对象方法
目标对象–实现Person—儿子–找对象
代理对象–实现Person–父亲–替儿子物色对象
a、顶层Person接口
b、儿子找对象实现Person
c、父亲帮儿子物色–Father类
4、测试
package com.ckw.proxy.staticproxy;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:27
* @description: 静态代理测试
*/
public class StaticProxyTest {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
2、案例二
具体应用场景:在分布式业务场景中,我们通常会对数据库进行分库分表(如:按照年份来进行分库), 分库分表之后使用 Java 操作时,就可能需要配置多个数据源,我们通过设置数据源路由 来动态切换数据源-----在创建订单之前根据该订单的创建时间来存储到不同数据库
目标对象—创建订单方法
代理对象—在创建订单之前根据订单的创建时间存储不同数据库,实现动态切换数据源(增强创建订单的功能)
a、创建 Order 订单实体
package com.ckw.proxy.dbroute;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:45
* @description: 订单实体类
*/
public class Order {
private Object orderInfo;
//订单创建时间进行按年分库
private Long createTime;
private String 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;
}
}
b、创建 OrderDao 持久层操作类
package com.ckw.proxy.dbroute;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:45
* @description: Dao层
*/
public class OrderDao {
public int insert(Order order){
System.out.println("OrderDao创建Order成功!");
return 1;
}
}
c、创建 IOrderService 接口
package com.ckw.proxy.dbroute;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:44
* @description: 顶层接口:创建订单方法
*/
public interface IOrderService {
int createOrder(Order order);
}
d、创建 OrderService 实现类
package com.ckw.proxy.dbroute;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:47
* @description: service层
*/
public class OrderService implements IOrderService {
private OrderDao orderDao;
public OrderService(){
//如果使用Spring应该是自动注入的
//我们为了使用方便,在构造方法中将orderDao直接初始化了
orderDao = new OrderDao();
}
@Override
public int createOrder(Order order) {
System.out.println("OrderService调用orderDao创建订单");
return orderDao.insert(order);
}
}
e、使用静态代理,主要完成的功能是,根据订单创建时间自动按年进行分库。原来写好的逻辑不去修改,通过代理对象来完成进行增强功能。先创建数据源路由对象,我们使用 ThreadLocal 的单例实现
package com.ckw.proxy.dbroute.proxy;
import com.ckw.proxy.dbroute.IOrderService;
import com.ckw.proxy.dbroute.Order;
import com.ckw.proxy.dbroute.OrderService;
import com.ckw.proxy.dbroute.db.DynamicDataSourceEntity;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:50
* @description: 静态代理类
*/
public class OrderServiceStaticProxy implements IOrderService {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy");
private OrderService orderService;
public OrderServiceStaticProxy(OrderService orderService) {
this.orderService = orderService;
}
//增强原有逻辑,但不会改变原有逻辑
@Override
public int createOrder(Order order) {
before();
Long createTime = order.getCreateTime();
Integer dbRouter = Integer.valueOf(simpleDateFormat.format(new Date(createTime)));
//增强逻辑(切换数据源)
DynamicDataSourceEntity.set(dbRouter);
System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据。");
//原有逻辑
this.orderService.createOrder(order);
after();
return 1;
}
private void before(){
System.out.println("Proxy before method.");
}
private void after(){
System.out.println("Proxy after method.");
}
}
f、测试
package com.ckw.proxy.dbroute;
import com.ckw.proxy.dbroute.proxy.OrderServiceStaticProxy;
import org.junit.Test;
import java.util.Date;
/**
* @author ckw
* @version 1.0
* @date 2020/6/9 19:54
* @description: 代理测试
*/
public class DbRouteProxyTest {
@Test
public void testStaticProxy(){
Order order = new Order();
order.setCreateTime(new Date().getTime());
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy(new OrderService());
orderServiceStaticProxy.createOrder(order);
}
}
g、静态代理缺点
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,则要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
注:由于篇幅比较长、动态代理下一章