您的位置:首页 > 编程语言 > Java开发

设计模式之动态代理(spring、cglib、 jdkproxy)

2020-06-11 04:41 465 查看

静态代理与动态代理的区别:

静态代理的目标对象比较单一 是某一类或实现同一接口的实例,不利于扩展,而动态代理的目标对象就没有限制,目标对象可以任意实例,如果目标对象实现了接口则可以使用jdk自带的代理类实现,如果没有实现接口则可以使用第三方的组件Cglib来实现。

动态代理

1) 先创建一个通用接口

package com.arno.summary.proxy.dynamicproxy.jdkproxy;

/**
* Created by Arno
*/
public interface IPerson {
void findLover();
}

2)实现该接口的两个实现类

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
/**
* Created by Arno
*/
public class Person1 implements IPerson {
public void findLover() {
System.out.println("Person1要求:有车有房学历高");
}
}
package com.arno.summary.proxy.dynamicproxy.jdkproxy;
/**
* Created by Arno
*/
public class Person2 implements IPerson {
public void findLover() {
System.out.println("Person2要求:肤白貌美大长腿");
}
}

3)创建一个代理类,实现java.lang.reflect.InvocationHandler,通过invoke来反射来生成有目标类的实例引用的代理class。

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by Arno
*/
public class JdkMeipo implements InvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target){
this.target = target;
Class<?> clazz =  target.getClass();
return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target,args);
after();
return result;
}

private void after() {
System.out.println("双方同意,开始交往");
}

private void before() {
System.out.println("我是媒婆,已经收集到你的需求,开始物色");
}
}

4)开始测试

package com.arno.summary.proxy.dynamicproxy.jdkproxy;
/**
* Created by Arno
*/
public class Test {
public static void main(String[] args) {
JdkMeipo jdkMeipo = new JdkMeipo();
IPerson person1 = jdkMeipo.getInstance(new Person1());
person1.findLover();

IPerson person2 = jdkMeipo.getInstance(new Person2());
person2.findLover();

}
}

5)运行结果

我是媒婆,已经收集到你的需求,开始物色
Person1要求:有车有房学历高
双方同意,开始交往
我是媒婆,已经收集到你的需求,开始物色
Person2要求:肤白貌美大长腿
双方同意,开始交往

6)分析
要想使用jdk原生的代理,必须实现接口InvocationHandler;且被代理的类都要实现接口。

静态代理:

1)创建一个实例

package com.arno.summary.proxy.staticproxy;

/**
* Created by Arno
*/
public class Person2  {

public void findLover() {
System.out.println("子女自己相处");
}

}

2)创建第二个实例。有第一个实例的引用,且他可以通过该饮用调用第一个实例的方法

package com.arno.summary.proxy.staticproxy;

/**
* Created by Arno
*/
public class Person1  {

private Person2 person2;

public Person1(Person2 person2) {
this.person2 = person2;
}

public void findLover() {
System.out.println("父母帮忙物色");
person2.findLover();
System.out.println("开始交往");
}

}

3)测试

package com.arno.summary.proxy.staticproxy;

/**
* Created by Arno
*/
public class Test {
public static void main(String[] args) {
Person1 person1 = new Person1(new Person2());
person1.findLover();
}
}

4)运行结果

父母帮忙物色
子女自己相处
开始交往

5)分析
spring中的依赖注入都是静态代理。如果想要使用被代理的类的新增方法,一定要修改代理类里的方法,比动态代理僵硬,只能硬编码

cglib实现动态代理的原理及与jdk原生的动态代理的区别

1)cglib的实现
CGLib代理执行代理方法的效率之所以比JDK的高,是因为CGlib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配index(int类型);这个index当做一个入参,FastClass就可以直接定位要调用的方法并直接进行调用欧冠,省去了反射调用,所以调用效率比JDK代理通过反射调用高。cglib无法代理final标记的方法

2)cglib与jdk的比较
a)JDK动态代理实现了被代理对象的接口,cglib代理继承了被代理对象。
b)JDK动态代理和cglib代理都在运行期间生成字节码,jdk动态代理直接写class字节码,cglib使用asm框架写class字节码,cglib代码实现原理更复杂,生成代理类比jdk动态代理低
c)JDK动态代理调用代理方法是通过反射机制调用的,cglib代理是通过fastclass机制直接调用方法,cglib执行效率更高。

3)spring中的代理选择策略
在spring当中,使用了jdk代理,也使用了cglib代理;
默认的代理选择原则为:
1)当bean有实现接口时,则使用jdk动态代理
2)当bean没有实现接口时,使用cglib实现动态代理
3)spring可以通过配置强行使用cglib代理:

<aop:aspectj-autoproxy proxy-target-class="true" />

总结

1)静态代理与动态代理的本质区别
a)静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则。
b)动态代理采用在运行时动态生成字节码的方式,取消了代理类的扩展限制,遵循开闭原则。
c)若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只增强新增策略类便可完成,无需修改代理类代码。

2)代理模式的优缺点:
优点
a)代理模式能将代理对象与真实被调用目标对象隔离
b)在一定程度上降低了系统的耦合性,扩展性好
c)可以起到保护目标对象作用
d)可以增强目标对象的功能
缺点
a)代理模式会造成系统设计中类的数量增加
b)在客户端与目标对象中增加一个代理对象,会导致请求处理速度变慢
c)增加系统的复杂度

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: