java动态代理

动态代理在java中广泛的应用
比如
1.Spring aop
2. Hibernate数据查询
3. 测试后端框架mooc
4. Rpc
5. java注解对象的获取

静态代理的代理关系在编译时确定的
静态代理适合代理类较少且确定的

java的动态代理有两种
1.原生的jdk代理
2.cglib动态代理

实例
静态代理的实现
抽取被代理方法的公共接口

public interface UserService {

    void say();

    void cry();
}

接口的实现类

public class UserServiceImpl implements UserService {

    public void say() {
        // TODO Auto-generated method stub

        System.out.println("UserService say");
    }

    public void cry() {
        // TODO Auto-generated method stub
        System.out.println("UserService cry");
    }

}

代理类 它和目标对象实现了相同的接口,并把目标对象填充到自己的内部,在调用代理类的方法时实际上是调用目标对象

public class MyStProxy implements UserService {

    private UserService userService=null;

    public MyStProxy(UserServiceImpl userServiceImpl) {
        // TODO Auto-generated constructor stub
        this.userService=userServiceImpl;
    }

    @Override
    public void say() {
        // TODO Auto-generated method stub

        System.out.println("before st invoke");

        userService.say();
        System.out.println("after st invoke");
    }

    public void cry() {
        // TODO Auto-generated method stub

        System.out.println("before st invoke");

        userService.cry();
        System.out.println("after st invoke");
    }

}

测试代码

UserService stUserService=new MyStProxy(new UserServiceImpl());
        stUserService.say();
        stUserService.cry();

运行结果

before st invoke
UserService say
after st invoke
before st invoke
UserService cry
after st invoke

然后再看jdk动态代理
前面的接口UserService和目标对象UserServiceImpl是通用的
//jdk动态代理 被代理的类要实现目标接口 从写构造函数 invoke方法 getProxy方法
//目标接口 UserService
//被代理类 UserServiceImpl
//代理类 uHandle 不用实现目标接口而是实现InvokitonHandler接口的invoke方法
代理类不用实现目标接口而是实现了InvoktionHandler接口的invoke方法

public class UserServiceInvocationHandler implements InvocationHandler {

    private  Object targetObject=null;

    //目标对象


    /**
     * 绑定代理对象 并返回一个代理类  也可以使用构造方式绑定
     * 返回一个目标对象的代理对象
     * 1.类加载器
     * 2.类的接口
     * 3.
     * @param targetObject
     * @return
     */
    public Object bind(Object targetObject){
        this.targetObject=targetObject;
        return Proxy.newProxyInstance(targetObject.getClass()
                .getClassLoader(), targetObject.getClass().getInterfaces(), this);

    }


    /**
     * 对被代理的方法进行处理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
            String name=method.getName();
            System.out.println("before invoke"+name+"...");
            //执行目标对象的方法
            Object result=method.invoke(targetObject, args);
            System.out.println("after invoke"+name+"...");
        return result;
    }

}

测试代码

    UserServiceInvocationHandler uHandler=new UserServiceInvocationHandler();
        UserService dyUserService=(UserService)uHandler.bind(new UserServiceImpl());
        dyUserService.say();
        dyUserService.cry();

然后总结一下jdk代理
还是使用接口指定协议,然后再用不同的实现来实现具体行为
在静态代理时 实现了相同的接口
而jdk动态代理做法是1.首先实现了InvocationHandler,方法调用转到了该类的invoke方法
2.然后在需要使用目标对象时,通过jdk代理获取代理对象

关键是Proxy.newProxyInstance(loader,interfaces,handler)
他会返回一个实现了指定接口的代理对象,该对象所有方法的调用会转给invocationHandler的invoke
动态代理神奇的地方在
代理对象在程序运行时参生
代理对象接口方法的调用转给handler的invoke
在invoke方法可以添加逻辑 比如修改方法参数 加入日志功能 安全检查功能
通过反射调用对象的相应方法还可以用rpc远程调用

JDK proxy会把object的hashcode equals toString转给handler 其他不会转
jdk生成的代理类类型是class.jdkproxy.$Proxy0
它的父类是class.lang.reflect.Proxy

但是jdk动态代理是基于接口的,如果没有接口 那么就可以使用cglib

Cglib代理 (code generation library)
是一个基于ASM字节码生成的库 ,允许在运行时对字节码修改和动态生成,通过继承方式实现。

假设目标类没有实现任何接口

public class UserServiceTarget implements UserService{

    public void say() {
        // TODO Auto-generated method stub

        System.out.println("UserService say");
    }

    public void cry() {
        // TODO Auto-generated method stub
        System.out.println("UserService cry");
    }

}

cglib代理类

//MethodInterceptor 主要的方法拦截器 是callback的子接口
//MethodProxy jdkmethod的代理类  可以对源对象调用

public class DyUserServiceImpl implements MethodInterceptor{

    private Object targetObject=null;
    public Object bind(Object targetObject){
        this.targetObject=targetObject;
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(targetObject.getClass());
        enhancer.setCallback(this);
        return enhancer;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before cglib invoke");
        Object result=methodProxy.invoke(o, objects);
        System.out.println("after  cglib invoke");
        return result;
    }


}

首先实现了一个methodinterceptor 方法调用转到该类的intercptor方法
在需要使用目标类时,cglib代理获取代理对象
测试代码

DyUserServiceImpl dyUserServiceImpl=new DyUserServiceImpl();        
        UserServiceTarget userServiceTarget=(UserServiceTarget)dyUserServiceImpl.bind(new UserServiceTarget());
        userServiceTarget.say();
        userServiceTarget.cry();

测试结果

before invokesay...
UserService say
after invokesay...
before invokecry...
UserService cry
after invokecry...

在上面methodintercptor和invocationHandler类似都是方法调用的中转站
从Object继承的方法cglib也会代理 比如hashcode equals toString 而final方法无法代理。
而对代理之后的类型深入
它的对象类型是cglib动态生成的类型 cglib.XXX

EnhancerByCGLB
xxx
父类是目标对象类,因此也验证了Cglib是通过继承实现代理
同时还实现包了net.sf.cglib.Factory接口 这个接口是cglib自己加入的包含一些工具方法。

关于它的具体实现 应该是内部有一个methodintercptor 当调用目标方法时 首先会尝试转发给intercptor ,如果没有会调用父类方法逻辑并不复杂,只是不需要我们去手写,运行时动态生成的。
cglib的缺点无法处理final的情况

最后如何获取cglib字节码 ???和cglib的细节
后面再讲

动态代理先讲这么多后面再不足之处再更新

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值