代理模式,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间祈祷中介的作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一是保护对象,二是增强目标对象
1.静态代理
首先先举一个简单的例子
创建顶层接口Person:
public interface Person {
public void findLove();
}
创建son类
public class Son implements Person {
@Override
public void findLove() {
// TODO Auto-generated method stub
System.out.println("儿子");
}
}
创建father类
public class Father {
private Son son;
public Father(Son son) {
this.son = son;
}
public void findLove() {
System.out.println("父亲前置");
this.son.findLove();
System.out.println("父亲后置");
}
}
进行测试
public static void main(String []args) {
Father father = new Father(new Son());
father.findLove();
}
测试结果
父亲前置
儿子
父亲后置
下面结合业务代码进行进一步演示
先创建Order订单类
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;
}
}
创建持久层操作类
public class OrderDao {
public int insert(Order order) {
System.out.println("OrderDao创建order成功");
return 1;
}
}
创建Service接口
public interface OrderService {
int createOrder(Order order);
}
创建service接口实现
public class OrderServiceImp implements OrderService {
private OrderDao orderdao;
public OrderServiceImp() {
this.orderdao = new OrderDao();
}
@Override
public int createOrder(Order order) {
System.out.println("Imp调用Dao创建订单");
return orderdao.insert(order);
}
}
接下来先创建数据路由对象,使用ThreadLocal的单例实现
public class DynamicDataSourceEntry {
public static final String DEFAULT_SOURCE = null;
private static final ThreadLocal<String> local = new ThreadLocal<String>();
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);
}
}
创建切换数据源代理类
public class OrderServiceStaticProxy implements OrderService {
private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
private OrderService orderService;
public OrderServiceStaticProxy(OrderService orderService) {
this.orderService =orderService;
}
@Override
public int createOrder(Order order) {
before();
Long time = order.getCreateTime();
Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
System.out.println("当前使用【DB_"+dbRouter+"】数据库");
DynamicDataSourceEntry.set(dbRouter);
orderService.createOrder(order);
after();
return 0;
}
private void before() {
System.out.println("before method");
}
private void after() {
System.out.println("after method");
}
}
进行测试
public class DbRouterTest {
public static void main(String args[]) {
try {
Order order = new Order();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date =sdf.parse("2017/02/01");
order.setCreateTime(date.getTime());
OrderService os = new OrderServiceStaticProxy(new OrderServiceImp());
os.createOrder(order);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
测试结果如下
before method
当前使用【DB_2017】数据库
Imp调用Dao创建订单
OrderDao创建order成功
after method
2.动态代理
动态代理和静态代理基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩张适应性更强
1.JDK实现方式
首先对第一个例子进行改造
public class Customer implements Person {
@Override
public void findLove() {
// TODO Auto-generated method stub
System.out.println("客户");
}
}
public class JDKMiPo implements InvocationHandler {
//被代理的对象,把引用保存下来
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 {
// TODO Auto-generated method stub
before();
Object obj = method.invoke(this.target,args);
after();
return null;
}
private void before() {
System.out.println("前置方法");
}
private void after() {
System.out.println("后置方法");
}
}
public class JDKDynamicTest {
public static void main(String args[]){
try {
Person obj = (Person) new JDKMiPo().getInstance(new Customer());
obj.findLove();
} catch (Exception e) {
// TODO: handle exception
}
}
}
测试结果
前置方法
客户
后置方法
下面对另一实例进行优化
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);
}
private void before(Object target) {
try {
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) {
// TODO: handle exception
e.printStackTrace();
}
}
private void after() {
System.out.println("after method");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(args[0]);
Object obj = method.invoke(target, args);
after();
return obj;
}
}
public class DbRouterTest {
public static void main(String args[]) {
try {
Order order = new Order();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date =sdf.parse("2017/02/01");
order.setCreateTime(date.getTime());
OrderService os = (OrderService) new OrderServiceStaticProxy().getInstance(new OrderServiceImp());
os.createOrder(order);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
测试结果如下
当前使用【DB_2017】数据库
Imp调用Dao创建订单
OrderDao创建order成功
after method
2.JDK代理实现原理
JDK动态代理采用字节重组,重新生成对象来代替原始对象,以达到动态代理的目的。JDK动态代理生成对象的步骤如下:
1.获取被代理对象的引用,并获取他的所有接口,反射获取
2.JDK代理重新生成一个类,同时新的类要实现被代理类的所有接口
3.动态生成Java代码,新加的业务逻辑代码由一定的逻辑代码调用
4.编译生成Java代码.class文件
5.重新加载到JVM中运行
3.静态代理和动态代理的本质区别
1.静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则
2.动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则
3.若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需新增策略就可完成,无需修改代码
4.代理模式的优缺点
代理模式具有以下优点:
1.代理模式能将代理对象与真是被调用对象分离
2.在一定程度上降低了系统的耦合性,扩展性好
3.可以起到保护对象的功能
4.可以增强对象的功能
但也有缺点
1.代理模式会导致系统设计中类的数量增加
2.在客户端和目标对象间加了一个对象,导致请求速度变慢
3.增加了系统的复杂度