一、代理模式
**代理**:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
**目标**:被代理“套用”了非核心逻辑代码的类、对象、方法。
**静态代理**:自己定义代理类或者通过工具提前生成代理类,在程序运行前,代理类的.class文件已经存在。
**动态代理**:代理类是动态生成的。程序运行前,还没有代理类.class文件。
二、例子
声明计算器接口Calculator,包含加减乘除的抽象方法。
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
计算机实现类
public class CalculatorImpl implements Calculator {
public int add(int i, int j) {
return i+j;
}
public int sub(int i, int j) {
return i-j;
}
public int mul(int i, int j) {
return i*j;
}
public int div(int i, int j) {
return i/j;
}
}
想要在每个方法前打印方法名称+参数,方法执行后增加结果输出,如果直接在实现类修改:
public int add(int i, int j) {
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
int result = i+j;
System.out.println("[日志] add 方法结束了,结果是:" + result);
return result;
}
问题:
对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
附加功能分散在各个业务功能方法中,不利于统一维护。
解决方案:
解耦,计算器实现类只关注自己的核心功能,计算。日志交给代理类来完成。
/**
* 通过静态代理,再方法执行前后加日志
*/
public class CalculatorStaticProxy implements Calculator {
/**
* 目标类,代理类中核心方法来自目标类
*/
private Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
public int add(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
int result = target.add(i, j);
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,结果是:" + i + "," + j);
return result;
}
public int sub(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
int result = target.sub(i, j);
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] sub 方法开始了,结果是:" + i + "," + j);
return result;
}
public int mul(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
int result = target.mul(i, j);
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] mul 方法开始了,结果是:" + i + "," + j);
return result;
}
public int div(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
int result = target.div(i, j);
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,结果是:" + i + "," + j);
return result;
}
}
测试
@Test
public void testStaticProxy() {
/**
* 静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来
* 说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代
* 码,日志功能还是分散的,没有统一管理。
* 提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理
* 类来实现。这就需要使用动态代理技术了。
*/
CalculatorStaticProxy calculatorStaticProxy = new CalculatorStaticProxy(new CalculatorImpl());
System.out.println(calculatorStaticProxy.add(1, 2));
System.out.println(calculatorStaticProxy.sub(1, 2));
System.out.println(calculatorStaticProxy.mul(1, 2));
System.out.println(calculatorStaticProxy.div(1, 2));
}
动态代理实现
package com.lucky;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 动态代理有两种:
* 1、jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口
* 在com.sun.proxy包下,类名为$proxy2
* 2、cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
*/
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy() {
/**
* newProxyInstance():创建一个代理实例
* 其中有三个参数:
* 1、classLoader:加载动态生成的代理类的类加载器
* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接
口中的抽象方法
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* proxy:代理对象
* method:代理对象需要实现的方法,即其中需要重写的方法
* args:method所对应方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("[动态代理][日志] " + method.getName() + ",参数:" + Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[动态代理][日志] " + method.getName() + ",结果:" + result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] " + method.getName() + ",异常:" + e.getMessage());
} finally {
System.out.println("[动态代理][日志] " + method.getName() + ",方法 执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
测试
@Test
public void test02() {
ProxyFactory factory = new ProxyFactory(new CalculatorImpl());
Calculator proxy = (Calculator) factory.getProxy();
System.out.println(proxy.add(1, 2));
System.out.println(proxy.div(1, 0));
}