前言:

本文内容:静态代理模式、静态回顾理解、动态代理详解

推荐免费Spring5基础教程视频:【狂神说Java】Spring5最新完整教程IDEA版通俗易懂_哔哩哔哩_bilibili

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!

租客------>中介(代理)------>房东

静态代理模式

角色分析:

  • 抽象角色:一般使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理后会做一些附属操作
  • 客户:访问代理对象的人

通过租房分析代理模式

租房接口Rent.java

1
2
3
4
5
6
7
8
9
10
package com.jokerdig.demo01;

/**
* @author Joker大雄
* @data 2022/5/18 - 17:13
**/
// 租房
public interface Rent {
public void rent();
}

房东Host.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.jokerdig.demo01;

/**
* @author Joker大雄
* @data 2022/5/18 - 17:14
**/
// 房东
public class Host implements Rent{

// 实现租客的方法
public void rent() {
System.out.println("房东要出租房子");
}
}

中介代理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
33
34
35
package com.jokerdig.demo01;

/**
* @author Joker大雄
* @data 2022/5/18 - 17:18
**/
// 代理(中介)
public class Proxy implements Rent{
private Host host;
public Proxy(){

}
public Proxy(Host host){
this.host = host;
}

public void rent() {
host.rent();
seeHouse(); // 看房
fare(); // 收中介费
contract();// 签合同
}
// 看房
public void seeHouse(){
System.out.println("中介带你看房");
}
// 收中介费
public void fare(){
System.out.println("收中介费");
}
// 签租赁合同
public void contract(){
System.out.println("签写租赁合同");
}
}

租客Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.jokerdig.demo01;

import org.junit.Test;

/**
* @author Joker大雄
* @data 2022/5/18 - 17:16
**/
// 租客
public class Client {
@Test
public void test(){
// 房东要租房子
Host host = new Host();
// 不通过代理直接租房
// host.rent();
// 通过代理租房 代理可以收中介费看房等等
Proxy proxy = new Proxy(host);
// 租客找中介租房
proxy.rent();
}
}

运行结果

1
2
3
4
5
6
房东要出租房子
中介带你看房
收中介费
签写租赁合同

Process finished with exit code 0

小结

静态代理模式的好处

  • 可以使真实角色的操作更纯粹!不用去关注一些公共的业务
  • 公共业务就交给代理角色,实现了业务的分工
  • 公共业务发生拓展的时候,方便集中管理

静态代理模式的缺点

  • 一个真实角色就会产生一个代理角色,代码量翻倍,效率变低

静态回顾理解

问题:在不改动原有代码的基础上,为原来的方法加入新的功能

解决:通过代理实现

UserService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.jokerdig.demo02;

/**
* @author Joker大雄
* @data 2022/5/18 - 18:11
**/
public interface UserService {
// CRUD
public void add();
public void delete();
public void update();
public void query();
}

UserServiceImpl.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
package com.jokerdig.demo02;

/**
* @author Joker大雄
* @data 2022/5/18 - 18:11
**/
// 真实对象
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("查询了一个用户");
}
}

UserServiceProxy.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
33
34
35
36
37
38
39
package com.jokerdig.demo02;

/**
* @author Joker大雄
* @data 2022/5/18 - 18:18
**/
public class UserServiceProxy implements UserService{

// 使用代理为每个功能添加日志输出
private UserServiceImpl userService;

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();
}
// 日志方法
public void log(String msg){
System.out.println("DEBUG:执行了"+msg+"方法");
}
}

UserTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.jokerdig.demo02;

import org.junit.Test;

/**
* @author Joker大雄
* @data 2022/5/18 - 18:16
**/
public class UserTest {
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();
// 使用代理方法
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
// 测试调用
proxy.add();
proxy.update();
proxy.delete();
proxy.query();
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
DEBUG:执行了add方法
添加了一个用户
DEBUG:执行了update方法
修改了一个用户
DEBUG:执行了delete方法
删除了一个用户
DEBUG:执行了query方法
查询了一个用户

Process finished with exit code 0

动态代理详解

  • 动态代理和静态代理角色一样

  • 动态代理类是动态生成的,不是我们直接写好的

  • 动态代理分为两大类:

    • 基于接口的动态代理:JDK

    • 基于类的动态代理:cglib

      java字节码实现:JAVAssist

需要了解两个类:Proxy 代理,InvocationHandler 调用处理程序

InvocationHandler

由代理实例的调用处理程序实现的接口

每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并且分派到其调用处理程序的invoke方法

Proxy

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类(SuperClass 也成为父类)

房东租客的例子改造

Rent.java

1
2
3
4
5
6
7
8
9
10
package com.jokerdig.demo03;

/**
* @author Joker大雄
* @data 2022/5/18 - 17:13
**/
// 租房
public interface Rent {
public void rent();
}

Host.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.jokerdig.demo03;

/**
* @author Joker大雄
* @data 2022/5/18 - 17:14
**/
// 房东
public class Host implements Rent {

// 实现租客的方法
public void rent() {
System.out.println("房东要出租房子");
}
}

ProxyInvoactionHandler.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
33
34
35
36
37
38
39
40
41
42
43
package com.jokerdig.demo03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* @author Joker大雄
* @data 2022/5/18 - 18:51
**/
// 我们用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

// 被代理的类
private Rent rent;
public void setRent(Rent rent){
this.rent = rent;
}

// 使用proxy创建动态代理
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("收取中介费");
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.jokerdig.demo03;

import org.junit.Test;

/**
* @author Joker大雄
* @data 2022/5/18 - 18:59
**/
public class Client {
@Test
public void test3(){
// 真实角色
Host host = new Host();
// 代理角色
ProxyInvocationHandler handler = new ProxyInvocationHandler();
// 通过调用程序处理角色来处理我们要调用的接口对象
handler.setRent(host);
// 这里的proxy就是动态生成的,我们并没有写
Rent proxy = (Rent) handler.getProxy();
proxy.rent();
}
}

运行结果

1
2
3
4
5
中介带看房子
房东要出租房子
收取中介费

Process finished with exit code 0

拓展

万能代理类

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
package com.jokerdig.demo04;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* @author Joker大雄
* @data 2022/5/18 - 19:13
**/
// 编写代理工具类
public class ProxyInvocationHandlerUtil implements InvocationHandler {

// 被代理的类
private Object target;

public void setTarget(Object target) {
this.target = target;
}

// 使用proxy创建动态代理
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}

// 处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
}

通过动态代理为CRUD增加日志输出

ProxyInvocationHandlerUtil1.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
33
34
35
36
37
38
package com.jokerdig.demo04;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* @author Joker大雄
* @data 2022/5/18 - 19:13
**/
// 编写代理工具类
public class ProxyInvocationHandlerUtil1 implements InvocationHandler {

// 被代理的类
private Object target;

public void setTarget(Object target) {
this.target = target;
}

// 使用proxy创建动态代理
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}

// 处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质就是使用反射机制实现
// 调用日志 通过method.getName()获取执行的方法名
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
// 日志输出
public void log(String msg){
System.out.println("DEBUG:执行了"+msg+"方法");
}
}

Client.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
package com.jokerdig.demo04;

import com.jokerdig.demo02.UserService;
import com.jokerdig.demo02.UserServiceImpl;
import org.junit.Test;

/**
* @author Joker大雄
* @data 2022/5/18 - 19:19
**/
public class Client {
@Test
public void test5(){
// 真实角色
UserServiceImpl userService = new UserServiceImpl();
// 创建代理角色
ProxyInvocationHandlerUtil1 handlerUtil = new ProxyInvocationHandlerUtil1();

handlerUtil.setTarget(userService);

// 动态生成代理类
UserService proxy = (UserService)handlerUtil.getProxy();
proxy.add();
proxy.query();
}
}

运行结果

1
2
3
4
5
6
DEBUG:执行了add方法
添加了一个用户
DEBUG:执行了query方法
查询了一个用户

Process finished with exit code 0

小结

动态代理的好处

  • 可以使真实角色的操作更纯粹!不用去关注一些公共的业务
  • 公共业务就交给代理角色,实现了业务的分工
  • 公共业务发生拓展的时候,方便集中管理
  • 一个动态代理类代理的是一个接口,一般就是对应的业务
  • 动态代理可以代理多个类,只要实现同一个接口即可