什么是代理?
在生活中,最常见的如经纪人,律师,等都可看做代理,在程序中,程序帮我们按预想结果做的一些事情,可看做代理,代理分为两种,静态代理和动态代理,动态代理也分为两种,一种是JDK动态代理,另一种为CGLIB动态代理,至于这两种动态代理有何不同,下面会一一介绍,凡事都要由浅入深,学习也是如此,我们先来一起看一个Hello World
我们先定义一个接口
/**
* Created by wumeng
*/
public interface Hello{
void hello(String name);
}
为这个接口写一个实现类
/**
* Created by wumeng
*/
public class HelloImpl implements Hello {
@Override
public void hello(String name) {
System.out.println("Hello:"+name);
}
}
现有假设需要在执行HelloImpl 类的hello方法之前及之后处理一些逻辑,如何实现?直接把逻辑代码写死在hello方法中?当然不是,菜鸟一般喜欢这么干,我们可以写一个代理类,专门用来在调用方法之前及之后处理逻辑,写一个代理类,实现Hello接口
/**
* Created by wumeng
*/
public class HelloImplProxy implements Hello{
private Hello proxy;
public HelloImplProxy(){
proxy = new HelloImpl();
}
@Override
public void hello(String name) {
before();
proxy.hello(name);
after();
}
public void before(){
System.out.println("previous。。。。");
}
public void after(){
System.out.println("behind。。。。");
}
}
我们在代理中声明了一个Hello接口对象,在其构造器中将实现类HelloImpl 赋给了此对象,实际上调用的还是实现类中的方法,在其执行之前及之后调用处理逻辑,这就是经典的代理模式,我们写的这个代理就称之为静态代理,写个方法测试一下
/**
* Created by wumeng
*/
public class TestProxy {
@Test
public void test(){
HelloImplProxy proxy = new HelloImplProxy();
proxy.hello("proxy");
}
}
运行结果
previous。。。。
Hello:proxy
behind。。。
至此一个静态代理模式就写完了,但是在项目中频繁的使用静态代理,会造成到处都是xxxxProxy类,于是,动态代理就应运而生,接下来我们一起来看如何实现动态代理
动态代理分为两种,我们先来学习第一种,JDK提供的动态代理,我们需要写一个代理类,此代理类需要实现JDK提供InvocationHandler接口,重写invoke方法
/**
* Created by wumeng
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target,args);
after();
return result;
}
public void before() {
System.out.println("previous。。。。");
}
public void after() {
System.out.println("behind。。。。");
}
}
类中的target对象即指的是被代理的目标,在调用invoke方法之前及之后处理逻辑,我们来做个简单测试
@Test
public void dynamicProxy() {
Hello hello = new HelloImpl();
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello proxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
dynamicProxy
);
proxy.hello("hello");
}
运行结果
previous。。。。
Hello:hello
behind。。。。
使用JDK的Proxy类提供的newProxyInstance方法,该方法需要三个参数,ClassLoader,被代理的接口对象,及包装对象,该方法会返回一个代理对象,使用代理对象执行方法即可,我们发现返回代理对象的newProxyInstance参数太多,之后还需要进行强制类型转换,于是我们可以把DynamicProxy 类重构一下,代码如下
/**
* Created by wumeng
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(){
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target,args);
after();
return result;
}
public void before() {
System.out.println("previous。。。。");
}
public void after() {
System.out.println("behind。。。。");
}
}
我们写了一个getProxy类,用来返回代理类对象,优化了调用的流程,做个测试
@Test
public void reProxy(){
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello hello = dynamicProxy.getProxy();
hello.hello("hello");
}
运行结果
previous。。。。
Hello:hello
behind。。。。
至此,一个使用JDK实现的动态代理就完成了,使用动态代理的优点是代理接口变了,代理逻辑变了,代理类均不需要改变,但静态代理则不能实现此效果,但是如果想代理一个类,或者一个方法,那么此时使用JDK动态代理则行不通了,JDK动态代理只能代理接口,那么如何实现代理一个类?这个时候就需要用到CGlib了
CGLIB(Code Generation Library)是一个开源项目!
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成,那么接下来,我们使用CGLIB提供的动态代理写一个示例
public class CGlibProxy implements MethodInterceptor {
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> tClass) {
return (T) Enhancer.create(tClass, this);
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
private void before() {
System.out.println("previous。。。。");
}
private void after() {
System.out.println("behind。。。。");
}
}
使用CGLIB需要实现MethodInterceptor接口,并重写intercept方法,在调用之前及之后处理逻辑,该类提供了一个getProxy方法用来返回代理类对象,使用Enhancer类的create方法即可返回一个代理类对象,我们做个简单测试
@Test
public void cglibProxy() {
CGlibProxy cGlibProxy = new CGlibProxy();
Hello hello = cGlibProxy.getProxy(HelloImpl.class);
hello.hello("hello");
}
运行结果
previous。。。。
Hello:hello
behind。。。。
用三行代码返回对象还是有些冗长,于是我们想到了单例模式,改造下
CGlibProxy 类
/**
* Created by wumeng
*/
public class CGlibProxy implements MethodInterceptor {
private static CGlibProxy cGlibProxy = new CGlibProxy();
private CGlibProxy(){
}
public static CGlibProxy getInstance(){
return cGlibProxy;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> tClass) {
return (T) Enhancer.create(tClass, this);
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
private void before() {
System.out.println("previous。。。。");
}
private void after() {
System.out.println("behind。。。。");
}
}
提供了一个private的构造器,防止外界通过new来创建对象,对外界暴露了一个getInstance方法,返回一个对象,做个简单测试
@Test
public void cglibProxy() {
Hello hello = CGlibProxy.getInstance().getProxy(HelloImpl.class);
hello.hello("hello");
}
运行结果
previous。。。。
Hello:hello
behind。。。。
至此,我们使用CGLIB动态代理通过一行代码就可以获取代理对象。