一个小需要: 给原有办法增加日志打印
假如当初咱们有一个类 Calculator,代表一个计算器,它能够进行加减乘除操作
public class Calculator {
// 加
public int add(int a, int b) {
int result = a + b;
return result;
}
// 减
public int subtract(int a, int b) {
int result = a - b;
return result;
}
// 乘法、除法...
}
现有一个需要:在每个办法执行前后打印日志。你有什么好的计划?
间接批改
很多人最直观的想法是间接批改 Calculator 类:
public class Calculator {
// 加
public int add(int a, int b) {System.out.println("add 办法开始...");
int result = a + b;
System.out.println("add 办法完结...");
return result;
}
// 减
public int subtract(int a, int b) {System.out.println("subtract 办法开始...");
int result = a - b;
System.out.println("subtract 办法完结...");
return result;
}
// 乘法、除法...
}
下面的计划是有问题的:
- 间接批改源程序,不合乎开闭准则。应该对扩大凋谢,对批改敞开
- 如果 Calculator 有几十个、上百个办法,批改量太大
- 存在反复代码(都是在外围代码前后打印日志)
- 日志打印硬编码在代理类中,不利于前期保护:比方你花了一上午终于写完了,组长通知你这个性能勾销,于是你又要关上 Calculator 花十分钟删除日志打印的代码!
所以,此种计划 PASS!
动态代理实现日志打印
“动态代理”四个字蕴含了两个概念:动态、代理。咱们先来理解什么叫“代理”,至于何为“动态”,须要和“动静”比照着讲。
代理是一种模式,提供了对指标对象的间接拜访形式,即通过代理拜访指标对象。如此便于在指标实现的根底上减少额定的性能操作,前拦挡,后拦挡等,以满足本身的业务需要。
援用博客:Java 动态代理和动静代理 – 纪煜楷 – 博客园
援用自:Java 动态代理和动静代理 – 纪煜楷 – 博客园
罕用的代理形式能够粗分为:动态代理和动静代理。
动态代理的实现比较简单:编写一个代理类,实现与指标对象雷同的接口,并在外部保护一个指标对象的援用。通过结构器塞入指标对象,在代理对象中调用指标对象的同名办法,并增加前拦挡,后拦挡等所需的业务性能。
按下面的形容,代理类和指标类须要实现同一个接口,所以我打算这样做:
- 将 Calculator 抽取为接口
- 创立指标类 CalculatorImpl 实现 Calculator
- 创立代理类 CalculatorProxy 实现 Calculator
接口
/**
* Calculator 接口
*/
public interface Calculator {int add(int a, int b);
int subtract(int a, int b);
}
指标对象实现类
/**
* 指标对象实现类,实现 Calculator 接口
*/
public class CalculatorImpl implements Calculator {
// 加
public int add(int a, int b) {
int result = a + b;
return result;
}
// 减
public int subtract(int a, int b) {
int result = a - b;
return result;
}
// 乘法、除法...
}
代理对象实现类
/**
* 代理对象实现类,实现 Calculator 接口
*/
public class CalculatorProxy implements Calculator {
// 代理对象外部保护一个指标对象援用
private Calculator target;
// 构造方法,传入指标对象
public CalculatorProxy(Calculator target) {this.target = target;}
// 调用指标对象的 add,并在前后打印日志
@Override
public int add(int a, int b) {System.out.println("add 办法开始...");
int result = target.add(a, b);
System.out.println("add 办法完结...");
return result;
}
// 调用指标对象的 subtract,并在前后打印日志
@Override
public int subtract(int a, int b) {System.out.println("subtract 办法开始...");
int result = target.subtract(a, b);
System.out.println("subtract 办法完结...");
return result;
}
// 乘法、除法...
}
应用代理对象实现加减乘除,并且打印日志
public class Test {public static void main(String[] args) {
// 把指标对象通过结构器塞入代理对象
Calculator calculator = new CalculatorProxy(new CalculatorImpl());
// 代理对象调用指标对象办法实现计算,并在前后打印日志
calculator.add(1, 2);
calculator.subtract(2, 1);
}
}
动态代理示意图
动态代理的长处:能够在不批改指标对象的前提下,对指标对象进行性能的扩大和拦挡。然而它也仅仅解决了上一种计划 4 大毛病中的第 1 点:
- 间接批改源程序,不合乎开闭准则。应该对扩大凋谢,对批改敞开 √
- 如果 Calculator 有几十个、上百个办法,批改量太大 ×
- 存在反复代码(都是在外围代码前后打印日志)×
- 日志打印硬编码在代理类中,不利于前期保护:比方你花了一上午终于写完了,组长通知你这个性能勾销,于是你又要关上 Calculator 花十分钟删除全副新增代码!×
-
- *
动态代理的问题
下面案例中,代理类是咱们当时编写的,而且要和指标对象类实现雷同接口。因为 CalculatorImpl(指标对象)须要日志性能,咱们即编写了 CalculatorProxy(代理对象),并通过结构器传入 CalculatorImpl(指标对象),调用指标对象同名办法的同时增加加强代码。
然而这里有个问题!代理对象结构器的参数类型是 Calculator,这意味着它只能承受 Calculator 的实现类对象,亦即咱们写的代理类 CalculatorProxy 只能给 Calculator 做代理,它们绑定死了!
如果当初咱们零碎须要全面革新,给其余类也增加日志打印性能,就得为其余几百个接口都各自写一份代理类 …
本人手动写一个类并实现接口切实太麻烦了。认真一想, 咱们其实想要的并不是代理类,而是代理对象! 那么,是否让 JVM 依据接口主动生成代理对象呢?
比方,有没有一个办法,我传入接口,它就给我主动返回代理对象呢?
答案是必定的。