概念
他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。
静态代理与动态代理的区别
静态代理在生成Jar包的时候class文件就确定下来了。
动态代理基于反射等技术,是在程序运行过程中动态生成的。
两种动态代理方式
- JDK内提供的动态代理
- cglib方式实现的动态代理
JDK动态代理实现方式与原理
JDK动态代理是基于反射去做的,在开发的时候要实现一个InvocationHandler
接口,并重写其中的invoke()
方法。
实现方式
使用的时候通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
拿到代理后的对象。
在这过程中如果已有代理类,可以直接获取到,如果没有,则会调用工具类生成一个动态代理类。
在动态代理类中,重写了目标类的方法,改为h.invoke()
去调用(这里的h就是我们自己实现的那个类)。
JDK动态代理与Cglib动态代理的区别
- JDK动态代理基于反射实现,Cglib通过继承实现,所以如果一个类不是接口的实现类,无法通过JDK方式去代理;如果一个方法是被
final
修饰的,则无法通过Cglib去代理。 - 通过Cglib方式生成代理类的速度会比JDK快一点,但是调用速度要慢于通过JDK方式实现的代理类。
- 因为第二点,所以Cglib适用于单例模式的场景。
代码
代码示例中包含以下:
- 接口类
HelloService
- 接口类的实现类
HelloServiceImpl
- 通过JDK去实现的动态代理
JDKDynamicProxy
- 通过Cglib实现的动态代理
CglibProxy
- 测试方法
接口类HelloService
package com.example.dynamicproxy.service;
import java.util.List;
/**
* HelloService
*
* @author mawengang
* @date 2021/02/02 15:00
*/
public interface HelloService {
Integer testInteger(Integer integer);
int testInt(Integer integer);
String testString(String s);
List<String> testList(List<String> list);
}
接口类的实现类HelloServiceImpl
package com.example.dynamicproxy.service.impl;
import com.example.dynamicproxy.service.HelloService;
import java.util.List;
/**
* HelloServiceImpl
*
* @author mawengang
* @date 2021/02/02 15:02
*/
public class HelloServiceImpl implements HelloService {
@Override
public Integer testInteger(Integer integer) {
System.out.println("实现类 testInteger " + integer.toString());
return integer;
}
@Override
public final int testInt(Integer integer) {
System.out.println("实现类 testInt " + integer.toString());
return integer == null ? 0 : integer;
}
@Override
public String testString(String s) {
System.out.println("实现类 testString " + s);
return s;
}
@Override
public List<String> testList(List<String> list) {
System.out.println("实现类 testList " + list.size());
return list;
}
}
JDK实现的动态代理JDKProxy
package com.example.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDKProxy
*
* @author mawengang
* @date 2021/02/02 15:04
*/
public class JDKProxy implements InvocationHandler {
private Object target; //添加被代理类引用
public JDKProxy(Object target) {
this.target = target;
}
public Object getProxy() {
ClassLoader loader = this.getClass().getClassLoader();
//类可以实现多个接口,因此这里的接口是个数组
Class<?>[] interfaces = target.getClass().getInterfaces();
//this即JDKProxy实例,其包含被代理类的引用,以及重写的方法,newProxyInstance方法将利用这些参数创建一个代理类的实例
return Proxy.newProxyInstance(loader, interfaces, this);
}
/**
* 通过下面这种方法,就不用强转了。
* @param <T>
* @return
*/
public <T> T getProxy2() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK 代理 " + method.getName());
return method.invoke(target, args);
}
}
Cglib实现的动态代理CglibProxy
package com.example.dynamicproxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CglibProxy
*
* @author mawengang
* @date 2021/02/02 15:05
*/
public class CglibProxy implements MethodInterceptor {
private Class proxied;
public CglibProxy(Class proxied) {
this.proxied = proxied;
}
public Object getProxy(){
//cglib 中增强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(proxied);
//设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(this);
//创建代理类
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {
System.out.println("CglibProxy " + method.getName());
return methodProxy.invokeSuper(object, args);
// return methodProxy.invokeSuper(object, args);
}
}
测试方法
package com.example.dynamicproxy;
import com.example.dynamicproxy.service.HelloService;
import com.example.dynamicproxy.service.impl.HelloServiceImpl;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
import java.util.List;
/**
* CglibProxyTest
*
* @author mawengang
* @date 2021/02/02 15:07
*/
public class Test {
public static void main(String[] args) {
// 实现类
System.out.println("Cgilib ===========");
CglibProxy cglibProxy = new CglibProxy(HelloServiceImpl.class);
HelloServiceImpl helloServiceCg = (HelloServiceImpl)cglibProxy.getProxy();
helloServiceCg.testInt(1);
helloServiceCg.testInteger(11);
// 传入实现类, 强转接口类
JDKProxy jdkProxy = new JDKProxy(new HelloServiceImpl());
HelloService helloService1 = (HelloService) jdkProxy.getProxy();
// 或者这样
HelloService helloService2 = jdkProxy.getProxy2();
System.out.println("JDKProxy1 ===========");
helloService1.testInt(2);
helloService1.testInteger(22);
System.out.println("JDKProxy2 ===========");
helloService2.testInt(3);
helloService2.testInteger(33);
}
}