前言:
本文内容:静态代理模式、静态回顾理解、动态代理详解
推荐免费Spring5基础教程视频:【狂神说Java】Spring5最新完整教程IDEA版通俗易懂_哔哩哔哩_bilibili
代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!
租客------>中介(代理)------>房东
静态代理模式
角色分析:
- 抽象角色:一般使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理后会做一些附属操作
- 客户:访问代理对象的人
通过租房分析代理模式
租房接口Rent.java
1 2 3 4 5 6 7 8 9 10
| package com.jokerdig.demo01;
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;
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;
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;
public class Client { @Test public void test(){ Host host = new Host(); 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;
public interface UserService { 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;
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;
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;
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;
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;
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;
public class ProxyInvocationHandler 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("收取中介费"); } }
|
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;
public class Client { @Test public void test3(){ Host host = new Host(); ProxyInvocationHandler handler = new ProxyInvocationHandler(); handler.setRent(host); 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;
public class ProxyInvocationHandlerUtil 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); }
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;
public class ProxyInvocationHandlerUtil1 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); }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 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;
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
|
小结
动态代理的好处
- 可以使真实角色的操作更纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的业务
- 动态代理可以代理多个类,只要实现同一个接口即可