一、代理的概念
1. 设计模式:前人总结的一套解决特定问题的代码套路
2. 代理设计模式优点:
2.1 保护真实对象
2.2 让真实对象职责更明确.
2.3 便于扩展功能
3. 代理设计模式的组成部件:三个
3.1 真实对象.(老总)
3.2 代理对象(秘书)
3.3 抽象对象(抽象功能):老总需要总的事情(为接口)
二、静态代理
1. 由代理对象代理所有真实对象的功能.
1.1 程序员字节编写代理类
1.2 每个代理的功能需要单独编写
2. 静态代理设计模式的缺点:
2.1 当代理功能比较多时,代理类中方法需要写很多.
3、静态代理例子:
A、抽象功能接口
* description :抽象功能接口,里面有两个方法,由程序员自主定义
*/
public interface Function {
public void eating();
public void working();
}
B、真实对象,实现功能接口,重写方法(也就是被代理对象,例子中的老总,老总具体干两件事:吃饭,工作)程序员自主定义
* description :真实对象
*/
public class Boss implements Function {
@Override
public void eating() {
System.out.println("吃饭!");
}
@Override
public void working() {
System.out.println("谈合作!");
}
}
C、代理对象,实现功能接口。(例子中的小秘书,找老总前必须先通过秘书,小秘书代理老总处理一些事情)程序员自主定义
* description :代理类
*/
public class MiShu implements Function {
private Boss boss;//被代理对象
public MiShu(Boss boss) {
this.boss=boss;
}
@Override
public void eating() {
System.out.println("秘书约时间!");
boss.eating();
System.out.println("秘书善后!");
}
@Override
public void working() {
System.out.println("秘书约时间!");
boss.working();
System.out.println("秘书善后!");
}
}
D、测试
public class Test {
public static void main(String[] args) {
MiShu mishu = new MiShu(new Boss());
mishu.eating();
mishu.working();
}
}
E、解释
通过上面的例子可以发现,静态代理需要:
1)接口
2)真实对象实现接口
3)代理对象,去代理真实对象
和老总吃饭、谈合作必须先和秘书约时间,吃完饭,谈完合作,由秘书善后。老总功能明确,职能分离。
缺点:
接口中有多少个方法,代理对象就必须对每个方法都有进行相同的处理。若有成百上千个方法,则代理类很臃肿。
三、JDK动态代理
1、为了解决静态代理的缺点(频繁编写代理的方法),JDK提供了动态代理的方式,即不需要对接口中的每一个方法进行处理。JDK动态代理是基于“反射原理”。
2、先看实例
JDK动态代理四个步骤:
1)创建接口,定义真实对象需要完成的功能
2)创建真实对象,实现接口(1、2步和静态代理相同)
3)创建InvocationHandler的实现类(我把它叫为处理器),在invoke方法中进行代理对象对被代理对象的功能的增强(改变、扩展都行)
4)使用Proxy的静态方法 Proxy.newProxyInstance,创建代理对象,被强转为接口对象,通过接口对象调用接口中的功能就能实现代理。
* description :抽象功能类
*/
public interface Function {
public void eating();
public void working();
}
* description :真实对象
*/
public class Boss implements Function {
@Override
public void eating() {
System.out.println("吃饭!");
}
@Override
public void working() {
System.out.println("工作!");
}
}
* description :处理器,实现代理功能
*/
public class MyHandler implements InvocationHandler {
private Boss boss = new Boss();
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("秘书善后!");
Object result = method.invoke(boss, objects);
System.out.println("秘书善后");
return result;
}
}
* description :测试类
*/
public class Test {
public static void main(String[] args) {
MyHandler myHandler = new MyHandler();
//第一个参数:反射使用的类加载器
//第二个参数:Proxy需要实现的接口
//第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法
Function f=(Function) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Function.class}, myHandler);
f.eating();
f.working();
}
}
运行结果:
预约时间!
吃饭!
秘书善后!
预约时间!
工作!
秘书善后!
3、运行的实质剖析
1、在 test中 new 一个MyHandler 的对象。MyHandler 实现了接口 InvocationHandler,重写 invoke 方法
invoke方法参数:
Object o:代理对象,由JDK自动传入,不需要程序员管理。
Method method:真实对象需要执行的功能方法对象,通过反射获取。也是有JDK自动传入,不需要程序员管理
Object[] objects:真实对象需要执行的功能方法所需要的参数,JDk自动传入
method是方法对象,由反射获取,这里的真实对象是 private Boss boss = new Boss();,通过反射获取到它的对应的方法对象。
通过 Object result = method.invoke(boss, objects);执行真实对象中的功能,并将执行结果返回
public class MyHandler implements InvocationHandler {
private Boss boss = new Boss();
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("预约时间!");
Object result = method.invoke(boss, objects);
System.out.println("秘书善后!");
return result;
}
}
2、问题的核心在:
Function f=(Function) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Function.class}, myHandler);
1) newProxyInstance 是用来创建一个代理类对象
2)参数
Test.class.getClassLoader()
类加载器(需要知道类加载器的概念和作用,以及类加载的过程:简单的说就是一个java代码文件,编译后生成对应的class文件(也就是字节码文件),类加载器就是将这个class文件加载到 jvm 内存,加载完成后会生成这个类的类对象(Class类型的对象),通过这个类对象我们可以获取到这个类的结果),通常是:AppClassLoader
new Class[]{Function.class}:
真实对象实现的接口的类对象,真实对象可能实现多个接口,因此需要CLass[]存放
myHandler:
处理器:也就 MyHandler,将处理器的处理以对象的形式传入。
3)newProxyInstance 获取到上面三个参数后,会动态创建对应的代理类,并返回这个代理类的实例。