代理模式

Meteor 2023-10-31 17:14:32
Categories: Tags:

简介

代理模式,为其它对象提供一种代理以控制对这个对象的访问

代理本质上就是对对象功能的增强,比如需要统计方法的执行时间或者观察方法的执行流程,那就可以利用代理的方式在执行方法的前后加上统计时间的代码或者日志,或者隐藏对真实对象的访问,对外只提供代理对象

代理分为静态代理和动态代理,其中动态代理较为常用,静态代理基本不用

源码:proxy

静态代理

静态代理的实现较为简单,首先定义一个目标类和接口,让目标类实现接口,然后让代理类和目标类实现一样的接口,将目标类通过组合的方式添加到代理类中,然后重写接口中的方法,在执行那个目标类的方法前后添加自定义的方法

静态代理类如下:

StaticProxy

package com.meteor.design.static_proxy;

import com.meteor.design.service.IUserService;

public class StaticProxy implements IUserService {
private IUserService userService;

// 通过组合的方式将目标类添加到代理类中
public StaticProxy(IUserService userService){
this.userService = userService;
}

// 重写方法,在执行目标类方法前后添加自定义操作
@Override
public void queryUserInfo() {
before();
userService.queryUserInfo();
after();
}

private void before(){
System.out.println("执行方法前的操作");
}

private void after(){
System.out.println("执行方法后的操作");
}
}

测试

public class AppTest 
{
@Test
public void testStaticProxy(){
StaticProxy userServiceProxy = new StaticProxy(new UserService());
userServiceProxy.queryUserInfo();
}
}

输出结果

执行方法前的操作
查询到用户信息
执行方法后的操作

Process finished with exit code 0

静态代理的缺点显而易见,每一个目标类都需要手动写一个代理类,接口新增方法时代理类也要做相应的修改,因此几乎不会使用

动态代理

动态代理和静态代理的区别在于动态代理是在运行时动态生成类字节码加载到 JVM 中的,而静态代理是在编译期就已经确定了有哪些类。简而言之,动态代理会自动生成代理类,而静态代理需要自定义代理类。

下面详细介绍两种动态代理的方式,当然,动态代理不只两种

JDK动态代理

JDK动态代理依赖于 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现,使用JDK动态代理的过程如下:

定义代理类实现 InvocationHandler 接口并重写 invoke() 方法,在 invoke() 方法中执行用户方法前后添加自定义操作,最后在代理工厂类中通过反射提供代理类。

代码如下:

MyInvocationHandler

package com.meteor.design.dynamic_proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
private final Object target;

// 通过组合的方式将目标类添加到代理类中
public MyInvocationHandler(Object target){
this.target = target;
}

// 重写方法,在执行目标类方法前后添加自定义操作
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object res = method.invoke(target, args);
after();
return res;
}

private void before(){
System.out.println("执行方法前的操作");
}

private void after(){
System.out.println("执行方法后的操作");
}
}

定义自己的 InvocationHandler 类,并重写 invoke() 方法,将原始对象通过组合的方式传进来,先执行 before() 方法,再执行原始对象的方法并将结果保存起来,然后执行 after() 方法,最后将结果返回

JDKProxyFactory

package com.meteor.design.dynamic_proxy.jdk;

import java.lang.reflect.Proxy;

public class JDKProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyInvocationHandler(target));
}
}

定义代理工厂类,通过反射提供代理对象,主要是调用Proxy.newProxyInstance()方法,需要提供三个参数:类加载器(ClassLoader)、目标类实现的接口数组(Class[])、InvocationHandler对象

测试

public class AppTest 
{
@Test
public void testJDKProxy(){
IUserService proxy = (IUserService)JDKProxyFactory.getProxy(new UserService());
proxy.queryUserInfo();
}
}

输出结果

执行方法前的操作
查询到用户信息
执行方法后的操作

Process finished with exit code 0

JDK动态代理是基于接口的,本质上是代理类和目标类实现了相同的接口,所以使用时感觉不到代理类和目标类的区别,当然,实际使用时,当调用代理类的方法时其实是调用了 InvocationHandler 的 invoke() 方法。其实上面两个类可以合二为一,只需要将代理工厂的 getProxy() 方法移动到 MyInvocationHandler 类中即可

当目标类没有实现任何接口的话,就要通过下面的Cglib的方式了

Cglib动态代理

Cglib同样是在运行时动态生成字节码实现动态代理的,和JDK动态代理的区别是,JDK动态代理是和目标类实现相同的接口,以此来替换目标类,而Cglib是继承目标类,用子类替代目标类

Cglib动态代理依赖于 net.sf.cglib.proxy.Enhancer 类和 net.sf.cglib.proxy.MethodInterceptor 类,Enhancer 类相当于上文的 Proxy 类,MethodInterceptor 类相当于上文的 InvocationHandler 类,由于Cglib是第三方包,因此需要手动引入依赖cglib,使用Cglib动态代理的过程如下:

定义自己的 MethodInterceptor 并重写 intercept() 方法,类似于上文中的 invoke(),在 intercept() 方法的前后添加额外的操作,创建代理工厂和 Enhancer 对象,设置类加载器、目标类、回调方法(MethodInterceptor对象),最后利用 Enhancer 的 create() 方法创建代理对象

代码如下:

package com.meteor.design.dynamic_proxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyMethodInterceptor implements MethodInterceptor {
// 重写 intercept 方法,实现额外的操作
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object object = methodProxy.invokeSuper(o, objects);
after();
return object;
}

private void before() {
System.out.println("执行方法前的操作");
}

private void after() {
System.out.println("执行方法后的操作");
}
}
package com.meteor.design.dynamic_proxy.cglib;

import net.sf.cglib.proxy.Enhancer;

public class CGLIBProxyFactory {

public static Object getProxy(Class<?> clazz) {
// 创建Enhancer增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置回调方法
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}

测试

public class AppTest 
{
@Test
public void testCGLIBProxy(){
UserService proxy = (UserService)CGLIBProxyFactory.getProxy(UserService.class);
proxy.queryUserInfo();
}
}

输出结果

执行方法前的操作
查询到用户信息
执行方法后的操作

Process finished with exit code 0

目录结构

src
├── main
│ └── java
│ └── com.meteor.design
│ ├── dynamic_proxy
│ │ ├── cglib
│ │ │ ├── CGLIBProxyFactory.java
│ │ │ └── MyMethodInterceptor.java
│ │ │
│ │ └── jdk
│ │ ├── JDKProxyFactory.java
│ │ └── MyInvocationHandler.java
│ │
│ ├── service
│ │ ├── IUserService.java
│ │ └── UserService.java
│ │
│ └── static_proxy
│ └── StaticProxy.java
└── test
└── java
└── com.meteor.design
└── AppTest.java