Java动态代理
最近准备复习spring框架,AOP原理那就离不开代理模式了,索性就先把这个知识再学习一下!
JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程;
动态代理是用来产生一个对象的代理对象的。
网上看到一个不错的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。
代理对象的两个概念:
- 代理对象存在的价值主要用于拦截对真实业务对象的访问。
- 代理对象应该具有和目标对象相同的方法。
代理步骤:
- 代理对象和真实对象建立代理关系
- 实现代理对象的代理逻辑方法
package t0411.inter;
/**
* 接口类
*/
public interface Person {
void sing(String name);
}
package t0411.bean;
import t0411.inter.Person;
public class TargetUser implements Person {
@Override
public void sing(String name) {
System.out.println(name+ " is singing...");
}
}
这是最简单的Java接口和实现类的关系,此时就可以开始动态代理了。先建立起代理对象和目标真实对象的关系,然后实现代理逻辑。
在JDK动态代理中,要实现代理逻辑类必须去实现java.lang.reflect.InvocationHandler
package t0411.proxyBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUser implements InvocationHandler {
//目标对象
private Object target;
/**
* 代理方法逻辑
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强方法
System.out.println("进入代理逻辑方法...");
System.out.println("在调度真实对象之前的服务");
//调用目标对象方法sing();
Object invoke = method.invoke(target, args);
System.out.println("在调度真实对象之后的服务");
return invoke;
}
/**
* 建立代理对象和真实对象的代理关系,并返回代理对象
* @param target 真实目标对象
* @return 代理对象
*/
public Object bind(Object target){
this.target = target;
Object newProxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return newProxyInstance;
}
}
第一步:建立代理对象和真实对象的关系。这里使用bind方法去完成的,方法中首先用类的属性target保存真实对象,然后建立并生成代理对象。
Object newProxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);
其中newProxyInstance方法包含了3个参数:
- 类加载器,采用target本身的类加载器
- 把生成的动态代理对象下挂在实现的接口下。
- 定义时间方法逻辑的代理类,this表示当前对象,它必须实现InvocationHandler接口的invoke方法,他就是代理逻辑方法的显示方法。
第二步,实现代理逻辑方法。invoke方法可以实现代理逻辑,invoke方法的3个参数含义:
- proxy,代理对象,就是bind方法生成的对象。
- method,当前调度的方法。
- args,调度方法的参数。
当使用了代理对象调度真实对象的方法,只是通过反射实现而已。
package t0411.test;
import t0411.bean.TargetUser;
import t0411.inter.Person;
import t0411.proxyBean.ProxyUser;
//测试
public class TestProxyBean {
public static void main(String[] args) {
//创建代理类对象
ProxyUser user = new ProxyUser();
//创建目标类对象
TargetUser targetUser = new TargetUser();
//调用创建代理对象方法
Person proObj = (Person)user.bind(targetUser);
proObj.sing("liudehua");
}
}
//输出
进入代理逻辑方法...
在调度真实对象之前的服务
liudehua is singing...
在调度真实对象之后的服务
首先通过bind方法绑定了代理关系,然后再代理对象调度sing方法时进入了代理的逻辑。
CGLIB动态代理
JDK动态代理必须提供接口才能使用,在一些不能提供接口的环境中,中能采用其他方法,比如CGLIB动态代理,它的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。
package t0411.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
/**
* 代理逻辑方法
* @param o 代理对象
* @param method 方法
* @param args 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("调用真实对象前...");
//CGLIB反射调用真实对象方法
Object result = methodProxy.invokeSuper(methodProxy,args);
System.out.println("调用真实对象后...");
return result;
}
/**
* 生成CGLIB代理对象
* @param cls Class类
* @return
*/
public Object getProxy(Class cls){
Enhancer enhancer = new Enhancer();
//设置增强类型
enhancer.setSuperclass(cls);
//定义代理逻辑对象为当前对象,要求当前对象实现MethodInaterceptor方法
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create();
}
}
这里用了CGLIB的加强哲Enhancer,通过设置超类的方法,通过setCallback方法设置哪个类为它的代理类。其中,参数为this就意味着是当前对象,那就要求用this这个对象实现接口MethodInterceptor的方法,然后返回代理对象。
public class TestCglib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
TargetUser obj = (TargetUser)cglibProxy.getProxy(TargetUser.class);
obj.sing("lidehua");
}
}
//输出
调用真实对象前...
lidehua is singing...
调用真实对象后...
推荐文章:https://blog.csdn.net/flyfeifei66/article/details/81481222
https://blog.csdn.net/yaomingyang/article/details/80981004