代理模式

代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理!

代理模式:

  • 静态代理
  • 动态代理

mark

1. 静态代理

  • 抽象角色:一般使用接口或者抽象类实现
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属的操作。
  • 客户:使用代理角色来进行一些操作。
  1. Rent.java 即抽象角色
1
2
3
4
// 提升的功能(也就是抽象角色)
public interface Rent {
public void rent();
}
  1. Host.java 即真实角色
1
2
3
4
5
6
7
//真实角色: 房东,房东要出租房子
public class Host implements Rent{

public void rent() {
System.out.println("我是房东,我要出租房子");
}
}
  1. Proxy.java 即代理角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//代理角色:中介

public class Proxy implements Rent{

// 寻找房东
private Host host;

// 允许new中介
public Proxy(Host host) {
this.host = host;
}

public Proxy() {
}



public void rent() {
seeHouse();
host.rent();
fare();
}

private void fare() {
System.out.println("收中介费");
}

private void seeHouse() {
System.out.println("带客官看房");
}

}
  1. Client.java 客户
1
2
3
4
5
6
7
8
9
10
11
12
13
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
// 房东这个人首先要活着
Host host = new Host();

// 再去找中介
Proxy proxy = new Proxy(host);

// 中介给你房子
proxy.rent();
}
}

以上就是一个非常轻松易懂的租房攻略(哦 不对 ,代理模式例子)

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点:

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

当改进了静态代理的缺点之后,动态代理就产生了!

2. 静态代理再理解

  1. 创建一个抽象角色(增删改查)
1
2
3
4
5
6
7
//抽象角色:增删改查业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
  1. 真实对象完成增删改查任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}

public void delete() {
System.out.println("删除了一个用户");
}

public void update() {
System.out.println("更新了一个用户");
}

public void query() {
System.out.println("查询了一个用户");
}
}
  1. 现在添加一个需求,增加一个日志功能
  • 思路1:在实现类上增加代码 【麻烦!】
  • 思路2:交给代理去做(在不改变原有代码的基础上,实现此功能)
  1. 设置一个代理类来处理日志(代理角色)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class UserServiceProxy implements UserService {
// 代理代理谁?
private UserServiceImpl userService;

// Spring会通过set注入
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

public void add() {
log("add");
userService.add();
}

public void delete() {
log("delete");
userService.delete();
}

public void update() {
log("update");
userService.update();
}

public void query() {
log("query");
userService.query();
}

// 日志功能
private void log(String msg) {
System.out.println("执行了" + msg + "方法");
}
}
  1. 测试(客户端)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
// 被代理的首先要活着
UserServiceImpl userService = new UserServiceImpl();


// 代理类来代理业务
UserServiceProxy proxy = new UserServiceProxy();

//使用代理类实现日志功能
proxy.setUserService(userService);
proxy.add();

}
}

OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;

  • 我们在不改变原来的代码的情况下,实现了对原有功能的增强,
  • 这是AOP中最核心的思想

mark

3. 动态代理(反射实现)

  • 动态代理的角色和静态代理一样
  • 动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
  • 动态代理分为两类:一类是基于接口的动态代理,一类是基于类的动态代理
    • 基于接口的动态代理:JDK动态代理(Proxy,InvocationHandler)
    • 基于类的动态代理:cglib
    • 现在用得比较多的是javasist来生成动态代理(可以百度一下)

我们这里使用JDK原生代码来实现,其余道理是一样的。

3.1 两个类

核心:

  • InvocationHandler
  • Proxy

【InvocationHandler:调用处理程序】

mark

1
2
3
4
5
6
7
8
9
10
11
12
13
package java.lang.reflect;


/**
proxy - 调用该方法的代理实例
method - 代理接口方法的实例
args - 代理接口方法的实例的参数
*/

public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

【Proxy : 代理】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    
// 主要是newProxyInstance方法,动态生成代理类
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

3.2 通用动态代理实现类

编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ConfigProxy 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);
}


// proxy : 代理类
// method : 代理类中要代理的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ....
// 业务逻辑代码
return method.invoke(target,args);
}
}

3.3 实例探究

抽象角色和真实角色和之前的一样!

  1. 抽象角色
1
2
3
4
5
6
package com.zhuuu.invokeProxy;

// 提升的功能(也就是抽象角色)
public interface Rent {
public void rent();
}
  1. 真实角色
1
2
3
4
5
6
7
8
//真实角色: 房东,房东要出租房子

public class Host implements Rent{

public void rent() {
System.out.println("我是房东,我要出租房子");
}
}
  1. 代理角色(动态代理)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class RentProxy implements InvocationHandler {
// 代理的功能
private Rent rent;

public void setRent(Rent rent) {
this.rent = rent;
}


// 生成代理类,重点是第二个参数,获取要代理的抽象角色!
// 之前都是一个角色,现在可以代理一类角色
public Object getProxy(){
return Proxy.newProxyInstance(
this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),
this);
}


// 处理代理实例上的方法调用并返回结果
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("收中介费");
}
}
  1. Client.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
// 首先一样的,房东要活着
Host host = new Host();

// 代理角色代理房东
RentProxy pih = new RentProxy();
pih.setRent(host);//将真实角色放置进去!


// 动态生成对应的代理类(注意返回类型是接口)
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}

核心:

  • 一个动态代理
    • 一般代理某一类业务,
    • 一个动态代理可以代理多个类
    • 代理的是接口

3.4 优点总结

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信