动态代理与静态代理
静态代理:
程序编写时,以手动编码的方式来对某一个类或者对象的功能进行增强.
代码示例:
public class StaticProxy {
public static void main(String[] args) {
Car car = new Car();
System.out.println("------方法执行前------");
car.run();
System.out.println("------方法执行后------");
}
}
class Car {
public void run() {
System.out.println("run......");
}
}
/**
* 运行结果
* ------动态调整------
* run......
* ------动态调整------
*/
动态代理:
在程序运行过程中动态来增强某些类的功能,并且不需要我们每一次都进行代码编写,增强了我们程序的灵活性.
Java中动态代理的实现方式有两种: JDK动态代理与CGLIB动态代理
一.JDK动态代理
JDK的动态代理是依赖于接口的,如果类没有实现任何接口是无法使用JDK动态代理的,原理在于动态代理时,会创建出一个实现被代理类实现的接口的被代理类的兄弟类的class文件并进行加载创建实例并进行缓存,对同一个类调用多次代理不会每次都创建代理对象.
调用代理对象的方法时,会被invoke方法拦截,执行增强方法,可以在增强方法中调用代理对象方法的invoke方法执行代理类的对应方法.
经过动态代理创建出来的代理对象,都是被 final修饰 继承自Proxy类 并实现被代理类实现的接口
$Proxy后面的数字代表生成代理类的次数
public final class $Proxy0 extends Proxy implements Car
实现代理的代码:
// 创建接口用于动态代理
public interface Car {
void run();
}
// 接口的一个实现类
public class Truck implements Car {
// 品牌
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public void run() {
System.out.println("Car run......");
}
}
public class JDKProxy {
public static void main(String[] args) {
// 创建被代理类对象
Car car = new Truck();
/**
* 参数介绍
* newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)
* loader 使用哪一个类加载器来加载代理类,通常我们使用当前类的加载器即可
* interfaces 被代理类实现了哪些接口,可以通过 被代理类.class.getInterfaces()来获取
* invocationHandler 具体的处理类,具体决定如何执行增强逻辑
*/
Car carProxy = (Car) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), Truck.class.getInterfaces(), new InvocationHandler() {
/**
* 参数介绍
* proxy 代理类本身 一般情况下不会使用,当我们需要在代理逻辑中返回对象本身的时候就可以拿来使用
* method 要增强的方法对象
* args 方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行接口中所有方法都会被此处拦截 可以在此处进行方法功能增强
System.out.println("------方法执行前------");
/**
* 此处表示执行被代理类(car)的方法
* result 是被代理类的返回值 通常我们直接返回 如果需要修改需要注意类型转化异常
*/
Object result = method.invoke(car, args);
System.out.println("------方法执行后------");
return result;
}
});
carProxy.run();
}
}
/**
* 执行结果
* ------方法执行前------
* Car run......
* ------方法执行后------
*/
二.CGLIB动态代理
cglib动态代理不依赖与接口,代理类无论是否实现接口都可以进行代理.cglib不能代理final修饰的方法,因为本质上cglib是创建当前类子类,所以是无法继承到final类型修饰的方法.
cglib使用ASM技术生成代理类的字节码文件,加载到内存创建实例
使用cglib动态代理,需要项目引入cglib的jar包.
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
实现代理的代码:
// 被代理对象
public class Car {
// 品牌
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void run() {
System.out.println("Car run......");
}
}
// 代理逻辑
public class CglibProxy {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
// 设置父类类型
enhancer.setSuperclass(Car.class);
// 设置具体的代理处理方法
enhancer.setCallback(new MethodInterceptor() {
/**
* 参数介绍
* @param obj 被代理类
* @param method 被代理方法
* @param args 方法参数
* @param methodProxy 代理方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("------方法执行前------");
/**
* 不要尝试直接执行被代理类的方法 method.invoke(obj, args) 因为每次调用都会被intercept拦截 会出现递归调用直到栈溢出
*/
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("------方法执行后------");
return result;
}
});
Car carProxy = (Car) enhancer.create();
carProxy.run();
}
}
/**
* 执行结果
* ------方法执行前------
* Car run......
* ------方法执行后------
*/
JDK动态代理与CGLIB动态代理的区别:
1.代理条件
JDK动态代理依赖于被代理类的接口
CGLIB动态代理不需要代理类实现接口
2.代理类的生成
.JDK动态代理通过反射与InvocationHanlder拦截器实现代理类,在调用被代理类的方法前后通过InvokeHandler进行拦截处理.
CGLIB通过ASM修改被代理类的字节码文件生成代理类的字节码文件(代理类的子类),过反射实例化代理类