23种设计模式之代理模式(Proxy)
结构型设计模式
意图: 为其它对象提供一个代理对象,并由代理对象控制这个对象的访问。
优点:
- 职责清晰
- 高扩展性
- 智能化
- 降低耦合度
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会在成请求的处理速度变慢
关键代码:
代理类实现接口,并且将被代理类作为属性引入
装饰器中的主要角色:
- 被代理的类
- 代理类(实现接口,接口类作为属性引入)
1. 代理模式和装饰者模式的异同
1.1 相同点:
- 都需要实现同一个接口或者继承同一个抽象类,并且代理角色和装饰角色都持有被代理角色和构件角色的引用。
- 两种模式都可以在被代理角色和具体构件角色的业务方法前后添加自己的方法。
1.2 不同点:
-
代理模式重点在于控制对象的行为,而装饰模式侧重于增加对象的职能(当然也可以削弱)。
通俗的来说,装饰者模式和代理模式根本上的差异就是目的性不同。 案例:公司领导为了更好的掌握公司一年的营收 1. 代理模式:领导找到一个专业的财务来处理 2. 装饰者模式:领导自己学习财务的技能,自己处理
2. 代理模式和适配器模式的差异
适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口
2.1 相同点
都是对某个对象进行包装
2.1 不同点
-
代理模式代理的是自己的同类,相同的接口或者父类
-
适配器包装的是不同类型的对象,然后将其伪装成自己的同类。
3. 代理,装饰,适配设计模式总结
代理主要 控制 代理对象的访问,装饰主要 增强 对象的功能,适配主要 伪装 异类对象成同类使用。
3. 代理模式代码实现:(静态代理)
public interface Action{
void movie();
}
public class Actor implement Action {
@Ovierride
public void movie(){
sout "The actor acted in a movie ";
}
}
public class Proxy implement Action {
private Actor actor;
public Proxy(Actor actor){
this.actor = actor;
}
@Ovierride
public void movie(){
sout("经纪人审查影视剧本的好坏,判断是否需要演员参演");
actor.movie();
}
}
public class DecoratorPatternDemo{
psvm{
Actor actor = new Actor();
Proxy proxy = new Proxy(actor);
proxy.movie();
}
}
4. 动态代理
动态代理:无需声明代理类。是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。
什么是动态代理?
当想要给实现了某个接口的类中的方法,加一些额外处理时,比如说加日志,加事务。顾名思意就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类不是定义好的,是动态生成的。具有解耦意义。灵活拓展性强。
4.1 JDK 动态代理
JDK 原生动态代理 :动态代理类和被代理类必须继承同一个接口。动态代理只能对接口中声明的方法进行代理。
实现步骤:
- 通过Proxy.newProxyInstance() 方法生成代理对象。
- 使用匿名内部类的方式,实现 InvocationHandle。
/**
* @param loader the class loader to define the proxy class :被代理类的类加载器
* @param interfaces the list of interfaces for the proxy class
* to implement :被代理类的接口集合
* @param h the invocation handler to dispatch method invocations to
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//动态代理
public interface Subject {
void rent();
}
//
public class RealSubject implements Subject {
@Override
public void rent() {
System.out.println("execute rent method");
}
}
//
public class ActiveProxyTest {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject subject1 = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before1 running");
method.invoke(realSubject,args);
System.out.println("after1 running");
return null;
}
});
subject1.rent();
}
}
第二种JDK动态代理实现
// JDK 原生动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TestJdkDynamicProxy {
public static void main(String[] args) {
IRegisterService iRegisterService = new RegisterServiceImpl();
InsertDataHandler insertDataHandler = new InsertDataHandler();
IRegisterService proxy = (IRegisterService)insertDataHandler.getProxy(iRegisterService);
proxy.register("RyanLee", "123");
}
}
class InsertDataHandler implements InvocationHandler {
Object obj;
public Object getProxy(Object obj){
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doBefore();
Object result = method.invoke(obj, args);
doAfter();
return result;
}
private void doBefore() {
System.out.println("[Proxy]一些前置处理");
}
private void doAfter() {
System.out.println("[Proxy]一些后置处理");
}
}
4.2 Cglib 动态代理
cglib 动态代理:是一个基于ASM的字节码生成库。它允许我们在运行时对字节码进行修改或动态生成。CGLib通过继承被代理类的方式实现代理。
实现步骤:
- 创建一个 Enhancer 对象
- 设置 enhancer 的类加载器
- 设置 enhancer 对象的父类(superClass)
- 设置回调方法(setCallBack)
- 通过 create() 创建代理对象
- 调用方法
public class RealSendMes {
public void SendMessage() {
System.out.println("hello moto");
}
}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyDemo {
public static void main(String[] args) {
RealSendMes realSendMes = new RealSendMes();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(realSendMes.getClass().getClassLoader());
enhancer.setSuperclass(realSendMes.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before send");
method.invoke(realSendMes,objects);
System.out.println("after send ");
return null;
}
});
RealSendMes sendMes = (RealSendMes)enhancer.create();
sendMes.SendMessage();
}
}
4.3 两种动态代理的差异
JDK 动态代理要求被代理对象必须基于接口来实现,JDK 动态代理类和被代理类必须继承同一个接口,JDK 动态代理只能对接口中声明的方法进行代理。对那些没有实现接口的 Bean 。JDK 动态代理无法代理,而 CGLIB 通过继承被代理类的方式实现代理。
5. 动态代理、静态代理的差异
- 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。
- 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。