JAVA基础——代理

概述

代理模式:通过某种方式给某个对象提供一个代理对象,在不改变原有对象代码的前提下对方法的增强。

为什么要使用代理模式?

在项目中如果需要打印方法入参及出参时、需要记录方法执行时间时、需要验证权限时、需要统一异常处理时等等各种场景,基本上都是通过拦截器和过滤器来实现。 这些拦截器过滤器的底层实现其实都是使用了代理模式。

静态代理

静态代理其实就是在程序运行之前,提前写好被代理方法的代理类,编译后运行。

1
2
3
4
5
6
7
8
public interface MainService {
void doSomeThing();
}
public class MainServiceImpl implements MainService {
public void doSomeThing() {
System.out.println("doSomeThing......");
}
}

如果想在 dosomeTime 方法前后做一些动作,比如记录方法执行时间,我们一般会这么做:

1
2
3
4
5
public void doSomeThing() {
System.out.println("begin time:"+System.currentTimeMillis());
mainService.doSomeThing();
System.out.println("end time:"+System.currentTimeMillis());
}

只是这样就改变了原有类的代码结构,这个时候我们就可以用静态代理来解决:

1
2
3
4
5
6
7
8
9
10
11
public class StaticProxy implements MainService {
private MainService mainService;
public StaticProxy(MainService mainService){
this.mainService=mainService;
}
public void doSomeThing() {
System.out.println("begin time:"+System.currentTimeMillis());
mainService.doSomeThing();
System.out.println("end time:"+System.currentTimeMillis());
}
}

这里创建的了一个代理类,代理类持有原对象,把所有新增的需求放到代理类中,这样就不需要修改代码了。 我们可以使用如下代码测试:

1
2
3
4
5
public static void main (String args[]){
MainService mainService=new MainServiceImpl();
MainService staticProxy=new StaticProxy(mainService);
staticProxy.doSomeThing();
}

使用静态代理虽然解决了不修改代码的需求,但是如果原对象有多个方法的话就必须全部实现且加上打印的逻辑

动态代理

动态代理主要是通过反射机制,在运行时动态生成所需代理的class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DynamicProxy {
private MainService mainService;
public DynamicProxy(MainService mainService){
this.mainService=mainService;
}

public Object getProxy() {
return Proxy.newProxyInstance(
mainService.getClass().getClassLoader(),
mainService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("begin time:"+System.currentTimeMillis());
method.invoke(mainService, args);
System.out.println("end time:"+System.currentTimeMillis());
return null;
}
});
}
}

可以看到上方的getProxy方法是返回的一个代理对象,切是在这个对象的所有方法执行前后都执行了打印执行时间的逻辑 看一下测试代码:

1
2
3
4
5
public static void main (String args[]){
MainService mainService=new MainServiceImpl();
DynamicProxy dynamicProxy=new DynamicProxy(mainService);
((MainService)dynamicProxy.getProxy()).doSomeThing();
}

关于动态代理设及到一些工具类和接口,比如:InvocationHandler接口

在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:

1
2
3
4
5
6
/**
* 调用处理程序
*/
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}

动态代理不会直接让代理类去调用委托类,实际上是通过调用中介类的 invoke 方法,由invoke决定调用 proxy 哪个委托类。真正的代理内容也是在中介类里定义。

Proxy.newProxyInstance(ClassLoader classloader,Class aclass[],InvocationHandler invocationhandler)方法会生成一个一个对中介类的代理类。

动态代理实现的已经非常优雅了,但是它还是有个缺点,那就是想要实现代理的原对象必须具有顶层接口,对没有实现的接口的类就无能为力了。

Cglib代理

cglib是使用字节码技术直接生成一个子类然后重写父类的方法:

1
2
3
4
5
6
7
8
9
10
public class CglibInterceptor implements MethodInterceptor {


public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("begin time:"+System.currentTimeMillis());
Object object = methodProxy.invokeSuper(obj, objects);
System.out.println("end time:"+System.currentTimeMillis());
return object;
}
}

看一下测试代码:

1
2
3
4
5
6
7
public static void main (String args[]){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MainServiceImpl.class);
enhancer.setCallback(new CglibInterceptor());
MainService proxy= (MainService)enhancer.create();
proxy.doSomeThing();
}

代理对象的生成过程由Enhancer类实现,大概步骤如下:

  1. 生成代理类Class的二进制字节码;
  2. 通过Class.forName加载二进制字节码,生成Class对象;
  3. 通过反射机制获取实例构造,并初始化代理类对象。

总结

DK是基于反射机制,生成一个实现代理接口的匿名类,然后重写方法,实现方法的增强.它生成类的速度很快,但是运行时因为是基于反射,调用后续的类操作会很慢。只能对实现了接口的类生成代理,而不能针对类。因为生成的代理类无法强行转换为被代理类。而由于java是单继承,代理类已经继承了Proxy类,所以只能用接口。

CGLIB是基于继承机制,继承被代理类,所以方法不要声明为final,然后重写父类方法达到增强了类的作用.它底层是基于asm第三方框架,是对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理.生成类的速度慢,但是后续执行类的操作时候很快.

文章目录
  1. 1. 概述
  2. 2. 为什么要使用代理模式?
  3. 3. 静态代理
  4. 4. 动态代理
  5. 5. Cglib代理
  6. 6. 总结
|