概念

  • 不改变原有代码的情况下,对方法进行增强。
  • 通过代理控制对象的访问,可在调用这个对象的方法前后扩展功能。
  • 隔离真实对象,开闭原则(对修改封闭,对扩展开放)

    场景

  • Spring AOP 、日志、异常、事务、权限。

    实现

  1. 定义接口和方法。
  2. 真实对象实现接口(非必须)
  3. 创建代理类,实现接口,并内部持有真是对象(private User user)。
  4. 动态代理是运行中自动生成,不用手动创建。
  5. 静态代理需要为每个代理对象创建一个代理类。

    分类

  6. 静态

  7. JDK动态
  8. cglib动态
  9. javaassist字节码操作库

    静态代理

  • 自己创建代理类,再编译代理类 ```java //没有代理类 //目标对象 public class UserDao{ public void save(){
    1. sout("save");
    } } //测试类 public class t{ public static void main(String[] args){
    1. UserDao u = new UserDao();
    2. u.save();
    } } //添加代理类 public ProxyUserDao extend UserDao{ private UserDao u; //定义目标类 //构造方法传入目标类 private ProxyUserDao(UserDao u){
    1. this.u = u;
    } //方法增强 public void save(){
    1. sout("事务、日志...");
    2. u.save();
    3. sout("事务、日志...");
    } } //静态代理测试 public void tt{ public static void main(String[] args){
    1. UserDao u = new UserDao();
    2. ProxyUserDao p = new ProxyUserDao(u);
    3. p.save();
    }

}

  1. <a name="v0ZBe"></a>
  2. #### 动态代理
  3. > 动态代理利用的是**反射技术**
  4. - JDK动态基于接口必须实现接口,目标业务必须实现接口
  5. > 涉及的两大核心类是Proxy类和InvocationHandler接口,重写invoke()方法;用Proxy.newProxyInstance(loader, interfaces,h);
  6. ```java
  7. //JDK动态代理
  8. //接口
  9. public interface UserDao{
  10. void save();
  11. }
  12. //接口类实现类
  13. public class UserDapImpl implements UserDao{
  14. public void save(){
  15. sout("save")
  16. }
  17. }
  18. //代理类
  19. //每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
  20. public class InvocationHandlerImpl implements InvocationHandler{
  21. //业务实现类对象,调用具体的业务方法
  22. private Object target;
  23. //构造函数传入目标对象。
  24. public InvocationHandlerImpl( Object target){
  25. this.target = target;
  26. }
  27. //动态代理实际运行的代理方法
  28. public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
  29. sout("调用开始");
  30. //invoke()以反射方式创建对象,target要创建的对象,args构成方法的参数,由args决定创建对象使用哪个构造方法。
  31. Object result = method.invoke(target,args);
  32. sout("调用结束");
  33. return result;
  34. }
  35. }
  36. //利用动态代理使用代理方法
  37. public class t{
  38. public ststic void main(String[] args){
  39. //被代理对象
  40. UserDao u = new UserDaoImpl();
  41. InvocationHandlerImpl invo = new InvocationHandlerImpl(u);
  42. //类加载器
  43. ClassLoader loader = u.getClass().getClassLoader();
  44. Class<?>[] interf = u.getClass().getInterfaces();
  45. //主要装载器、一组接口及调用处理器动态代理实例
  46. UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader,interf,invo);
  47. newProxyInstance.save();
  48. }
  49. }
  • Cglib动态基于子类,创建目标对象的子类
  • 不用实现接口,可直接代理目标,底层时字节码,实现目标子类。final不能。 ```java //Cglib动态代理 //接口 public interface UserDao{ void save(); } //接口实现类—目标对象 public class UserDaoImpl implements UserDao{ public void save(){
      sout("save");
    
    } }

//代理主要类 public class CglibProxy implements MethodInterceptor{ private Object targetObject;

//这里的目标类型为Object,可接收任意参数作为被代理类。
public Object getInstance(Object target){
    //设置需要创建子类的类
    this.targetObject = target;
    Enhancer enh = new Enhancer();
    enh.setSuperclass(target,getClass());
    enh.setCallback(this);
    return enh.creater();
}

//代理实际方法
public Object intercept(Object obj, Method method,Object[] args,MethodProxy proxy) throws Throwable{
    sout("开启事务");
    Object result = proxy.invoke(targetObject,args);
    sout("关闭事务");

    //返回代理对象
    return result;
}

}

//测试Cglib动态代理 public class t{ public static void main(String[] args){ CglibProxy cg = new CglibProxy(); UserDao u = (UserDao) c.getinstance(new UserDaoImpl()); u.save(); } } ```

jdk动态代理 VS cglib

JDK Proxy 的优势:

  • 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
  • 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。
  • 代码实现简单。
    cglib 优势:
  • 有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。
  • 只操作我们关心的类,而不必为其他相关类增加工作量。
    总结
  1. 代理模式: 为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问。
  2. jdk动态代理生成的代理类继承了Proxy类并实现了被代理的接口;而cglib生成的代理类则仅继承了Proxy类。
  3. jdk动态代理最大缺点:只能代理接口,既委托类必须实现相应的接口
  4. cglib缺点:由于是通过“子类化”的方式, 所以不能代理final的委托类或者普通委托类的final修饰的方法。

参考