代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,同时还可以在不改变目标对象的前提下,为客户端提供附加的功能。
代理模式的优点:
- 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
- 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
静态代理
需要定义接口或父类,目标对象与代理对象一起实现相同的接口或继承相同的父类。
优点:可以在不修改目标对象的功能前提下,对目标对象功能进行扩展。
缺点:代理对象需要与目标对象实现一样的接口,会有很多代理类。一旦接口增加方法,目标对象与代理对象都需要维护。
- 创建公共接口
package org.example.base.service;
/**
* 租房接口
**/
public interface Tenancy {
void renting();
}
- 创建委托类
package org.example.base.service.impl;
import org.example.base.service.Tenancy;
public class TenancyImpl implements Tenancy{
@Override
public void renting() {
System.out.println("付钱");
}
}
3.创建代理类,并测试
package org.example.base.test;
import org.example.base.service.Tenancy;
import org.example.base.service.impl.TenancyImpl;
/**
* @author l
* @date Created in 2021/4/20 15:53
* 设计模式-》静态代理
*/
public class TenancyStaticProxy implements Tenancy {
/**
* 持有被代理引用
**/
private final Tenancy tenancy;
public TenancyStaticProxy(Tenancy tenancy) {
this.tenancy = tenancy;
}
@Override
public void renting() {
System.out.println("中介找房子");
tenancy.renting();
System.out.println("中介办理入住合同");
}
public static void main(String[] args) {
//没有经过中介代理的租房子过程(实例化)
TenancyImpl tenancy = new TenancyImpl();
//经过中介代理(类似spring aop环绕通知)
TenancyStaticProxy proxy = new TenancyStaticProxy(tenancy);
//执行
proxy.renting();
}
}
动态代理
代理对象不实现接口,目标对象实现接口,可使用动态代理,否则不可使用
动态代理,也叫做JDK代理,接口代理。
代理对象,利用java反射机制,动态在内存中构建,不需要实现接口,但需指定接口类型。
package org.example.base.test;
import org.example.base.service.Tenancy;
import org.example.base.service.impl.TenancyImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author l
* @date Created in 2021/4/20 15:53
* 设计模式-》动态代理
*/
public class TenancyDynamicProxy implements InvocationHandler {
//委托对象
private final Object object;
public TenancyDynamicProxy(Object tenancy) {
this.object = tenancy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("中介找房子");
Object invoke = method.invoke(object, args);
System.out.println("中介办理入住合同");
return invoke;
}
/**
* 获取代理对象
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(), new TenancyDynamicProxy(object));
}
public static void main(String[] args) {
TenancyDynamicProxy proxy = new TenancyDynamicProxy(new TenancyImpl());
Tenancy proxyInstance = (Tenancy) proxy.getProxyInstance();
proxyInstance.renting();
}
}
基于CGLIB的动态代理
静态代理和动态代理模式,都需要目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这时可以使用以目标对象子类的方式类实现代理,这种方法叫做,cglib代理。也叫做子类代理,它是在内存中构建一个子类对象从而实现目标对象的扩展。但是不能对final修饰的类进行代理。
package org.example.base.test;
import org.example.base.service.impl.TenancyImpl;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author l
* @date Created in 2021/4/22 17:06
*/
public class TenancyCglibProxy implements MethodInterceptor {
/**
* 委托对象
*/
private final Object object;
public TenancyCglibProxy(Object object) {
this.object = object;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("中介找房子");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("中介办理入住合同");
return result;
}
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public static void main(String[] args) {
TenancyCglibProxy cglibProxy = new TenancyCglibProxy(new TenancyImpl());
TenancyImpl tenancy = (TenancyImpl) cglibProxy.getProxyInstance();
tenancy.renting();
}
}
装饰模式和代理模式的区别
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
外观模式和代理模式的区别
代理与外观的主要区别在于,代理对象代表一个单一对象而外观对象代表一个子系统,代理的客户对象无法直接访问对象,由代理提供单独的目标对象的访问,而通常外观对象提供对子系统各元件功能的简化的共同层次的调用接口。代理是一种原来对象的代表,其他需要与这个对象打交道的操作都是和这个代表交涉的。
适配器模式和代理模式的区别
适配器模式改变所考虑的对象的接口,代理模式不能改变所代理对象的接口。