笔者在学习这块内容是,根据《javaEE互联网轻量级框架整合开发》这本书,复盘了这个例子,顺便把自己的理解记录下来。
动态代理: 生成一个占位(代理对象),来代替真实对象,从而控制真实对象的访问。
例如:
如上如所示,客户是通过商务(代理对象)去访问工程师(真实对象)的。
其中代理的作用就是,在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制是否使用真实对象,这个例子中,商务控制了客户对软件工程师的访问。
按照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);
}
}
其中,程序执行的顺序如图所示:
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互联网轻量级框架整合开发》