静态代理与动态代理

187 阅读3分钟

前言

代理(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动态代理的速度快很多了 测试速度链接