Spring JDK动态代理

笔者在学习这块内容是,根据《javaEE互联网轻量级框架整合开发》这本书,复盘了这个例子,顺便把自己的理解记录下来。

动态代理: 生成一个占位(代理对象),来代替真实对象,从而控制真实对象的访问。

例如:

Spring JDK动态代理_第1张图片

如上如所示,客户是通过商务(代理对象)去访问工程师(真实对象)的。

其中代理的作用就是,在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制是否使用真实对象,这个例子中,商务控制了客户对软件工程师的访问。

按照demo分析:

1. 首先是实体类Role

package com.cmb.test;

public class Role {
    
	private Long id;
	private String name;
	private String note;
	
	public Role(Long id, String name, String note){
		this.id = id;
		this.name = name;
		this.note = note;
	}
	
	public Long getId(){
		return id;
	}
	public String getName(){
		return name;
	}
	public String getNote(){
		return note;
	}
}

2.  自定义接口RoleService

package com.cmb.test;

public interface RoleService {

	public void printRole(Role role);
	
}

3.  实现这个接口,RoleServiceImpl

package com.cmb.test;
public class RoleServiceImpl implements RoleService {

	public void printRole(Role role) {
			System.out.println( "{id=" + role.getId()
					+ ",roleName = " + role.getName()
					+ ", note = " + role.getNote()
					);
	}

}

4. 在定义一个拦截器接口 Interceptor

package com.cmb.test;

public interface Interceptor {

	public void before(Object obj);
	
	public void after(Object obj);
	
	public void afterReturning(Object obj);
	
	public void afterThrowing(Object obj);
	
}

5. 实现这个拦截器 RoleInterceptor

package com.cmb.test;

public class RoleInterceptor implements Interceptor {

	public void before(Object obj) {
        System.out.println("准备打印角色信息");
	}

	public void after(Object obj) {
		System.out.println("已经完成角色信息的打印处理");
	}

	public void afterReturning(Object obj) {
		System.out.println("刚刚完成打印工作,一切正常");
	}

	public void afterThrowing(Object obj) {
		System.out.println("打印功能执行异常,查看一下角色对象为空了吗?");
	}

}

6. 铺垫结束了,我们用类ProxyBeanFactory去生成对应的对象

package com.cmb.test;

public class ProxyBeanFactory {

	public static  T getBean(T obj, Interceptor interceptor){
		return (T) ProxyBeanUtil.getBean(obj,interceptor);
	}
	
}

其中,程序执行的顺序如图所示:

Spring JDK动态代理_第2张图片

7. 高潮来了,ProxyBeanFactory中的ProxyBeanUtil是如何工作的呢,这里就用到了动态代理,代码中有备注

package com.cmb.test;

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

public class ProxyBeanUtil implements InvocationHandler {

	//被代理对象 即真实对象
	private Object obj;
	//拦截器
	private Interceptor interceptor = null;
	
	/**
	 * 获取动态代理对象
	 * @param obj 被代理对象
	 * @param interceptor 拦截器
	 * @return 动态代理对象
	 */
	public static Object getBean(Object obj, Interceptor interceptor){
		//使用当前类,作为代理方法,此时被代理对象执行方法的时候,会进入当前类的invoke方法里
		ProxyBeanUtil _this = new ProxyBeanUtil();
		//保存被代理对象
		_this.obj = obj;
		//保存拦截器
		_this.interceptor = interceptor;
		
		//Proxy.newProxyInstance 参数解释:
		//第一个是    类加载器,这里采用的是类本身的类加载器
		//第二个是   把生成的动态代理对象下挂 到哪些接口下,这里就是放在obj实现的接口下,即RoleService
		//第三个是   定义实现方法逻辑的代理类
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), _this);
		
	}
	
	/**
	 * 代理方法 : 当我们使用了代理对象调度真实对象的方法后,它就会进入到invoke方法里面
	 * @param proxy 代理对象
	 * @param method 当前调度对象
	 * @param args 调度方法的参数
	 * @return 方法返回
	 * @throws Throwable 异常 
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object retObj = null;
        //是否产生异常
        boolean exceptionFLag = false;
        //before 方法
        interceptor.before(obj);
        try{
        	//反射原有方法
        	retObj = method.invoke(obj, args);
        }catch(Exception ex){
        	exceptionFLag = true;
        }finally{
        	//after方法
        	interceptor.after(obj);
        }
        
        if(exceptionFLag){
        	//afterThrowing 方法
        	interceptor.afterThrowing(obj);
        }else{
        	//afterReturning方法
        	interceptor.afterReturning(obj);
        }
		
		return retObj;
	}

}

我们debug程序时会发现程序在执行printRole时,会调用invoke函数,这里说明动态代理生成并且绑定了对应的代理方法,结果如下:

准备打印角色信息
{id=1,roleName = role_name_1, note = role_note_1
已经完成角色信息的打印处理
刚刚完成打印工作,一切正常
=============测试afterthrowing 方法==============
准备打印角色信息
已经完成角色信息的打印处理
打印功能执行异常,查看一下角色对象为空了吗?

总结:

在7中,通过getBean方法,保存了真实对象,拦截器和参数,为之后的调用奠定了基础,然后生成了jdk动态代理对象(proxy),同时绑定了ProxyBeanUtil返回的对象作为其代理类,这样当代理对象调用方法时,也就是调用printRole时,就会进入ProxyBenaUtil的invoke方法中(debug也证明了这一点),逻辑和6中图示一样。

参考文献:《javaEE互联网轻量级框架整合开发》

你可能感兴趣的:(Spring,javaweb)