代理模式
代理模式使用代理对象来代替真实对象的访问,在不修改原有对象的前提下,提供额外的操作,扩展目标对象的功能。代理模式分为静态代理和动态代理。
静态代理
手动为目标对象中的方法进行增强,通过实现相同接口重写方法进行增强。非常不灵活,当对象中新增方法时,代理类同样需要增加代理方法。静态代理是在代码编译时生成的代理类
实现步骤
- 定义接口和实现类
- 定义代理类实现接口
- 将被代理类注入,重写方法,在方法中增强
代码展示
-
接口
public interface Image { void display(String name); }
-
实现类
public class RealImage implements Image { @Override public void display(String name) { System.out.println(name + " display"); } }
-
代理类
public class StaticProxy implements Image { private RealImage realImage; @Override public void display(String name) { realImage = new RealImage(); System.out.println("start"); realImage.display(name); System.out.println("end"); } public static void main(String[] args) { Image image = new StaticProxy(); image.display("图片"); } }
静态代理简单并且局限性太高,一般没有人使用
动态代理
相较于静态代理,动态代理更加灵活,可以被多个类创建统一代理类。针对实现类和接口分别有JDK动态代理
和CGLIB动态代理
。动态代理是在运行时动态生成代理类并加载到JVM中去的。
JDK动态代理
JDK动态代理是为已经实现接口的类创建代理类。核心是**InvocationHandler
接口和Proxy
类。Proxy
类用于生成代理对象,InvocationHandler
接口用于自定义处理逻辑。当我们用Proxy类中的newProxyInstance()
方法生成的动态代理对象调用方法时,这个方法的调用会转发到InvocationHandler
接口中的invoke
**方法中来调用。
Proxy
类中使用频率最高的方法是:newProxyInstance()
,这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
这个方法一共有 3 个参数:
- loader :类加载器,用于加载代理对象。
- interfaces : 被代理类实现的一些接口;
- h : 实现了
InvocationHandler
接口的对象;
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
public interface InvocationHandler {
/**
* 当你使用代理对象调用方法的时候实际会调用到这个方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
invoke()
方法有下面三个参数:
- proxy :动态生成的代理类
- method : 与代理类对象调用的方法相对应
- args : 当前 method 方法的参数
实现步骤
- 定义接口和实现类
- 自定义**
InvocationHandler
并重写invoke
**方法,在方法中调用被代理的方法并实现自定义处理逻辑 - 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象 - 使用代理对象进行方法调用
代码展示
-
接口和实现类同上
-
InvocationHandler
/** * @author linmeng * @date 2023/2/22 00:42 */ public class JdkInvocationHandler implements InvocationHandler { /** * 被代理类 **/ private Object target; public JdkInvocationHandler(Object target) { this.target = target; } /** * @Author linmeng * @Description * @date 2023/2/24 15:57 * @param proxy 动态生成的代理类 * @param method 代理方法 * @param args 方法参数 * @return java.lang.Object **/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用方法前打印方法名称;" + method.getName()); Object res = method.invoke(target, args); System.out.println("调用方法后打印方法名称;" + method.getName()); return res; } }
-
代理对象生成
import java.lang.reflect.Proxy; /** * @author linmeng * @date 2023/2/22 00:44 */ public class JDKProxy { public static Object getProxy(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkInvocationHandler(target)); } }
-
测试
import org.junit.Test; /** * @author linmeng * @date 2023/2/22 00:50 */ public class JdkTest { @Test public void jdkTest(){ Image proxy = (Image) JDKProxy.getProxy(new RealImage()); proxy.display("图片"); }
CGLIB动态代理
当类没有实现接口时,是不能用JDK动态代理的。这个时候可以用CGLIB动态代理,他是通过继承的方式实现代理。
**在 CGLIB 动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。**通过实现MethodInterceptor
接口中的intercept
方法自定义处理逻辑,Enhancer
类创建代理类。
你需要自定义 MethodInterceptor
并重写 intercept
方法,intercept
用于拦截增强被代理类的方法。
public interface MethodInterceptor
extends Callback{
// 拦截被代理类中的方法
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}
- obj : 被代理的对象(需要增强的对象)
- method : 被拦截的方法(需要增强的方法)
- args : 方法入参
- proxy : 用于调用原始方法
你可以通过 Enhancer
类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor
中的 intercept
方法。
实现步骤
- 实现类
- 自定义
MethodInterceptor
并重写intercept
方法,自定义增强逻辑 - 通过
Enhancer
中的create()
方法创建代理对象 - 使用代理对象调用方法
代码展示
-
实现类
public class RealImage2 { public void display(String name) { System.out.println(name + " display"); } }
-
自定义
MethodInterceptor
并重写intercept
方法/** * @author linmeng * @date 2023/2/24 14:10 */ public class CGLIBInterceptor implements MethodInterceptor { /** * @param o 被代理对象 * @param method 被拦截的方法 * @param objects 方法入参 * @param methodProxy 用于调用元素方法 * @return java.lang.Object * @Author linmeng * @Description * @date 2023/2/24 14:11 **/ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("调用方法前打印方法名称;" + method.getName()); Object res = methodProxy.invokeSuper(o, objects); System.out.println("调用方法后打印方法名称;" + method.getName()); return res; } }
-
通过
Enhancer
中的create()
方法创建代理对象import net.sf.cglib.proxy.Enhancer; /** * @author linmeng * @date 2023/2/24 14:15 */ public class CGLIBProxy { public static Object getProxy(Class<?> clazz) { // 动态代理增强类 Enhancer enhancer = new Enhancer(); // 类加载器 enhancer.setClassLoader(clazz.getClassLoader()); // 被代理类 enhancer.setSuperclass(clazz); // 增强类 enhancer.setCallback(new CGLIBInterceptor()); // 代理类创建 return enhancer.create(); } }
-
使用
import org.junit.Test; /** * @author linmeng * @date 2023/2/22 00:50 */ public class CGLIBTest { @Test public void cglibTest(){ RealImage2 proxy = (RealImage2) CGLIBProxy.getProxy(RealImage2.class); proxy.display("图片"); } }