最近在学狂神的spring,看得好慢,花了两三个星期才看玩ioc然后刚进入aop前奏的代理模式,今天突然不想看新的,想把之前学的代理模式好好总结一下
**接口类:**简单增删改查的功能
package com.fat.client.Demo02;
/**
* @author fatsea
* @date 2022/9/3 - 20:43
*/
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
真实角色:实现接口
package com.fat.client.Demo02;
/**
* @author fatsea
* @date 2022/9/3 - 20:43
*/
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add user");
}
@Override
public void delete() {
System.out.println("delete user");
}
@Override
public void update() {
System.out.println("update user");
}
@Override
public void query() {
System.out.println("query user");
}
//1.改动原有的业务代码,在公司中是大忌
}
Hovewer,这时候产品来个需求说要,对每个接口的方法打印一下日志
但是改动原有的业务代码是公司中的大忌
我们可以把真实对象抽出来,把这些打日志的杂七杂八交给代理中介做
因此,我们可以考虑定义一个proxy,它负责照料真实角色的业务,也有自己一些特有的逻辑:
代理角色:
package com.fat.client.Demo02;
/**
* @author fatsea
* @date 2022/9/3 - 20:46
*/
// 代理UserService
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("add");
userService.add();
}
@Override
public void update() {
log("add");
userService.add();
}
@Override
public void query() {
log("add");
userService.add();
}
//日志方法
public void log(String msg) {
System.out.println("[Debug]使用了" + msg + "方法");
}
}
首先的话,它要定义一个真实对象(表示它代理了谁),然后再在真实对象的方法上,加上他自己的一些杂七杂八的操作,例如这里的log操作
客户类
package com.fat.client.Demo02;
/**
* @author fatsea
* @date 2022/9/3 - 20:44
*/
public class Client {
// 使用代理不改变原有代码,扩展功能
// 业务和额外逻辑分离
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
客户的话,首先定义好它想要接除的真实对象UserServiceImpl
然后生成一个proxy负责代理这个真实对象
最后调用这个代理的add方法,一方面完成了真实对象的操作,另一方面也完成proxy的额外操作
以上就是静态代理的一个例子
它的核心就是,真实对象该干啥干啥,花里胡哨的边角功能都交给代理,实现解耦
我们知道代理可以实现分工,使得耦合性变低,但是静态代理的方法会使得代理翻倍
因此如果我们又想代理,又不想类增加,就要使用反射动态加载一些类
需要了解两个类:Proxy代理、InvocationHandler调用处理程序
InvocationHandler:invoke方法
proxy代理谁,method代理什么方法,args什么参数
返回结果(实例类)
还是分之前的四步走,但这里的显式proxy将会被一个proxy生成器代替
租房接口:
package com.fat.client.Demo03;
/**
* @author fatsea
* @date 2022/8/31 - 21:39
*/
// 租房
public interface Rent {
public void rent();
}
真实对象:房东
package com.fat.client.Demo03;
/**
* @author fatsea
* @date 2022/8/31 - 21:40
*/
// 房东
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子");
}
}
那么,关键来了,我们看看下面的
ProxyInvocationHandler:可以理解为召唤Proxy的类
package com.fat.client.Demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author fatsea
* @date 2022/9/3 - 21:10
*/
// 我们会用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
// Proxy提供了创建动态代理类和实例的静态方法
// 生成得到代理类
// 1.ClassLoader
// 2.代理类的接口
// 3.InvocationHandler(本身就是)
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
// 处理代理实例,并返回结果
// 执行代理真正要干的事情
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse(); // 中介特有的
// 通过反射执行方法
// 动态代理的本质就是使用反射机制实现
Object result = method.invoke(rent, args);
fare(); // 中介特有的
return result;
}
public void seeHouse() {
System.out.println("中介带看房子");
}
public void fare() {
System.out.println("中介收中介费");
}
}
在这个里面,还是要把需要被代理的真实对象定义好,然后需要注意的是它继承了InvocationHandler说明是一个调用处理器
getProxy:获取proxy,通过Proxy类的创建动态代理类和实例的静态方法,需要三个参数ClassLoader(就是本身,通过反射获取),代理类的接口(这里是rent的接口,通过反射获取),InvocationHandler(本身就是);很好,我们就有无中生有,生出一个proxy对象了,但是它的方法我们还没写好
invoke:负责方法的调用,里面可以插入一些中介特有的方法,例如看房子和收费;但是怎么调用原来的真实对象的方法呢,就要通过method.invoke(里面放入方法名字和参数即可),这里也是使用反射实现
客户类
package com.fat.client.Demo03;
/**
* @author fatsea
* @date 2022/9/3 - 21:17
*/
public class Client {
// 没有代理类
public static void main(String[] args) {
// 真实角色
Host host = new Host();
// 代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 通过调用程序处理角色,来处理我们要调用的接口对象!
// 设置真实要代理的角色
pih.setRent(host);
// 通过方法动态生成代理类
// 这里的proxy就是动态生成的代理, 我们并没有写
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
客户的调用链是:生成真实对象,获取一个proxy生成器,把真实对象丢进生成器里面,然后再获取一个继承真是对象接口的proxy;最后就可以调用代理的方法了(也就是加工了的真实对象的方法)
把真实对象写成Object,可以写成代理生成的工具类
package com.fat.client.Demo04;
import com.fat.client.Demo03.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author fatsea
* @date 2022/9/3 - 21:10
*/
// 我们会用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口(这里不写死,变成工具类)
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// Proxy提供了创建动态代理类和实例的静态方法
// 生成得到代理类
// 1.ClassLoader
// 2.代理类的接口
// 3.InvocationHandler(本身就是)
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// InvocationHandler处理代理实例,并返回结果
// 执行代理真正要干的事情
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
// 通过反射执行方法
// 动态代理的本质就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
public void log(String msg) {
System.out.println("执行了" + msg + "方法");
}
}
套路都是一样,只不过把之前实现的rent改成万金油object
这里注意打日志的话,又来了一个反射,获取方法的名字即可
框架处处体现着反射
代理这种思想就是解耦
什么类该干什么分工明确,加强可扩展性,可维护性
不改变原有的代码
静态代理需要重写代理类,而动态代理可以使得同一个接口(同一类业务)共享一个代理