Java代理模式 (静态代理,JDK动态代理,CGlib动态代理)

一. 代理模式简介

Java中代理模式分为静态代理,JDK动态代理,CGlib动态代理三种。下面针对每一项具体介绍。代理是为了在方法前后做一些操作,比如记录方法的运行时间,记录,拦截等等。

二. 静态代理

静态代理就是使用一个代理类可以用来完成原本需要在实现类调用的方法前后添加操作,比如需要调用sayHello(name)方法,此时,代理类生成了原实现类HelloImpl的实例,并调用,从main类的调用来看,没有在sayHello(name)前后调用方法,但是确实中代理类调用了before()和after()方法。代理如下

1.新建Hello接口

package com.canfengli.chapter4.proxy.staticProxy;

public interface Hello {
    void sayHello(String name);
}

2.新建Hello的实现类HelloImpl

package com.canfengli.chapter4.proxy.staticProxy;

public class HelloImpl implements Hello {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello! " + name);
    }
}

3.新建代理类HelloProxy

package com.canfengli.chapter4.proxy.staticProxy;

public class HelloProxy implements Hello{
    Hello hello;

    public HelloProxy() {
        hello = new HelloImpl(); //此处是为了生成Hello的实例,用于后续调用sayHello(name)方法
    }

    @Override
    public void sayHello(String name) {
        before();
        hello.sayHello(name);
        after();
    }

    //添加我们要在sayHello(name)中前后都需要实现的方法

    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }

}

4.新建主类测试

package com.canfengli.chapter4.proxy.staticProxy;

/**
 * @author canfengli
 * 2017-09-27
 * 这是测试主类
 *
 */

/*
* 1.要实现在Hello接口中sayHello(name)前后做一些方法处理,此处我们不能接受在实现类里面直接添加before和after操作,高度耦合
* 2.我们采用一个HelloProxy类代理,在类的sayHello(name)方法里,直接调用此类的before和after,实例化接口时通过此方法,调用HelloImpl类中已经实现的sayHello方法
* 3.由于我们需要调用HelloImpl的方法,所以需要实例化接口。此处可以采用静态直接生成,如下
*   private static Hello hello =  new HelloImpl(); 或者在实例化代理类时创建hello的实例,如HelloProxy所示
*
* 4.我们没有破坏原有的代码结构的情况下,实例化了代理类。代理类调用了原实现类HelloImpl中实现的方法,这种间接调用就称为静态代理
*
*/

public class HelloMain {
    public static void main(String[] agrs){
        Hello helloProxy = new HelloProxy();
        helloProxy.sayHello("Java");
    }
}
测试结果如下:

Before
Hello! Java
After

我们成功实现了在不破坏原有实现类的基础上实现了在调用sayHello(name)时能进行一些方法执行前后的操作。


二. 动态代理

1. 新建动态代理类DynamicProxy。实现InvocationHandler接口

package com.canfengli.chapter4.proxy.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 实现自己的InvocationHandler
 * @author licf
 * @since 2017-09-27
 *
 */

public class DynamicProxy implements InvocationHandler {

    // 目标对象
    private Object target;

    /**
     * 构造方法
     * @param target 目标对象
     */
    public DynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 执行目标对象的方法
     * @param
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在目标对象的方法执行之前
        before();
        Object result = method.invoke(target, args);
        // 在目标对象的方法执行之后
        after();
        return result;
    }

    /**
     * 获取目标对象的代理对象
     * @return 代理对象
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy() {

        /**
         * loader:类加载器
         * interfaces:目标对象实现的接口
         * h:InvocationHandler的实现类
         * 总体思路就是他通过字符串化产生一个新的java类,再动态编译返回对象。
         * 返回Object,强制转换为T,这是向下转型。IDE会报警告
         */
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }


}

2. 编写入口类DynamicMain

package com.canfengli.chapter4.proxy.dynamicProxy;

import com.canfengli.chapter4.proxy.staticProxy.Hello;
import com.canfengli.chapter4.proxy.staticProxy.HelloImpl;

public class DynamicMain {
    public static void main(String[] args) {

        DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl()); //入参Hello的引用hello DynamicProxy初始化入参的实例
        Hello helloProxy = dynamicProxy.getProxy();

        //当我们调用接口的方法时,实际调用的是代理类的方法,而代理类的方法经过getProxy()方法处理后返回的字节码里面sayHello(name)方法调用的是invoke方法
        //而invoke方法就是我们自己定义的方法
        helloProxy.sayHello("my girl!"); //从此处进入DynamicProxy 的invoke方法
    }
}

动态代理的详细原理,可参考: http://rejoy.iteye.com/blog/1627405

三. CGlib动态代理

1. CGlib动态代理与JDk动态代理的区别

    CGlib动态代理可以实现对类的代理,而JDK动态代理只能对有接口的类代理,Spring的配置文件里,如果配置proxy-target-class="true",即

    <aop:config proxy-target-class="true">
        <aop:advisor advice-ref="druid-stat-interceptor"
                     pointcut-ref="druid-stat-pointcut" />
    </aop:config>

此时代表基于类的代理将起作用。设为false或者不设置使用的是JDK动态代理。

2. 新建CGlibProxy类,实现MethodInterceptor接口(这里需要导入Spring AOP包)

package test;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author licf
 * @since 2017-09-27
 */

public class CGlibProxy implements MethodInterceptor {
    private static CGlibProxy instance = new CGlibProxy(); //饿汉式单例模式

    private CGlibProxy() {} //私有化构造方法,防止被外部实例化

    public static CGlibProxy getInstance() { //调用此方法返回对象实例
        return instance;
    }

    //获取代理类实例
    public <T> T getProxy(Class<T> cls) {
        return (T) Enhancer.create(cls, this);
    }

    //要实现CGlib给我们提供的MethodInterceptor接口的intercept方法
    //MethodProxy 参数说明是针对方法的拦截(可以理解为方法拦截器)
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(object, args); //传入被代理的对象和方法的参数
        after();
        return result;
    }

    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }
}

3. 新建入口类CGlibMain

package test;

public class CGlibMain {
    public static void main(String[] args) {
        Hello helloProxy = CGlibProxy.getInstance().getProxy(HelloImpl.class);
        helloProxy.sayHello("my love!");
    }
}

输出如下:

Before
Hello! my love!
After

好了,三大代理模式的简单使用以及部分原理就先写到这里了,以后再分析CGlib动态代理的源码



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值