java代理模式之静态、动态代理

什么是代理?
在生活中,最常见的如经纪人,律师,等都可看做代理,在程序中,程序帮我们按预想结果做的一些事情,可看做代理,代理分为两种,静态代理和动态代理,动态代理也分为两种,一种是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动态代理通过一行代码就可以获取代理对象。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值