代理模式(Proxy Pattern)
定义
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。——《headfirst设计模式》
个人理解:通过一个对象生成一个新的对象,新对象除了原对象的功能,还增加了些新的功能。如原对象方法执行前做些什么,执行后做些什么,异常了做些什么。原对象就是被代理对象,新对象就是代理对象。
生活中的例子
- 经纪人与明星
- 中介:房地产中介(代理开发商)、瓜子二手车直卖网(代理车主)、淘宝卖家(代理厂家)等
注意:中间人是代理对象,但被代理对象可以是两边的任一边,如房地产中介,既是代理开发商卖房,也可以是代理买家买房
好处
- 增强对象功能,且符合开闭原则
- 抽取重复代码/相同逻辑(动态代理)
- 隐藏与业务无关的代码和逻辑,如事务、日志等
应用
- Spring声明式事务
- SpringAOP
- 日志打印
- 权限控制
代理分类
- 静态代理
- jdk动态代理
- cglib动态代理
1. 静态代理
程序员自己编写代理对象,也就是.java文件
特点
- 需要实现接口或继承父类,代理类与被代理类为同一接口或同一父类的子类
没有实现接口或继承父类,也可以对它进行功能增强,但这就不属于代理模式了(这也是多态的体现,父类引用指向子类对象)
- 代理类与被代理类一一对应,编写成本高,效率低
代理一个类还好,要你编写100个代理类,你得哭
- 代理类很多时候都是一个种类,如事务、日志、权限等,代理类多了,则出现大量重复代码
如事务,每个操作数据库的类,都需要编写一个代理类,增加开启事务、提交事务、出错回滚这样的代码
代码演示
// 接口1
public interface OrderService {
void save();
void delete();
void update();
void select();
}
// 被代理类1,操作数据库-用户
public class OrderServiceImpl implements OrderService {
@Override
public void save() {
System.out.println("保存订单数据");
}
@Override
public void delete() {
System.out.println("删除订单数据");
}
@Override
public void update() {
System.out.println("更改订单数据");
}
@Override
public void select() {
System.out.println("查询订单数据");
}
}
// 代理类1,在执行被代理类的每个方法前开启事务,执行后提交事务
public class OrderStaticProxy implements OrderService {
private OrderService orderService;
public OrderStaticProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public void save() {
System.out.println("open transaction");
orderService.save();
System.out.println("commit transaction");
}
@Override
public void delete() {
System.out.println("open transaction");
orderService.delete();
System.out.println("commit transaction");
}
@Override
public void update() {
System.out.println("open transaction");
orderService.update();
System.out.println("commit transaction");
}
@Override
public void select() {
System.out.println("open transaction");
orderService.select();
System.out.println("commit transaction");
}
}
// 接口2
public interface UserService {
void save();
void delete();
void update();
void select();
}
// 被代理类2,操作数据库-订单
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户数据");
}
@Override
public void delete() {
System.out.println("删除用户数据");
}
@Override
public void update() {
System.out.println("更改用户数据");
}
@Override
public void select() {
System.out.println("查询用户数据");
}
}
// 代理类2,在执行被代理类的每个方法前开启事务,执行后提交事务
public class UserStaticProxy implements UserService {
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userService = userService;
}
@Override
public void save() {
System.out.println("open transaction");
userService.save();
System.out.println("commit transaction");
}
@Override
public void delete() {
System.out.println("open transaction");
userService.delete();
System.out.println("commit transaction");
}
@Override
public void update() {
System.out.println("open transaction");
userService.update();
System.out.println("commit transaction");
}
@Override
public void select() {
System.out.println("open transaction");
userService.select();
System.out.println("commit transaction");
}
}
// 客户端
public class Client01 {
public static void main(String[] args) {
// UserService userService = new UserServiceImpl(); // 使用代理类前的方式
UserService userService = new UserStaticProxy(new UserServiceImpl()); // 使用代理类后(下面的代码从来不变)
userService.save();
userService.delete();
userService.update();
userService.select();
/*
open transaction
保存用户数据
commit transaction
open transaction
删除用户数据
commit transaction
open transaction
更改用户数据
commit transaction
open transaction
查询用户数据
commit transaction
*/
// OrderService orderService = new OrderServiceImpl(); // 使用代理类前的方式
OrderService orderService = new OrderStaticProxy(new OrderServiceImpl()); // 使用代理类后(下面的代码从来不变)
orderService.save();
orderService.delete();
orderService.update();
orderService.select();
/*
open transaction
保存订单数据
commit transaction
open transaction
删除订单数据
commit transaction
open transaction
更改订单数据
commit transaction
open transaction
查询订单数据
commit transaction
*/
}
}
总结
从演示代码中可以看到,被代理类有两个,一是操作用户数据,二是操作订单数据,代理类也有两个,但是这两个代理类的代码基本一样,类中的各个方法也是重复代码。这个例子说明了特点中的第2点和第3点,如果操作数据库的类有100个,那酸爽。。。
如果直接把代理类中的增强代码放到被代理类中,则不符合开闭原则,而且每个类的每个方法都要人为地做事务的开启、提交和回滚操作,难免出现遗漏,从而产生bug
2. jdk动态代理
使用jdk反射机制实现动态生成代理类
Spring大量使用动态代理,当被代理类有实现接口时,就用jdk动态代理方式,否则用cglib实现代理
特点
- 需要实现接口,有一定的限制
处理类生成代理类时,需要被代理类的接口作为参数:Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- 为一系列类增强同一功能时,只需要一个处理类,大大减少类的编写和重复代码,降低了维护的复杂性和成本
"open transaction"和"commit transaction"只出现一次
- 学习成本较静态代理高
代码演示
// 接口1-用户
public interface UserService {
void save();
void delete();
void update();
void select();
}
// 被代理类1-操作用户
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户数据");
}
@Override
public void delete() {
System.out.println("删除用户数据");
}
@Override
public void update() {
System.out.println("更改用户数据");
}
@Override
public void select() {
System.out.println("查询用户数据");
}
}
// 接口2-订单
public interface OrderService {
void save();
void delete();
void update();
void select();
}
// 被代理类2-操作订单
public class OrderServiceImpl implements OrderService {
@Override
public void save() {
System.out.println("保存订单数据");
}
@Override
public void delete() {
System.out.println("删除订单数据");
}
@Override
public void update() {
System.out.println("更改订单数据");
}
@Override
public void select() {
System.out.println("查询订单数据");
}
}
// 代理类处理类(动态生成代理类、增加同一的新功能)
public class ProxyHandler implements InvocationHandler {
private Object obj;
public Object newProxyInstance(Object realObj) {
this.obj = realObj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("open transaction"); // 新功能
method.invoke(obj, args); // 执行被代理类方法
System.out.println("commit transaction"); // 新功能
return null;
}
}
// 客户端
public class Client02 {
public static void main(String[] args) {
ProxyHandler proxyHandler = new ProxyHandler();
UserService userService = (UserService) proxyHandler.newProxyInstance(new UserServiceImpl());
userService.save();
userService.delete();
userService.update();
userService.select();
/*
open transaction
保存用户数据
commit transaction
open transaction
删除用户数据
commit transaction
open transaction
更改用户数据
commit transaction
open transaction
查询用户数据
commit transaction
*/
OrderService orderService = (OrderService) proxyHandler.newProxyInstance(new OrderServiceImpl());
orderService.save();
orderService.delete();
orderService.update();
orderService.select();
/*
open transaction
保存订单数据
commit transaction
open transaction
删除订单数据
commit transaction
open transaction
更改订单数据
commit transaction
open transaction
查询订单数据
commit transaction
*/
}
}
总结
只需要一个处理类,就可以实现所有操作数据库的类的代理了(增加事务操作)
3. cglib动态代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展
特点
- 不需要实现接口,适用性更广
- 需要引入第三方jar包(asm、cglib),其实Spring的核心包中已经包括了Cglib功能相关jar包
- Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类
- 代理类是被代理类的子类,重写父类方法实现功能增强,所以被代理类的方法不能使用final修饰
如果方法为final或static修饰时,则不会这个方法进行增强,但也不会报错,保留原功能
- 使用起来与jdk动态代理差不多,也是一个处理类生产一系列的代理对象
代码演示
// 被代理类1-操作用户
public class UserServiceImpl {
public void save() {
System.out.println("保存用户数据");
}
public void delete() {
System.out.println("删除用户数据");
}
public void update() {
System.out.println("更改用户数据");
}
public void select() {
System.out.println("查询用户数据");
}
}
// 被代理类2-操作订单
public class OrderServiceImpl {
public void save() {
System.out.println("保存订单数据");
}
public void delete() {
System.out.println("删除订单数据");
}
public void update() {
System.out.println("更改订单数据");
}
public void select() {
System.out.println("查询订单数据");
}
}
// 代理类处理类(动态生成代理类、增加同一的新功能)
public class CglibProxyHandler implements MethodInterceptor {
public Object newProxyInstance(Object target)
{
//Cglib中的加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(target.getClass());
//设置回调,这里相当于是对于代理类上所有方法的调用,都会调用Callback,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
{
System.out.println("open transaction");
proxy.invokeSuper(obj, args);
System.out.println("commit transaction");
return null;
}
}
// 客户端
public class Client03 {
public static void main(String[] args) {
CglibProxyHandler proxyHandler = new CglibProxyHandler();
UserServiceImpl userService = (UserServiceImpl) proxyHandler.newProxyInstance(new UserServiceImpl());
userService.save();
userService.delete();
userService.update();
userService.select();
/*
open transaction
保存用户数据
commit transaction
open transaction
删除用户数据
commit transaction
open transaction
更改用户数据
commit transaction
open transaction
查询用户数据
commit transaction
*/
OrderServiceImpl orderService = (OrderServiceImpl) proxyHandler.newProxyInstance(new OrderServiceImpl());
orderService.save();
orderService.delete();
orderService.update();
orderService.select();
/*
open transaction
保存订单数据
commit transaction
open transaction
删除订单数据
commit transaction
open transaction
更改订单数据
commit transaction
open transaction
查询订单数据
commit transaction
*/
}
}
总结
两个被代理类并没有实现接口,动态生成的代理类,其实就是被代理类的子类,所以可以用被代理类的引用来接口代理类