(1) 设计模式中的代理
代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替实际对象的对象。代理是保证了在任何时刻可以把额外的工作从“实际”对象中分离到不同的地方,希望可以很容易地做出修改,从没有使用这些操作到可以使用这些操作,或者是反过来,这个时候代理就显得很有用。
如下代码表示的是RealObject实现了Interface接口 ,并且完成了一定的功能,现在想在完成已有的工作的基础上,再进行一层封装,用SimpleProxy同样实现Interface接口中的方法并且代理RealObject类,在实现方法内部调用被代理实例的方法之前或之后都可以再添加其他的操作。
interface Interface{
void doSomething();
void doSomethingElse(String args);
}
class RealObject implements Interface{
@Override
public void doSomething() {
System.out.println("doSomething");
}
@Override
public void doSomethingElse(String args) {
System.out.println("doSomethingElse "+args);
}
}
public class SimpleProxy implements Interface{//代理类
private Interface proxied;//被代理的实例
public SimpleProxy(Interface proxied){
this.proxied = proxied;
}
@Override
public void doSomething() {
System.out.println("SimpleProxy doSomething");
proxied.doSomething();
}
@Override
public void doSomethingElse(String args) {
System.out.println("SimpleProxy doSomethingElse "+args);
proxied.doSomethingElse(args);
}
}
public class SimpleProxyDemo {
public static void consumer(Interface iface){
iface.doSomething();
iface.doSomethingElse("zzzkkk");
System.out.println("hello world");
}
public static void main(String[] args){
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}
输出如下:
(2)java中的动态代理
Java中的动态代理比设计模式中的静态代理更前进了一步,因为java中的动态代理可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。
java.lang.reflect 包中的InvocationHandler 接口和Proxy类提供了生成动态代理类的能力。
InvocationHandler接口
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。Invoke方法有三个参数:
① obj - 在其上调用方法的代理实例.
② method - (指代的是我们所要调用真实对象的某个方法的Method对象)。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
③ args - 包含传入代理实例上方法调用的参数值的对象数组(指代的是调用真实对象某个方法时接受的参数),如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
Proxy类
Proxy类是提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。Proxy.newProxyInstance创建动态代理,需要三个参数,Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
①是类加载器ClassLoader loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载.
②是该代理实现的接口列表(不是类或抽象类)Class<?>[] interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了.
③是InvocationHandler接口的一个实现:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上.
Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
代码示例如下:
package exercise20160310;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class DynamicProxyHander implements InvocationHandler{
private Object proxied;//真是的被代理的实例
public DynamicProxyHander(Object proxied){
this.proxied = proxied;
}
/**
* 在代理实例上处理方法调用并返回结果。
* 在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
* obj - 在其上调用方法的代理实例.
* method - 对应于在代理实例上调用的接口方法的 Method 实例(指代的是我们所要调用真实对象的某个方法的Method对象)。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
* args - 包含传入代理实例上方法调用的参数值的对象数组(指代的是调用真实对象某个方法时接受的参数),如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
*/
@Override
public Object invoke(Object obj, Method method, Object[] args)
throws Throwable {
System.out.println();
System.out.println("代理调用实际的方法前,可以添加一些额外的工作操作比如下面的打印一些信息~~~");
System.out.println("******** obj: "+obj.getClass() +", method: "+method+", args: "+args);
if(args!=null){
for(Object arg : args){
System.out.println(" "+arg);
}
}
method.invoke(proxied, args);//把请求分发给真正代理的对象。
System.out.println("代理调用实际的方法后~~~");
return null;
}
}
package exercise20160310;
import java.lang.reflect.*;
public class SimpleDynamicProxy {
public static void consumer(Interface iface){
iface.doSomething();
iface.doSomethingElse("zzzkkk");
System.out.println("hello world");
}
public static void main(String[] args){
RealObject real = new RealObject();
consumer(real);
/*
* Proxy.newProxyInstance创建动态代理,需要三个参数,Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 一个是类加载器ClassLoader loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载.
* 一个是该代理实现的接口列表(不是类或抽象类)Class<?>[] interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了.
* 一个是InvocationHandler接口的一个实现:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上.
*
* 下面的这行代码表示的是动态代理RealObject的一个实例对象real,指定由Interface.class的类加载器加载这个代理的对象,该对象实现了Interface接口。
* 注意在这里Interface只是我声明的一个接口。
*
* 当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke方法来进行调用.
*/
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHander(real));
consumer(proxy);
System.out.println(proxy.getClass().getName());
}
}
interface Interface{
void doSomething();
void doSomethingElse(String args);
}
class RealObject implements Interface{
@Override
public void doSomething() {
System.out.println("doSomething");
}
@Override
public void doSomethingElse(String args) {
System.out.println("doSomethingElse "+args);
}
}
输出如下:
(3)动态代理总结
动态代理说白了就是RealObject目前可以完成一定的工作,但是现在想要在不改变RealObject工作的基础上给他增加额外的工作,这个时候就用到了动态代理,写了一个DynamicProxyHander类实现 InvocationHandler接口,实现了接口中的invoke方法,增加完成代理类方法工作前后的一些额外工作,并且在调用的时候是通过下面这条代码实现的。
Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHander(real));
当我们通过代理对象调用一个方法的时候比如proxy作为参数调用consumer(proxy);时,这个方法内部有需要去调用形参proxy对象内的方法,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。具体表示含义可见上面的讲解。这说明了动态代理会将所有调用重定向到调用处理器,因此通常调用的时候会向调用处理器的构造器传递一个实际对象的引用,从而使得调用处理器在执行其中介任务时可以将请求转发。
上述代码的类图如下:
(4) JDK动态代理与CGLIB代理
JDK动态代理只能针对实现了接口的类生成代理。CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
①.JDK动态代理
此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。
②.CGLIB代理
CGLIB(CODE GENERLIZELIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
AOP包括切面(aspect)、通知(advice)、连接点(joinpoint),实现方式就是通过对目标对象的代理在连接点前后加入通知,完成统一的切面操作。