共计 4189 个字符,预计需要花费 11 分钟才能阅读完成。
代理模式
定义
为其他对象提供一种代理,以控制对这个对象的访问。(代理对象在客户端和目标对象之间起到中介的作用)
优点
代码模式能将代理对象与真实被调用的目标对象分离。
一定程度上降低了系统的耦合度,扩展性好。
保护目标对象。(客户端直接交互的是代理类而不是目标对象,这样就保护了目标对象)
增强目标对象
缺点
在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
增加了系统的复杂度。
分类
静态代理
在代码中显式的代理目标。
动态代理
jdk 中的动态代理只能对实现了接口的类进行动态代理。并不能针对一个具体的类进行动态代理。无法代理可以代理接口,在代理类的时候只有在调用到代理类是才由 jvm 动态的创建,jvm 根据传进来的对象以及代理方法名动态的创建了代理类的 class 文件,这个 class 文件被字节码引擎执行,然后通过该代理类的对象进行方法的调用。(比 CGLib 快大约百分之 10)
CGLib 代理
主要针对 jdk 无法代理具体的类的问题。实现原理是生成一个被代理类的子类,通过覆盖父类的方法来实现动态代理。也就是用过继承然后重写,所以被代理的不能是一个 final 的类或者是代理方法不能是一个 final 方法。
spring 中代理选择
当 bean 有实现接口时,spring 就会使用 jdk 的动态代理。
当 bean 没有实现接口时,spring 使用 CGLib
可以强制使用 CGLib (具体配置自行谷歌吧)
下面我们实现一下静态。我们假设一个应用场景就是,要保存一个订单,订单就包括订单实体类,service。dao 等,我们要用代理的方式来实现数据库分库,不同的订单要放在不同的数据库中。下面开始看代码。
订单实体类。
public class Order {
private Object orderInfo;
private Integer userId;
public Object getOrderInfo() {
return orderInfo;
}
public void setOrderInfo(Object orderInfo) {
this.orderInfo = orderInfo;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
订单操作的 dao 接口
public interface IOrderDao {
int insert(Order order);
}
dao 的实现类,我们这里只是模拟一下数据库操作。
public class OrderDaoImpl implements IOrderDao {
@Override
public int insert(Order order) {
System.out.println(“Dao 层添加 Order 成功 ”);
return 1;
}
}
service 接口
public interface IOrderService {
int saveOrder(Order order);
}
service 实现类,我们没有集成 spring 模拟操作
public class OrderServiceImpl implements IOrderService {
private IOrderDao iOrderDao;
@Override
public int saveOrder(Order order) {
//Spring 会自己注入,我们课程中就直接 new 了
iOrderDao = new OrderDaoImpl();
System.out.println(“Service 层调用 Dao 层添加 Order”);
return iOrderDao.insert(order);
}
}
要分库我们来用 ThreadLocal 实现一个上下文对象,模拟暂放在哪个数据的操作。
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>();
public static void setDBType(String dbType){
CONTEXT_HOLDER.set(dbType);
}
public static String getDBType(){
return (String)CONTEXT_HOLDER.get();
}
public static void clearDBType(){
CONTEXT_HOLDER.remove();
}
}
动态的使用数据库的实现。(模拟操作)
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDBType();
}
}
进入重点,代理类的实现。这里的核心就是在执行代理方式之前加入我们的增强操作。
public class OrderServiceStaticProxy {
private IOrderService iOrderService;
public int saveOrder(Order order){
beforeMethod(order);
iOrderService = new OrderServiceImpl();
int result = iOrderService.saveOrder(order);
afterMethod();
return result;
}
private void beforeMethod(Order order){
int userId = order.getUserId();
int dbRouter = userId % 2;
System.out.println(“ 静态代理分配到【db”+dbRouter+”】处理数据 ”);
//todo 设置 dataSource;
DataSourceContextHolder.setDBType(“db”+dbRouter);
System.out.println(“ 静态代理 before code”);
}
private void afterMethod(){
System.out.println(“ 静态代理 after code”);
}
}
最后就是测试类
public class StaticProxyTest {
public static void main(String[] args) {
Order order = new Order();
order.setUserId(2);
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
orderServiceStaticProxy.saveOrder(order);
}
}
输出结果
静态代理分配到【db0】处理数据
静态代理 before code
Service 层调用 Dao 层添加 Order
Dao 层添加 Order 成功
静态代理 after code
静态代理每次代理都要写一个代理类,代理方法,容易产生类爆炸的情况。下面我们实现一下动态代理。实现动态的时我们需要实现 InvocationHandler 接口,实现 invoke 方法这个方法需要 Object proxy, Method method, Object[] objs args 三个参数,一个参数是动态生成的类的对象,一般我们在 invoke 方法中很少用到。method 顾名思义就是需要代理的方法,Object 数据就是参数集合。在使用的时候先要 Proxy.newProxyInstance 创建一个被代理的对象,这个方法有 ClassLoader loader, Class<?>[] interfaces, InvocationHandler h 三个参数,ClassLoader 对象我们通过 getClass 方法的 getClassLoad 方法很容易拿到,Class<?>[] interfaces 就是接口集合了,通过 getInterfaces 也可以很容易拿到,最后一个就是动态代理类的对象了。下面我们看一下代码。
public class OrderServiceDynamicProxy implements InvocationHandler {
private Object target;
public OrderServiceDynamicProxy(Object target) {
this.target = target;
}
public Object bind(){
Class cls = target.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod(argObject);
Object object = method.invoke(target,args);
afterMethod();
return object;
}
private void beforeMethod(Object obj){
int userId = 0;
System.out.println(“ 动态代理 before code”);
if(obj instanceof Order){
Order order = (Order)obj;
userId = order.getUserId();
}
int dbRouter = userId % 2;
System.out.println(“ 动态代理分配到【db”+dbRouter+”】处理数据 ”);
//todo 设置 dataSource;
DataSourceContextHolder.setDBType(“db”+String.valueOf(dbRouter));
}
private void afterMethod(){
System.out.println(“ 动态代理 after code”);
}
}
代理模式就这些内容啦。