zoukankan      html  css  js  c++  java
  • 动态代理

    代理的作用

    就是在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制是否使用真实对象。

    代理的步骤

    1.代理对象和真实对象建立代理关系

    2.实现代理对象的代理逻辑方法

    代理的技术

    Spring常用JDK和CGLIB,MyBatis还使用了Javassist,理念都是相似的。

    JDK动态代理中必须使用接口,而CGLIB不需要。

    具体实现案例

    一.jdk动态代理

    1.定义接口

    package test;
    
    public interface HelloWorld {
        public void sayHelloWorld();
    }

    2.接口实现

    package test;
    
    public class HelloWorldImpl implements HelloWorld{
    
        @Override
        public void sayHelloWorld() {
            // TODO Auto-generated method stub
            System.out.println("hello world");
        }
    
    }

    3.代理的两个步骤的实现

    package test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JdkProxyExample implements InvocationHandler{
    
        private Object target=null;
    
        /**
         * 建立代理对象和真实对象之间的逻辑关系,并返回代理对象
         * @param target
         * @return
         */
        public Object bind(Object target) {
            this.target=target;
            //this表示当前对象,它就必须实现invoke方法,但是是当代理对象调用相应方法的时候才会调用
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
        
    
        /**
         * 代理方法逻辑
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // TODO Auto-generated method stub
            System.out.println("进入代理逻辑方法");
            System.out.println("在调度真实对象之前的服务");
            //这行代码是调用真实对象的方法,是通过反射实现的
            Object obj=method.invoke(target, args);
            System.out.println("在调度真实对象之后的服务");
            return null;
        }
        
    }

    4.测试

    package test;
    
    public class testJdkProxy {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            JdkProxyExample jdk=new JdkProxyExample();
            HelloWorld proxy=(HelloWorld)jdk.bind(new HelloWorldImpl());
            //代理对象调用真实方法时就进入了invoke的逻辑里,这一步也是通过Object obj=method.invoke(target, args);来实现
            //这样就实现了在真实方法调用前后可以加一些自己的逻辑去实现某些控制
            proxy.sayHelloWorld();
        }
    
    }

    二.CGLIB动态代理

    1.定义类

    package test;
    
    public class ReflectServiceImpl {
        public void sayHello(String name) {
            System.out.println("hello"+name);
        }
    }

    2.CGLIB动态代理

    package test;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    //要下载CGLIB的jar包
    public class CglibProxyExample implements MethodInterceptor{
    
        public Object getProxy(Class cls) {
            //CGLIB enhancer增强类对象
            Enhancer enhancer=new Enhancer();
            //设置增强类型 代理类继承目标类
            enhancer.setSuperclass(cls);
            //this定义代理逻辑对象为当前对象,跟jdk的this作用一样,要实现MethodInterceptor方法
            enhancer.setCallback(this);
            //生成并返回代理对象
            return enhancer.create();
        }
        /**
         * 代理逻辑方法
         */
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            // TODO Auto-generated method stub
            System.out.println("调用真实对象前");
            //CGLIB同样是利用反射调用真实方法
            Object result=methodProxy.invokeSuper(proxy, args);
            System.out.println("调用真实对象后");
            return result;
        }
    
    }

    3.测试

    package test;
    
    public class testCglibProxy {
        public static void main(String[] args) {
            CglibProxyExample cpe=new CglibProxyExample();
            ReflectServiceImpl obj=(ReflectServiceImpl)cpe.getProxy(ReflectServiceImpl.class);
            obj.sayHello("张三");    
        }    
    }

    结果报错。。。

    Exception in thread "main" java.lang.NoClassDefFoundError: 
    org/objectweb/asm/Type
    
        at net.sf.cglib.core.TypeUtils.parseType(TypeUtils.java:184)
        at net.sf.cglib.core.KeyFactory.<clinit>(KeyFactory.java:72)
        at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:72)
        at test.CglibProxyExample.getProxy(CglibProxyExample.java:12)
        at test.testCglibProxy.main(testCglibProxy.java:6)
    Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 5 more

    报错显示有找不到的类,上网查了以下知道了很多java字节码操作和分析的第三方类库都引用了asm.jar文件,由于工程不是Maven管理的,无法解决以来传递问题,所以要手动引入asm.jar文件。把asm.jar文件添加到项目路径类,运行,然后就正常了。

    JDK动态代理必须要目标类要实现某一个或几个接口,假如我们的类没有实现接口怎么啊?这就日了狗了,于是就有了CGLib动态代理,这种代理方式刚好弥补了JDK动态代理的缺陷,其实就是生成一个目标类的子类,这个子类就是我们需要的代理类,重写一下父类的所有方法,那么代理类所有方法的名字就和目标类一样了,再然后就是反射调用父类的方法。

    参考:https://www.cnblogs.com/bigmonkeys/p/7823268.html

  • 相关阅读:
    缓存地图 ArcGIS ——Local compact and exploded tile cache layer for WPF API
    ArcGIS Runtime数据制作教程
    ArcGis 的测距
    图片展示上移效果
    积累
    html/css 图片展示效果
    javascript 回到顶部效果的实现
    javascript实现下拉菜单的显示与隐藏
    html/css 实现下拉菜单效果
    信息排列效果
  • 原文地址:https://www.cnblogs.com/xc-xinxue/p/12369975.html
Copyright © 2011-2022 走看看