
代理模式的最典型应用是AOP. 本文主要介绍代理模式的几种实现模式: 静态代理和动态代理. 在这里,动态代理可以分为jdk代理和Cglib代理. 此外,本文还将对此进行讨论. 比较了几种代理模型的优缺点.
顾名思义,代理用于代表被请求者处理相关事务. 代理对象通常委派被请求者的所有权利. 客户端访问代理对象就像访问被请求者一样. 尽管代理对象最终仍可以访问被请求者,但它可以在请求之前或之后执行某些操作. 多余的工作或客户的请求是非法的,直接拒绝了客户的请求. 下图显示了代理模型的简化图:

代理模式的作用:
静态代理模式也是上图中描述的模式. 从图中可以看出,SubjectProxy存储一个ISubject实例. 当客户端调用SubjectProxy的request()方法时,它不仅会做其他工作. 它还调用ISubject实例的request()方法. 这是这三个类的简单实现:
public interface ISubject {
void request();
}
public class SubjectImpl implements ISubject {
@Override
public void request() {
System.out.println("request SubjectImpl.");
}
}
public class SubjectProxy implements ISubject {
private ISubject target;
public SubjectProxy(ISubject target) {
this.target = target;
}
@Override
public void request() {
System.out.println("before safety check.");
target.request();
System.out.println("after safety check.");
}
}

您可以看到在调用代理对象的方法之前和之后,代理对象都会打印相关语句. 这是一个客户请求的示例:
public class Client {
@Test
public void testStaticProxy() {
ISubject subject = new SubjectImpl();
ISubject proxy = new SubjectProxy(subject);
proxy.request();
}
}
运行上述用例,可以获得以下结果:
before safety check.
request SubjectImpl.
after safety check.
从客户端访问方法可以看出,客户端获得了实现ISubject接口的实例,并且它调用的request()方法实际上是代理对象的request()方法. 此代理方法称为静态代理,该代理方法也是最有效的一种,因为已经编写了所有类,并且客户端仅需要获取代理对象并执行它即可.
尽管静态代理更有效,但它也具有不可避免的缺陷. 可以看出,当客户端调用代理对象时,它使用由代理对象和代理对象实现的接口. 我们可以将此接口理解为定义特定业务需求的实现规范. 如果存在其他业务需求(例如数据修改),则该需求与当前需求平行且没有交叉,但是在正常业务之外执行的安全验证工作与当前需求一致. 以下是我们用于此数据修改业务的实现代码:

public interface IUpdatable {
void update();
}
public class UpdatableImpl implements IUpdatable {
@Override
public void update() {
System.out.println("update UpdatableImpl.");
}
}
public class UpdatableProxy implements IUpdatable {
private IUpdatable updatable;
public UpdatableProxy(IUpdatable updatable) {
this.updatable = updatable;
}
@Override
public void update() {
System.out.println("pre safety check.");
updatable.update();
System.out.println("after safety check.");
}
}
以下是客户端代码:
public class Client {
@Test
public void testStaticProxy() {
ISubject subject = new SubjectImpl();
ISubject proxy = new SubjectProxy(subject);
proxy.request();
IUpdatable updatable = new UpdatableImpl();
IUpdatable proxy = new UpdatableProxy(updatable);
proxy.update();
}
}
可以看出,要实现相同的对象代理功能(安全性验证),静态代理方法需要为每个接口实现一个代理类,并且这些代理类中的代码几乎相同. 这可能会导致大型系统中的重大维护问题.
①jdk代理商
所谓的jdk代理是指使用jdk提供的相关类来实现代理模型aop实现方案优缺点,主要有两个类: InvocationHandler和Proxy. 实现代理模式时,仅需要实现InvocationHandler接口. 以下是实现此接口的示例:

public class SafetyInvocationHandler implements InvocationHandler {
private Object target;
public SafetyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before safety check.");
Object result = method.invoke(target, args);
System.out.println("after safety check.");
return result;
}
}
以下是客户端调用方法:
public class Client {
@Test
public void testDynamicProxy() {
ISubject subject = new SubjectImpl();
ISubject proxySubject = (ISubject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{ISubject.class}, new SafetyInvocationHandler(subject));
proxySubject.request();
IUpdatable updatable = new UpdatableImpl();
IUpdatable proxyUpdatable = (IUpdatable) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IUpdatable.class}, new SafetyInvocationHandler(updatable));
proxyUpdatable.update();
}
}
您可以看到客户端在调用代理对象时使用相同的SafetyInvocationHandler. 在这里,jdk代理实际上使用反射为每个需要代理的对象创建一个InvocationHandler实例. 调用目标对象时aop实现方案优缺点,它将首先调用代理对象,然后在代理对象的逻辑中请求目标对象. 这就是为什么可以将目标对象实例保存在代理类中的原因,例如上述的SafetyInvocationHandler,该类声明了一个Object类型属性来保存目标对象实例.
jdk代理解决了静态代理需要为每个业务接口创建代理类的问题. 尽管使用反射创建代理对象的效率略低于静态代理,但在现代高速jvm中也可以接受. 在Spring的AOP代理中,默认情况下是使用jdk代理实现的. jdk代理的局限性在这里很明显,也就是说,需要成为代理的对象必须实现一个接口. 如果代理对象没有实现任何接口,或者代理业务方法没有相应的接口,我们可以使用另一种方法来实现它,即Cglib代理.
②Cglib代理

Cglib代理是最强大的代理方法,因为它不仅解决了静态代理需要创建多个代理类的问题,而且还解决了jdk代理需要通过代理对象来实现以实现接口的问题. 对于需要代理的类,如果可以为其创建一个子类并在该子类中编写相关的代理逻辑,因为“父类的子类实例”可以在进行调用时直接调用该子类对象的实例. 达到代理的效果. Cglib代理的原理实际上是动态生成代理类的子类字节码. 由于其字节码是根据jvm的已编译类文件的规范编写的,因此可以由jvm正常加载和运行. 这就是为什么Cglib代理不需要为每个代理类编写代理逻辑的原因. 此处应注意,根据Cglib的实现原理,由于它通过创建子类字节码来实现代理,因此,如果将代理类的方法声明为最终类型,则Cglib代理将无法正常工作,因为无法使用final Type方法. 覆盖. 这是使用Cglib代理的示例:
/**
* 被代理类
*/
public class Suject {
public void request() {
System.out.println("update without implement any interface.");
}
}
/**
* 代理类
*/
public class SafetyCheckCallback implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before safety check.");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after safety check.");
return result;
}
}
以下是客户端访问方法:
public class Client {
@Test
public void testCglibProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Suject.class);
enhancer.setCallback(new SafetyCheckCallback());
Suject proxy = (Suject) enhancer.create();
proxy.request();
}
}
您可以看到,首先在客户端代码中创建了一个Enhancer对象,并且设置了父类和代理回调类对象. Enhancer对象为目标类创建相关的子类字节码,并将代理代码嵌入到子类字节码中.
本文主要详细介绍了代理模式的三种实现模式,并比较了每种代理模式的优缺点. Spring主要使用动态代理模式来实现方面编程. 读者在这里可能会有一个问题,即在上面的代理代码中,根据实现的不同,客户端代码有些侵入性,例如静态代理客户端需要入侵代理类实例,jdk代理需要入侵代理类. Cglib代理需要插入子类和子类对象创建代码. 从理论上讲,客户端仅需要获取目标对象(无论它是否是代理),然后调用其相关方法即可实现特定功能. 这实际上是工厂方法的强大功能,因为工厂方法封装了对象的创建. 对象的特定创建过程可以根据特定业务进行处理. 客户只需要依靠工厂类来调用相关方法. 这也解释了为什么Spring IoC容器是AOP代理的自然支持,因为它将对象创建过程留给了容器.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-161139-1.html
重要的事说三遍
12海里基本就确认了
美单舰直入