1, 代理模式介绍:
1.1, 为什么要学习代理模式? 因为这就是 Spring Aop 的底层!(SpringAop 和 SpringMvc)
1.2, 代理模式的分类:
静态代理
动态代理
1.3, 代理模式关系图 (以租房子为例)
2, 静态代理
2.1, 角色分析:
抽象角色: 一般会使用接口或者抽象类来解决
真实角色: 被代理的角色
代理客户: 代理真实角色. 代理真实角色后, 我们一般会做一些附属的操作
客户: 访问代理对象的人
2.2, 例 1(租房子演示)
2.2.1, 抽象角色实现 (房东抽象类)
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo1;
- /**
- * 租房
- */
- public interface Rent {
- void rent();
- }
2.2.2, 真实角色实现 (房东的实现类)
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo1;
- /**
- * 房东
- */
- public class Host implements Rent {
- @Override
- public void rent() {
- System.out.println("房东要出租房子!");
- }
- }
2.2.3, 不通过代理进行租房
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo1;
- public class Client {
- public static void main(String[] args) {
- // 直接找房东
- Host host = new Host();
- host.rent();
- }
- }
运行结果:
房东要出租房子!
2.2.4, 弊端
在实际的生活场景中, 房东可能只卖房, 而不负责租房
如果要提高租房子的成功率, 必然还需要一些其他的服务, 如: 看房, 谈价钱, 签合同等, 而这些房东并不想参与, 因此产生了中介, 也就是代理.
2.2.5, 代理出现
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo1;
- public class Proxy implements Rent {
- private Host host;
- public Proxy() {
- }
- public Proxy(Host host) {
- this.host = host;
- }
- @Override
- public void rent() {
- seeHouse();
- host.rent();
- fare();
- hetong();
- }
- // 看房
- public void seeHouse() {
- System.out.println("中介代理看房");
- }
- // 收中介费
- public void fare() {
- System.out.println("收中介费");
- }
- public void hetong() {
- System.out.println("签合同");
- }
- }
2.2.6, 通过代理来租房子
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo1;
- 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();
- }
- }
运行结果:
中介代理看房
房东要出租房子!
收中介费
签合同
2.3, 分析:
代理模式的好处
可以使真实角色的操作更加纯粹, 不用去关注一些公共的业务.
公共的业务交给了代理角色去完成, 实现了业务的分工.
公共业务发生扩展的时候, 方便集中管理.
缺点:
一个真实角色就会产生一个代理的角色, 代码量翻倍, 开发效率变低.
2.4, 例 1(增删改查业务的演示)
2.4.1, 编写 service 的抽象类
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo2;
- public interface UserService {
- void add();
- void del();
- void update();
- void query();
- }
2.4.2, 编写 service 的实现类
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo2;
- /**
- * 真实对象
- */
- public class UserServiceImpl implements UserService {
- @Override
- public void add() {
- System.out.println("增加了一个用户");
- }
- @Override
- public void del() {
- System.out.println("删除了一个用户");
- }
- @Override
- public void update() {
- System.out.println("更新了一个用户");
- }
- @Override
- public void query() {
- System.out.println("查询了一个用户");
- }
- }
2.4.3, 如果此时想不改变原有的代码, 而增加方法日志打印的功能, 此时代理诞生
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo2;
- /**
- * 代理角色, 增加日志的功能
- */
- public class UserServiceProxy implements UserService {
- private UserService userService;
- public void setUserService(UserService userService) {
- this.userService = userService;
- }
- @Override
- public void add() {
- userService.add();
- log("add");
- }
- @Override
- public void del() {
- userService.del();
- log("del");
- }
- @Override
- public void update() {
- userService.update();
- log("update");
- }
- @Override
- public void query() {
- userService.query();
- log("query");
- }
- public void log(String msg) {
- System.out.println("使用了" + msg + "方法!");
- }
- }
2.4.4, 编写客户端类, 使用 service 的方法, 并打印日志
package com.xgp.company. 结构性模式. 代理模式. 静态代理. Demo2;
- public class Client {
- public static void main(String[] args) {
- UserService userService = new UserServiceImpl();
- UserServiceProxy proxy = new UserServiceProxy();
- proxy.setUserService(userService);
- proxy.add();
- proxy.del();
- proxy.update();
- proxy.query();
- }
- }
运行结果:
增加了一个用户
使用了 add 方法!
删除了一个用户
使用了 del 方法!
更新了一个用户
使用了 update 方法!
查询了一个用户
使用了 query 方法!
2.5, 反思: 在实际的业务中, 横向开发的好处
可以保证原有代码的可行性, 不会对软件原来的版本进行破坏, 向过去兼容.
3, 动态代理
3.1, 动态代理的特点
动态代理和静态代理一样
动态代理的代理类是动态生成的, 不是我们直接写的
3.2, 动态代理实现方式的分类
基于接口 --JDK 动态代理 (本节需要讲述的方式)
基于类: cglib
java 字节码实现: javasist
3.3, 需要了解的两个类
Proxy: 代理类
InvocationHandler: 调用处理程序类
具体的使用参照 java 的 API
3.4, 例 1-- 房东租房的例子
3.4.1, 房东的接口和实现类不变
3.4.2, 实现自动生成代理的类
package com.xgp.company. 结构性模式. 代理模式. 动态代理. Demo1;
- 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;
- }
- /**
- * 获得一个代理
- * @return
- */
- public Object getProxy() {
- return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
- }
- /**
- * 处理代理实例, 并放回结果
- * @param proxy
- * @param method
- * @param args
- * @return
- * @throws Throwable
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // 用反射来执行方法
- seeHouse(); // 调用代理自身的方法
- // 动态代理的本质, 就是使用反射机制来实现
- Object result = method.invoke(rent, args);
- fare();
- hetong();
- return result;
- }
- public void seeHouse() {
- System.out.println("中介带看房子!");
- }
- // 收中介费
- public void fare() {
- System.out.println("收中介费");
- }
- public void hetong() {
- System.out.println("签合同");
- }
- }
3.4.3, 实现客户端使用代理来租房子
package com.xgp.company. 结构性模式. 代理模式. 动态代理. Demo1;
- public class Client {
- public static void main(String[] args) {
- // 真实角色
- Host host = new Host();
- // 代理角色
- ProxyInvocationHandler pih = new ProxyInvocationHandler();
- // 通过调用程序处理角色, 来处理我们要调用的接口对象
- pih.setRent(host);
- // 放回代理类
- // 这里的代理类就是动态生成的, 我们并没有写他
- Rent proxy = (Rent) pih.getProxy();
- proxy.rent();
- }
- }
运行结果:
中介带看房子!
房东要出租房子!
收中介费
签合同
3.5, 例 2-- 增删改查的例子
3.5.1, 先来实现一个万能的代理生成类
package com.xgp.company. 结构性模式. 代理模式. 动态代理. Demo2;
- 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;
- }
- /**
- * 获得一个代理
- * @return
- */
- public Object getProxy() {
- return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
- }
- /**
- * 处理代理实例, 并放回结果
- * @param proxy
- * @param method
- * @param args
- * @return
- * @throws Throwable
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // 用反射来执行方法
- // 动态代理的本质, 就是使用反射机制来实现
- Object result = method.invoke(target, args);
- log(method.getName());
- return result;
- }
- public void log(String msg) {
- System.out.println("使用了" + msg + "方法!");
- }
- }
也就是将上一个例子的具体实现类 -- 房东类, 替换成 Object 类
3.5.2,UserService 的接口和实现类不变
3.5.3, 编写客户端类使用代理调用增删改查方法
package com.xgp.company. 结构性模式. 代理模式. 动态代理. Demo2;
- public class Client {
- public static void main(String[] args) {
- // 真实角色
- UserService userService = new UserServiceImpl();
- // 代理角色
- ProxyInvocationHandler pih = new ProxyInvocationHandler();
- pih.setTarget(userService);
- UserService proxy = (UserService) pih.getProxy();
- proxy.add();
- proxy.del();
- proxy.update();
- proxy.query();
- }
- }
运行结果:
增加了一个用户
使用了 add 方法!
删除了一个用户
使用了 del 方法!
更新了一个用户
使用了 update 方法!
查询了一个用户
使用了 query 方法!
3.6, 弊端分析:
虽然使用动态代理能够节省代码量, 并且实现静态代理的全部优点. 但是, 动态代理的核心是反射技术, 通过反射技术调用方法效率较大, 因此也可能影响系统效率.
3.7, 动态代理的好处
一个动态代理的是一个接口, 一般就是对应的一类业务.
来源: https://www.cnblogs.com/xgp123/p/12308415.html