代理模式的应用场景
在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式(ProxyPattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。
案例一
package test1;
public interface Subject {
void request();
}
package test1;
/**
* 被代理对象(真实对象)
*/
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("real service is called.");
}
}
package test1;
/**
* 代理对象
*/
public class Proxy implements Subject {
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
@Override
public void request() {
before();
subject.request();
after();
}
public void before(){
System.out.println("called before request().");
}
public void after(){
System.out.println("called after request().");
}
}
package test1;
/**
* 为其它对象提供一种代理,以控制对这个对象的访问
*/
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.request();
}
}
Subject 是顶层接口,RealSubject是真实对象(被代理对象),Proxy是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象方法,同时也调用被代理对象的方法,但是在代理对象前后增加一些处理。在代码中,我们想到代理,就会理解为是代码增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构型模式,有静态代理和动态代理。
静态代理
举个例子:人到了适婚年龄,父母总是迫不及待希望早点抱孙子。而现在社会的人在各种压力之下,都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲,比子女自己还着急。这个相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
案例一
package test2;
/**
* 人有很多行为,要谈恋爱,要住房子,要购物,要工作
*/
public interface Person {
public void findLove();
}
儿子要找对象,实现Son类:
package test2;
public class Son implements Person {
@Override
public void findLove() {
//我没有时间
//工作忙
System.out.println("儿子要求:肤白貌美大长腿");
}
}
父亲要帮儿子相亲,实现 Father 类:
package test2;
public class Father {
private Son son;
public Father(Son son){
this.son = son;
}
//目标对象的引用给拿到
public void findLove(){
System.out.println("父母物色对象");
son.findLove();
System.out.println("双方同意交往,确立关系");
}
}
测试代码:
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
执行结果:
案例二
在分布式业务场景中,我们通常会对数据库进行分库分表,分库分表之后使用 Java 操作时,就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。
先创建 Order 订单实体:
package test3;
/**
* 订单
*/
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;
}
}
创建 OrderDao 持久层操作类:
package test3;
public class OrderDao {
public int insert(Order order){
System.out.println("OrderDao 创建 Order 成功!");
return 1;
}
}
创建 IOrderService 接口:
package test3;
public interface IOrderService {
int createOrder(Order order);
}
创建 OrderService 实现类:
package test3;
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);
}
}
接下来使用静态代理,主要完成的功能是,根据订单创建时间自动按年进行分库。根据开闭原则,原来写好的逻辑我们不去修改,通过代理对象来完成。先创建数据源路由对象,我们使用 ThreadLocal 的单例实现DynamicDataSourceEntry 类:
package test3;
/**
* 动态数据源切换
*/
public class DynamicDataSourceEntry {
/**
* 默认数据源
*/
public final static String DEFAULT_SOURCE = null;
private final static ThreadLocal<String> local = new ThreadLocal<String>();
private DynamicDataSourceEntry(){
}
/**
* 清空数据源
*/
public static void clear(){
local.remove();
}
/**
* 获取当前正在使用的数据源名字
* @return
*/
public static String get(){
return local.get();
}
/**
* 还原当前切面的数据源
*/
public static void restore(){
local.set(DEFAULT_SOURCE);
}
/**
* 设置已知名字的数据源
* @param source
*/
public static void set(String source){
local.set(source);
}
/**
* 根据年份动态设置数据源
* @param year
*/
public static void set(int year){
local.set("DB_"+year);
}
}
创建切换数据源的代理 OrderServiceSaticProxy 类:
package test3;
import java.text.SimpleDateFormat;
import java.util.Date;
public class OrderServiceStaticProxy implements IOrderService {
private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
private IOrderService orderService;
public OrderServiceStaticProxy(IOrderService orderService){
this.orderService = orderService;
}
@Override
public int createOrder(Order order) {
before();
Long time = order.getCreateTime();
Integer dbRouter = Integer.valueOf(yearFormat.format(new Date()));
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.");
}
}
测试代码:
package test3;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test3Main {
public static void main(String[] args) throws Exception {
Order order = new Order();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = sdf.parse("2017/02/01");
order.setCreateTime(date.getTime());
IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
orderService.createOrder(order);
}
}
测试结果:
动态代理
动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。如果还以找对象为例,使用动态代理相当于是能够适应复杂的业务场景。不仅仅只是父亲给儿子找对象,如果找对象这项业务发展成了一个产业,进而出现了媒婆、婚介所等这样的形式。那么,此时用静态代理成本就更大了,需要一个更加通用的解决方案,要满足任何单身人士找对象的需求。
JDK动态代理
创建媒婆(婚介)JDKMeipo 类:
package meipo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKMeipo implements InvocationHandler {
private Object target;
public Object getInstance(Object target) throws Exception{
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 {
before();
Object obj = method.invoke(this.target,args);
after();
return obj;
}
public void before(){
System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
System.out.println("开始物色");
}
public void after(){
System.out.println("如果合适的话,就准备办事");
}
}
创建单身客户Customer 类:
package meipo;
import test2.Person;
public class Customer implements Person {
@Override
public void findLove() {
System.out.println("高富帅");
System.out.println("身高 180cm");
System.out.println("胸大,6 块腹肌");
}
}
测试代码:
public static void main(String[] args) throws Exception {
Person obj = (Person) new JDKMeipo().getInstance(new Customer());
obj.findLove();
}
测试结果:
JDK改造动态路由案例
创建动态代理的类 OrderServiceDynamicProxy,代码如下:
package daymicRoute;
import sun.dc.pr.PRError;
import test3.DynamicDataSourceEntry;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
public class OrderServiceDynamicProxy 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 {
before(args[0]);
Object object = method.invoke(target,args);
after();
return object;
}
private void before(Object target){
try {
System.out.println("Proxy before method");
Long time = (Long)target.getClass().getMethod("getCreateTime").invoke(target);
Integer dbRouter = Integer.valueOf(yearFormat.format(time));
System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据。");
DynamicDataSourceEntry.set(dbRouter);
}catch (Exception e){
e.printStackTrace();
}
}
private void after(){
System.out.println("Proxy after method");
}
测试代码:
package daymicRoute;
import test3.IOrderService;
import test3.Order;
import test3.OrderService;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DaymicRouteTest {
public static void main(String[] args) throws Exception {
Order order = new Order();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = sdf.parse("2018/02/01");
order.setCreateTime(date.getTime());
IOrderService orderService = (IOrderService)new OrderServiceDynamicProxy().getInstance(new
OrderService());
orderService.createOrder(order);
}
}
测试结果:
依然能够达到相同运行效果。但是,动态代理实现之后,我们不仅能实现 Order 的数据源动态路由,还可以实现其他任何类的数据源路由。当然,有比较重要的约定,必须要求实现 getCreateTime()方法,因为路由规则是根据时间来运算的。当然,我们可以通过接口规范来达到约束的目的,在此就不再举例。
Cglib代理
package test4;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MeiPoCgLibProxy implements MethodInterceptor {
/**
*业务类对象,供代理方法中进行真正的业务方法调用
*/
private Object target;
public Object getInstance(Object target){
//给业务对象赋值
this.target = target;
//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类
enhancer.setSuperclass(this.target.getClass());
//设置回调:对于代理类上所有方法的调用,都会调用CallBack
enhancer.setCallback(this);
//创建动态代理类对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("业务处理前-----------");
methodProxy.invokeSuper(object,args);
System.out.println("业务处理后-----------");
return null;
}
}
package test4;
public class PersonImpl {
public void hello(){
System.out.println("hello 您好!");
}
}
测试代码:
package test4;
public class TestCglib {
public static void main(String[] args) {
PersonImpl bookCglib=(PersonImpl)new MeiPoCgLibProxy().getInstance(new PersonImpl());
bookCglib.hello();
}
}
执行结果:
- 当Bean实现接口时,Spring就会用JDK的动态代理。
- Bean没有实现接口时,Spring使用CGlib是实现。
- 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)。