前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring5系列(十) | 动态代理底层实现

Spring5系列(十) | 动态代理底层实现

作者头像
一缕82年的清风
修改2021-12-13 09:43:45
2520
修改2021-12-13 09:43:45
举报
文章被收录于专栏:lsqingfeng

我们在上面的几篇文章中已经了解了如何使用spring进行aop的开发,本篇文章我们来介绍一下动态代理的底层实现。

一. AOP编程概念

代码语言:javascript
复制
AOP: Aspect oriented programming: 面向切面编程 = spring动态代理开发。以切面为基本单位的程序开发,通过切面间的批次协同,相互调用,完成程序构建。  切面 = 切入点 + 额外功能

OOP: Object Oriented Programming: 面向对象编程,以对象为基本单位的程序开发,通过过程间的批次协同,相互调用,完成程序构建

POP: Procedure Oriented Programming: 面向过程(函数,方法)编程,以过程为基本单位的程序开发,通过过程间彼此协同,相互调用,完成程序构建
复制代码
代码语言:javascript
复制
AOP编程的本质就是Spring动态代理开发,通过代理类为原始类增加额外功能。好处就是利于原始类的维护

注意: aop编程不可能取代oop功能,是一种有力的补充。
复制代码

回顾AOP编程的开发步骤:

  • 原始对象
  • 额外功能(MethodBeforeAdvice, MethodInterceptor)
  • 切入点(切入点表达式)
  • 组装
代码语言:javascript
复制
切面 = 切入点 + 额外功能
复制代码

核心问题:

代码语言:javascript
复制
1. AOP如何创建代理类:
  通过动态字节码技术
  
2. Spring工厂如何加工创建代理对象
  通过原始对象的id, 获得的是代理对象
复制代码

二. 动态代理实现

spring底层的动态代理有两种实现方式,一是JDK的动态代理技术,而是Cglib开源框架提供的动态代理技术。

2.1 JDK动态代理

jdk的动态代理,必须是基于接口进行代理,也就是我们的目标类必须实现一个接口,才能进行代理。我们给出案例,这里省略了UserService, 和 UserServiceImpl.

代码语言:javascript
复制
创建代理三个要素: 
  1. 原始对象
  2. 额外功能
  3. 代理对象和原始对象实现相同的接口
        
JDK为我们了提供了Proxy.newInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) 方法类实现动态代理技术。

参数介绍  
@param: ClassLoader var0: 创建代理对象所需的类加载器
@param: interfaces: 和原始对象实现的接口数组
@param: InvocationHandler: 额外功能

这里InvocatioHandler也是一个接口,所以我们需要传如一个实现。该接口有一个方法需要实现:  Object invoke(Object proxy, Method method, Object[] args);

@param Object proxy:代表代理对象,忽略,不要使用
@param Methdod method: 代表额外功能增加给的原始方法
@param Object[] args: 原始方法的参数
@return: Object: 原始方法的返回值

这种写法和我们之前的MethodInterceptor很像,毕竟底层就是这么实现的。我们直接程序应该怎么写
复制代码
代码语言:javascript
复制
public class JDKProxyTest{

  public static void main(String[] args){
    // 1. 创建原始对象
    Userservice userService = new UserServiceImpl();
    
    // 2. 创建动态代理类
      UserService proxy = (UserService)Proxy.newInstance(userService.getClass.getClassLoader() ,userService.getClass.getInterfaces() , new InvocationHandler(){
      
      public Object invoke(Object proxy, Method method, Object[]args){
        
        // 前置额外功能
       	Object obj =  method.invoke(userService, args);
        // 后置额外功能
        return obj;
      }
    });

    proxy.login("abc", "123456");

  }
}
复制代码

我们在InvocationHandler接口中的实现方法里加入了我们想要的额外功能,并通过调用method.invoke() 完成原始方法的调用。同时将原始方法的返回值返回,此时我们需要使用接口来接收代理对象(因为代理对象和原始对象实现了同一个接口),要注意的是这个代理对象是动态生成的,所以我们可能找不到他的具体源码,当我们使用他调用方法的时候,额外功能就可以执行了。

注意事项:

代码语言:javascript
复制
类加载器的作用:
1. 通过类加载器吧对应的类字节码文件加载到JVM中
2. 通过类加载器创建类的Class对象进而创建这个类的对象

如何获得类加载器:
虚拟机为每一个类的.class文件自动分配与之对应的ClassLoader,动态代理类没有源文件,它是通过动态字节码技术生成,把字节码直接写入jvm.
此时在动态代创建的过程中,需要ClassLoader创建代理类的Class对象,可是因为动态代理类没有.classs文件,JVM也就不会为他分配ClassLoader,但是又需要,就只能借用一个。所以找一个我们自己写的类获取Class对象在调用getClassLoader()即可,这里注意不要使用JDK的类获取,因为不是一个类加载器。
复制代码

2.2 CgLib动态代理

上面说了JDK的动态代理技术的实现。但是JDK的动态代理技术有一个弊端,就是原始类必须要实现一个接口,如果原始类没有实现任何接口,此时想要给他创建动态代理类,JDK的动态代理就实现不了了。而Cglib可以实现。

代码语言:javascript
复制
cglib动态代理原理:
  cglib所创建的代理类是通过继承的方式实现的,他会继承原始类。原始类作为父类,代理类作为子类,这样就可以保证二者方法相同。而不需要实现接口。
复制代码

代码实现:

代码语言:javascript
复制
public class UserService{
  
  public boolean login(String name, String password){
    	System.out.println("login...");
  }
  
  public void register(User user){
     System.out.println("register...");
  }
  
}


// 测试类
class Test{
  /**
  	Enhancer.setClassLoader();
  
  */
  public static void main(String[] args){
    	UserService userService = new UserService();
    	Enhancer enhancer = new Enhancer();
    	enhander.setClassLoader(Test.class.getClassLoader());
    	enhander.setSuperClass(userService.class);
    	MethodInteceptor interceptor = new MethodInterceptor(){
        @Override
        public Object(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
          	
          	System.out.println("----log-----");
          	Object obj = method.invoke(userService, args);
          	return obj;
         	 
        }
      };
    	enhancer.setCallBack(interceptor);
    	UserService proxy = (UserService)Enhancer.create();
    	proxy.login("abc", "123");
   	 	proxy.register(new User());
  }
}

复制代码

总结:

  1. JDK动态代理,Proxy.newInstance : 通过接口创建代理的实现类
  2. CGlib: 动态代理, Enhancer, 通过父子类继承

三. spring工厂如何创建代理对象

上面我们讲述了spring中两种动态代理的实现。通过动态代理技术,就可以创建出代理对象。那么在spring中,为什么我们通过原始bean的id就可以得到代理对象呢。我们来浅析一下他的原理。其实他主要还是通过BeanPostProcessor这个接口实现的。这个接口我们在前面介绍过。

image.png
image.png

相当于我们在加工的过程中,创建了他的动态代理对象,进行了返回。这样我们获取的对象就是动态代理对象了。我们来写个案例。

代码语言:javascript
复制
public interface UserService{
  
  void register(User user);
  
  boolean login(String name, String password)
  
}

public class UserServiceImpl implements UserService{
  @Override
  public void register(User user){
    System.out.println("registe----")
  }
  
  @Override
  public boolean login(String name, String password){
    System.out.println("login-----")
  }
}

public class ProxyBeanPostProcessor implements BeanPostProcessor{
  
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{
    return bean;
  }
  
   @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{
    return Proxy.newInstance(this.getClass.getClassLoader(), bean.getClass.getInterfaces,new InvocationHandler(){
      public Object invoke(Object proxy, Method method, Object[] args){
        System.out.println("---log advice ---");
        Object ret = method.invoke(bean, args);
        
        return ret;
      }
      
    });
  }
  
}
复制代码
代码语言:javascript
复制
<bean id="userService" class="com.xxx.UserServiceImpl" />
<bean id="proxy" class="com.xxx.ProxyBeanPostProcessor" />
复制代码

而在spring的源码中,是通过一个叫做AbstractAutoProxyCreator,这个类就是专门用来创建代理对象的,而他的本质就是实现了BeanPostProcessor接口,在方法中创建了代理对象。

image.png
image.png
image.png
image.png

好了关于动态代理的一些概念和底层实现我们就先介绍到这里.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/04/19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. AOP编程概念
  • 二. 动态代理实现
    • 2.1 JDK动态代理
      • 2.2 CgLib动态代理
      • 三. spring工厂如何创建代理对象
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档