本篇文章是根据B站狂神说Java系列的Spring5的笔记记录 —— 第二篇代理模式介绍
gitee:https://gitee.com/ywq869819435/spring5
1、代理模式
概述
- SpringAOP的底层实现就是采用代理模式
- 代理模式分类(属于23种模式之一[单例模式、原型模式等等])
- 静态代理
- 动态代理
举例
生活例子:
- 中介租房
- 房东只想出租房子,不想去交谈价格什么的,这时候就交由中介去代理出租房子这件事,但是最终还是出租的是自己的房子
2、静态代理
角色分析
- 抽象角色:一般使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤
- 抽象类或者接口(真实角色需要代理角色完成事情)
- 真实角色
- 代理角色
- 客户端访问代理角色
简单示例(租房)
抽象类或者接口(房东要中介出租他的房子)
// 租房
public interface Rent {
public void rent();
}
真实角色
// 房东
public class Homeowners implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子!");
}
}
代理角色
// 中介,代理
public class Proxy implements Rent {
// 少用继承,采用这种组合的方式获取房东
private Homeowners homeowners;
public Proxy() {
}
public Proxy(Homeowners homeowners) {
this.homeowners = homeowners;
}
@Override
public void rent() {
// 中间带你看房
findHouse();
// 帮房东出租房子
homeowners.rent();
// 中介带你签合同
signTheContract();
// 收中介费
collectFee();
}
public void findHouse(){
System.out.println("中介带你看房!");
}
public void signTheContract(){
System.out.println("签租房合同");
}
// 收中介费
public void collectFee(){
System.out.println("收中介费");
}
}
客户访问代理角色
// 租客、客户
public class Client {
public static void main(String[] args) {
// 房东出租房子,但不想做多余的事情
Homeowners homeowners = new Homeowners();
// 交给中介,中介帮房东出租房(代理房东)
// 但是代理角色会带有自己的附属操作
// 若无其他附属操作,这样代理无意义
Proxy proxy = new Proxy(homeowners);
// 不用面对房东,而是面对中介
// 虽然走的是代理,但是最终出粗的还是房东的房子
proxy.rent();
}
优点
- 可以使真实角色的操作更加纯粹!不需要关注一些公关的业务
- 公关业务交由代理角色!实现了业务的分工!
- 公关业务发生扩展的时候,方便集中管理
缺点
- 一个真实角色就会产生一个代理角色,代码量会成倍增长,降低开发效率
复杂示例(CURD)
实现一个业务服务,且在后台打印出执行日志
服务接口类
public interface UserService {
void create();
void update();
void read();
void delete();
}
服务接口实现类
- 纯粹的业务实现,不掺杂其他业务无关的代码
- 之所以关于日志的打印就交由代理去做,是因为它是业务无关的操作,且为了保证业务代码正常运作
public class UserServiceImpl implements UserService{
@Override
public void create() {
System.out.println("增加一个用户");
}
@Override
public void update() {
System.out.println("修改一个用户");
}
@Override
public void read() {
System.out.println("查询一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
}
代理实现类
- 实现业务服务
- 增强的功能在这里增加实现,比如日志功能
public class UserServiceProxy implements UserService{
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void create() {
log("create");
userService.create();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void read() {
log("read");
userService.read();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
public void log(String msg){
System.out.println("执行了" + msg + "方法");
}
}
main方法
public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.create();
proxy.update();
proxy.read();
proxy.delete();
}
}
有关AOP的启示
纵向开发,横向切入(增强功能)
3、动态代理
3.1、概述
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口典例:JDK动态代理
- 基于类的典例:
- cglib
- java字节码实现(JAVAsist)
3.2、基于接口的动态代理
- 需要了解两个类:
- Proxy:代理
- InvocationHandler:调用处理程序
InvocationHandler类
- reflect下的一个接口
InvocationHandler
是由代理实例的调用处理程序实现的接口 。- 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的
invoke
方法。 - 只有一个接口,就是invoke,用于处理代理实例上的方法调用并返回结果,有三个参数
- proxy:你要代理谁
- method:代理这个类里面的哪一个方法
- args:一些参数,method方法需要用的参数
Proxy类
-
提供了动态代理类和实例的静态方法
-
static InvocationHandler getInvocationHandler(Object proxy) // 返回指定代理实例的调用处理程序
-
static class<?> getProxyClass(ClassLoader loader, class<?>... interfaces) // 给出类加载器和接口数组的代理类的 java.lang.Class对象
-
static boolean isProxyClass(Class<?> cl) // 如果且仅当使用 getProxyClass方法或 newProxyInstance方法将指定的类动态生成为代理类时,则返回true
-
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) // 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序 // 类加载器来定义代理类 // 需要代理的代理类的接口列表 // 调用处理代理实例的方法
-
例子1
该列子演示如何由程序去生成代理类,但是还没有动态的切换设置代理类的代理对象,例子2中表明【区别在于生成代理的程序,是否采用泛型去设置代理对象,当采用泛型去设置的时候就可以达到动态的生成代理类,也就是多个真实对象去完成其他统一的操作】
代理生成类(处理程序)
// 会用这类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口(这里是写死的代理对象)
private Rent rent;
public Rent getRent() {
return rent;
}
public void setRent(Rent rent) {
this.rent = rent;
}
// Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), //类加载器来定义代理类
// new Class<?>[] { Foo.class }, // 需要代理的代理类的接口列表
// handler); // 调用处理代理实例的方法
// 生成得到代理对象
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);
fareHouse();
return result;
}
private void seeHouse(){
System.out.println("中介带你看房子");
}
private void fareHouse(){
System.out.println("收中介费");
}
}
需要代理的对象
// 房东
public class Homeowners implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子!");
}
}
代理的实现的接口(动作)
// 租房
public interface Rent {
public void rent();
}
调用代理对象的方法
public class Client {
public static void main(String[] args) {
// 真实角色
Homeowners homeowners = new Homeowners();
// 代理角色:暂时没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 通过调用程序处理角色来处理要调用的接口对象
pih.setRent(homeowners);
// 这里的rent就是动态生成的,并没有写它
Rent rent = (Rent) pih.getProxy();
rent.rent();
}
}
例子2
这里例子是将被代理的对象采用泛型来处理,也就是具有动态性。可以实现多个被代理对象统一完成一类操作。本例子中统一完成类似日志打印的功能
生成代理的类(处理程序)
// 会用这类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的对象
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
// Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), //类加载器来定义代理类
// new Class<?>[] { Foo.class }, // 需要代理的代理类的接口列表
// handler); // 调用处理代理实例的方法
// 生成得到代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
// 处理代理实例并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质 就是使用反射机制实现
Object result = method.invoke(target,args);
// 比如类似日志打印的功能
System.out.println(method.getName() + "实现了");
return result;
}
}
调用代理对象的方法
public class Client {
public static void main(String[] args) {
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 生成代理类的类
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 设置要被代理的对象
pih.setTarget(userServiceImpl);
UserService userService = (UserService) pih.getProxy();
userService.create();
Homeowners homeowners = new Homeowners();
// 设置要被代理的对象
pih.setTarget(homeowners);
Rent rent = (Rent) pih.getProxy();
rent.rent();
}
}
3.3、动态代理优点
- 可以使真实角色的操作更加纯粹!不需要关注一些公关的业务
- 公关业务交由代理角色!实现了业务的分工!
- 公关业务发生扩展的时候,方便集中管理
- 一个动态代理类代的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。
4、总结
静态代理
- 只能作为某些真实对象的代理帮它完成它要完成的事(由其他人实现,代理对象只是去通知那个他去完成这件事)的前后,完成代理需要做的一些额外的事
- 原则:不去改变原来真实对象的任何东西的时候,完成自己的要做的事
- 实现:代理里面包含有这个真实对象的类,将真实对象实现类完好的放入自己的类里面,之后代理再在执行完真实对象的需求的前后增加完成或者说获取自己的额外收获
- 过程:真实对象向代理对象请求完成这件事,代理对象接受后交由真实对象的实现类去完成这件事的前后,附加完成自己要做的事
动态代理
- 任意的真实对象的代理帮它完成它要完成的事(由其他人实现,代理对象只是去通知那个他去完成这件事)的前后,完成代理需要做的一些额外的事
- 原则:不去改变原来真实对象的任何东西的时候,完成自己的要做的事【原则与静态代理是一样的,不同的是处理真实对象需求的能力】
- 实现:代理里面包含一个泛型类,通过某种方式(常见的有反射)方式将不同的真实对象实现类完好的放入自己的类里面,之后代理再在执行完真实对象的需求的前后增加完成或者说获取自己的额外收获
- 过程:真实对象向代理对象请求完成这件事,代理对象接受后交由真实对象的实现类去完成这件事的前后,附加完成自己要做的事