JDK动态代理及example

JDK动态代理


一谈到Spring AOP可能就会想到动态代理,因为实质上它们是异曲同工的东西,如果弄明白动态代理,相信 Spring AOP的概念也就很容易理解了,下面我介绍一个动态代理的example:

  • 首先来看一个简单到不需要去解释的接口:
    简易接口HelloService:
package com.springboot.chapter3.service;

public interface HelloService {
	public void sayHello(String name);
}

这个接口很简单,就是定义一个sayHello的方法,其中的参数name是名字,

这样就可以对该名字说hello了。于是很快我们可以得到这样的一个实现类:

package com.springboot.chapter3.service.implement;

import com.springboot.chapter3.service.HelloService;

public class HelloServiceImpl implements HelloService {
	
	@Override
	public void sayHello(String name) {
		if(name == null || name.trim() == "") {
			throw new RuntimeException ("parameter is null!!");
		}
		System.out.println("hello" + name);
	}
}

好了,这里的代码也很简单,方法sayHello首先判断name是否为空。如果为空,则抛出异常,告诉调用者参数为空;如果不为空,则对该名字说hello。

这样一个几乎就是最简单的服务写好了。下面先来定义一个拦截器接口,它十分简单,只是存在几个方法:

package com.springboot.chapter3.intercept;

import java.lang.reflect.InvocationTargetException;
import com.springboot.chapter3.invoke.Invocation;

public interface Interceptor {
	//事前方法
	public boolean before();
	
	//事后方法
	public void after();
	
	/**
	 * 取代原有事件方法
	 * @param invocation --回调参数,可以通过它的processd方法,回调原有事件
	 * @return 原有事件返回对象
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 */
	
	public Object around(Invocation invocation)
		throws InvocationTargetException, IllegalAccessException;
	
	//是否返回方法。事件没有发生异常后执行
	public void afterReturning();
	
	//事后异常方法,当事件发生异常后执行
	public void afterThrowing();
	
	//是否使用around方法取代原有方法
	boolean useAround();
}

这个接口的定义我也是设计了一番的,后面会给出约定,将这些方法织入流程中。
这里我们首先给出around方法中的参数Invocation对象的源码:

package com.springboot.chapter3.invoke;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Invocation {
	private Object[] params;
	private Method 	method;
	private Object target;
	
	public Invocation(Object target, Method method, Object[] params) {
		this.params = params;
		this.method = method;
		this.target = target;
	}

	//反射方法
	public Object proceed() throws
		InvocationTargetException, IllegalAccessException{
		return method.invoke(target, params);
	}
/**************getter and setter*************/
}

它没有太多可以探讨的内容,唯一值得探讨的就是proceed方法,它会以反射的形式去调用原有的方法。
接着,你可以根据拦截器(Interceptor)接口的定义开发一个属于你自己的拦截器MyInterceptor:

package com.springboot.chapter3.intercept;

import java.lang.reflect.InvocationTargetException;
import com.springboot.chapter3.invoke.Invocation;

public class MyInterceptor implements Interceptor {

	@Override
	public boolean before() {
		System.out.println("before.............");
		return true;
	}

	@Override
	public void after() {
		System.out.println("after..............");
		
	}

	@Override
	public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
		System.out.println("around before............");
		Object obj = invocation.proceed();
		System.out.println("around after............");
		return obj;
	}

	@Override
	public void afterReturning() {
		System.out.println("afterReturning.........");
		
	}

	@Override
	public void afterThrowing() {
		System.out.println("afterThrowing............");
		
	}

	@Override
	public boolean useAround() {
		return true;
	}

}

这个拦截器的功能也不复杂,接着就要谈谈我和你的约定了

  • ProxyBean的实现:
    如何将服务类和拦截方法织入对应的流程,是ProxyBean要实现的功能。首先要理解动态代理模式。其实代理很简单,例如,当你需要采访一名儿童时,首先需要经过他父母的同意,在一些问题上父母也许会替他回答,而对于另一些问题,也许父母觉得不太适合这个小孩会拒绝掉,显然这时父母就是这名儿童的代理(proxy)了。通过代理可以增强或者控制对儿童这个真实对象(target)的访问:
    在这里插入图片描述
    也就是需要一个代理对象。在JDK中,提供了类Proxy的静态方法——newProxyInstance,其内容具体如下:
    在这里插入图片描述
    给予我们来生成一个代理对象(proxy),它有3个参数:
    •classLoader——类加载器;
    •interfaces——绑定的接口,也就是把代理对象绑定到哪些接口下,可以是多个;
    •invocationHandler ——绑定代理对象逻辑实现。
    这里的invocationHandler是一个接口InvocationHandler对象,它定义了一个invoke方法,这个方法就是实现代理对象的逻辑的,然后通过目标对象(target)、方法(method)和参数(args)就能够反射方法运行了,于是我们可以实现ProxyBean的源码如下:
package com.springboot.chapter3.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.springboot.chapter3.intercept.Interceptor;
import com.springboot.chapter3.invoke.Invocation;

public class ProxyBean implements InvocationHandler {
	
	private Object target = null;
	
	private Interceptor interceptor = null;
	
	/**
	 * 绑定代理对象
	 * @param target 被代理对象
	 * @param interceptor 拦截器
	 * @return 代理对象
	 */
	public static Object getProxyBean(Object target, Interceptor interceptor) {
		ProxyBean proxyBean = new ProxyBean();
		//保存被代理对象
		proxyBean.target = target;
		//保存拦截器
		proxyBean.interceptor = interceptor;
		//生成代理对象
		
		Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), 
			target.getClass().getInterfaces(), 
			proxyBean);
		
		//返回代理对象
		return proxy;
	}

	/**
	 * 处理代理对象方法逻辑
	 * @param proxy 代理对象
	 * @param method 当前方法
	 * @param args 运行参数
	 * @return 方法调用结果
	 * @throws Throwable 异常
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//异常标识
		boolean exceptionFlig = false;
		Invocation invocation = new Invocation(target, method, args);
		Object retObj = null;
		try {
			if (this.interceptor.before()) {
				retObj = this.interceptor.around(invocation);
			} else {
				retObj = method.invoke(target, args);
			}
		} catch (Exception ex) {
			//生产异常
			exceptionFlig = true;
		}
		this.interceptor.after();
		if (exceptionFlig) {
			this.interceptor.afterThrowing();
		} else {
			this.interceptor.afterReturning();
			return retObj;
		}
		return null;
	}
}

首先,这个ProxyBean实现了接口InvocationHandler,因此就可以定义invoke方法了。其中在getBean方法中,我让其生成了一个代理对象,并且创建了一个ProxyBean实例保存目标对象(target)和拦截器(interceptor),为后面的调用做好了准备。其次,生成了一个代理对象,而这个代理对象挂在target实现的接口之下,所以你可以用target对象实现的接口对这个代理对象实现强制转换,并且将这个代理对象的逻辑挂在ProxyBean实例之下,这样就完成了目标对象(target)和代理对象(proxy)的绑定。最后,将代理对象返回给调用者。

在这里插入图片描述

于是你就可以使用它拿到proxy了,接下来我们测试是否能够成功织入:

package com.springboot.chapter3;

import com.springboot.chapter3.intercept.MyInterceptor;
import com.springboot.chapter3.proxy.ProxyBean;
import com.springboot.chapter3.service.HelloService;
import com.springboot.chapter3.service.implement.HelloServiceImpl;

public class TestProxy {

	public static void main(String[] args) {
		HelloService helloservice = new HelloServiceImpl();
		//按约定获取proxy
		HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloservice, new MyInterceptor());
		proxy.sayHello("Zachary");
		System.out.println("\n#######################name is null###################\n");
		proxy.sayHello(null);
	}
}

结果如下:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值