给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:
- 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
- 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件
参考资料:
- 深入理解Java动态代理 - 知乎
- 【动态代理】InvocationHandler_点不点外卖的博客-CSDN博客_invocationhandler
静态代理模式很容易理解.即代理类与被代理类都需要实现同一个接口cuiyaonan2000@163.com
package cui.yao.nan.optional;
import java.util.Optional;
import java.util.Random;
/**
* @Author: cuiyaonan2000@163.com
* @Description: todo
* @Date: Created at 2022-1-12 14:46
*/
public class Test {
public interface Vehicle{
public void run();
}
public class Car implements Vehicle{
private String name;
@Override
public void run() {
System.out.println(" my name is :" + name);
}
public Car(String name) {
this.name = name;
}
}
public class CarProxy implements Vehicle{
private Car car;
@Override
public void run() {
car.run();
}
public CarProxy(String name) {
this.car = new Car(name);
}
}
public static void main(String[] args){
Vehicle vehicle = new Test().new CarProxy("三轮车");
vehicle.run();
}
}
动态代理
java根据代理类的实现主要区分如下的两种方式:
- 通过实现接口的方式 : JDK动态代理
- 通过继承类的方式 :CGLIB动态代理
被代理类需要实现业务接口,代理类需要实现接口InvocationHandler. 然后通过Proxy来生成代理类
InvocationHandler该接口在反射包下.且只有一个接口方法.该方法就是用于对目标对象的方法进行增强的.
/*
* Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.lang.reflect;
/**
* {@code InvocationHandler} is the interface implemented by
* the invocation handler of a proxy instance.
*
* Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
*
* @author Peter Jones
* @see Proxy
* @since 1.3
*/
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Proxy
同理也是在反射包中(java.lang.reflect)
该类是用于动态生成代理类(注意这的动态生成,是在项目启动的时候生成一个class类cuiyaonan2000@163.com)
具体使用如下的方法来生成一个代理类,生成的代理类可以保存在本地哦~~~~~
//在生成代理类的环境中设置如下的配置
//则会将生成的代理类保存在,本地名字默认名称以 $Proxy0 格式命名
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
或者使用代码保存
package cui.yao.nan.optional;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author: cuiyaonan2000@163.com
* @Description: todo
* @Date: Created at 2022-5-24 9:24
*/
public class Test2 {
public interface Vehicle{
public void run();
}
public class Car implements Vehicle{
public void run(){
System.out.println("i am a car");
}
}
public class ProxyHandle implements InvocationHandler{
private Object object;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy:"+proxy.getClass().getName());
System.out.println("method:"+method.getName());
// System.out.println("args:"+args[0].getClass().getName());
System.out.println("Before invoke method...");
Object result=method.invoke(this.object,args);
System.out.println("After invoke method...");
return result;
}
public ProxyHandle(Object object) {
this.object = object;
}
}
public static void main(String[] args){
Car car = new Test2().new Car();
InvocationHandler invocationHandler = new Test2().new ProxyHandle(car);
Vehicle vechicle = (Vehicle) Proxy.newProxyInstance(car.getClass().getClassLoader(),car.getClass().getInterfaces(),invocationHandler);
vechicle.run();
// 根据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass( "jjj", new Class[]{car.getClass()});
String paths = car.getClass().getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
//保留到硬盘中
out = new FileOutputStream(paths + "jjj" + ".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch ( IOException e) {
e.printStackTrace();
}
}
}
}
CGLIB动态代理
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
CGLIB动态代理就是对指定的类生成一个子类,覆盖其中所有的方法并环绕增强.所以被代理类的方法如果是final 修饰的就不能进行代理cuiyaonan2000@163.com
Spring已经集成了CGLIB的相关库,所以引入如下的即可
org.springframework
spring-core
实例
package cui.yao.nan.optional;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author: cuiyaonan2000@163.com
* @Description: todo
* @Date: Created at 2022-5-24 9:24
*/
public class Test2 implements MethodInterceptor{
public Test2() {
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before you method running");
return method.invoke(o,objects);
}
public class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before you method running");
return method.invoke(o,objects);
}
}
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before you method running");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after you method running");
return result;
}
});
Car car =(Car)enhancer.create();
car.run();
}
}