动态代理

1、概述
在Java中,动态代理是指代理类的生成发生在程序运行时期,根据被代理类动态生成代理类的字节码文件(class文件),并且装载到运行环境中,像其他的类一样使用,该字节码文件的生存期随着运行环境的关闭而消失。JDK1.3以及之上的版本提供了动态代理的机制,它和反射机制结合在一起。

JDK提供的接口和类
InvocationHandler接口:
    public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[]args) throws Throwable;
}


它定义了唯一的方法invoke,该方法的参数为代理对象proxy,截获的方法对象method,和方法调用的参数,返回方法执行的结果。开发者需要实现该接口,在invoke方法中添加对截获的方法的代理操作,并调用被代理对象的方法。
Proxy类
该类是生成代理类的帮助类,主要方法如下:
public Static Class getProxyClass (ClassLoader loader, Class[] interfaces):根据指定的类加载器和接口获得一个代理类,其中,loader是类加载器,interfaces是被代理类所拥有的接口。
public Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):根据指定的类加载器、接口以及截获器,返回代理类的一个实例对象。其中:
ClassLoader loader ----指定被代理对象的类加载器
Class[] Interfaces ----指定被代理对象所以事项的接口
InvocationHandler h ----指定需要调用的InvocationHandler对象

2、动态代理的实现
场景如下:定义一个人类接口UserManager,UserManagerImpl是该接口的一个实现类,用于实现用户的增、删、改、查功能。现在,需要在执行这些功能之前验证安全性。
接口和实现类:
UserManager类:
    public interface UserManager {
public void add(String username, String password);
public void delete(String id);
public String loadById(String id);
public void update(String username, String password);
}

UserManagerImpl类:
    public class UserManagerImpl implements UserManager {
public void add(String username, String password) {
System.out.println("==========add()==========");
}
public void delete(String id) {
System.out.println("==========delete()==========");
}
public String loadById(String id) {
System.out.println("==========load()==========");
return null;
}
public void update(String username, String password) {
System.out.println("==========update()==========");
}
}

基于JDK的动态代理的实现
截获器SecurityHandler类:
    public class SecurityHandler implements InvocationHandler {
private Object targetObject;// 被拦截的类
public Object newProxy(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);// 利用Proxy类取得代理对象
}
// 调用目标对象的方法是会执行下面这个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
checkSecurity();
Object ret = null;
try {
ret = method.invoke(this.targetObject, args);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return ret;
}
private void checkSecurity() {
System.out.println("==========checkSecurity()==========");
}
}

其中invoke()方法就是Proxy动态代理类所代理的接口类的抽象方法的真实实现:
Object proxy -----代理类对象
Method method -----被代理对象的方法(这里不是接口的抽象方法了,是具体的实现类中的方法)
Object[] args -----该方法的参数数组
说明:这里可以把上面的类抽象分成两个类处理,即SecurityHandler和SecurityProxyCreator类:
    public class SecurityHandler implements InvocationHandler {
private Object targetObject;// 被拦截的类
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
// 调用目标对象的方法是会执行下面这个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
checkSecurity();
Object ret = null;
try {
ret = method.invoke(this.targetObject, args);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return ret;
}
private void checkSecurity() {
System.out.println("==========checkSecurity()==========");
}
}
public class SecurityProxyCreator {
public Object create(Class<?>[] interfaces, InvocationHandler h) {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, h);
}
}

Client测试类:
    public class Client {
public static void main(String[] args) {
SecurityHandler handler = new SecurityHandler();
UserManager userManager = (UserManager) handler.newProxy(new UserManagerImpl());// 生成代理类
userManager.add("", "");
}
}

测试结果如下:
==========checkSecurity()==========
==========add()==========
3 分析总结
分析JDK提供的帮助类Proxy,可以发现,它只支持实现接口方式的代理,不支持继承超类方式的代理,这就意味着,被代理的类必须要有接口,并且需要拦截的方法必须都在接口中进行声明。在本例中,通过截获帮助类Proxy生成的代理类的字节码文件,反编译之后,发现,其类的声明如下:
public final class proxy0 extends Proxy implements UserManager
其中,proxy0就是动态生成的代理类的名字,在该类的方法中,都会将调用委托到拦截器的invoker方法。
JDK中具体的动态代理类是怎么产生的呢?
1、产生代理类$Proxy0类
执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体;
2、将代理类$Proxy0类加载到JVM中
根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中
3、创建代理类$Proxy0类的对象
调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象
参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数
这个参数就是实现的InvocationHandler对象,InvocationHandler对象中组合加入了代理类代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现;
4、生成代理类的class byte
动态代理生成的都是二进制class字节码
备注:JDK提供的动态代理仅支持接口方式的代理,那么如果被代理的类没有实现任何接口,则可基于CGLib的动态代理,CGLib同时也支持继承超类的方式的代理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值