目录
一、引言
谈及动态代理,我们首先要了解一个概念:什么是代理?
举一个例子,假如你要选购房屋,一般情况下你联系的肯定是房屋中介,通过房屋中介来看房子,你不可能直接去找房地产开发商联系,而此时的房屋中介充当的就是代理的角色。
那么我们再来回顾一下设计模式中的代理模式:
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3 种:
Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
RealSubject(真实主题角色):真正实现业务逻辑的类;
Proxy(代理主题角色):用来代理和封装真实主题;
代理模式的核心是代理角色。
二、静态代理
所谓静态代理,简单来说,在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
代码实现:
Subject 抽象主题角色:
public interface Subject {//抽象主题,规范了其子类的操作
//对外暴露的统一行为方法
public void request();
}
RealSubject真实主题角色:
public class RealSubject implements Subject{//真实主题
@Override
public void request() {
System.out.println("1.");
System.out.println("2.");
System.out.println("3.");
}
}
SubjectProxy 代理角色:
public class SubjectProxy implements Subject{
private Subject target;//真实主题角色
public SubjectProxy() {
target=new RealSubject();//创建真实主题角色
}
@Override
public void request() {
System.out.println("---begin---");//实现的额外功能
target.request();//真实主题的功能
System.out.println("----end---");
}
}
测试类:
//代理
public class ProxyDemo {
public static void main(String[] args) {
//创建代理主题
//接口的引用 ==> 代理对象
Subject sub=new SubjectProxy();
sub.request();
}
}
在我们这个例子上,可以看出,是将代理主题类SubjectProxy在编译期就写好了,即将代理类写死。
为什么说是将代理类写死呢?是因为你创建的这个代理角色的,它的具体实现是根据当前的真实主题和抽象主题创建的,换一个别的真实主题和抽象主题就不适用了。
我们怎么解决这一问题的?这就要利用动态代理来实现了。
三、动态代理
动态代理是指在程序运行期间由JVM根据反射等机制动态的生成代理类。
代码实现:
UserService抽象主题角色
public interface UserService {//抽象主题角色
void select();
void update();
}
UserServiceImpl真实主题角色
// 真正的实现类
public class UserServiceImpl implements UserService {
@Override
public void select() {
System.out.println("select * ..................");
System.out.println("数据库中完成用户信息的查询执行!");
}
@Override
public void update() {
System.out.println("update ...................");
System.out.println("数据库中用户状态的更新执行!");
}
}
LogInvacationHandler日志处理器对象类()
public class LogInvacationHandler implements InvocationHandler {
//创建目标对象
private Object target;
public LogInvacationHandler(Object obj) {
this.target=obj;
}
//拦截器
//该方法作用,根据传入的代理对象(proxy)以及调用的方法(method)来决定具体的调用
//invoke()方法在创建具体的代理类是被调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("方法%s开始加载,Time:%s\n",method.getName(),new Date());
//执行目标对象target的方法
//根据你传入的具体实现类target及参数args调用具体的方法
Object o=method.invoke(target, args);
Thread.sleep(1000);
System.out.printf("方法%s开结束加载,Time:%s\n",method.getName(),new Date());
return o;
}
}
测试类:
public class DynamicUserServiceProxyDemo {
public static void main(String[] args) {
//创建动态处理器
//根据你传入的具体实现类不同,实现不同的方法调用
LogInvacationHandler handler=new LogInvacationHandler(new UserServiceImpl());
//创建动态代理对象
//根据该接口的类加载器、该接口以及动态处理器创建具体的代理角色
UserService proxy=(UserService)Proxy.newProxyInstance(
UserService.class.getClassLoader(), //该接口的类加载器
new Class[] {UserService.class},//该接口
handler//动态处理器
);
//代理对象调用对应的方法时,被动态处理器invoke方法拦截
//invoke方法根据创建的代理对象以及调用的方法来决定具体调用哪个方法
proxy.select();//调用对应的method方法
}
}
实现结果:
创建的LogInvacationHandler类的invoke()方法实现了将具体实现类的方法和增添的部分结合起来,根据具体传入的目标对象的不同来达到不同的实现。
在具体的测试类中,创建的具体的LogInvacationHandler类,但invoke()方法并没有被直接调用,但它仍然起到了连接具体实现类和增添的新功能的作用,这是为什么呢?
先观察一下invoke()方法:
可以结合LogInvacationHandler的代码以及其本身方法头看出,invoke()方法在这里充当拦截器的作用,创建了具体的日志处理对象handler后,在具体的根据Proxy的newProxyInstance()方法传入公共的类加载器、具体实现类的实现接口(或接口数组)以及处理器创建具体代理对象时,invoke()被隐式的调用了。
创建好proxy这个对象后,
此时,将方法名 "select" 以及具体的代理类 "proxy" 传入invoke()方法,此时方法的参数为0,所以invoke()方法的args是一个空数组。然后结合具体实现类的 select () 方法包装成一个具有更多功能的类并输出。
为什么说动态代理利用了反射机制呢?就是因为,两个关键字:“运行时”和“动态”。运行时才能具体的确定业务需求,实现的功能,然后动态的去生成代理类,在实现对具体业务需求的包装,最后输出,这就离不开反射。
可以说,没有反射机制,动态代理类就无法生成。
总结:
1.动态代理实现了更好的代码复用。
2.java反射机制(运行时+动态)是动态代理的关键。
3.充分理解invoke()方法的实现机制和作用。