概念
按照代理的创建时期,代理类可以分为两种:
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
静态代理
先来看看静态代理的实现吧?
/**
* @ClassName Star
* @Description 公共接口
* @Author lzq
* @Date 2019/8/1 10:39
* @Version 1.0
**/
public interface Star {
public void sell(); //卖商品
}
/**
* @ClassName RealStar
* @Description 真实对象 委托类
* @Author lzq
* @Date 2019/8/1 10:39
* @Version 1.0
**/
public class RealStar implements Star{
@Override
public void sell() {
System.out.println("卖商品");
}
}
/**
* @ClassName StarProxy
* @Description 代理类
* @Author lzq
* @Date 2019/8/1 10:38
* @Version 1.0
**/
public class StarProxy implements Star{
private RealStar realStar = new RealStar();
@Override
public void sell() {
realStar.sell();
}
}
测试代码,我们只需要和委托类交互即可,不必管它的底层实现:
/**
* @ClassName Test
* @Description 测试
* @Author lzq
* @Date 2019/8/1 11:08
* @Version 1.0
**/
public class Test {
public static void main(String[] args) {
StarProxy proxy = new StarProxy();
proxy.sell();
}
}
运行结果:
卖商品
其实,动态代理也是这么个原理,只不过它的创建对象的方式是通过反射来完成的,
动态代理
在Java中,实现动态代理的技术有很多,比如JDK自带的、CGLIB、Javassist、ASM等,其中最常用的就是JDK、CGLIB,下面重点解释这两种;
JDK动态代理
上面我们用静态代理的时候,是定义一个公共接口,然后让委托类和代理类分别去实现这个接口(相当于后面说的,代理对象和真实对象都是挂在在这个公共接口下面的),最后在代理类里面定义一个委托类的对象,通过底层去调用委托类的相应的方法去实现代理逻辑的,在动态代理里面也是一样,也需要一个公共接口,一个已经实现了的委托类,但是不同的是,它的代理类是根据接口、委托类动态生成的;
首先我们定义一个接口HelloWorld:
/**
* @ClassName HelloWorld
* @Description 公共接口
* @Author lzq
* @Date 2019/8/1 11:26
* @Version 1.0
**/
public interface HelloWorld {
public void sayHelloWorld();
}
定义一个委托类实现这个接口:
/**
* @ClassName HelloWorldImpl
* @Description 委托类 真实对象类
* @Author lzq
* @Date 2019/8/1 11:27
* @Version 1.0
**/
public class HelloWorldImpl implements HelloWorld{
@Override
public void sayHelloWorld() {
System.out.println("Hello World");
}
}
在JDK动态代理中,要实现代理逻辑必须去实现java.lang.reflect.InvocationHandler
接口,它里面定义了一个invoke方法,并提供接口数组用于挂在代理对象,下面这个类主要实现动态代理绑定和代理逻辑实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
1. @ClassName JDKProxyExample
2. @Description 动态代理逻辑代码
3. @Author lzq
4. @Date 2019/8/1 11:28
5. @Version 1.0
**/
public class JDKProxyExample implements InvocationHandler {
private Object object = null; //真实对象 委托类对象
/**
* 获取代理对象
* @param o
* @return
*/
public Object bind(Object o) {
this.object = o;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass().getInterfaces(),this);
}
/**
* 代理方法逻辑
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑前的处理...");
System.out.println("调用真实对象前的服务...");
Object obj = method.invoke(object,args);
System.out.println("调用真实对象之后的服务...");
return obj;
}
}
在这个类里面,我们需要:
1、建立代理对象和真实对象之间的关系
在这个类里面,我们是提供bind方法实现的,方法里面首先用类的属性object保存了真实对象,然后通过如下代码建立并生成代理对象:
Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass()
.getInterfaces(),this);
这三个参数的意思是:
- 第一个是类加载器,就是要加载这个代理对象用哪个类加载器加载,这里我们用的是加载真实对象的类的类加载器;
- 第二个是把生成的代理对象下挂在哪些接口下面,这个写法就是放在object实现的接口下,HelloWorldImpl对象的接口显然就是HelloWorld;
- 第三个是定义实现方法逻辑的代理类,this表示当前对象,它必须实现InvocationHandler接口中的invoke方法,它就是代理逻辑方法的实现方法;
2、实现代理逻辑方法
invoke方法可以实现代理逻辑,它的三个参数含义如下:
- proxy,代理对象,就是bind方法生成的对象;
- method,当前调度的方法;
- args,调度方法的参数;
当我们使用代理对象调度方法之后,它就会进入到invoke方法里面:
Object obj = method.invoke(object,args);
//method.invoke(" 要调用的方法的名字所隶属的对象实体",方法的参数值);
这行代码相当于调度真实对象的的方法,只不过是通过发射实现而已。
这些各部分的关系就相当于:
- proxy 相当于商务对象,代理对象的意思;
- object 相当于软件工程师对象,真实对象,委托者;
- bind 这个方法就是建立商务和软件工程师代理关系的方法;
- invoke 这就是商务逻辑,它将控制软件工程师的访问;
测试代码如下:
/**
* @ClassName Test
* @Description 测试代码
* @Author lzq
* @Date 2019/8/1 11:32
* @Version 1.0
**/
public class Test {
public static void main(String[] args) {
JDKProxyExample jdk = new JDKProxyExample();
HelloWorld proxy = (HelloWorld)jdk.bind(new HelloWorldImpl());
proxy.sayHelloWorld();
}
}
那么其实动态代理的好处在哪呢?因为它的代理对象都是通过反射动态生成的,如果需要新的功能,那么只需要在公共接口中添加方法,真实对象实现该方法即可,动态代理绑定和代理逻辑根本就不需要动;
测试代码运行结果:
CGLIB动态代理
JDK动态代理必须提供公共接口才可以使用,在一些不能提供接口的环境中,只能采用其他的第三方技术,比如CGLIB动态代理,它的优势在于不需要提供公共接口,只要一个非抽象类就能实现动态代理;
CGLIB依赖:
<!--CGlib动态代理-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
代码如下:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @ClassName CglibProxyExample
* @Description
* @Author lzq
* @Date 2019/8/1 12:12
* @Version 1.0
**/
public class CglibProxyExample implements MethodInterceptor {
/**
* 生成CGLIB代理对象
* @param cls
* @return
*/
public Object getProxy(Class cls) {
//CGLIB Enhancer 增强类对象
Enhancer enhancer = new Enhancer();
//设置增强类型
enhancer.setSuperclass(cls);
//带你一代理逻辑对象为当前对象,这就要求当前对象实现MethodInterceptor的方法intercept
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create();
}
/**
* 代理逻辑方法
* @param o 代理对象
* @param method 方法
* @param objects 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @throws Throwable 异常
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用真实对象之前的处理逻辑...");
Object result= methodProxy.invokeSuper(o,objects);
System.out.println("调用真实对象之后的处理逻辑");
return result;
}
}
这里用到了CGLIB的加强者类Enhancer,通过设置超类的方法(setSuperclass),然通过setCallback方法设置哪个类为它的代理类,其中,参数this就意味着当前对象,那就要求this这个对象实现接口MethodInterceptor的方法——intercept,然后返回代理对象;
那么此时当前类的intercept方法就是代理逻辑方法,其参数含义见代码注解;
再写一个真实对象类;
/**
* @ClassName Test
* @Description 委托类 真实对象类
* @Author lzq
* @Date 2019/8/1 12:28
* @Version 1.0
**/
public class Test {
public void say(String x) {
System.out.println(x);
}
}
测试代码:
public static void main(String[] args) {
CglibProxyExample cpe = new CglibProxyExample();
Test test = (Test)cpe.getProxy(Test.class);
test.say("你好");
}
运行结果:
动态代理的实现其实都很相似,它们都是用getProxy方法生成代理对象,制定代理的逻辑类,而代理逻辑类要实现一个接口的一个方法,那么这个接口定义的方法就是代理逻辑方法,它可以控制真实对象的方法;
JDK动态代理和CGLIB动态代理的区别:
在JDK的动态代理里面,我们可以看到:
那它生成的代理对象和真实对象是同级的,都是公共接口下的实现类,它们是横向关系;
再来看看CGLIB的:
它的原理是生成一个代理对象来拦截真实对象的方法,但这个代理对象却是真实对象的子类,继承关系,所以在CGLIB里面,代理对象是真实对象的子类,它们是纵向关系;