1.静态代理:
我们先看一个这样的生活场景:当你找老板们去审批一个报销单的时候,往往是直接找不到老板本人,通常是通过老大的秘书是帮忙代劳的,大家都知道秘书和老板走的最近的,她可以帮老板们完成一些工作,这些工作是老大授权给他们的。我们通常找秘书相对也比较方便一点。这里其实就是我们代理的一个基本的原型:外界的请求不和真实的实际对象打交道,而是和真实的对象的代理打交道,通过代理对象来传递请求的对象的诉求和返回对应的结果给请求对象。下面我们按照老板和秘书的情况来举例看看静态代理怎么做。场景:员工找秘书请求老板批年假的申请。
一般情况下,静态代理必须要有如下几个部分组成:抽象行为接口(批年假),真实对象(老板:具备批年假的权限),代理对象(老板的代言人,拥有老板赋予的部分权限,比如说批年假),客户端(员工)。
创建一个静态代理的步骤如下:
第一步:创建一个抽象的的行为接口
AbstractAction.java
package com.java.staticproxy;
/**
*
* @author bluesky
* 抽象行为类,定义对应的抽象方法
*/
public abstract class AbstractAction {
public abstract String request();
}
第二步:真实类(被代理类,上面说到的老板角色类)
RealSubject.java
package com.java.staticproxy;
/**
*
* @author bluesky
* 真实类,实现抽象接口所定义的方法,这里是动作的真正执行者
*/
public class RealSubject extends AbstractAction {
@Override
public String request() {
return "同意年假申请";
}
}
第三步:代理类(秘书角色),代理类需要同样实现抽象类所实现的方法,同时需要持有一个对真实类的引用(如秘书持有一个对老板类的应用,这个应用可以看做是老板所授予的对应特权给了秘书)。
ProxySubject.java
package com.java.staticproxy;
public class ProxySubject extends AbstractAction {
RealSubject boss ;//代理对象通过一个对真是对象的应用来完成代理的动作
public ProxySubject() {
boss = new RealSubject();
}
public void preReq(){
System.out.println("审批是否符合要求");
}
@Override
public String request() {
preReq();
String ret = boss.request();
System.out.println(ret);
postReq();
return ret;
}
public void postReq(){
System.out.println("备案、结束");
}
}
最后一步:客户端(员工请假请求)
Client.java
package com.java.staticproxy;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
AbstractAction request = new ProxySubject();//这里用到了多态,看出来了没?
request.request();
}
}
OK,至此一个静态代理模式的基本上的结构都完成了。输出结果如下:
审批是否符合要求
同意年假申请
备案、结束
静态代理相对来说比较简单,但是这种方式有一定的局限性:
1)必须知道真实代理的角色,代理类越多,真实代理类跟着越多,真实代理类需要时限就真实存在。会造成代理类的不断庞大。一般场景用到的不是非常多,但是不代表不重要。还是要理解通透。
如何解决代理类过多的问题?动态代理。动态代理相对比较灵活,他是在运行时才决定所需要代理的类,。下面一起看看动态代理。
动态代理:
JAVA 动态代理位于
package com.java.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
*
* 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
* 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
* 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后
* 加入自己的一些额外方法。
*
* 这个类其实类似于前面说的秘书类,但是仔细观察这个类很特殊的地方是这个类里面没有一点和realSub相关的信息,初始化部分都是
* 用的是object的类型,invoke里面的都是通过main里面一次性生成代理实例传递过来的。这样就可以做到多个被代理类通过一个代理类
* 动态的来实现代理,避免了一个被代理类就有一个代理类与之对应的冗余设计。
*/
public class DynamicSubject implements InvocationHandler {
/**
* 这里用来传递真实的代理对象进来。但是这里不指定realSub,
* 因为动态代理里面是在运行时从Client客户端Proxy.newInstance里面生成代理的时候才指定
*/
Object sub;
public DynamicSubject(Object obj) {
this.sub = obj;
}
//Object proxy:代理实例本身,一般情况下用不上
//method 所代理的那个方法对象
//method方法所承载的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("资格审核中");//可以自己加入一些准备等附加操作
System.out.println("before invoke: "+method);
//这里打印出来的其实和Client里面Proxy.newProxyInstance生成的对象是一样的
System.out.println("Object proxy: "+ proxy.getClass().getName());
//这个invoke方法必须调用,否则将不会调用到对应的Subject类的方法。
Object ret = method.invoke(sub, args);
System.out.println("done");
System.out.println("after invoke: "+method);
return ret;
}
}
第二步: 创建被代理类的接口和代理类
package com.java.dynamicproxy;
public interface Subject {
public String request();
}
被代理类:RealSubject.java
package com.java.dynamicproxy;
public class RealSubject implements Subject {
@Override
public String request() {
System.out.println("同意年假申请");
return "同意年假申请";
}
}
package com.java.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
RealSubject realSub = new RealSubject();
InvocationHandler dynamicProxy = new DynamicSubject(realSub);
//如下代码一次性生成代理。你给他传入什么真实对象,他就能代理什么。比如说你这里传入的realSub2,那他就代理的realSub2所实现接口。
//代理本身不做实际操作,实际操作都会转移到invocationhandler里面去处理。
//第一个参数:类加载器,代理类所对应的类加载器。
//第二个参数:代理类所实现的接口,就是RealSubject所实现的接口。 the list of interfaces for the proxy class to implement
//第三个参数:the invocation handler to dispatch method invocations to
// 这里的其实就是dynamicProxy ,因为dynamciProxy实现了invocationHandler 接口。
//Subject sub = (Subject)Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(), realSub.getClass().getInterfaces(), dynamicProxy);
Subject sub = (Subject)Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(), new Class<?>[]{Subject.class}, dynamicProxy);
String ret = (String)sub.request();//调用被代理类的函数,调用该函数之后调用就会直接传递给InvocationHandler dynamicProxy 去执行对应的invoke方法去执行,并返回结果。
System.out.println("代理结果是:"+ret);
//这里的sub.getClass和DynamicSubject里面的
//public Object invoke(Object proxy, Method method, Object[] args)
//第一个参数是一样的,object proxy是从这个sub传递过去的。
System.out.println(sub.getClass());
System.out.println("=====================================华丽丽的分割线===============================");
}
}
before invoke: public abstract java.lang.String com.java.dynamicproxy.Subject.request()
Object proxy: $Proxy0
同意年假申请
done
after invoke: public abstract java.lang.String com.java.dynamicproxy.Subject.request()
代理结果是:同意年假申请
class $Proxy0
=====================================华丽丽的分割线===============================
注意看Object proxy: $Proxy0 和class $Proxy0这两个打印,一个是在DynamicSubject 里面
System.out.println("Object proxy: "+ proxy.getClass().getName());的打印的,另外一个是在测试main函数里面调用<pre name="code" class="java">System.out.println(sub.getClass());打印出来的,可以看到,一次性生成的代理Subject sub 和传递到<pre name="code" class="java">public Object invoke(Object proxy, Method method, Object[] args) 里面的Object proxy 参数其实是一个东西。
当一次生生成的代理对象sub调用sub.request()之后,对应的处理流程会传递到实现了InvocationHandler的dynamicSubject对象当中的invoke方法中去,从打印的结果来看,也的确是执行到那块了。invoke方法里面调用了method.invoke(Object obj,Object args...)方法,这里的obj我们再最开始的时候用的是Object类型,和任何实际代理类无关,在需要什么样的代理类的时候就传递什么样的对象进去即可。args就是method所需要调用的参数。这里的object是在
InvocationHandler dynamicProxy = new DynamicSubject(realSub);传递进去的,就是realSubject对象,所以代理的是它。
OK,下面再看看一个动态代理类的另外一个案例,就能看到动态代理的相对灵活性了。还是按照上面说的创建一个动态代理类的步骤来做:
第一步:创建一个实现InvocationHandler的方法,这个类必须实现invoke方法,否则对应的代理类方法就无法调用起来。
package com.java.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CommonInvocationHandler implements InvocationHandler {
private Object foos;
public CommonInvocationHandler(Object obj) {
this.foos = obj;
}
public CommonInvocationHandler()
{
}
public void setFoo(Object foo) {
this.foos = foo;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
method.invoke(foos, args);
return null;
}
}
第二步: 创建被代理类的接口和代理类
package com.java.dynamicproxy;
public interface Foo {
void doAction();
}
package com.java.dynamicproxy;
public class FooImp1 implements Foo {
@Override
public void doAction() {
System.out.println("in FooImpl doAction!");
}
}
FooImp2.java
package com.java.dynamicproxy;
public class FooImp2 implements Foo {
@Override
public void doAction() {
System.out.println("in FooImpl2 doAction!");
}
}
第三步:生成代理和测试:
package com.java.dynamicproxy;
import java.lang.reflect.Proxy;
public class TestFoo {
/**
* @param args
*/
public static void main(String[] args) {
Foo f = null;
CommonInvocationHandler commanhander = new CommonInvocationHandler();
Foo f1 = (Foo) new FooImp1();
commanhander.setFoo(f1);
f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, commanhander);
f.doAction();
System.out.println("========分割线,开始生成第二个代理了======================");
commanhander.setFoo(new FooImp2());
f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, commanhander);
f.doAction();
}
}
执行结果:
========分割线,开始生成第二个代理了======================
in FooImpl2 doAction!