代理(Proxy):
代理是一种设计模式,提供了对目标对象另外的访问方式,即通过代理访问目标对象。这样就可以在目标对象实现的基础上,增强额外的功能操作(扩展目标对象的功能)。可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。
静态代理:
可以在不修改目标对象的前提下扩展目标对象的功能。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。一旦接口增加方法,目标对象与代理对象都要进行修改,代理对象需要与目标对象实现一样的接口。所以静态代理不易维护。静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。
动态代理:
动态代理不需要实现接口,但是要求对象必须实现接口,否则不能使用动态代理。代理对象生成实例域JDK API,动态的在内存中构建代理对象(需要我们指定创建代理对象、目标对象、实现的接口的类型)。动态代理是运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到jvm中。
java提供了一个Proxy类,调用他的newProxyInstance方法可以生成某个对象的代理对象,该方法需要三个参数:
参数一:ClassLoader loader 生成代理对象使用哪个类装载器(一般使用代理类的装载器)
参数二:Class<>[] interface 生成哪个对象的代理对象,通过接口指定(指定要代理类的接口)
参数三:生成的代理对象的方法里干什么事(实现handler接口)
代理对象用于目标对象相同的方法,因为参数二指定了对象的接口
用户调用代理对象的审美方法,都是在调用处理器的invoke方法
使用JDK动态代理必须要有接口
public class XiaoMing implements Person {
@Override
public void sing(String name) {
System.out.println("小明唱" + name);
}
@Override
public void dance(String name) {
System.out.println("小明跳" + name);
}
}
public interface Person {
void sing(String name);
void dance(String name);
}
public class XiaoMingProxy {
//代理只是一个中介,实际干活的还是小明,于是需要在代理类上维护小明这个变量
XiaoMing xiaoMing = new XiaoMing();
//返回代理对象
public Person getProxy() {
/**
* 参数一:代理类的类加载器
* 参数二:被代理对象的接口
* 参数三:InvocationHandler实现类
*/
return (Person)Proxy.newProxyInstance(XiaoMingProxy.class.getClassLoader(), xiaoMing.getClass().getInterfaces(), new InvocationHandler() {
/**
* proxy : 把代理对象自己传递进来
* method:把代理对象当前调用的方法传递进来
* args:把方法参数传递进来
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果别人想要让小明唱歌
if (method.getName().equals("sing")) {
System.out.println("给1000万来再唱");
//实际上唱歌的还是小明
method.invoke(xiaoMing, args);
}
return null;
}
});
}
}
代理与适配器模式的区别:
适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
代理与装饰器模式的区别:
装饰器模式为了增强功能,而代理模式是为了加以控制。
public interface Image {
void display();
}
//真实对象
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
//代理类
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
//结果:
Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg
cglib代理:
cglib是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象,从而实现对目标对象功能的扩展。
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用cglib实现。cglib是一个强大的高性能的代码生成包,他可以在运行期扩展java类与实现java接口。广泛的被应用在AOP框架中,为他们提供interception(拦截)。cglib包的底层是通过一个小而块的字节码处理框架ASM,来转换字节码并生成新的类。
cglib与动态代理最大的区别:
使用动态代理的对象必须实现一个或多个接口,使用cglib代理的对象则无需实现接口。
cglib代理无需实现接口,通过生成类字节码实现代理,比反射少块,不存在性能问题,但是cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。
代理模式优缺点
优点:
协调调用者和被吊用着,降低了系统的耦合度
代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用
缺点:
由于客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢
实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。
PS:在其他人博客里看到的,代理模式就相当于明星的经济人,海外的代购者哈哈,形象具体生动