动态代理

代理的作用

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

代理的步骤

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

原文地址:https://www.cnblogs.com/xc-xinxue/p/12369975.html