一,代理的概念
代理是一个对象,代理对象为其他对象提供一种代理,以控制对这个对象的访问,代理对象起到中介作用,可以去掉或者增加额外的服务。
如:火车票代售点就是火车站售票处的一个代理对象,可通过访问代售点进行业务处理。
二,静态代理的2种实现方式:继承和聚合
静态代理中的代理和被代理对象在代理之前关系是确定的。它们都实现了相同的接口或者继承相同的抽象类。
下面的例子分别讲述了所有的类实现同一个接口,类对象之间是如何代理的。
1,继承
首先定义一个Movable接口
package com.jimmy.proxy;
//首先定义一个接口,接口里面有一个Move函数
public interface Movable {
void Move();
}
package com.jimmy.proxy;
import java.util.Random;
public class Car implements Movable{
@Override
public void Move() {
//业务代码
try {
Thread.sleep(new Random().nextInt(1000)); //随机睡一会
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.jimmy.proxy;
public class Car2 extends Car{
@Override
public void Move() {
long startTime = System.currentTimeMillis();
System.out.println("start moving..");
super.Move(); //调用父类的Move()方法,这里体现了代理的思想。前后加上自己的业务代码。
long endTime = System.currentTimeMillis();
System.out.println("end move"+" time:"+(endTime-startTime));
}
}
定义一个Car3类,继承自Car2,并重写Move()方法
package com.jimmy.proxy;
public class Car3 extends Car2 {
@Override
public void Move() {
System.out.println("starting log");
super.Move(); //同样,调用父类的Move()方法。前后加上自己的业务代码
System.out.println("end log");
}
}
现在测试上面3个类的代理关系
package com.jimmy.proxy;
public class MovableClient {
public static void main(String[] args) {
Movable mm = new Car3();
mm.Move(); //执行Car3类的Move()方法,但是都访问到了Car和Car2类的Move()方法
//于是,Car3的对象就是Car和Car2对象的代理对象。
}
}
2,聚合
一个类中用另一个类的对象做成员变量。
首先还是定义Movable接口(同继承)
package com.jimmy.proxy;
//首先定义一个接口,接口里面有一个Move函数
public interface Movable {
void Move();
}
同样定义一个Car类(同继承)
package com.jimmy.proxy;
import java.util.Random;
public class Car implements Movable{
@Override
public void Move() {
//业务代码
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
定义一个时间代理类CarTimeProxy,同样实现Movable接口
package com.jimmy.proxy;
public class CarTimeProxy implements Movable{
Movable cc = null; //接收一个实现了Movable接口的类对象作为变量,也就是所谓的聚合
public CarTimeProxy(Movable cc) {
this.cc = cc;
}
@Override
public void Move() {
long startTime = System.currentTimeMillis();
System.out.println("start moving..");
cc.Move(); //调用被聚合对象的Move()方法,前后加上自己的业务代码。
long endTime = System.currentTimeMillis();
System.out.println("end move"+" time:"+(endTime-startTime));
}
}
定义一个日志代理类CarLogProxy,同样实现Movable接口
package com.jimmy.proxy;
public class CarLogProxy implements Movable{
Movable cc = null; //接收一个实现了Movable接口的类对象作为变量,也就是所谓的聚合
public CarLogProxy(Movable cc) {
this.cc = cc;
}
@Override
public void Move() {
System.out.println("start logging..");
cc.Move(); //调用被聚合对象的Move()方法,前后加上自己的业务代码。
System.out.println("end log");
}
}
现在来看,Car类,CarTimeProxy类和CarLogProxy类是平行的,均实现了Movable接口。
CarTimeProxy类和CarLogProxy类中拿到了Car类的对象。
下面测试他们之间的代理关系。
package com.jimmy.proxy;
public class MovableClient {
public static void main(String[] args) {
Car cc = new Car(); //首先获得Car对象cc
Movable mm1 = new CarTimeProxy(cc); //将cc传入CarTimeProxy类,于是mm1就是cc的代理对象
Movable mm2 = new CarLogProxy(mm1); //将mm1传入CarLogProxy类,于是mm2就是mm1和cc的代理对象
mm2.Move();
}
}
3,静态代理中的继承和聚合那个更好?存在的问题
聚合好,代理有时需要叠加并且叠加顺序不一样,如:一种情况是先代理1,再代理2,最后代理3。另一种情况是先代理2,再代理3,最后代理1等等。
对于继承来说,因为继承模式下,代理的顺序都是写死了的,针对每一种情况,都要写一个继承类。类的个数将无限制增加。
对于聚合来说就灵活很多了,只需要根据业务逻辑,由内到外一层一层创建对象即可。
但是,现在我有其他的继承自Movable接口的类(如:bike类,motocycle类等等)想实现时间和日志代理,同样要写一系列代理类出来。类的个数将同样无限制增加。
要解决以上问题,就引入了动态代理。
二,动态代理
由静态代理我们知道,代理的部分由接口,代理对象和代理逻辑组成。
而在静态代理中,这些部分都是写死的,不能灵活变换。现在动态代理将这些部分分离出来,然后组合使用。
动态代理相比于静态代理:不需要编写代理类的代码,只需调用函数即可实现代理。灵活性高。
动态代理的使用比较套路。来看使用:
jdk的动态代理由java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类组成。
1,首先定义一个Movable接口
package com.jimmy.dynamicProxy;
public interface Moveable {
public void run(double speed, String destnation); //同静态代理
}
2,定义一个类,实现Movable接口
package com.jimmy.dynamicProxy;
public class Car implements Moveable{
@Override
public void run(double speed,String destnation) {
System.out.println("running with speed:"+speed+"km/h"+" drive to "+destnation);
}
}
3,现在我们就不用再写代理类了,直接由函数得到代理对象,然后直接使用。
package com.jimmy.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Proxy.newProxyInstance(loader, interfaces, h)函数返回一个Object类型的对象proxy
* 第1个参数loader:指定代理对象的类加载器,跟被代理对象的类加载器是一样的,所以固定写法为:cc.getClass().getClassLoader()
* 第2个参数interfaces:指定代理对象实现的接口,因为代理对象和被代理对象要实现相同的接口,所以固定写为:cc.getClass().getInterfaces()
* 第3个参数h:是InvocationHandler接口的对象,该接口只定义了一个invoke(Object proxy, Method method, Object[] args)方法,所以在实例化该接口时要实现该方法
*
* invoke(Object proxy, Method method, Object[] args)方法的三个参数:
* 第1个参数proxy:指代理对象本身,一般用不到
* 第2个参数method:指被代理对象的函数,被代理对象的函数通过反射被调用。所以写法固定。
* 第3个参数args:是被代理对象函数的参数,直接加在invoke()方法的参数即可。
*
* 重点是method.invoke(cc, args)前后的代码,是代理对象对被代理对象函数功能的扩展。
*
* 由此可见动态代理的好处真的是很大,它解耦和了代理和被代理对象的复杂关系。具有高扩展性。
*
* 看下面2个代理对象。
*
*
*/
public class CarProxy {
public static void main(String[] args) {
final Moveable cc = new Car(); //得到被代理的对象cc
//得到代理对象proxy
Moveable proxy = (Moveable) Proxy.newProxyInstance(cc.getClass().getClassLoader(),
cc.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("准备出发。。。"); //前后增加的内容
method.invoke(cc, args); //proxy调用cc的函数时,由该invoke方法反射执行。
System.out.println("车停了。。。");
return null;
}
});
proxy.run(120, "home");//代理对象调用被代理对象的方法,就会执行接口中的invoke方法,那么,增加的代理内容和被代理对象的方法都会执行。
Moveable proxy2 = (Moveable) Proxy.newProxyInstance(cc.getClass().getClassLoader(),
cc.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("准备维修。。。");
method.invoke(cc, args);
System.out.println("修好了。。。");
return null;
}
});
proxy2.service();
}
}