1. 代理模式
1.1 什么是代理模式
代理模式:是Java常用的一种设计模式,代理提供了对目标对象另外的访问方式,这样在能在原来的基础上增加其他功能。
代理模式分类:静态代理、动态代理
2. 静态代理
在静态代理中需要定义两个实现类:目标对象类、代理对象类。
⚠️注意:目标对象类和代理对象类需要实现同一个接口。
2.1 角色分类
静态代理模式中有四种角色。
Subject(抽象角色):通过接口或抽象类声明真实角色的业务方法。
RealSubject(真实角色):实现抽象角色的类。
Proxy(代理角色):实现真实角色的业务逻辑,并附加自己的逻辑。
Client(客户角色):使用代理角色进行操作。
2.2 代码实现(一)
现实需求:房东需要出租房子,顾客想要租房子,原来房东可以直接把房子租给顾客。现在加入代理,房东依旧要实现出租房子,代理能够帮助房东实现出租房子,但代理会和顾客完成看房、签租赁合同、收代理费的业务。
接口为:ZuFang
房东实现租房:FangDong
代理实现租房:DaiLi
顾客租房:Client
租房的业务如下:
// 租房的业务
public interface ZuFang {
public void Zufang();
}
2.2.1 房东实现租房
// 房东类实现租房的业务
public class FangDong implements ZuFang{
@Override
public void Zufang() {
System.out.println("房东出租房子");
}
}
// 租客租房
public class Client {
public static void main(String[] args) {
FangDong fangDong = new FangDong();
fangDong.Zufang();
}
}
/*
输出:
房东出租房子
*/
2.2.2 代理实现租房
房子实际是房东的,所以代理需要先定义房东类。并且需要有参/无参构造、get/set方法。
public class DaiLi implements ZuFang {
private FangDong fangDong;
public FangDong getFangDong() {
return fangDong;
}
public void setFangDong(FangDong fangDong) {
this.fangDong = fangDong;
}
public DaiLi() {
}
public DaiLi(FangDong fangDong) {
this.fangDong = fangDong;
}
@Override
public void Zufang() {
fangDong.Zufang();
}
}
上面代理只实现了租房的业务,代理还具有添加自己业务的能力。额外的方法包括看房、签租赁合同、收代理费。
public void zuFang() {
seeHouse();
fangDong.zuFang();
heTong();
fare();
}
// 看房
public void seeHouse(){
System.out.println("带人看房");
}
// 收中介费
public void fare(){
System.out.println("收中介费");
}
// 签租赁合同
public void heTong(){
System.out.println("签合同");
}
// 租客租房
public class Client {
public static void main(String[] args) {
// 真实角色
FangDong fangDong = new FangDong();
// 代理角色
DaiLi daiLi = new DaiLi(fangDong);
daiLi.Zufang();
}
}
/*
输出:
带人看房
房东出租房子
签合同
收中介费
*/
2.3 代码实现(二)
现实需求:实现一个增删查改的方法,并通过代理的方式添加日志输出。
抽象角色:UserService
真实角色:UserService
代理角色:DaiLi
测试:MyTest
2.3.1 抽象、真实角色
// 抽象角色:增删改查业务
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
// 真实角色,实现增删查改
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("添加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void update() {
System.out.println("更改一个用户");
}
@Override
public void query() {
System.out.println("查询用户");
}
}
2.3.2 代理、测试
public class DaiLi implements UserService {
//真实角色
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public DaiLi(UserServiceImpl userService) {
this.userService = userService;
}
public DaiLi() {
}
@Override
public void add() {
Log("add");
userService.add();
}
@Override
public void delete() {
Log("delete");
userService.delete();
}
@Override
public void update() {
Log("update");
userService.update();
}
@Override
public void query() {
Log("query");
userService.query();
}
// 添加日志的方法,并在重写的方法中调用该方法
public void Log(String str){
System.out.println("执行了" + str + "方法");
}
}
// 测试
public class MyTest {
@Test
public void TestUserService(){
// 真实角色
UserServiceImpl userService = new UserServiceImpl();
// 代理角色
DaiLi daiLi = new DaiLi();
// 使用代理实现日志的功能
daiLi.setUserService(userService);
daiLi.add();
daiLi.delete();
daiLi.update();
daiLi.query();
}
}
2.4 优缺点
静态代理的优点:
- 代理可以扩展自己的功能。
- 代理模式能将真实角色与客户角色分离
- 降低了代码的耦合度,提高了代码的可扩展性
- 公共业务扩展时变得更加集中和方便
静态代理的缺点:
- 增加代码的冗余、程序的复杂度
- 降低了开发效率
3. 动态代理
动态代理:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。
3.1 核心类/接口
在java的java.lang.reflect
包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
核心:InvocationHandler(调用处理程序) 和 Proxy (代理)
InvocationHandler是由代理实例的调用处理程序实现的接口。
3.1.1 InvocationHandler
InvocationHandler接口的内容如下:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
proxy
:调用该方法的代理实例
method
:所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args
:包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer
或java.lang.Boolean
。
3.1.2 Proxy
Proxy类中重要的方法是:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h);
通过该静态来创建代理对象。通常这样使用:
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
3.2 代码实现(一)
利用静态代理模式中用到的房东租房的例子来改为动态代理,需求不变,功能不变。
接口为:ZuFang
房东实现租房:FangDong
代理实现:Proxy_Invo
测似:MyTest
3.2.1 抽象、真实角色
public interface ZuFang {
public void Zufang();
}
public class FangDong implements ZuFang {
@Override
public void Zufang() {
System.out.println("房东出租房子");
}
}
3.2.2 代理、测试
代理步骤:
- 实现代理接口:InvocationHandler
- 定义需要代理的业务(租房)
- 生成代理类(getProxy)
- 处理代理类(invoke)
// 代理类
public class Proxy_Invo implements InvocationHandler {
// 需要被代理的接口
public ZuFang zuFang;
public void setZuFang(ZuFang zuFang) {
this.zuFang = zuFang;
}
// 生成代理类
// 第二个参数:zuFang.getClass().getInterfaces(),需要代理的抽象角色
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), zuFang.getClass().getInterfaces(), this);
}
// 处理代理类并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
// 本质:通过反射实现
Object invoke = method.invoke(zuFang, args);
fare();
return invoke;
}
// 其他业务
public void seeHouse() {
System.out.println("看房子");
}
public void fare(){
System.out.println("收中介费");
}
}
public class Mytest {
public static void main(String[] args) {
// 真实对象
FangDong fangDong = new FangDong();
// 代理实例化
Proxy_Invo proxy_invo = new Proxy_Invo();
// 通过调用程序处理角色来处理我们要调用的接口对象!
proxy_invo.setZuFang(fangDong);
// 自动生成代理
ZuFang proxy = (ZuFang) proxy_invo.getProxy();
proxy.Zufang();
}
}
method.invoke:
public Object invoke(Object obj, Object... args)
obj:调用底层方法的对象
args:用于方法调用的参数
🌟 核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、
3.3 代码实现(二)
利用静态代理模式中用到的增删查改的例子来改为动态代理,需求不变,功能不变。
接口为:UserService
增删查改实现:UserServiceImpl
代理实现:Proxy_Invo
测似:TestUserService
3.3.1 抽象、真实角色
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("添加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void update() {
System.out.println("更改一个用户");
}
@Override
public void query() {
System.out.println("查询用户");
}
}
3.3.2 代理、测试
public class Proxy_Invo implements InvocationHandler {
// 设置代理对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
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 {
Log(method.getName());
method.invoke(target, args);
return null;
}
public void Log(String str){
System.out.println("执行了" + str + "方法");
}
}
// UserService,动态代理测试
@Test
public void TestUserService(){
// 真实对象
UserServiceImpl userService = new UserServiceImpl();
//代理对象的调用处理程序,即自己写的代理对象
Proxy_Invo proxy_invo = new Proxy_Invo();
proxy_invo.setTarget(userService); //设置需要代理的对象
UserService proxy = (UserService) proxy_invo.getProxy(); // 动态生成代理对象
proxy.delete();
proxy.add();
proxy.query();
proxy.update();
}
/*
输出:
执行了delete方法
删除一个用户
执行了add方法
添加一个用户
执行了query方法
查询用户
执行了update方法
更改一个用户
*/
3.4 优缺点
动态代理的优点:
- 减少了不必要的冗余代码(代理类)
- 类职责更加单一,复用性更强
动态代理的缺点:
- JDK动态代理基于接口实现,不支持实现类的代理