Java中动态代理
场景
有一个业务功能接口,接口中有两个业务方法,创建对应的实现类。
package com.klaus.service;
public interface MyService {
public void service1();
public void service2();
}
package com.klaus.service;
public class MyServiceImpl implements MyService {
@Override
public void service1() {
System.out.println("业务方法一");
}
@Override
public void service2() {
System.out.println("业务方法二");
}
}
此时业务功能正常
package com.klaus;
import com.klaus.service.MyService;
import com.klaus.service.MyServiceImpl;
public class main {
public static void main(String[] args) {
MyService myService = new MyServiceImpl();
myService.service1(); //业务方法一
myService.service2(); //业务方法二
}
}
新增功能
此时新增要求,在每个业务执行前打印日志,业务执行后进行事务提交。
解决方法
一、源码更改
第一种很简单,直接在原业务方法上更改。这种方式看起来很简单,但却在业务类中出现了大量重复的、与业务无直接关联的冗余代码。
package com.klaus.service;
public class MyServiceImpl implements MyService {
@Override
public void service1() {
System.out.println("日志信息");
System.out.println("业务方法一");
System.out.println("提交事务");
}
@Override
public void service2() {
System.out.println("日志信息");
System.out.println("业务方法二");
System.out.println("提交事务");
}
}
二、代理类
可以新建一个第三方类,在这个类中呢,既有业务对象,又有新增业务。执行业务的时候不直接在 MyServiceImpl 中调用业务方法,而是首先在第三方类中先调用日志方法(因为会多次用到,所以可以新建工具类吧把新增的日志功能、提交事务功能编成方法加进去);然后通过第三方类中的业务对象调用调用业务方法;最后再在第三方类中调用提交事务的方法。这样就可以实现新的功能,并且不在原来的业务代码上进行改动。新建的这个第三方类称作代理类(自己什么都没有,像是一个中介)。具体示例如下
代理类
package com.klaus;
import com.klaus.service.MyServiceImpl;
import com.klaus.util.ServiceTools;
public class Test {
MyServiceImpl service;
public Test(MyServiceImpl service) {
this.service = service;
}
public void service1(){
ServiceTools.log();
service.service1();
ServiceTools.trans();
}
public void service2(){
ServiceTools.log();
service.service2();
ServiceTools.trans();
}
}
测试类
package com.klaus;
import com.klaus.service.MyService;
import com.klaus.service.MyServiceImpl;
public class main {
public static void main(String[] args) {
Test test = new Test(new MyServiceImpl());
test.service1();
//Thu Aug 12 11:45:42 CST 2021
//业务方法一
//提交事务
System.out.println("++++++++++++++++++++");
//++++++++++++++++++++
test.service2();
//Thu Aug 12 11:45:42 CST 2021
//业务方法二
//提交事务
}
}
三、动态代理
上面的代码是我自己手写的,但是在 Java 中,可以基于 JDK 实现动态代理。这里代理类是MyProxy.java,或者说代理对象是 MyProxy 中的 proxy。JDK 中并没由把代理功能全放在一起实现,而是把方法执行的功能呢交给MyInvocationHandler 做的是接收传递来的方法并处理。
主要方法:
-
Proxy.newProxyInstance()。第一个参数 loader 是目标类(要被代理的类)的类加载器。
第二个参数 interfaces 是目标类实现的接口。
第三个参数 h 是代理对象要执行的功能。
前两个参数都是通过反射得到的。通过代理执行方法,会调用 h 中的 invoke() 方法。public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
-
Method.invoke()。第一个参数 obj 是 要执行的方法所在的类。
第二个参数 args 是方法的参数。
public Object invoke(Object obj, Object... args)
就是和反射一模一样的思想。反射的讲述可以参考这里。
MyInvocationHandler.java
package com.klaus.handler;
import com.klaus.util.ServiceTools;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ServiceTools.log();
Object res = method.invoke(target, args);
ServiceTools.trans();
return res;
}
}
MyProxy.java
package com.klaus.proxy;
import com.klaus.handler.MyInvocationHandler;
import com.klaus.service.MyService;
import com.klaus.service.MyServiceImpl;
import java.lang.reflect.Proxy;
public class MyProxy {
public static void main(String[] args) {
MyService service = new MyServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(service);
MyService proxy =(MyService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
handler);
proxy.service1();
//Thu Aug 12 11:57:34 CST 2021
//业务方法一
//提交事务
System.out.println("++++++++++++++++++++++");
//++++++++++++++++++++++
proxy.service2();
//Thu Aug 12 11:57:34 CST 2021
//业务方法二
//提交事务
}
}
最后是我的
本文来自博客园,作者:klaus08,转载请注明原文链接:https://www.cnblogs.com/klaus08/p/15132115.html
原文地址:https://www.cnblogs.com/klaus08/p/15132115.html
- 博弈论及算法实现
- Hadoop数据分析平台实战——160Sqoop介绍离线数据分析平台实战——160Sqoop介绍
- HDU 2186 悼念512汶川大地震遇难同胞——一定要记住我爱你
- Hadoop数据分析平台实战——150Flume介绍离线数据分析平台实战——150Flume介绍
- Codeforces 714A Meeting of Old Friends
- Code forces 719A Vitya in the Countryside
- Hadoop数据分析平台实战——190Highcharts介绍离线数据分析平台实战——190Highcharts介绍
- HUST 1555 A Math Homework
- HUST 1541 Student’s question
- HDU 3785 寻找大富翁
- Hadoop数据分析平台实战——250JSSDK数据收集引擎编写离线数据分析平台实战——250JSSDK数据收集引擎编写
- HDU 2564 词组缩写
- 约瑟夫问题方法总结
- 欧里几德及扩展欧里几德算法
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- CppSQLite - C++ Wrapper for SQLite
- 在.NET Core中使用MongoDB明细教程(3):Skip, Sort, Limit, Projections
- wsl 2 unbuntu 部署 asp.net core 使用 nginx 做反向代理,调试文件上传失败
- .NET Core + K8S + Apollo 玩转配置中心
- 休眠与唤醒机制
- Guava - 布隆过滤器的使用
- [浅析] 特定场景下代替优化 if-else 的方案 (二)
- 面试官:mysql如何重置自增id
- 如何在kubernete集群上部署springboot应用
- lettuce连接池很香,撸撸它的源代码
- 浅谈kubernete中的flannel网络插件
- 聊聊Java中CompletableFuture的使用
- 开发更高可用、高质量的服务的一些建议
- kubernete编排技术一:pod
- Golang逃逸分析