目录
  1. 代理模式
    1. 应用场景
  2. JDK动态代理
  3. JDK动态代理的局限性
  4. 与spring AOP的关联
spring AOP(1) 代理模式与JDK动态代理

代理模式

代理模式是一个如下的结构

其中A为真实对象,A_proxy为代理对象,A与A_proxy实现了共同的接口A_interface。

在通常情况下我们访问A时直接通过new A来访问,但是在代理模式下,我们通过A_proxy来访问A。

因为A_proxy和A都实现了同样的接口,所以我们可以像直接使用A一样使用A_proxy来间接访问A对象。

应用场景

这个时候,我们可能会产生这样一个疑问,就是这样做有什么用吗?

我们想象一下这样的场景,我们需要对于A的某个方法计算其运行时间,或者我们需要对于其所有的方法执行前后都进行日志记录。

以计算运行时间为例。若是A如下:

1
2
3
4
5
public class A implements A_interface {
public void output() {
System.out.println("Hello world!");
}
}

那么我们的A_proxy就可以如下

1
2
3
4
5
6
7
8
9
10
public class A_proxy implements A_interface {
A a = new A();

public void output() {
int startTime = System.currentTimeMillis();
a.output();
int endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}

这其实是一种静态代理实现。因为我们需要实现知道A的内部代码,并且也需要事先写好A_proxy,所以其能力会大打折扣。

JDK动态代理

在java中,已经提供了动态代理机制。

在java动态代理机制中有一个非常重要的接口InvocationHandler。我们就是通过实现该接口在运行原始方法前后实现自己所需的功能。

我们来看看具体的代码。

1
2
3
4
// Hello接口
public interface Hello {
String hello(String msg);
}
1
2
3
4
5
6
// HelloImpl实现类
public class HelloImpl implements Hello {
public String hello(String msg) {
return "hello " + msg;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// InvocationHandler的实现类,可以看到里面的作用就是计算函数运行时间
public class HelloInvocationHandler implements InvocationHandler {
private static Logger logger = LoggerFactory.getLogger(HelloInvocationHandler.class);

private Object object;

public HelloInvocationHandler(Object object) {
this.object = object;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(object, args);
long endTime = System.currentTimeMillis();
logger.info("{} cost {}ms", method.getName(), endTime - startTime);
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 在这里我写了一个工厂类来获取代理对象
// 最后的生成代理的部分就是通过执行Proxy.newProxyInstance方法
public class HelloFactory {
public static Hello getInstance(String helloClassName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName(helloClassName);
Hello hello = (Hello) clazz.newInstance();
InvocationHandler handler = new HelloInvocationHandler(hello);

Hello proxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
handler
);

return proxy;
}
}

那么最后我们只需要调用HelloFactory.getInstance(“com.example.HelloImpl”)来获取代理对象。

JDK动态代理的局限性

我们可以看到JDK动态代理实现有一个非常大的前提,就是目标类必须实现接口。

那么若是对于那些没有接口的类,JDK动态代理就无法使用了。这时又有一种叫做cglib的代理方式,cglib是通过继承原来的类来实现代理。不过cglib也有缺点,由于继承的特性,对于final方法无法继承。具体此处不详解。

与spring AOP的关联

如果我们稍微敏感一点,JDK动态代理和spring AOP有异曲同工之妙。其实spring AOP框架中就是针对接口和非接口分别使用JDK动态代理以及cglib代理实现的。

文章作者: 谷河
文章链接: https://www.lyytaw.com/spring/spring_aop_JdkDynamicProxy/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG