动态代理的优点:
- 可以在不改变源码的情况下,实现对业务方法功能的增强,提高了代码的复用,降低了耦合
- 简化了编程工作,提高了开发效率,同时提高了软件系统的可扩展性
java动态代理主要就两种方式:
- JDK 代理 : 基于接口的动态代理技术
- cglib 代理:基于父类的动态代理技术
下面用计算类例子来说明动态代理技术
1、.不用动态代理的情况下
新建一个计算类Calculator
package org.fyd.spring.proxy.v1;
/**
* fyd
* 2023-09-18
**/
public interface Calculator {
int add(int a, int b); // 加法
int sub(int a, int b); // 减法
int mul(int a, int b); // 乘法
int div(int a, int b); // 除法
}
实现类
package org.fyd.spring.proxy.v1;
/**
* 业务代码和非业务代码混在一起
*/
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
System.out.println("执行add方法,参数信息:" + a + "," + b);
int result = a + b;
System.out.println("add方法执行完毕,计算结果:" + result);
return result;
}
@Override
public int sub(int a, int b) {
System.out.println("执sub方法,参数信息:" + a + "," + b);
int result = a - b;
System.out.println("sub方法执行完毕,计算结果:" + result);
return result;
}
@Override
public int mul(int a, int b) {
System.out.println("执行mul方法,参数信息:" + a + "," + b);
int result = a * b;
System.out.println("mul方法执行完毕,计算结果:" + result);
return result;
}
@Override
public int div(int a, int b) {
System.out.println("执行div方法,参数信息:" + a + "," + b);
int result = a / b;
System.out.println("div方法执行完毕,计算结果:" + result);
return result;
}
}
从上面可以看出,每种方法都有打印前和打印后信息,造成了代码冗余和重复,不利于维护
2、使用JDK的动态代理将打印信息抽离出来
package org.fyd.spring.proxy.v2;
import org.fyd.spring.proxy.v1.Calculator;
/**
* 把业务代码和非业务代码分离
* jdk 动态代理
*/
public class CalculatorImpl2 implements Calculator {
@Override
public int add(int a, int b) {
int result = a + b;
return result;
}
@Override
public int sub(int a, int b) {
int result = a - b;
return result;
}
@Override
public int mul(int a, int b) {
int result = a * b;
return result;
}
@Override
public int div(int a, int b) {
int result = a / b;
return result;
}
}
打印信息工具类
package org.fyd.spring.proxy.v2;
import java.util.Arrays;
public class Logging {
/**
* 方法执行前打印参数
* @param className 类名
* @param methodName 方法名
* @param args 执行方法时的参数
*/
public static void beforeMethod(String className, String methodName, Object[] args){
System.out.println("执行" + className + "类中的" + methodName + "方法,参数:" + Arrays.toString(args));
}
/**
* 方法执行后打印执行结果
* @param className 类名
* @param methodName 方法名
* @param result 方法的返回值
*/
public static void afterMethod(String className, String methodName, Object result){
System.out.println(className + "类中的" + methodName + "方法执行完毕,结果:" + result);
}
}
代理类
package org.fyd.spring.proxy.v2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyProxy {
// 目标对象,需要增强的对象
private Object target;
// 有参构造器为目标对象进行赋值
public MyProxy(Object target) {
this.target = target;
}
// 获取代理对象的方法
public Object getProxyObject(){
// 代理对象
Object proxyObject = null;
// 获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 获取目标对象实现的所有接口
Class<?>[] interfaces = target.getClass().getInterfaces();
// 调用Proxy.newProxyInstance()方法,创建代理对象 new调用处理器
proxyObject = Proxy.newProxyInstance(classLoader,interfaces, new InvocationHandler() {
/**
* @param proxy 代理对象
* @param method 要执行的方法
* @param args 执行方法时需要的参数
* @return 调用目标对象方法后返回的执行结果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 目标对象类名
String targetClassName = target.getClass().getName();
// 要执行的方法名
String targetMethodName = method.getName();
// 在方法调用之前,打印方法参数
Logging.beforeMethod(targetClassName, targetMethodName, args);
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 调用方法之后,输出执行结果
Logging.afterMethod(targetClassName, targetMethodName, result);
// 将执行结果返回
return result;
}
});
// 将代理对象返回
return proxyObject;
}
}
3、用cgilib的动态代理
pom导入cglib坐标,如果已经导入spring-core坐标了,就不需要导入cglib坐标了
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
package org.fyd.spring.proxy.v3;
import org.fyd.spring.proxy.v1.Calculator;
/**
* 把业务代码和非业务代码分离
* cglib动态代理
*/
public class CalculatorImpl3 {
public int add(int a, int b) {
int result = a + b;
return result;
}
public int sub(int a, int b) {
int result = a - b;
return result;
}
public int mul(int a, int b) {
int result = a * b;
return result;
}
public int div(int a, int b) {
int result = a / b;
return result;
}
@Override
public String toString() {
return "CalculatorImpl3{}";
}
}
package org.fyd.spring.proxy.v3;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.fyd.spring.proxy.v2.Logging;
import java.lang.reflect.Method;
/**
* fyd
* 2023-09-18
**/
public class MyProxy2 {
private Object target;
public MyProxy2(Object target) {
this.target = target;
}
public Object getProxyObject(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//target父类 obj是代理后的子类 ,method是调用方法 ,args是方法入参 , proxy是MethodProxy代理对象
// System.out.println(obj.toString()); //会报错
System.out.println(target.toString());
// 目标对象类名
String targetClassName = target.getClass().getName();
// 要执行的方法名
String targetMethodName = method.getName();
// 在方法调用之前,打印方法参数
Logging.beforeMethod(targetClassName, targetMethodName, args);
// 调用目标对象的方法
Object result = method.invoke(target, args); //正确
// Object result = methodProxy.invokeSuper(obj, args); //正确
// Object result = method.invoke(obj, args); //错误
// 调用方法之后,输出执行结果
Logging.afterMethod(targetClassName, targetMethodName, result);
// 将执行结果返回
return result;
}
});
return enhancer.create();
}
}
运行类
package org.fyd;
import org.fyd.spring.proxy.v1.Calculator;
import org.fyd.spring.proxy.v1.CalculatorImpl;
import org.fyd.spring.proxy.v2.CalculatorImpl2;
import org.fyd.spring.proxy.v2.MyProxy;
import org.fyd.spring.proxy.v3.CalculatorImpl3;
import org.fyd.spring.proxy.v3.MyProxy2;
/**
* fyd
* 2023-09-18
**/
public class Main {
public static void main(String[] args) {
// System.out.println("Hello world!");
execV3();
}
public static void execV1(){
CalculatorImpl calculator = new CalculatorImpl();
calculator.add(1,1);
calculator.sub(2,1);
calculator.mul(100,2);
calculator.div(444,2);
}
public static void execV2(){
CalculatorImpl2 calculator = new CalculatorImpl2();
MyProxy myProxy = new MyProxy(calculator);
Calculator calculatorImpl2 = (Calculator) myProxy.getProxyObject();
calculatorImpl2.add(1,3);
calculatorImpl2.sub(444,4);
calculatorImpl2.mul(3,7);
calculatorImpl2.div(555,5);
}
public static void execV3(){
CalculatorImpl3 calculator = new CalculatorImpl3();
MyProxy2 myProxy = new MyProxy2(calculator);
CalculatorImpl3 calculatorImpl3 = (CalculatorImpl3) myProxy.getProxyObject();
calculatorImpl3.add(111,3);
calculatorImpl3.sub(999,3);
calculatorImpl3.mul(88,7);
calculatorImpl3.div(888,2);
}
}
参考博客: