代理模式:
定义:为其他对象提供一个代理以控制对某个对象的访问,即通过代理对象访问目标对象。这样做的好处,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
关键点:代理对象和目标对象,代理对象是对目标对象的扩展,并会调用目标对象。
1、静态代理
静态代理再使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同的类。
关键:在编译期确定代理对象,在程序运行前代理类的.class文件就已经存在了。
比如在代理对象中实例化被代理对象或者将被代理对象传入代理对象的构造方法。
public interface Message() {
void sendMessage();
}
public class MessageDao implements Message {
public void sendMessage() {
System.out.print(“发送成功”);
}
}
public class MessageDaoProxy implements Message {
private MessageDao target;
public MessageDaoProxy(MessageDao target) {
this.target = target;
}
public void sendMessage() {
target. sendMessage();
}
}
调用方式:
MessageDao target = new MessageDao)();
MessageDaoProxy proxy = new MessageDaoProxy(target);
proxy.save();
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点:代理类和被代理类实现相同的接口,同时要实现相同的方法。代码出现大量的重复,接口增加一个方法,所有的代理类和实现类都要实现此方法。增加了代码维护的复杂性。
2、动态代理
1.在运行期,通过反射机制创建一个实现了一组给定接口的新类
2.在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了这些 interface。该class的实 例可以当作这些interface中的任何一个来用。但是这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工 作。
3.动态代理也叫做:JDK代理,接口代理
4.接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
a、JDK的动态代理
java.lang.reflect.Proxy:生成动态代理类和对象;
java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现对真实角色的代理访问。
每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象。
static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)
//第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
//第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)
//第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler。
b、Cglib的动态代理
为没有实现接口的类提供代理。
Cglib 动态代理是针对代理的类, 动态生成一个子类, 然后子类覆盖代理类中的方法, 如果是private或是final类修饰的方法,则不会被重写。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
cglib与jdk的代理的区别:
1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。
何时使用jdk、cglib:
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
spring如何选择:
当bean实现接口时,spring就会使用jdk。
当bean没有实现接口,spring使用cglib实现。
cglib比jdk快吗:
CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,
在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,
因为CGLib原理是动态生成被代理类的子类。
在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,
只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点 。在jdk8之后代理效率高于cglib。
静态代理:代理类在编译阶段生成,程序运行前就已经存在,那么这种代理方式被成为静态代理,这种情况下的代理类通常都是我们在Java代码中定义的。
动态代理:代理类在程序运行时创建,也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
代理模式与装饰者模式的区别
UML类图基本没区别,都是实现同一个接口,一个类包装另一 个类。 两者的定义:
-
装饰器模式:能动态的新增或组合对象的行为
在不改变接口的前提下,动态扩展对象的功能 -
代理模式:为其他对象提供一种代理以控制对这个对象的访问
在不改变接口的前提下,控制对象的访问
装饰模式是“新增行为”,而代理模式是“控制访问”。关键就是我们如何判断是“新增行 为”还是“控制访问”。你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大 家就知道是在限制。
参考:https://segmentfault.com/a/1190000009235245