社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
在生活中经常会遇到代理,比如买房我们是去找中介,而不是自己一栋楼一栋楼去挑选,这里的中介就是代理。代理即通过代理对象访问目标对象,还可以在目标对象基础上增强额外的功能。java的代理分为静态代理和动态代理。静态代理即在代码运行前,代理类就已经存在了。动态代理指代理类不是写在代码中的,而是在运行过程中产生的。
静态代理就是在代码运行之前,代理类就已经存在了。通过一个实例来模拟静态代理;:
以租房为例,租房有两种方式,一种是直接找房东去租房,另一种是找租房软件比如自如(不是广告),这种就是通过代理访问目标对象。
public interface Renting {
String rent(String name);
}
接着新建一个Person类实现接口,这个类的意思是直接找房东租房,也就是目标对象:
public class Person implements Renting{
@Override
public String rent(String name) {
return "租了"+name;
}
}
然后新建代理类Ziru,可以直接通过代理类去访问目标对象,我们前面也讲到了代理类不光可以实现访问目标对象,还可以在目标对象基础上增强额外的功能。因此增加一条收取服务类的额外功能。
public class Ziru implements Renting{
Person person;
public Ziru(){
person=new Person();
}
@Override
public String rent(String name) {
System.out.println("收取手续费");
return person.rent(name);
}
}
public class run {
public static void main(String[] args) {
//不使用代理
// Person person=new Person();
// person.rent("碧桂园");
//使用代理
Ziru ziru=new Ziru();
System.out.println(ziru.rent("碧桂园"));
}
}
动态代理指代理类不是写在代码中的,而是在运行过程中产生的。java提供了两种实现动态代理的方式,分别是基于Jdk的动态代理和基于Cglib的动态代理。
其中Renting和Person代码和上面的静态代理相同,新建一个JdkProxy类,这个类是动态代理的核心部分,实现InvocationHandler 接口。InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类invoke,由它实现处理内容。这段代码的另外一个核心是Proxy.newProxyInstance,该方法需要三个参数,目的是运行期间生成代理类,每个参数的功能已经写在了注释中。这段代码的意思就是Proxy 动态产生的代理对象会调用 InvocationHandler 实现类invoke。
public class JdkProxy implements InvocationHandler {
Renting obj;
public Renting getProxy(Renting obj){
this.obj=obj;
//运行期间创建对象
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
//obj.getClass().getClassLoader()类加载器
//obj.getClass().getInterfaces() 目标类实现的接口
//InvocationHandler对象
return (Renting) proxy;
}
//proxy 代理对象
//method 要实现的方法
//args 方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//拿到目标方法执行结果
Object invoke = method.invoke(obj, args);
return invoke;
}
}
public class run {
public static void main(String[] args) {
Person person=new Person();
JdkProxy jdkProxy=new JdkProxy();
Renting proxy = jdkProxy.getProxy(person);
String res = proxy.rent("碧桂园");
System.out.println(res);
}
}
当调用proxy.rent()方法时,会自动执行invoke方法,以实现动态代理。
你可以在invoke方法中增加一些自己的代码,比如我在invoke方法中写一条
运行时就会在控制台上打印出来,这也是Spring AOP的核心
JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。
JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()
通过CGlib也能实现动态代理,在写代码之前导入两个jar包asm-3.3.1,cglib-2.2.2
CGlib动态代理的实现,其中Renting和Person代码和静态代理相同,这里就不再展示。CGlib还可以直接代理类,为了比较,这里依旧使用接口。新建CglibProxy,实现了MethodInterceptor接口,该接口需要实现intercept方法。intercept方法中依旧调用method.invoke()方法。与Jdk不同的是创建代理对象的方式,CGlib中使用Enhance创建代理对象。
public class CglibProxy implements MethodInterceptor {
Renting obj;
//通过Enhance动态创建对象
public Renting getProxy(Renting obj){
this.obj=obj;
Enhancer enhancer=new Enhancer();
//设置父类
enhancer.setSuperclass(obj.getClass());
//设置回调方法
enhancer.setCallback(this);
return (Renting) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = method.invoke(obj, objects);
return invoke;
}
}
public class run {
public static void main(String[] args) {
Person person=new Person();
CglibProxy cglibProxy=new CglibProxy();
Renting proxy = cglibProxy.getProxy(person);
String res = proxy.rent("碧桂园");
System.out.println(res);
}
}
1.JDK动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
2.JDK动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!