Spring的动态代理有两种:一是JDK的动态代理(需要提供接口);另一个是cglib动态代理(通过修改字节码来实现代理)。大部分情况下,一般使用JDK动态代理,因为JDK动态代理的速度要比CGLIB要快,在SpringAOP中,如果一个Bean有接口声明,那么Spring就会使用JDK动态代理代理它,否则启用CGLIB。今天咱们主要讨论JDK动态代理的方式。JDK的代理方式主要就是通过反射跟动态编译来实现的。
反射上一篇文章已经说过了(Java反射),这里就不在做论述。我们先了解一下动态代理。
假如,A是一个软件工程师,A所在的公司是一个软件公司。现在,B为客户,B需要A所在的软件公司开发一款软件。显然,B需要找的是A公司的商务,跟商务进行沟通。最终,软件的开发由A来落实。在这里边,A公司的商务就相当于一个代理。代理的实际就是在真实服务对象(A)之前,加一个代理对象(商务)。这个代理对象,可以根据调用者(B)的要求去控制真实服务对象(A)的访问。
代理模式的好处:可以增加一些服务,同时可以根据需要选择是否需要启用真实服务对象。
扯了半天,下边进入正题,JDK动态代理
需要提供一个简单的接口
public interface HelloService
{
public void sayHello(String name);
}
实现类
public class HelloServiceImpl implements HelloService
{
@Override
public void sayHello(String name)
{
System.err.println("hello " + name);
}
}
接着,需要生成代理对象(proxy),分成两步:
1.生成代理对象需要建立代理对象(proxy)和真实对象(HelloServiceImpl)的代理关系
2.实现代理方法
在JDK动态代理中需要实现接口:java.lang.reflect.InvocationHandler.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class HelloProxy implements InvocationHandler
{
private Object target;
/**
* 生成代理对象,并和真实服务对象绑定.
* @param target 真实服务对线下
* @return 代理对象 */
public Object bind(Object target)
{
this.target = target;
//生成代理对象,并绑定.
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //类的加载器
target.getClass().getInterfaces(), //对象的接口,明确代理对象挂在哪些接口下
this);//指明代理类,this代表用当前类对象,那么就要求其实现InvocationHandler接口的invoke方法
return proxy;
}
/**
* 当生成代理对象时,第三个指定使用HelloProxy进行代理时,代理对象调用的方法就会进入这个方法。
* @param proxy ——代理对象
* @param method -- 被调用的方法
* @param args -- 方法参数
* @return 代理方法返回。
* @throws Throwable -- 异常处理 */
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.err.println("反射真实对象方法前");
Object obj = method.invoke(target, args);//相当于sayHello方法调用.
System.err.println("反射真实对象方法后");
return obj;
}
}
说明一下上边的代码:
首先声明了一个类的属性target,它的作用是保存真实服务对象(B);然后用bind方法绑定代理对象(proxy商务)和真实对象(A)。
用之前的比喻就是proxy就是商务,它代理了target(A),而商务代理的逻辑方法放在this这个对象的invoke方法中,只是this这个对象需要实现InvocationHandler接口而已。这样,声明就会进入当前类的invoke方法中,它实现的是代理逻辑,它有三个参数
Object proxy——当前代理对象(商务)
Method method——当前调度的方法
Object[] args——方法参数
然后我们通过反射调度真实对象的方法,Object obj = method.invoke(target, args);//相当于sayHello方法调用,实现功能。
控制台进行打印
public class Chapter1Main
{
public static void main(String[] args)
{
HelloProxy helloProxy = new HelloProxy();
//因为使用了接口HelloService绑定了代理对象,所以可以用HelloService作为代理对象的声明.
HelloService proxy = (HelloService) helloProxy.bind(new HelloServiceImpl());
proxy.sayHello("张三");//此时使用代理对象运行方法进入HelloProxy的invoke方法里
}
}
打印出来的结果
反射真实对象方法前
hello 张三
反射真实对象方法后
至此,HelloService实际上已经是一个代理对象了。