java动态代理的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010285684/article/details/80265745

Spring的IOC容器和AOP切面中用到大量的动态代理,今天记录一下我自己学习的动态代理的过程,我没有看spring的源代码,但是估计底层应该也是这个样子。

java.lang.reflect.Proxy类,使用的用例API已经给出了

Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

创建某一接口 Foo 的代理:

     InvocationHandler handler = new MyInvocationHandler(...);
     Class proxyClass = Proxy.getProxyClass(
         Foo.class.getClassLoader(), new Class[] { Foo.class });
     Foo f = (Foo) proxyClass.
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });
 
或使用以下更简单的方法:
     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

Proxy中的主要方法如下


java.lang.reflect.InvocationHandler接口中的

invoke(Object proxy, Method method, Object[] args)方法是在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

参数说明:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。


返回:
    从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出ClassCastException。

下面是我的代码的示例

需求:1.提供一个除法接口,一个接口的实现类;2.一个代理类,在除法进行的前后进行拦截,打印参数;3.参数的校验,如果参数合规则,则进行除法计算,如果不合规则,则不进行计算。返回0

一、接口及实现类

package com.dadi.aop;

/**
 * 计算接口
 */
public interface CalculationService {
	/**
	 * 加
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int add(int i, int j);

	/**
	 * 减
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int sub(int i, int j);

	/**
	 * 乘
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int mul(int i, int j);

	/**
	 * 加
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int div(int i, int j);
}

我们在没有用到动态代理的时候,如果要验证参数和在方法执行的前后来打印日志的话,只能和业务放在一起,如下

public class CalculationServiceImpl implements CalculationService {
	private int result = 0;

	@Override
	public int add(int i, int j) {
		System.out.println("i : " + i + " , j : " + j);
		System.out.println("CalculationServiceImpl--->add  start");
		result = i + j;
		System.out.println("result : " + result);
		System.out.println("CalculationServiceImpl--->add  end");
		return result;
	}

	@Override
	public int sub(int i, int j) {
		System.out.println("i : " + i + " , j : " + j);
		System.out.println("CalculationServiceImpl--->sub  start");
		result = i - j;
		System.out.println("result : " + result);
		System.out.println("CalculationServiceImpl--->sub  end");
		return result;
	}

	@Override
	public int mul(int i, int j) {
		System.out.println("i : " + i + " , j : " + j);
		System.out.println("CalculationServiceImpl--->mul  start");
		result = i * j;
		System.out.println("result : " + result);
		System.out.println("CalculationServiceImpl--->mul  end");
		return result;
	}

	@Override
	public int div(int i, int j) {
		System.out.println("i : " + i + " , j : " + j);
		System.out.println("CalculationServiceImpl--->div  start");
		result = i / j;
		System.out.println("result : " + result);
		System.out.println("CalculationServiceImpl--->div  end");
		return result;
	}
}

像上面这样的话,会使代码量非常的庞大,而且看起来非常的凌乱。

如何使我们只关系业务,而把数据的校验和日志放在日志只关心的类里面呢?所以我们在这里借助动态代理类来实现。

只关心核心业务的实现代码如下

package com.dadi.aop;


public class CalculationServiceImpl2 implements CalculationService {

	@Override
	public int add(int i, int j) {
		return i + j;
	}

	@Override
	public int sub(int i, int j) {
		return i - j;
	}

	@Override
	public int mul(int i, int j) {
		return i * j;
	}

	@Override
	public int div(int i, int j) { return i / j; }
}

二、动态代理计算的对象,并把被除数为0的值进行校验,如果为0则不计算。

package com.dadi.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Objects;

/**
 * 算数计算效验和日志的记录,通过动态代理对象,对对象的方法的参数的参数进行校验和日志的输出
 */
public class CalculationServiceLoggingProxy {

	private CalculationService target;

	public CalculationServiceLoggingProxy(CalculationService target) {
		this.target = target;
	}

	public CalculationService getLoggingProxy() {
		// 当具体调用代理对象的方法时, 实际上就是调用 InvocationHandler 的 invoke 方法
		InvocationHandler invocationHandler = new InvocationHandler() {
			/**
			 * @param proxy 该方法被调用的代理实例
			 * @param method 代理实例上调用的接口方法对应的Method实例
			 * @param args 包含在代理实例上的方法调用中传递的参数值的对象数组,如果interface方法不带任何参数,则为null
			 * @return 从代理实例上的方法调用返回的值
			 * @throws Throwable
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				String methodName = method.getName();
				// 方法开始执行,打印进来的参数
				System.out.println("方法开始之前[before], 方法名:" + methodName + ",参数:" + Arrays.asList(args));
				// 这个方法就是实现代理类的方法,target是代理的类,args是方法中的参数
				Object result;
				if (Objects.equals("div", methodName) && Objects.equals("0", String.valueOf(args[1]))) {
					result = 0;
				} else {
					result = method.invoke(target, args);
				}
				// 方法运行结束,打印结束日志
				System.out.println("方法结束之后[after], 方法名:" + methodName + ",结果:" + result);
				return result;
			}
		};
		// 通过类加载器来加载要动态代理的类
		ClassLoader classLoader = target.getClass().getClassLoader();
		// 指定代理对象的类型. 即代理对象中可以有哪些方法
		Class[] interfaces = {CalculationService.class};
		return (CalculationService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
	}
}

三、测试用例

	@Test
	public void proxy(){
		CalculationService cs = new CalculationServiceImpl();
		CalculationService proxy = new CalculationServiceLoggingProxy(cs).getLoggingProxy();
		int result = proxy.div(8,0);
		String.format("结果: %s",result);
		System.out.println("*************");

		result = proxy.div(8,2);
		String.format("结果: %s",result);
	}

猜你喜欢

转载自blog.csdn.net/u010285684/article/details/80265745