代理模式
- SpringAOP的底层就是代理模式, 代理模式分为: 静态代理和动态代理
静态代理
- 角色分析:
- 抽象角色:一般使用接口或者抽象类解决
- 真实角色: 带代理的角色
- 代理角色: 代理真实角色, 代理真实角色后, 我们一般会做一些附属操作
- 客户: 访问代理角色真实的人!
- 代理模式的好处:
- 可以使真实角色操作更加纯粹, 不用去关心一些闲杂的事情,
- 闲杂事情让代理去做,实现业务的分工,
- 当闲杂事情发生变化的时候, 真实角色是不用去管的, 代理角色去管就行, 这样就方便各角色的集中管理
-
缺点就是: 一个真实角色就会产生一个代理角色, 代码量会大, 开发效率会低
-
代码步骤就是:
- 统一要做的事情的接口
- 一个真实角色去实现接口
- 一个代理角色实现接口还要包含一个真实角色(内部new一个真实角色)
- 客户访问代理角色
- 简而言之就是把原先直接访问的真实角色换成现在访问的是代理角色, 但是最终处理的事情都是一样的, 都是处理的真实角色的事情, 这样做我们就不需要去修改真实角色, 因为如果这个真实角色被多个人使用, 我们修改真实角色就会涉及到很大的范围, 但是我们只修改代理角色, 就不会影响都其他人, 在团队开发过程中这是很重要的, 使用静态代理模式极大的保护了真实角色的代码, 更多我们需要处理的事情就交给代理去做
演示
- 就如同上图所示, 我现在要给dao层的代码加日志显示, 下面我就用静态代理的方式去实现, 注意观察我并不修改dao层的代码, 只是增加了一个dao类的代理类
- 数据库接口
public interface Database {
void add();
void delete();
void update();
void query();
}
- dao层
public class Dao implements Database {
@Override
public void add() {
System.out.println("add数据");
}
@Override
public void delete() {
System.out.println("delete数据");
}
@Override
public void update() {
System.out.println("update数据");
}
@Override
public void query() {
System.out.println("query数据");
}
}
- dao的代理类
public class DaoProxy implements Database {
private Dao dao;
public DaoProxy() {
this.dao = new Dao();
}
@Override
public void add() {
journal("add");
dao.add();
}
@Override
public void delete() {
journal("delete");
dao.delete();
}
@Override
public void update() {
journal("update");
dao.update();
}
@Override
public void query() {
journal("query");
dao.query();
}
private void journal(String srt) {
System.out.println("[DEBUG] 执行了" + srt);
}
}
- service层
public class Service1 {
private DaoProxy daoProxy;
public Service1() {
daoProxy = new DaoProxy();
}
public void add() {
daoProxy.add();
}
public void delete() {
daoProxy.delete();
}
public void update() {
daoProxy.update();
}
public void query() {
daoProxy.query();
}
}
- controller层
public class Controller {
public static void main(String[] args) {
Service1 service = new Service1();
service.add();
service.delete();
service.update();
service.query();
}
}
动态代理
动态代理和静态的代理的角色分布是一样的
动态代理的代理类是动态生成的, 不是我们直接写好的
动态代理分为俩大类 : 基于接口的动态代理, 基于类的动态代理
- 基于接口的: 如JDK动态代理
- 基于类: 如cglib
- java字节码实现: 如javasist
下面主要演示基于接口的动态代理, 需要知道Proxy:代理和InvocationHandler: 调用动态管理程序 这俩个类
动态代理有静态代理的所有好处, 还有一个额外的好处就是
- 一个动态代理 代理的是一个接口, 可以代理一类业务
- 一个动态代理可以代理多个类, 只要实现同一个接口就行, 动态代理就会通过反射这个类,得到一个动态代理的类
演示
Proxy和InvocationHandler的官方文档
https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/Proxy.html
https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/InvocationHandler.html
- 文档中的实例和注解很详细, 比如给出的伪代码
- 也就是只需要一个实现了InvocationHandler接口的类重写其invoke方法, 使用在Proxy的静态方法newProxyInstance获取到一个代理类.
- 我们在使用newProxyInstance需要传入被代理对象的ClassLader,和interfaces还有我们实现InvocationHandler接口的类
- 当要执行代理类中的方法时, 就会被分配到invoke方法, 这时, 你就可以增加额外的操作, 使用method.invoke就会执行被代理类中的方法
- 动态代理类
//每个代理实例都有一个关联的调用处理程序。
// 在代理实例上调用方法时,该方法调用将被编码并分派到invoke 其调用处理程序的方法。
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getTarget() {
//通过Proxy的静态方法得到代理接口的代理类
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
//实现代理并返回结果
//其关联的代理实例上调用方法时,将在调用处理程序上调用该方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//调用invoke方法会经过反射 处理被代理实例中的代码
Object ret = method.invoke(target, args);
end(method.getName());
return ret;
}
private void end(String name) {
System.out.println("[OVER]" + name);
}
private void log(String name) {
System.out.println("[DEBUG]" + name);
}
}
- 演示: 动态代理的框架
package dyAgent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created with IntelliJ IDEA.
* Description: If you don't work hard, you will be a loser.
* User: Listen-Y.
* Date: 2020-10-23
* Time: 16:06
*/
//每个代理实例都有一个关联的调用处理程序。
// 在代理实例上调用方法时,该方法调用将被编码并分派到invoke 其调用处理程序的方法。
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getTarget() {
//通过Proxy的静态方法得到代理接口的代理类
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
//实现代理并返回结果
//其关联的代理实例上调用方法时,将在调用处理程序上调用该方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//调用invoke方法会经过反射 处理被代理实例中的代码
Object ret = method.invoke(target, args);
end(method.getName());
return ret;
}
private void end(String name) {
System.out.println("[OVER]" + name);
}
private void log(String name) {
System.out.println("[DEBUG]" + name);
}
}
public class Demo {
public static void main(String[] args) {
//主角
Service service = new Service();
//获得动态代理类
MyInvocationHandler handler = new MyInvocationHandler();
handler.setTarget(service);
//我们使用的是JDK中基于接口实现的动态代理, 所以得用接口去接受, 所以上面set进去的一定要实现下面的这个接口
Database serviceProxy = (Database) handler.getTarget();
serviceProxy.add();
serviceProxy.delete();
serviceProxy.query();
serviceProxy.update();
}
}