设计模式(三):代理模式之JDK动态代理(JDK Dynamic Proxy)

JDK 1.3 版本后,Java提供了动态代理技术,允许应用在运行期创建接口的代理对象。

JDK 提供的代理只能针对接口做代理。也有更强大的代理库 cglib,可以实现对类的代理。

JDK 的 java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,Proxy类利用InvocationHandler动态创建一个符合某一接口的代理,生成(运行时)目标类的代理对象。

在普通编程过程中,无须使用动态代理,但在编写框架或底层基础代码时,动态代理的作用就非常大。

代理模式

代理:Proxy Pattern(即:代理模式),23种常用的面向对象软件的设计模式之一。
定义:**通过代理对象访问目标对象,这样做的目的可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的关键有两个角色,代理对象目标对象,代理对象是对目标对象的扩展,并调用目标对象。

理解举例:
代理对象可以形象的理解为经纪人角色,艺人则是目标对象,涉及市场的一些商演或活动的琐碎事项交给经纪人来处理,而主办方是通过经纪人找艺人演出(不能直接 new 艺人);经纪人可以负责多个艺人(动态)。

动态代理

Proxy

JDK 的 java.lang.reflect.Proxy类提供了static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法来获取代理对象。

  1. ClassLoader loader:指定代理对象的类加载器,加载代理类到 JVM 方法区。

  2. Class<?>[] interfaces:指定代里类需要实现的接口,是个Class数组,也就目标类可能实现多个接口。

  3. InvocationHandler:指调用具体的处理器实例,具体要处理的逻辑都在这个类实例里面。

    执行目标对象方法时会触发该处理器的invoke方法,会把当前执行目标对象的方法作为参数传入。

InvocationHandler

InvocationHandlerinvoke(Object proxy, Method method, Object[] args)方法通过反射机制间接调用目标对象的方法,InvocationHandler可以更好的理解为编织器,这个特性是非常适用于AOP思想的实现的,可以将横切逻辑代码和目标业务类方法的业务逻辑编织在一起。

  1. Object proxy:最终生成的代理实例,一般用不到。
  2. Method method:被代理的目标对象的方法。
  3. Object[] args:方法需要的参数。

JDK动态代理使用步骤

  1. 一个接口和接口实现类。
  2. 一个实现了InvocationHandler接口的类,将目标对象做为该类的构造方法的参数传入。重写invoke方法,在该方法里添加目标类的扩展功能。
  3. 使用Proxy类调用newProxyInstance(),传入InvocationHandler对象来生成一个代理对象。
  4. 通过代理对象调用接口(目标对象实现的接口)里的方法,需要的话给接口里的方法传入参数。

实现示例一

接口

1
2
3
4
5
6
7
/**
* @desc 接口
* @date 2023/6/3
*/
public interface HelloService {
void sayHello(String name);
}

实现类

1
2
3
4
5
6
7
8
9
10
11
/**
* @author gxing
* @desc 实现类
* @date 2023/6/3
*/
public class HelloServiceImpl implements HelloService{
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @desc 代理类
* 提供真实对象的绑定和代理方法
* 代理类实现InvocationHandler接口的代理方法
* 当一个对象被绑定后,执行其方法的时候就会进入到代理方法里
* @date 2023/6/3
*/
public class HelloServiceProxy implements InvocationHandler {

// 真实服务对象
private Object target;

/**
* @param target 真实服务对象
* @desc 绑定委托对象并返回一个代理类
* @date 2023/6/3
*/
public Object bind(Object target) {
this.target = target;
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}

/**
* @param proxy 代理对象
* @param method 被调用方法
* @param args 方法的参数
* @desc 通过代理对象调用方法首先进入这个方法
* @date 2023/6/3
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("..........这是JDK动态代理..........");
// 代理前调用
System.out.println("代理前调用,我准备说Hello。");
// 执行方法,相当于调用 HelloServiceImpl 的 sayHello
Object result = method.invoke(target, args);
// 反射方法后调用
System.out.println("代理后调用,我已说过Hello了");
return result;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @desc 测试类
* @date 2023/6/3
*/
public class HelloMain {

public static void main(String[] args) {
HelloServiceProxy helloServiceProxy = new HelloServiceProxy();
HelloService proxy = (HelloService)helloServiceProxy.bind(new HelloServiceImpl());
proxy.sayHello("Kitty");
}
}

实现示例二

一个接口

1
2
3
4
5
6
/**
* 接口
*/
public interface UserDao {
void update(int id, String name);
}

一个接口实现类

1
2
3
4
5
6
7
8
9
/**
* 目标对象(被代理对象)
*/
public class UserDaoImpl implements UserDao {
@Override
public void update(int id, String name) {
System.out.println("根据ID更新名称,id = " + id + "; name = " + name );
}
}

一个获取代理对象的类
Proxy.newProxyInstance()方法的InvocationHandler对象参数传入一个匿名的InvocationHandler对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class UserDaoJdkProxy<T> {

//目标对象,即实现接口的目标类
private T obj;

//将目标对象做为参数传入构造方法
public UserDaoJdkProxy(T obj) {
this.obj = obj;
}

/**
* 获取代理对象
* 传入InvocationHandler,重写invoke方法
* 扩展功能在invoke方法里执行
*
* @return
*/
public Object getProxyInstance() {

return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//给目标对象方法传入的参数
System.out.println("开始事务。。。。。");
//执行目标对象方法
Object methodValue = method.invoke(obj, args);
System.out.println("结束事务。。。。。");
return methodValue;
}
});
}
}

客户端调用代理对象执行目标方法

1
2
3
4
5
6
7
8
public class Client {

public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
userDao = (UserDao) new UserDaoJdkProxy(userDao).getProxyInstance();
userDao.update(100, "root");
}
}

实现示例三

自定义实现InvocationHandler接口的类

实现InvocationHandler接口,重写invoke方法,在该方法里做目标对象的功能扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 实现InvocationHandler接口方式
*/
public class CustomizeInvocationHandler implements InvocationHandler {

// 目标对象,即被代理对象
private Object target;

public CustomizeInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("transaction begin....");
Object object = method.invoke(target, args);
System.out.println("transaction end....");
return object;
}
}

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 执行动态代理对象的所有方法时,都会被替换成执行如下invoke方法
* 其中:
* proxy:指动态代理对象
* method:指要执行的方法
* args:代表调用目标方法时传入的实参
*/
public class UserDaoJdkProxy<T> implements InvocationHandler {

//目标对象,接口实现类
private T target;

public UserDaoJdkProxy(T target) {
this.target = target;
}

//把目标实现类传进有参构造
public Object getProxyObject() {
//返回代理对象,绑定的是个接口
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}

/**
* 重写invoke方法
* 扩展功能在invoke方法里执行
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("事务开启。。。。");
Object result = method.invoke(target, args);
System.out.println("事务结束。。。。");
return result;
}
}

客户端调代理对象类创建代理对象,通过代理对象执行目标对象的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {

public static void main(String[] args) {
UserDao userDao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(),
new Class[]{UserDao.class},
new CustomizeInvocationHandler(new UserDaoImpl()));
userDao.save(100L, "Kitty");

UserDao userDao1 = new UserDaoImpl();
UserDaoJdkProxy<UserDao> proxy = new UserDaoJdkProxy<>(userDao1);
userDao1 = (UserDao) proxy.getProxyObject();
userDao1.save(100L, "Kitty");
}
}

原理分析

JDK 的动态代理需要依赖接口,Proxy.newProxyInstance()需要传入目标对象实现的接口作为参数;也就是说如果目标对象没有实现接口,则无法使用JDK动态代理。查看该方法的原码,进行了验证、优化、缓存、同步、生成字节码,显示加载类等操作。

原码分析

Proxy.newProxyInstance():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* Look up or generate the designated proxy class.
* 根据接口查找或生成指定的代理类,需传入类加载器和接口
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//得到代理类的含有参数的构造对像
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//通过构造器来创建对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

getProxyClass0()

1
2
3
4
5
6
7
8
9
10
//获取代理类
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//对实现接口个数进行限制
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//缓存了代理类
return proxyClassCache.get(loader, interfaces);
}

相关参考

  1. 细说JDK动态代理的实现原理
  2. JDK动态代理实现原理
  3. 深入剖析JDK动态代理机制

设计模式(三):代理模式之JDK动态代理(JDK Dynamic Proxy)

http://blog.gxitsky.com/2018/03/15/DesignPatterns-03-Proxy-Dynamic/

作者

光星

发布于

2018-03-15

更新于

2023-06-06

许可协议

评论