Java proxy 代理


 

proxy 代理:在目标类的基础上进行扩展,做一些额外处理,增加一些额外功能,代替目标类进行工作。
 

静态代理

/**
 * 静态代理
 */
public class UserServiceProxy implements UserService {

    /**
     * 把目标接口实例作为成员变量
     */
    private UserService userService;

    /**
     * 提供构造方法或者setter方法注入目标接口实例
     */
    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    /**
     * 依次为:前增强、调用目标方法、后增强,前、后增强可选
     */
    @Override
    public void login() {
        System.out.println("前增强...");
        userService.login();
        System.out.println("后增强...");
    }

    /**
     * 如果不需要增强,直接调用目标方法即可
     */
    @Override
    public void register() {
        userService.register();
    }

}
//目标对象
UserService userServiceImpl = new UserServiceImpl();
//代理
UserService userServiceProxy = new UserServiceProxy(userServiceImpl);
//使用代理代替目标对象进行工作
userServiceProxy.login(); 

 
核心点

  • 代理需要实现目标接口或继承目标类:以便重写目标接口、目标类中的方法,代替目标方法进行工作
  • 注入目标实例:可以通过带参构造器,或者无参构造器+setter方法进行注入
  • 重写目标方法

 

jdk动态代理

/**
 * jdk动态代理
 */
public class UserServiceProxyFactory {

    /**
     * 传入目标实例,获取代理
     * jdk动态代理只能代理接口,所以声明的目标类型只能是目标接口
     */
    public static UserService getProxyInstance(UserService userService) {
        //获取目标的类加载器、接口
        ClassLoader classLoader = userService.getClass().getClassLoader();
        //因为要getInterfaces()获取接口,所以只能代理接口,目标必须要是实现了接口的
        Class<?>[] interfaces = userService.getClass().getInterfaces();

        //使用lambda表达式实现InvocationHandler接口,编写增强
        //参数依次为:Object proxy 将返回的代理, Method method 目标方法, Object[] args 实参表
        InvocationHandler invocationHandler = (proxy, method, args) -> {
            System.out.println("前增强...");
            //调用目标方法,传入目标实例、实参表,returnValue是目标方法的返回值
            Object returnValue = method.invoke(userService, args);
            System.out.println("后增强...");
            //返回目标方法的返回值
            return returnValue;
        };
        //创建代理
        Object userDaoProxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        //返回值是Object,强转为目标类型返回
        return (UserService) userDaoProxy;
    }

}
//目标实例
UserService userServiceImpl = new UserServiceImpl();
//代理
UserService userServiceProxy = UserServiceProxyFactory.getProxyInstance(userServiceImpl);
//使用代理代替目标对象进行工作
userServiceProxy.login();

 
核心点

  • 只能代理接口:这句话的意思是说声明的目标类型只能是接口、不能是类,可以代理目标接口的所有实例,只要目标继承了接口就可以被代理。
  • 不能代理 static 方法
  • 不需要实现目标接口或继承目标类
  • 需要把Object型的代理强转为目标接口类型返回:不然代理是Object类型,userServiceProxy.login() 通过代理调用目标方法,都通不过编译

 

看invoke()的参数,在编译时不能确定要增强的方法,运行时根据传入的参数进行增强,动态生成代理,所以叫做动态代理;静态代理在编译时就确定了要增强的方法。

使用jdk自带的反射机制实现的动态代理,叫做jdk动态代理;使用第三方的cglib库实现的代理叫做cglib代理。

 

cglib动态代理

需要添加cglib的依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
/**
 * cglib代理
 */
public class UserServiceProxyFactory implements MethodInterceptor {

    /**
     * 目标实例,可以声明为目标接口或目标类
     */
    private UserService userService;

    /**
     * 注入目标实例
     */
    public UserServiceProxyFactory(UserService userService) {
        this.userService = userService;
    }

    /**
     * 拦截目标方法进行增强
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前增强...");
        //调用目标方法,传入目标实例、实参表,returnValue是目标方法的返回值
        Object returnValue = method.invoke(userService, objects);
        System.out.println("后增强...");
        //返回目标方法的返回值
        return returnValue;
    }

    /**
     * 获取代理,返回值是目标类型
     */
    public UserService getProxyInstance() {
        //Enhancer是操作字节码的工具类
        Enhancer en = new Enhancer();
        //设置基类,即会继承目标类
        en.setSuperclass(userService.getClass());
        //设置回调,即使用目标对象的方法时会使用当前对象(代理)的intercept()进行拦截,代替目标方法
        en.setCallback(this);
        //创建代理,强转为目标类型返回
        return (UserService) en.create();
    }

}
//目标实例
UserService userServiceImpl = new UserServiceImpl();
//获取代理
UserService userServiceProxy = new UserServiceProxyFactory(userServiceImpl).getProxyInstance();
//使用代理代替目标对象进行工作
userServiceProxy.login();

 
核心点

  • 需要引入cglib的依赖
  • 可以代理接口、类,返回的代理属于目标接口、目标类的实例
  • 需要实现MethodInterceptor接口,注入目标实例
     

cglib代理的实现原理

  • 借助ASM(一个短小精悍的字节码操作框架),在运行时动态修改目标类的字节码,根据修改后的字节码创建代理。并非修改.class文件中的字节码,只是修改jvm加载到内存中Class对象,本质仍是基于字节码操作,但织入时机是运行时。
  • 修改字节码时要setSuperclass()继承目标类,基于继承目标类实现,所以不能代理 final 类,不能代理 private 方法、static方法。

 

静态代理、动态代理的区别

静态代理

  • 可以代理接口、类,在编译时就确定了要增强的方法
  • 需要编写代理类,代理是一个新的类,编译会生成代理类的.class文件;
  • 代理需要实现目标接口或继承目标类,代理本身属于目标接口、目标类的实例;
  • 优点是简单、灵活,缺点是代码侵入强、耦合度高,编码量大,不好维护
     

动态代理

  • 不需要实现目标接口或继承目标类,在运行时根据传入的参数动态生成代理
  • 参数中包含了目标实例、Method、实参表,可根据这些参数对目标类、目标方法进行筛选
  • 优点是编码量小,对代码侵入小,好维护;缺点是性能比静态代理差。

 

jdk动态代理、cglib代理的区别

jdk动态代理

  • 基于jdk自带的反射机制实现,不需要引入第三方依赖
  • 只能代理接口,目标实现了接口才能被代理;
  • 不能代理 static 方法
  • 不需要实现目标接口或继承目标类,但需要把创建的Object型的代理强转为目标接口类型返回
     

cglib代理

  • 需要引入cglib的依赖
  • 可以代理接口、类(不要求目标实现接口)
  • 基于运行时动态修改字节码、继承目标类实现,不能代理 final 类,不能代理 private 方法、static方法

 

代理与目标类的关系

  • 静态代理:需要实现目标接口或继承目标类,代理属于目标接口|类的实例
  • cglib代理基于继承目标类实现,代理自然也是目标类的实例
  • jdk动态代理:虽然没有实现目标接口或继承目标类,但会把Object型的代理转换为目标接口类型,所以也是目标接口的实例。

3种代理都属于目标类型的实例。

 

使用建议

静态代理、cglib动态代理都可以代理接口、类,jdk动态代理只能代理接口。

如果要针对目标类中的众多方法设置不同的增强,用静态代理,静态代理只能小范围使用,不然重复代码多、不好维护。

如果对目标类中的方法的增强逻辑都一样,用动态代理,实现了目标实现了接口,用jdk动态代理、cglib代理均可,如果目标没有实现接口,则用cglib代理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值