核心思想
为其他对象提供一种代理来控制对这个对象的访问。其核心在于访问控制。
具体实现
抽象业务:一般是一个抽象类或者是一个接口,用于定义相关的业务函数。
业务主体:这个类具体实现了抽象业务中定义的方法,或者实现了抽象类中的接口,在实际的运行中是由该部分实际的承担着相关业务的逻辑与运行。
代理委托:这个类也实现抽象业务中的相关方法或者接口,但是其并不具体实现相关的方法,而是产生一个业务主体的类通过这个类的实例来处理抽象业务。
简单实现
先定义一个业务1
2
3public interface Service{
public void deal();//业务方法
}
这里我们通过一个接口来定义一个deal()方法作为我们抽象业务的主体部分。
之后我们需要具体的实现这个业务逻辑。我们以一个RealA类来表示。1
2
3
4
5
6public class RealA implements Service{
public void deal(){
System.out.println("deal");//具体的业务实现
}
}
这样我们就完成了基本的业务部分了,如果在实际应用时就可以直接的使用RealA来进行处理了,但是我们还需要一个代理类来实现对业务类的访问控制。这个类也实现了抽象接口的定义,但是实际是通过调用业务类的方法实现的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class ProxyA implements Service{
private Service realDeal;//实际的业务处理者,被代理的对象
public ProxyA (Service service){//构造方法传入被代理的对象
this.realDeal=service;
}
//处理前的操作
private void beforeDeal(){
System.out.println("before deal");
}
//处理后的操作
private void afterDeal(){
System.out.println("after deal");
}
public void deal(){
beforeDeal();
realDeal.deal();//实际还是被代理对象在进行处理
afterDeal();
}
}
这样一个简单的代理模式就完成了,在实际的代理操作中我们可以在代理操作之前后之后增加一些处理,比如在操作前进行一些条件判断啥的,即是上面的beforeDeal()和afterDeal()的作用了。
实际运用
普通代理
先定义一个业务1
2
3public interface Service{
public void deal();//业务方法
}
实际的业务处理者(被代理者)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class RealA implements Service{
private String args;
public RealA (Service proxy,String args){
if(proxy==null){
throw new Exception("无法创建");//保证只有代理类才可以创建被代理者,彻底的代理。
}else{
this.args=args;//传入需要的参数。
}
}
public void deal(){
System.out.println(args);//具体的业务实现
}
}
代理类的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class ProxyA implements Service{
private Service realDeal;//实际的业务处理者,被代理的对象
public ProxyA (String args){//构造方法传入被代理的对象的参数,由代理类来进行被代理对象的创建。
this.realDeal=new RealA(this,args);//创建被代理对象,被代理类对象的代理类是当前类的实例。
}
//处理前的操作
private void beforeDeal(){
System.out.println("before deal");
}
//处理后的操作
private void afterDeal(){
System.out.println("after deal");
}
public void deal(){
beforeDeal();
realDeal.deal();//实际还是被代理对象在进行处理
afterDeal();
}
}
这种使用与上面的简单实现有一个最大的不同,就是对实际的被代理类对象的创建是完全由代理类控制的,这样就保证了代理类的访问控制。当具体的业务需要变更时只需要对被代理类中的逻辑进行修改就可以了,对其他模块的使用不会造成任何影响。
强制代理
先看看其代码实现,先是业务定义:1
2
3
4
5public interface Service{
public void deal();//业务方法
public Proxy getProxy();//新加了一个方法用于获取代理
}
相比之下多了一个方法用于获取代理类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public class RealA implements Service{
private String args;
private Proxy proxy=null;
public RealA (String args){
this.args=args;//传入需要的参数。
}
public Proxy getProxy(){
if(!isProxy()){
proxy=new Proxy(this);//如果没有指定代理就创建一个
}
return proxy;
}
public void deal(){
if(isProxy()){
System.out.println(args);//具体的业务实现
}else{
throw new Exception("非法访问");//保证只有代理类才可以调用具体的业务实现
}
}
private boolean isProxy(){
if(proxy==null){
return false;
}else{
return true;
}
}
}
这里多了有一个isProxy的方法用于检查对代理的设置,如果没有指定代理就不允许对相关方法进行访问,同时getProxy方法直接提供了对代理的管理,只有通过这个方法建立代理类才能对相关的业务函数进行访问,直接new一个代理类也无法实现对被代理类的访问,“强制”也是在这里体现。
之后是代理类,实现也没什么特别,只是getProxy的方法指向了自身。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Proxy implements Service{
private RealA real=null;
public Proxy (RealA real){
this.real=real;//设置被代理对象
}
public Proxy getProxy(){//设置代理类的代理就是自身
return this;
}
public void deal(){
real.deal();//被代理对象进行实际处理
}
}
强制代理的使用就有意思了,我们想使用代理但是却是对被代理类进行实例化,之后在通过被代理类的getProxy方法获得代理实例,再由代理类对被代理对象进行访问。听起来有点绕,我们就具体的用一下。1
2
3
4
5
6public static void main(String[] args) {
RealA realA = new RealA("args");
Proxy proxy=realA.getProxy();
proxy.deal();
}
//输出 args
这里一定是由被代理对象生成代理,之后在对其业务进行操作,如果是直接调用被代理类,或是是自己创建代理就会无法调用相关方法。比如:1
2
3
4
5public static void main(String[] args) {
RealA realA = new RealA("args");
realA.deal();
}
//输出 非法访问
还有这样直接创建代理也不行1
2
3
4
5
6public static void main(String[] args) {
RealA realA = new RealA("args");
Proxy proxy= new Proxy(realA);
proxy.deal();
}
//输出 非法访问
代理扩展
对于代理类来说,我们还可以通过实现多个接口来扩展代理的功能,以完成各种需求,这里就不再赘述。
动态代理
动态代理是根据被代理的接口生成所有的方法, 也就是说给定一个接口,动态代理会实现该接口下的所有方法,而要实现动态代理就必须要了解一下InvocationHandler这个接口,它是Java中用于处理动态代理的接口。先看源码1
2
3public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
这个接口中也没啥,就只有一个invoke方法。但是实现这个接口之后,就可以通过这个invoke方法来调用被代理类中的接口方法。再看看一个简单的动态代理实现:1
2
3
4//业务抽象
public interface Service{
public void deal();//业务方法
}
1 | //被代理类 |
1 | //实现了InvocationHandler的代理类 |
这里的method.invoke()方法是Method类中的方法,用于运行具体的method,传入的参数是方法所在的类实例和实现该方法的参数。
我们来实际运用一下1
2
3
4
5
6
7
8public static void main(String[] args) throws Throwable {
Service realA = new RealA();
InvocationHandler handler = new ProxyA(realA);
ClassLoader cl = realA.getClass().getClassLoader();
Service proxy=(Service) Proxy.newProxyInstance(cl, new Class[] {Service.class}, handler);
proxy.deal();
}
//输出 deal
这里我们首先创建了一个具体的被代理类,之后有创建了一个InvocationHandler接口,最后使用Proxy中的newProxyInstance方法来创建了代理proxy,完成了方法的调用。其中Proxy.newProxyInstance方法用于实现对代理类的创建,传入的值为被代理类的类加载器,相关的接口类,还有具体的InvocationHandler接口实例。
动态代理的核心就在于InvocationHandler接口与Proxy.newProxyInstance()方法,前者用于实际的运行代理类接口中的方法,而后者配合前者,通过传入的接口类与被代理类的类加载器,还有反射机制动态的创建代理类,具体的实现步骤可以看看Proxy类的源码(在java.lang.reflect包中)。
这里动态代理只是创建代理类的方式不同,但是其具体的思想还是与上面一致的,我们可以根据需求结合使用。
小结
代理模式的运用还是比较广泛的,我们熟知的AOP就与其息息相关。静态代理相对简单,动态代理就没有那么好理解,其中包含有反射机制和一些复杂类的调用比如(Proxy),但是实现起来还是不困难的,但是注意动态代理中代理的方法是接口方法,不能是抽象方法,这是由Proxy.newProxyInstance()中传入的接口类型参数而固定的,也就是说其传入的只能是接口类型的class不能是抽象类,这点要注意。只要了解其基本的思想我们就可以很容易的进行代理的设计来实现需求。