1. 代理设计模式
代理模式属于结构性设计模式的一种,核心思想是通过创建现有对象的代理对象来对外界提供业务
比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层
代理模式可以分类为静态代理模式和动态代理模式
静态代理模式
静态代理模式的组成包括:源对象(被代理的对象),源对象实现的接口,代理对象,下面看一个实例
接口:
public interface Subject {
void request(int id);
}
被代理对象:
public class RealSubject implements Subject {
public void request(int id) {
System.out.println("request : " + id);
}
}
代理对象:
PS:我们使用代理模式给原有方法新增了一个功能,也就是下面的打印开始请求提示
public class Proxy implements Subject {
private Subject target;
public Proxy(Subject s) {
target = s;
}
public void request(int id) {
System.out.println("开始请求!");
target.request(id);
}
public void setTarget(Subject realSubject) {
this.target = realSubject;
}
}
测试类:
public class Client {
public static void main(String args[]) {
Subject s = new Proxy(new RealSubject());
s.request(3);
}
}
输出:
开始请求!
request : 3
动态代理
动态代理通过引入反射的机制,让原本由我们自己写的代理类(Proxy)转变成动态生成
动态代理也分为基于接口的动态代理和基于类的动态代理,主要写一下基于接口的动态代理相关
动态代理的核心是下面这两个类
Proxy
类- 用来动态创建一个代理对象的类,主要是用的是
newProxyInstance
静态方法来创建
- 用来动态创建一个代理对象的类,主要是用的是
InvocationHandler
接口- 每一个动态代理类都必须要实现
InvocationHandler
这个接口,并且每个代理类的实 例都关联到了一个handler
,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler
这个接口的invoke
方法来进行调用
- 每一个动态代理类都必须要实现
对于下面这个接口
public interface RentDAO {
void view();
}
以及被代理对象
public class CarShop implements RentDAO {
@Override
public void view() {
System.out.println("看车");
}
}
我们可以生成一个用于创建代理对象的类
public class MyDynamicProxy implements InvocationHandler {
private Object proxyTarget;
public MyDynamicProxy(Object proxyTarget) {
this.proxyTarget = proxyTarget;
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxyTarget, args);
}
}
之后我们进行测试
public class Client {
public static void main(String[] args) {
CarShop cs = new CarShop();
MyDynamicProxy dynamicProxy = new MyDynamicProxy(cs);
RentDAO proxy =(RentDAO) dynamicProxy.getProxy();
proxy.view();
}
}
输出:
看车
可以看见我们仍然生成了代理对象,但是这个对象不是我们自己编写的,而是由Proxy
类中的静态方法newProxyInstance
得到的,这里提一下这个方法所需要的三个参数
ClassLoader loader
:一个类加载器,我们直接传入this.getClass().getClassLoader()
方法的返回值即可得到类加载器Class<?>[] interfaces
:目标代理对象实现的接口类型,同样使用proxyTarget.getClass().getInterfaces()
来得到InvocationHandler
:指定动态处理器,这里我们自己的MyDynamicProxy
就实现了InvocationHandler
接口
同样我们如果需要对目标方法进行增强的话,只需要这样子
public class MyDynamicProxy implements InvocationHandler {
private Object proxyTarget;
public MyDynamicProxy(Object proxyTarget) {
this.proxyTarget = proxyTarget;
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
reinforcement();
return method.invoke(proxyTarget, args);
}
public void reinforcement(){
System.out.println("方法增强啦!");
}
}
输出:
方法增强啦!
看车
动态代理使我们不必再手动维护代理类,保持了代理类的灵活性,而且可以很方便的在不需要更改原业务逻辑代码的前提下新增加新的业务功能,符合七大设计原则中的开闭原则——面对修改关闭,面对拓展开放
2. AOP
通俗版解释AOP:不通过修改源代码的方式增加新的主干功能(遵循开闭原则)
使用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP相关术语
连接点
类中的可以被增强的方法,被称作连接点
切入点
实际被增强的方法称为切入点
通知(增强)
实际增强的代码部分称为通知
通知有多种类型
- 前置通知
- 后置通知
- 环绕通知(前面和后面都做增强)
- 异常通知(类似于catch关键字下的)
- 最终通知(类似于finally关键字下的)
切面
切面指的是一个动作:把通知加入到切入点的过程
因此很自然的,AOP底层也是使用的是动态代理
不过Spring的AOP除了有基于接口的动态代理之外,还有基于CGLIB的动态代理
调用者Bean尝试调用目标方法,但是被生成的代理拦截
代理根据通知的种类,对通知首先进行调用
之后代理再调用目标方法,并返回调用结果给调用者Bean