前言
代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式;即通过代理访问目标对象。
代理代理,就是你能够代表我又能够帮我做一些事情的,像明星的经纪人、车间的包工头一样。常用代理模式来增强方法,达到一些目的。看spring aop模块时对它的代理总有些不解,在此做些记录
静态代理
public interface ProductDao {
void save();
void delete();
void update();
void find();
}
public class ProductImp implements ProductDao {
@Override
public void save() {
System.out.println("保存商品...");
}
@Override
public void delete() {
System.out.println("删除商品...");
}
@Override
public void update() {
System.out.println("更新商品...");
}
@Override
public void find() {
System.out.println("查找商品...");
}
}
/**
* 静态代理类
* 需要实现与被代理对象相同的接口
*/
public class ProductProxy implements ProductDao {
//目标对象,实际去干活的还是你这个类
private ProductDao target;
ProductProxy(ProductDao target){
this.target = target;
}
@Override
public void save() {
System.out.println("开启事务...");
target.save();
System.out.println("关闭事务...");
}
@Override
public void delete() {
System.out.println("开启事务...");
target.save();
System.out.println("关闭事务...");
}
@Override
public void update() {
System.out.println("开启事务...");
target.find();
System.out.println("关闭事务...");
}
@Override
public void find() {
System.out.println("开启事务...");
target.find();
System.out.println("关闭事务...");
}
}
public class Test {
public static void main(String[] args) {
ProductDao productDao = new ProductImp();
ProductProxy proxy = new ProductProxy(productDao);
proxy.save();
}
}
静态代理比较繁琐、而且需要实现与代理的目标对象相同接口、如果需要更改则大家一起改,耦合高
动态代理
JDK动态代理
JDK动态代理就解决了静态代理的缺陷、代理对象不需要实现接口。Java提供了一个Proxy类public interface OrderDao {
void save();
}
public class OrderDaoImpl implements OrderDao {
@Override
public void save() {
System.out.println("保存订单...");
}
}
public class OrderProxy {
//代理干的是包工头的活儿,实际还是得工人上阵搬砖
OrderDao orderDao = new OrderDaoImpl();
//返回代理对象
public OrderDao getProxy() {
/**
* 参数一:代理类的类加载器
* 参数二:被代理对象的接口
* 参数三:InvocationHandler实现类
*/
return (OrderDao) Proxy.newProxyInstance(OrderProxy.class.getClassLoader(), OrderDaoImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("save")) {
System.out.println("事务开始..");
//搬砖的干活儿了
method.invoke(orderDao, args);
System.out.println("提交事务..");
}
return null;
}
});
}
}
public class Test {
public static void main(String[] args) {
//找包工头
OrderProxy proxy = new OrderProxy();
//包工头找搬砖的去干活
OrderDao orderProxy = proxy.getProxy();
//搬砖的干活了
orderProxy.save();
System.out.println(orderProxy.getClass());
}
}
Cglib动态代理
Cglib是第三方的库、但是spring的核心包中已经包括了cglib功能、Cglib的原理是在内存中构建子类来做扩展,继承的话所以被代理对象不能未final修改,方法不能是private修饰public class UserDao {
public void save() {
System.out.println("保存用户...");
}
}
//需要实现cglib的MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor {
// 维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象创建代理对象
public Object getProxyInstance() {
//1. 工具类
Enhancer en = new Enhancer();
//2. 设置父类
en.setSuperclass(target.getClass());
//3. 设置回调函数
en.setCallback(this);
//4. 创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始事务.....");
// 执行目标对象的方法
Object returnValue = method.invoke(target, objects);
System.out.println("提交事务.....");
return returnValue;
}
}
public class Test {
public static void main(String[] args) {
//UserDAO未实现接口
UserDao userDao = new UserDao();
System.out.println(userDao.getClass());
//获取代理对象
UserDao userDaoProxy = (UserDao) new ProxyFactory(userDao).getProxyInstance();
System.out.println(userDaoProxy.getClass());
userDaoProxy.save();
}
}
比较起来大致是这样
- 静态代理:缺点是繁琐、代码耦合、代理类需要与被代理对象一样实现接口
- JDK动态代理:使用JDK动态代理后,代理类可以不实现接口但是被代理对象需要实现接口
- Cglib动态代理:被代理对象可以不实现接口、弥补JDK动态代理的不足
SpringAop的动态代理
SpringAop的动态代理模式有几种?默认是哪种?如何切换?哪种方式快?代理模式:
两种:JDK动态代理、Cglib动态代理
默认情况下:
如果spring的被代理对象实现了接口,它采用jdk的动态代理方式,通俗的理解这个动态代理类是目标对象的另外一个版本。
如果未实现接口,采用的是cglib的动态代理、其生成的动态代理对象是目标类的子类。
如何切换?
使用AspectJ的话强制开启cglib:<aop:aspectj-autoproxy proxy-target-class="true">
哪种快?
JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了 测试速度链接