Java 代理

代理模式介绍

代理的作用就是想增强一个目标类的方法,但是又不修改目标类,这样就可以用到代理。即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,达到扩展目标类方法的目的。

Java代理有三种方式:静态代理,动态代理,cglib代理。

静态代理

这种代理方式需要目标列和代理类实现一样的接口。

UserDao (接口)

1
2
3
public interface UserDao {
public void save();
}

UserDaoImpl (目标类)

1
2
3
4
5
6
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存数据");
}
}

UserDaoProxy (代理类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UserDaoProxy implements UserDao {
private UserDaoImpl userDao;

public UserDaoProxy(UserDaoImpl userDao){
this.userDao = userDao;
}

@Override
public void save() {
System.out.println("开启事务");
userDao.save();
System.out.println("关闭事务");
}

public static void main(String[] args) {
UserDaoProxy proxy = new UserDaoProxy(new UserDaoImpl());
proxy.save();
}
}

可以看见目标类和代理都实现了同一个接口.

最后执行的效果是:

1517

动态代理

  • 静态代理:是在程序编译以前就在代码中已经定义好的代理方式。也就就说Java编译以后的代理文件是一个class的文件
  • 动态代理:代理是在程序运行是生成的。也就是Java编译完成后没有实际的class文件,而是在运行是动态生成的类字节码,并加载到JVM中。

Java实现动态代理的大致步骤如下:

1.定义一个目标类和公共接口

2.定义一个类,实现 InvocationHandler 接口。代理类调用任何方法都会经过这个调用处理器类。

3.生成代理对象,为代理对象指定(目标对象,目标对象实现的接口,调用处理器的实例)。由此看出一个代理对象对应一个目标对象,对应一个调用处理器实例。


Java实现动态代理涉及以下几个类

  • Java.lang.reflect.Proxy:这是生成代理对象所要使用的类,通过Proxy类生成的代理类都继承了Proxy类。Proxy类主要的方法为:
1
2
//创建代理对象  
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

这个静态函数的第一个参数是类加载器对象,即哪个类加载器来加载这个代理类到JVM方法区(目标类的类加载器)。第二个参数是这个代理类所要实现的接口(一般为目标类实现的接口)。第三个参数是调用处理器的实例,用于指定代理类中具体实现了什么。


  • Java.lang.reflect.InvocationHandler:称他为“调用处理器”,是一个接口,我们动态生成的代理类需要自己定义一个类实现InvocationHandler接口。

InvocationHandler接口中是方法:

1
invoke (Object proxy, Method method, Object[] args);

这个方法是在代理对象调用任何方法都会调用的。第一个参数是代理对象(标识哪个代理对象调用了方法)。第二个参数是Method(表示哪个方法被调用了)。第三个参数是调用方法的参数。


ProxyHandler(调用处理器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ProxyHandler implements InvocationHandler {
private UserDao userDao;
public ProxyHandler(UserDao userDao){
this.userDao = userDao;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(userDao, args);
System.out.println("after");
return result;
}

public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
ProxyHandler proxy = new ProxyHandler(userDao);
UserDao proxyUserDao (UserDao)Proxy.newProxyInstance(
UserDaoImpl.class.getClassLoader(),UserDaoImpl.class.getInterfaces(), proxy);
System.out.println(userDao.getClass());
System.out.println(proxyUserDao.getClass());
proxyUserDao.save();
}
}

运行结果:

2103

由打印结果可以看出来,Proxy.newProxyInstance返回的是一个代理对象。该对象有以下几个特点:

  • 继承Proxy类。
  • public final
  • 命名方式是$ProxyN,其中 N 会慢慢增加。

cglib代理

先查看cglib源码的目录结构:

2135

图中圈出的 Enhancer 和 MethodInterceptor 这两个两个类是cglib的核心。cglib生成代理类的思想就是生成一个新的类(Proxy),生成的代理类继承目标类,并对目标类方法实现一些前后操作。代理类直接继承自目标类,目标类就不用实现接口。

ProxyFactory (生成代理的工厂类)实现 MethodInterceptor接口,也可以使用匿名内部类方法

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 ProxyFactory implements MethodInterceptor {
private Object taget;
public ProxyFactory(){}
public ProxyFactory(Object o){
taget = o;
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
//执行目标类的方法
Object retunrValue = method.invoke(taget, args);
//执行代理类的父类的方法,即执行目标类方法
methodProxy.invokeSuper(o,args);
System.out.println("after");
return returValue;
}

public Object getProxyInstance() {
Enhancer en = new Enhancer();
//设置代理类继承自那个类
en.setSuperclass(taget.getClass());
en.setCallback(this);
return en.create();
}

public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
System.out.println(userDao.getClass());
UserDaoImpl proxy = (UserDaoImpl) new ProxyFactory(userDao).getProxyInstance();
System.out.println(proxy.getClass());
proxy.save();
}
}

执行结果:

1107

可以看到执行了两次目标类的方法。

总结

  • 静态代理:就是代理类和目标类实现同一个接口,在代理类中调用目标类的方法,同时增强目标类的方法。这种实现代理的方式有很强的局限性。如果目标类的方法增加了,同样代理类中也要增加相应的方法来增强
  • 动态代理:相比于静态代理,动态代理解决了如果目标类增加新方法时,代理类也需要做出相应修改的问题。同时可以做到一个代理类可以代理不同的目标类。但是使用动态代理要求目标类需要实现接口,同时生成的代理类是继承自 Proxy动态代理是不需要第三方库的支持

  • cglib 代理:和动态代理最终实现的效果相似。但是生成的代理类是继承自目标类,同时不需要目标类实现接口。而 cglib 依赖于 cglib 类库。同时在 cglib 实现接口方法中提供了一个额外的参数, MethodProxy 代表着当前执行方法的代理,但是一般不会使用。