动态代理与静态代理

你的名字 2022-02-23 12:15 363阅读 0赞

动态代理与静态代理

    • 静态代理—-硬编码
    • 动态代理
      • JDK动态代理—-反射、Proxy、InvocationHandler
      • CGLIB动态代理—-修改字节码,生成新的子类Class

静态代理—硬编码

没啥好说的,就是像装饰模式一样

  1. public class StaticProxy extends Object{
  2. Object object;
  3. public StaticProxy(Object object) {
  4. this.object = object;
  5. }
  6. @Override
  7. public int hashCode() {
  8. System.out.println("proxy decorate start...");
  9. int hashCode = object.hashCode();
  10. System.out.println("proxy decorate end...");
  11. return hashCode;
  12. }
  13. public static void main(String[] args) {
  14. Object proxy = new StaticProxy(new Object());
  15. System.out.println(proxy.hashCode());
  16. }
  17. }

动态代理

JDK动态代理—反射、Proxy、InvocationHandler

jdk动态代理要求对象必须实现接口
并且仅会代理接口方法以及equals,hashCode和toString三个Object类的方法
生成的代理类也仅是实现这些接口和继承Proxy所以接口里没有的方法在代理对象里不存在

  1. public class DynamicProxy {
  2. static class BAInvocationHandler implements InvocationHandler{
  3. private Object realObj;
  4. public BAInvocationHandler(Object realObj) {
  5. this.realObj = realObj;
  6. }
  7. public void doBefore() {
  8. System.out.println("before ...");
  9. }
  10. public void doAfter() {
  11. System.out.println("after ...");
  12. }
  13. @Override
  14. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  15. //proxy为代理对象,所以此处不能调用proxy的方法否则会栈溢出死锁
  16. //proxy是在newProxyInstance时生成并在调用时传进来的,因为invocationHandler与Proxy是聚合模式
  17. //所以在invocation中获取不到代理对象,只能通过这种方式传递进来
  18. doBefore();
  19. Object result = method.invoke(realObj, args);
  20. System.out.println("invoke result :"+result);
  21. doAfter();
  22. return result;
  23. }
  24. }
  25. static <T>T getInstance(Object realObj,Class<T>... clazz) {
  26. //第一个T泛型方法
  27. //clazz 该代理类实现的接口
  28. return (T)Proxy.newProxyInstance(clazz[0].getClassLoader(), clazz,new BAInvocationHandler(realObj));
  29. }
  30. public static void main(String[] args) {
  31. Object o = getInstance(new Object(),Serializable.class);
  32. o.hashCode();
  33. System.out.println(o instanceof Proxy);
  34. }
  35. }

由于jdk动态代理基于接口的特性,使得其只能代理接口对象的方法,也就是必须实现接口
至于为什么,我个人的理解是在运行时
1.其要继承Proxy,因为Proxy封装了动态代理的底层实现,所以Proxy不能是接口,因此只能通过继承来组合(通过聚合或者组合还是离不开要修改字节码)
2.jdk反射只能得到每一个class,interface等的field的名称,返回值类型,形参类型,方法体是得不到的,
否则就得通过读取修改字节码来生成新的class的字节码文件(cglib就是这么做的),而读取解析修改字节码效率肯定低一点
因此jdk代理只代理接口对象,实际需要一个原对象,再生成一个代理对象,使用的是代理对象
注意是对象因为原对象一些在接口中不存在的属性在代理对象中是不存在的,这也是需要getter、setter存在的一个原因

CGLIB动态代理—修改字节码,生成新的子类Class

当面对特殊的场景,比如就是没有实现任何接口,或者希望获取到所有属性,得到一个子类型的类型,就需要通过cglib了
cglib的做法是,通过读取修改字节码,创建一个继承了原有class的子类,所以cglib是代理类,代理方法,而不是代理对象
cglib会代理方法中所有不是final类型的非静态方法方法(因为静态方法不会使用代理对象和代理类)
使用时是直接使用代理类去创建对象,只有一个对象,因此效率较jdk代理低一点
在spring中就是根据要代理的类是否有实现接口,而决定使用cglib还是jdk动态代理

  1. public class CGLIBDynamicProxy {
  2. static class Test{
  3. public final void finalMethod() {
  4. System.out.println("final");
  5. }
  6. public void method() {
  7. System.out.println("method");
  8. }
  9. public static void staticMethod(){
  10. System.out.println("staticMethod...");
  11. }
  12. }
  13. static class BAInterceptor implements MethodInterceptor{
  14. public void doBefore() {
  15. System.out.println("\nbefore ...");
  16. }
  17. public void doAfter() {
  18. System.out.println("after ...\n");
  19. }
  20. @Override
  21. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  22. //proxy---代理类对象
  23. //method---代理后的方法
  24. //args---参数
  25. //methodProxy---方法的代理对象
  26. //不能直接使用 method.invoke,因为会再调用invoke导致死锁stackOverFlow
  27. doBefore();
  28. //使用父类方法,也就是原类方法获取返回值
  29. Object result = methodProxy.invokeSuper(proxy, args);
  30. System.out.println("invoke super result..."+result);
  31. doAfter();
  32. return result;
  33. }
  34. }
  35. static <T> T getInstance(Class<T> clazz) {
  36. //第一个T泛型方法
  37. Enhancer enhancer = new Enhancer();
  38. enhancer.setSuperclass(clazz);
  39. enhancer.setCallback(new BAInterceptor());
  40. return (T) enhancer.create();
  41. }
  42. public static void main(String[] args) {
  43. Test test = getInstance(Test.class);
  44. test.hashCode();
  45. test.finalMethod();
  46. test.method();
  47. test.staticMethod();
  48. }
  49. }

更多文章,请搜索公众号歪歪梯Club
更多资料,请搜索公众号编程宝可梦

发表评论

表情:
评论列表 (有 0 条评论,363人围观)

还没有评论,来说两句吧...

相关阅读

    相关 静态代理动态代理

               记得刚接触代理,是在大话设计模式,最近在技术总结和学些java的过程又接触到静态代理和动态代理,尤其是动态代理,在学习AOP时,用到了动态代理,下面我用