一. 代理模式
1.1 静态代理
1.1.1 静态代理角色分析
- 抽象角色:一般使用接口或者抽象类来实现
- 真实角色:被代理的对象
- 代理角色:代理真实角色;代理真实角色后,一般会做一些附属的操作
- 客户:使用代理角色来进行一些操作
其实这个挺好理解的
就按照租房子的例子来讲
1.1.2 静态代理代码实现
租房接口
package com.eason.demo01;
//一个租房的接口,真实角色和代理角色要实现的
public interface Rent {
void rent();
}
真实角色:房东
package com.eason.demo01;
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租的房子");
}
}
代理角色:中介
package com.eason.demo01;
public class Proxy {
private Host host;
public Proxy () {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
fare();
}
//中介可以看很多套房
public void seeHouse() {
System.out.println("中介带你看很多很多套房子!");
}
//收中介费
public void fare() {
System.out.println("收中介费!");
}
}
客户
package com.eason.demo01;
//我要租房子,我是客户
public class Client {
public static void main(String[] args) {
//我们可以直接找房租去租房子,直接就可以租到房子
//Host host = new Host();
//host.rent();
//但是很多时候我们找不到房东,我们只能去找中介公司
//首先房东这个角色还是存在的,但是我们现在找不到房东,我们没有办法调用到房东,只能由中介公司调用房东
//房东要出租房子,但是不想一个一个要去找用户
Host host = new Host();
//代理中介,中介帮房东租房子,代理也有一些附属的操作,例如看房,收中介费
Proxy proxy = new Proxy(host);
//用户可以不用面对房东,可以直接找中介租房子
proxy.rent();
}
}
1.1.3 分析代码
房东的目的就是出租房子,客户的目的是租房子,中介的目的就是帮助房东去出租房子,帮助客户去租房子
房东就可以直接把房子交给中介就可以不用理睬了,然后客户也是提出需求就等到中介帮我们找到所需房子,这样可以让房东和客户的目的更加纯粹,所有的业务逻辑都交由中介去完成,客户可以看不同房东出租的房子,房东也可以看到不同的租客去权衡去选择,将一对一的关系转化为了多对多的关系!
同时中介就是一个代理角色,房东也就是真实角色,抽象角色就是租房子这一件事情。
1.1.4 静态代理的好处
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情
- 公共的业务由代理来完成 . 实现了业务的分工
- 公共业务发生扩展时变得更加集中和方便
1.1.5 静态代理的缺点
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低
- 我们要想实现静态代理,就必须一个真实角色对应一个类,所有类会有很多
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
1.2 动态代理
1.2.1 动态代理角色分析
动态代理的角色和静态代理的角色是一样的
- 抽象角色:一般使用接口或者抽象类来实现
- 真实角色:被代理的对象
- 代理角色:代理真实角色;代理真实角色后,一般会做一些附属的操作
- 客户:使用代理角色来进行一些操作
动态代理的代理类是动态生成的,不像静态代理那样是我们直接写好的
1.2.2 动态代理类别
动态代理分为三大类:
- 基于接口的代理——JDK动态代理
- 基于类的动态代理——cglib
- Java字节码实现——javassist
需要两个类:
- Proxy:代理
- InvocationHandler:代理处理程序,是一个反射包下的一个接口
了解动态代理需要了解反射,反射才是真正的Java灵魂,动态代理就是基于反射来完成的
下面通过增删改查的案例代码来帮助理解动态代理的实现
动态代理和静态代理一样有四大角色
1.2.3 动态代理代码实现
抽象对象
- 也就是相当于你去租房子租房这一件事情,是我们最终要完成的业务
- 这个案例里面有四个需要完成的业务(增删改查),相当于我们的MVC里的业务逻辑层,这个抽象对象没什么好讲的,就是添加上我们的业务就好
package com.eason.proxyTest;
//抽象对象
public interface UserService {
//查询方法
void select();
//添加方法
void insert();
//删除方法
void delete();
//更新方法
void update();
}
真实角色
- 这个是我们的业务逻辑层,可以在这里面写上相应的业务逻辑,是抽象对象的实现类
- 这个里面是我们要实现的一些基本逻辑,也就是相当于租房子里面的房东,里面是一些简单纯粹的房东需求,房东就将这里面的方法传递给中介,让中介进行呈现给用户查看
package com.eason.proxyTest;
//真实角色
public class UserServiceImpl implements UserService{
@Override
public void select() {
System.out.println("执行了select方法");
}
@Override
public void insert() {
System.out.println("执行了insert方法");
}
@Override
public void delete() {
System.out.println("执行了delete方法");
}
@Override
public void update() {
System.out.println("执行了update方法");
}
}
代理对象(重点)
- 动态代理,顾名思义就是让他动态生成一个能够代理的对象,是介于房东和客户中间的一个中介,不像静态代理那样需要手动创建
- 那么这个时候就需要两个类来帮我们动态的创建这个代理对象了
- 一个是Proxy类和InvocationHandler类
- 这里面我们需要掌握的就是Proxy和InvocationHandler这两个类
- 掌握反射的Method类和invoke方法
代码里面的注释都挺详细的了,大家可以看一看,如果看不明白的话可以返回去看一下Java基础的反射,里面主要用到了反射的两个参数Method类和invoke方法
这相当于一个工具类,动态生成代理的一个工具类,我们只需要将我们真实角色类型参数值传入,也就是相当于把房东传进来,这个是用户来实现的,就会为这个房东自动生成一个代理对象,然后通过这个代理对象去实现房东的需求,和实现一些中介的附加方法,里面的log和logs方法就是实现中介的附加方法
package com.eason.proxyTest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态生成的代理对象
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到的代理类
public Object getProxy() {
//新建一个动态代理对象
//在这里下面的method方法就映射到了target这个类里面,所以在下面我们可以获取到target这个类里面的方法
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces() , this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//使用反射类去调用方法名字来获取信息
//这里是执行代理附加的方法
log(method.getName());
//执行方法,来利用反射完成调用target类里面的方法
Object invoke = method.invoke(target, args);
//这里同样也是执行代理附加的方法
logs(method.getName());
return invoke;
}
public void log(String msg) {
System.out.println(msg + "了一个用户!");
}
public void logs(String msg) {
System.out.println("一个用户" + msg + "完毕!");
}
}
用户类
- 用户类需要注意的是,我们依旧需要创建一个真实角色对象
- 创建真实对象之后我们还需要创建一个动态代理对象
- 将真实角色对象传入动态代理对象,让动态代理对象能够识别真实角色对象里面的方法
package com.eason.proxyTest;
//客户
public class User {
public static void main(String[] args) {
//新建一个真实角色,使用真实角色完成方法
UserServiceImpl userService = new UserServiceImpl();
//动态新建一个代理对象
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//将我们的真实角色传入,让ProxyInvocationHandler这个类进行读取我们真实角色对象里面的方法,方便我们调用
pih.setTarget(userService);
UserService user = (UserService) pih.getProxy();
user.select();
user.delete();
}
}
二. 代理模式总结
代理模式其实说到底用到的还是我们Java反射的知识,毕竟生成动态代理所继承的接口InvocationHandler就是Java反射包下的
代理类(ProxyInvocationHandler)和真实角色类(UserServiceImpl)都是实现同一个接口来完成接口里面的方法,只是在实现的时候用户是通过代理对象去实现的,并没有通过真实角色类实现。
我们在真实处理业务的过程中,如果不改变原有的业务逻辑,但是需要添加一些在实现业务逻辑之前我们要实现的方法的时候就可以用到代理模式,比如说实现方法之前打印日志,那么这个时候我们就可以不改变原有的代码,而是生成一个代理对象,在我们的代理对象里面添加这个日志的业务逻辑,毕竟改变原代码的风险还是很大的,一不留神可能就是层出不穷的Bug!
这篇Java基础篇——代理对象的文章就写到这里,上面一些内容都是自己对于Java代理模式的理解,感觉理解的不是很透彻,自己水平也不是很高,大家看了有不对的可以多多提出,一起交流,共同学习,共同进步!