8. Spring之AOP前奏
程序员文章站
2022-04-11 12:59:17
...
文章目录
Spring之AOP前奏
1. 提出问题
情景:数学计算器
要求:
- 执行加减乘除运算
- 日志:在程序执行期间追踪正在发生的活动
- 验证:希望计算器只能处理正数的运算
常规实现
Calculator
/**
* @Date 2020/5/23 23:23
* @Version 10.21
* @Author DuanChaojie
*/
public interface Calculator {
int add(int m,int n);
int sub(int m,int n);
// ...
}
CalculatorImpl
/**
* @Date 2020/5/23 23:24
* @Version 10.21
* @Author DuanChaojie
*/
public class CalculatorImpl implements Calculator {
public int add(int m, int n) {
System.out.println("日志:执行了add方法参数1="+m+",参数2="+n);
int result = m + n;
System.out.println("日志:执行add方法结束,结果为"+result);
return result;
}
public int sub(int m, int n) {
System.out.println("日志:执行了sub方法参数1="+m+",参数2="+n);
int result = m - n;
System.out.println("日志:执行sub方法结束,结果为"+result);
return result;
}
// ...
}
2. 问题分析
代码混乱
:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀。每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。代码分散
: 以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码。如果日志需求发生变化,必须修改所有模块。
3. 动态代理
原理:
- 代理设计模式的原理:使用一个代理将原本对象包装起来,然后用该代理对象”取代”原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
方式:
- 基于接口实现动态代理:
JDK动态代理
- 基于继承实现动态代理:
Cglib
、Javassist
动态代理
4. 动态代理改进后
注释,之前在CalculatorImpl中的日志信息。
4.1 创建代理对象
newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。- 内部类中使用Calculator,需要是final定义。
/**
* @Date 2020/5/24 9:50
* @Version 10.21
* @Author DuanChaojie
* // java.lang.reflect.Proxy
* // newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
* // 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
*/
public class CalculatorProxy {
/**
* 帮calculator对象创建代理对象
* @param calculator 被代理的对象
* @return
*/
public static Calculator getProxy(final Calculator calculator){
InvocationHandler h = new InvocationHandler() {
/**
* 方法执行器,帮我们被代理对象执行目标方法
* @param proxy 代理对象给jdk使用的,我们任何时候都不要使用这个对象
* @param method 当前将要执行的目标对象
* @param args 这个方法调用时外界传入的参数值
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("这是动态代理帮你执行的方法");
// 利用反射执行目标方法
// 内部类中使用,需要是final定义的
Object result = null;
try {
LogUtils.logStart(method,args);
// int a = 1/0; 异常的测试
result = method.invoke(calculator, args);
LogUtils.logEnd(method,result);
} catch (Exception e) {
LogUtils.logError(method,e);
e.printStackTrace();
}finally {
LogUtils.logFinally(method,result);
}
// 返回值必须返回出去外界才能拿到真正执行后的返回值
return result;
}
};
// 拿到接口
Class<?>[] interfaces = calculator.getClass().getInterfaces();
// 拿到类加载器
ClassLoader loader = calculator.getClass().getClassLoader();
// Proxy为目标对象创建代理对象
Calculator cal = (Calculator) Proxy.newProxyInstance(loader, interfaces, h);
return cal;
}
}
4.2 创建日志工具类
/**
* @Date 2020/5/24 10:51
* @Version 10.21
* @Author DuanChaojie
*/
public class LogUtils {
public static void logStart(Method method,Object args){
System.out.println("["+method.getName()+"]:方法执行了,其参数列表为"+ Arrays.asList(args));
}
public static void logEnd(Method method,Object result){
System.out.println("["+method.getName()+"]:方法正常执行完成了,其结果为"+ result);
}
public static void logError(Method method,Exception e){
System.out.println("["+method.getName()+"]:方法执行出现了异常,原因为:"+ e.getCause());
}
public static void logFinally(Method method,Object result){
System.out.println("["+method.getName()+"]:方法最终执行完成了,其结果为"+ result);
}
}
4.3 测试动态代理
/**
* @Date 2020/5/24 10:39
* @Version 10.21
* @Author DuanChaojie
*/
public class TestCalculator {
@Test
public void testCalculator() {
Calculator calculator = new CalculatorImpl();
// 没使用动态代理之前
int addRes = calculator.add(2, 3);
// 使用动态代理,如果拿到这个对象的代理对象,代理对象执行加减乘除
Calculator proxy = CalculatorProxy.getProxy(calculator);
// proxy.getClass() = class com.sun.proxy.$Proxy4
// 代理对象和被代理对象唯一能产生的关系就是实现了同一个接口
System.out.println("proxy.getClass() = " + proxy.getClass());
int add = proxy.add(5,6);
System.out.println("测试结果 result = " + add);
}
}
5. 使用动态代理实现存在的问题
- 写起来很复杂,实现复杂
- jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的。
☆
上一篇: 计算机网络课程设计