半夏的博客

liangbogopher's blog

说说jdk代理以及cglib动态代理

代理实现可以分为静态代理和动态代理, 下面使用jdk和cglib来分别说明。

静态代理

具体实现:

public interface Subject {

    void request();

}

class RealSubject implements Subject {
    public void request(){
        System.out.println("Call RealSubject");
    }
}

class Proxy implements Subject {
    private Subject subject;

    public Proxy(Subject subject) {
        this.subject = subject;
    }

    public void request() {
        System.out.println("begin");
        subject.request();
        System.out.println("end");
    }
}

public class ProxyTest {

    public static void main(String args[]) {
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
    }
}

其中:
1、RealSubject 是委托类,Proxy 是代理类;
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;

静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。

动态代理

动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面通过一个例子看看如何实现jdk动态代理。

jdk动态代理

1、定义业务逻辑

public interface IService {

    void add();

}

public class UserServiceImpl implements IService {

    @Override
    public void add() {
        System.out.println("call user add method.");
    }
}

2、定义代理类的实现

public class JdkDynamicProxy implements InvocationHandler {

    //被代理对象
    private Object target;

    //传递代理目标的实例
    public JdkDynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 参数说明:
     * proxy是生成的代理对象,method是代理的方法,args是方法接收的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //目标方法之前执行
        System.out.println("do something before...");

        Object result = method.invoke(target, args);

        //目标方法之后执行
        System.out.println("do something after...");
        return result;
    }

    /**
     * 生成代理对象
     */
    public Object getProxy() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        return Proxy.newProxyInstance(loader, interfaces, this);
    }

}

3、使用动态代理

public class ProxyTest {

    public static void main(String[] args) {
        //创建目标对象
        IService target = new UserServiceImpl();
        //将目标类和横切类编织在一起
        JdkDynamicProxy handler = new JdkDynamicProxy(target);

        IService proxy = (IService) handler.getProxy();

        proxy.add();
    }
}

执行结果:

do something before...
call user add method.
do something after...

Process finished with exit code 0

jdk动态代理使用的局限性

通过反射类Proxy和InvocationHandler回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。

cglib动态代理

使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码,下面通过一个例子看看使用CGLib如何实现动态代理。

1、定义业务逻辑

public class UserServiceImpl {

    public void add() {
        System.out.println("add a user");
    }

    public void delete(int id) {
        System.out.println("delete a user, id: " + id );
    }

}

2、使用cglib定义方法的拦截器

public class CglibProxy implements MethodInterceptor {

    //增强器,动态代码生成器
    private Enhancer enhancer = new Enhancer();

    /**
     * 拦截方法:在代理实例上拦截并处理目标方法的调用,返回结果
     *
     * target: 目标对象代理的实例
     * method: 目标对象调用父类方法的method实例
     * args: 调用父类方法传递参数
     * methodProxy: 代理的方法去调用目标方法
     */
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //目标方法之前执行
        System.out.println("Before: " + method);
        //通过代理类实例调用父类的方法,即是目标业务类方法的调用
        Object object = methodProxy.invokeSuper(target, args);
        //目标方法之后执行
        System.out.println("After: " + method + "\n");
        return object;
    }

    /**
     * 创建代理对象
     * @param clazz
     * @return 返回代理对象
     */
    public Object getProxy(Class clazz) {
        //设置目标类(被代理的类)
        enhancer.setSuperclass(clazz);
        //设置回调(在调用父类方法是,回调this.intercept())
        enhancer.setCallback(this);
        //通过字节码技术动态创建子类实例
        return enhancer.create();
    }
}

3、执行测试类

public class ProxyTest {

    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        UserServiceImpl userService = (UserServiceImpl) proxy.getProxy(UserServiceImpl.class);

        userService.add();

        userService.delete(1);
    }
}

执行结果:

Before: public void me.ilbba.aop.example.cglib.UserServiceImpl.add()
add a user
After: public void me.ilbba.aop.example.cglib.UserServiceImpl.add()

Before: public void me.ilbba.aop.example.cglib.UserServiceImpl.delete(int)
delete a user, id: 1
After: public void me.ilbba.aop.example.cglib.UserServiceImpl.delete(int)


Process finished with exit code 0

jdk和cglib动态代理实现的区别

1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法。

Top