什么是代理
代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。
对于某一功能来说(这些功能对应为一些接口),一个类A继承这些接口并提供这个功能的实现。但是 不能直接调用类A的对象来使用这些接口定义的功能,而是新建一个类B来调用类A的对象。同过类B来调用类A实现的功能,并决定何时以及如何调用这些功能。 这里的类A和类B分别即为被代理类与代理类。
1.一个类(被代理类)拥有某些功能(方法,这些方法被定义在一个功能接口中),这些功能的方法需要通过另一个类(代理类)来执行。即用户不能直接使用被代理类对象来执行其提供的方法,需要通过一个中介(代理类)来执行。
2.代理类可以在被代理类提供的功能的基础上做一些扩展(比如在执行被代理类方法前后打印一些日志)。而用户在通过代理类对象使用被代理类提供的功能时,同样可以使用这些扩展的功能。
3.代理类不关心如何实现,它只对这些方法的运行进行管理。它就像是一个管家,或者说是一个包装类,对被代理类进行包装和管理。
静态代理
静态代理的特点是: 一个代理类与 一类 特定的被代理类绑定(指继承了相同的功能接口的实现类),在编译时就已确定其间的绑定关系。因而需要为每一类被代理类写一个代理类。他们之间有这种种绑定关系是因为代理类与被代理类需要实现相同的功能接口。
考虑下面的例子:有一个功能接口定义了一个造衣服的方法
//一个定义了如何造衣服的接口
interface Cloth {
void produceCloth();//这个方法定义如何造衣服
}
然后耐克公司定义了一个实现类来实现这个接口:
/**
* 耐克公司的被代理类,实现了如何造衣服
*/
class Nike implements Cloth {
@Override
public void produceCloth() {
System.out.println("Nike->Many Cloth be made!");
}
}
李宁公司也实现了这一接口:
/**
* 李宁公司的被代理类,实现了如何造衣服
*/
class LiNing implements Cloth {
@Override
public void produceCloth() {
System.out.println("LiNing->Many Cloth be made!");
}
}
然后一家销售公司(代理类)获取这种衣服的生产线(被代理类),并控制衣服的生产(被代理类方法的执行)与销售(代理类添加的附加功能):
/**
* 一个静态代理类,代理了实现Cloth接口的实现类
*/
class ClothProxy implements Cloth {
//既然要代理被代理类的方法,就要把被代理类的对象传进来,因为代理类并不提供Cloth方法的实现,
Cloth cloth; // 真正的功能实现者
//传入被代理类对象,注意使用的是接口类型,这保证了该代理类对象可以代理同一接口的不同实现类
public ClothProxy(Cloth cloth) {
this.cloth = cloth;
}
//代理类对象执行接口里的方法,实际是调用被代理类的实现
@Override
public void produceCloth() {
//注意:由于代理类实际控制着被代理类实现的方法,所以代理类可以在调用被代理类实现的方法的前后做一些扩展操作(本例可以对应产品的销售活动)
System.out.println("被代理类的对象方法被执行了");//代理类的对象对被代理类对象做的扩展
cloth.produceCloth();//执行实际的生产衣服的功能
System.out.println("被代理类的对象方法执行结束");//代理类的对象为被代理类对象做的扩展
}
}
下面是测试
@Test
public void testProxyStatic() {
//实例化一个被代理类对象
Nike nike = new Nike();
//实例化另一个被代理类对象
LiNing liNing = new LiNing();
//实例化一个代理类对象,根据传入的被代理类对象的不同,执行不同的实现
ClothProxy proxy = new ClothProxy(nike);
ClothProxy proxy1 = new ClothProxy(liNing);
//执行代理类的方法
proxy.produceCloth();
System.out.println();
proxy1.produceCloth();
}
测试结果
小结: 静态代理因为代理类要实现被代理类相同的接口,所以他在编译时就已经确定了两者之间的绑定关系。耦合度较高。
动态代理
静态代理的代理类与被代理类进行绑定(具体来说是与那些接口绑定),这样代码耦合度高。动态代理实现了使用同一个代理类来代理所有不同的被代理类(即使被代理类实现的接口不同)。他的技术基础是Java反射。通过反射来动态创建代理类对象。
动态代理的特点是: 动态代理在运行时才确定与哪一类被代理类绑定。在运行时通过反射动态创建代理类对象来代理不同的代理类。
注:动态只是在编译期间是动态的,在程序运行时,代理类代理的被代理类都是确定的。
分析:既然动态代理不会与被代理类实现的接口绑定,那么在被代理类代码中不能编写任何与特定被代理类耦合的代码。它使用 Proxy.newProxyInstance()动态创建代理类,使用InvocationHandler接口来处理被执行的被代理类方法。
举例来说:一个接口定义了动物发出什么样的声音:
//定义动物的叫声
interface Yeal {
void sound();
}
一个实现类(被代理类)实现了这一接口,定义猴子的叫声:
//定义猴子叫声的实现类
class Monkey implements Yeal {
@Override
public void sound() {
System.out.println("a monkey`s voice!");
}
}
下面是一个动态代理类,他不指定实现任何接口(注意:他的对象本身不执行被代理类的功能,他只为传进来的被代理类对象动态生成一个代理类,然后由这个代理类执行功能):
class DynamicProxy {
//包装被代理类,因为不指定被代理类,所以成员是顶级类Object的对象
private Object obj;
//这个方法根据传入的被代理对象obj,通过反射动态创建被代理类的代理对象,并做一些扩展
public Object bind(Object obj){
this.obj = obj;
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
(proxy1,method,args)->{
System.out.println("方法"+method.getName()+"正在执行"); //扩展功能1
Object result = method.invoke(obj, args);//执行被代理类所实现的方法
System.out.println("方法"+method.getName()+"执行完毕");//扩展功能2
return result;//被代理类方法的返回值
});
return proxy;//返回这个被代理类的代理类对象
}
}
测试:
//动态代理
@Test
public void testDynamicProxy(){
//实例化一个被代理类对象
Nike nike = new Nike();
//实例化另一个被代理类对象
Monkey monkey = new Monkey();
//实例化一个用于生成代理类的对象
DynamicProxy dynamicProxy1 = new DynamicProxy();
//生成Nike的代理类
Cloth cloth = (Cloth) dynamicProxy1.bind(nike);
//执行造衣服
cloth.ProduceCloth();
System.out.println("--------------------------");
//生成monkey的代理类
Yeal yeal = (Yeal) dynamicProxy1.bind(monkey);
//执行发出叫声
yeal.sound();
}
测试结果
总结
动态代理与静态代理的区别在于代理类是否绑定了被代理类实现的接口。静态代理的代理类实现了被代理类同样的接口,绑定关系在编译时就已经确定。代码耦合度高。动态代理的代理类不指定任何接口,他通过反射动态获取被代理类对象的结构并创建被代理类的代理对象。而且动态代理的代理类对被代理类功能的扩展是面向切面编程(aop)的基本原理。