动态代理在java中广泛的应用
比如
1.Spring aop
2. Hibernate数据查询
3. 测试后端框架mooc
4. Rpc
5. java注解对象的获取
静态代理的代理关系在编译时确定的
静态代理适合代理类较少且确定的
java的动态代理有两种
1.原生的jdk代理
2.cglib动态代理
实例
静态代理的实现
抽取被代理方法的公共接口
public interface UserService {
void say();
void cry();
}
接口的实现类
public class UserServiceImpl implements UserService {
public void say() {
// TODO Auto-generated method stub
System.out.println("UserService say");
}
public void cry() {
// TODO Auto-generated method stub
System.out.println("UserService cry");
}
}
代理类 它和目标对象实现了相同的接口,并把目标对象填充到自己的内部,在调用代理类的方法时实际上是调用目标对象
public class MyStProxy implements UserService {
private UserService userService=null;
public MyStProxy(UserServiceImpl userServiceImpl) {
// TODO Auto-generated constructor stub
this.userService=userServiceImpl;
}
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("before st invoke");
userService.say();
System.out.println("after st invoke");
}
public void cry() {
// TODO Auto-generated method stub
System.out.println("before st invoke");
userService.cry();
System.out.println("after st invoke");
}
}
测试代码
UserService stUserService=new MyStProxy(new UserServiceImpl());
stUserService.say();
stUserService.cry();
运行结果
before st invoke
UserService say
after st invoke
before st invoke
UserService cry
after st invoke
然后再看jdk动态代理
前面的接口UserService和目标对象UserServiceImpl是通用的
//jdk动态代理 被代理的类要实现目标接口 从写构造函数 invoke方法 getProxy方法
//目标接口 UserService
//被代理类 UserServiceImpl
//代理类 uHandle 不用实现目标接口而是实现InvokitonHandler接口的invoke方法
代理类不用实现目标接口而是实现了InvoktionHandler接口的invoke方法
public class UserServiceInvocationHandler implements InvocationHandler {
private Object targetObject=null;
//目标对象
/**
* 绑定代理对象 并返回一个代理类 也可以使用构造方式绑定
* 返回一个目标对象的代理对象
* 1.类加载器
* 2.类的接口
* 3.
* @param targetObject
* @return
*/
public Object bind(Object targetObject){
this.targetObject=targetObject;
return Proxy.newProxyInstance(targetObject.getClass()
.getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
/**
* 对被代理的方法进行处理
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
String name=method.getName();
System.out.println("before invoke"+name+"...");
//执行目标对象的方法
Object result=method.invoke(targetObject, args);
System.out.println("after invoke"+name+"...");
return result;
}
}
测试代码
UserServiceInvocationHandler uHandler=new UserServiceInvocationHandler();
UserService dyUserService=(UserService)uHandler.bind(new UserServiceImpl());
dyUserService.say();
dyUserService.cry();
然后总结一下jdk代理
还是使用接口指定协议,然后再用不同的实现来实现具体行为
在静态代理时 实现了相同的接口
而jdk动态代理做法是1.首先实现了InvocationHandler,方法调用转到了该类的invoke方法
2.然后在需要使用目标对象时,通过jdk代理获取代理对象
关键是Proxy.newProxyInstance(loader,interfaces,handler)
他会返回一个实现了指定接口的代理对象,该对象所有方法的调用会转给invocationHandler的invoke
动态代理神奇的地方在
代理对象在程序运行时参生
代理对象接口方法的调用转给handler的invoke
在invoke方法可以添加逻辑 比如修改方法参数 加入日志功能 安全检查功能
通过反射调用对象的相应方法还可以用rpc远程调用
JDK proxy会把object的hashcode equals toString转给handler 其他不会转
jdk生成的代理类类型是class.jdkproxy.$Proxy0
它的父类是class.lang.reflect.Proxy
但是jdk动态代理是基于接口的,如果没有接口 那么就可以使用cglib
Cglib代理 (code generation library)
是一个基于ASM字节码生成的库 ,允许在运行时对字节码修改和动态生成,通过继承方式实现。
假设目标类没有实现任何接口
public class UserServiceTarget implements UserService{
public void say() {
// TODO Auto-generated method stub
System.out.println("UserService say");
}
public void cry() {
// TODO Auto-generated method stub
System.out.println("UserService cry");
}
}
cglib代理类
//MethodInterceptor 主要的方法拦截器 是callback的子接口
//MethodProxy jdkmethod的代理类 可以对源对象调用
public class DyUserServiceImpl implements MethodInterceptor{
private Object targetObject=null;
public Object bind(Object targetObject){
this.targetObject=targetObject;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
enhancer.setCallback(this);
return enhancer;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("before cglib invoke");
Object result=methodProxy.invoke(o, objects);
System.out.println("after cglib invoke");
return result;
}
}
首先实现了一个methodinterceptor 方法调用转到该类的intercptor方法
在需要使用目标类时,cglib代理获取代理对象
测试代码
DyUserServiceImpl dyUserServiceImpl=new DyUserServiceImpl();
UserServiceTarget userServiceTarget=(UserServiceTarget)dyUserServiceImpl.bind(new UserServiceTarget());
userServiceTarget.say();
userServiceTarget.cry();
测试结果
before invokesay...
UserService say
after invokesay...
before invokecry...
UserService cry
after invokecry...
在上面methodintercptor和invocationHandler类似都是方法调用的中转站
从Object继承的方法cglib也会代理 比如hashcode equals toString 而final方法无法代理。
而对代理之后的类型深入
它的对象类型是cglib动态生成的类型 cglib.XXX
父类是目标对象类,因此也验证了Cglib是通过继承实现代理
同时还实现包了net.sf.cglib.Factory接口 这个接口是cglib自己加入的包含一些工具方法。
关于它的具体实现 应该是内部有一个methodintercptor 当调用目标方法时 首先会尝试转发给intercptor ,如果没有会调用父类方法逻辑并不复杂,只是不需要我们去手写,运行时动态生成的。
cglib的缺点无法处理final的情况
最后如何获取cglib字节码 ???和cglib的细节
后面再讲
动态代理先讲这么多后面再不足之处再更新