Spring基础进阶系列

前言
本篇文章基于Spring框架,内容包含IOC、AOP理解、IOC的各种实现方式、Bean声明周期解析、懒加载、两种事务解析等等,希望对大家有所帮助。

一、Spring框架基础知识
Spring的优势
1.方便解耦,简化开发:通过spring的ioc容器,将对象间的依赖关系交由Spring进⾏控制,避免硬编码所造成的

过度程序耦合,⽤户也不必再为单例模式类、属性⽂件解析等这些很底层的需求编写代码,可以更专注于上层的应⽤。

2. AOP编程的⽀持: 通过Spring的AOP功能,⽅便进⾏⾯向切⾯的编程,许多不容易⽤传统OOP实现的功能可以通过

AOP轻松应付。

3.声明式事务的支持:@Transactional 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式⽅式灵活的进⾏事务的管理,提⾼开发效率和质量。

4.方便程序的测试

5.方便集成各种框架

6.降低Javaee api的使用难度

Spring的核心结构
数据处理模块、web模块、aop模块、core container模块、test模块

1. Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中

bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。

基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。

2. ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。

3. 数据访问与集成(Data Access/Integration)

Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。

4. Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。

5. Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。

什么是IOC
控制反转/反转控制:控制指的是对象的创建、管理、实例化权利,反转指的是控制权交给了外部容器Spring框架。Ioc容器

具体实现:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可,而传统开发需要通过new对象来获取,依赖性强,通过IOC解决了对象之前的耦合问题。

IOC和DI的区别
DI其实就是依赖注入,和IOC描述的都是对象实例化和依赖关系维护这件事情,但是角度不同,IOC站在对象的角度,对象实例化及其管理的权利交给了(反转)容器,ID是站在容器的角度,容器会把对象依赖的其他对象注入,比如A对象在实例化过程中声明了一个B类型的属性,就要容器把B对象注入A。

什么是AOP
Aop是oop的延续,传统oop三大特性封装、继承、多态,成一种垂直继承体系,存在顶级分类代码重复无法优化问题,无法解决横向切面逻辑代码问题,所以就产生了aop,通过横向抽取机制,将横切逻辑代码和业务逻辑代码分析。在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复

为什么叫横向界面编程
「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑

「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个⾯的概念在⾥⾯

手写IOC和AOP银行转账项目理解
问题:

1.service层和dao层具体实现类的调用时,都需要通过new来创建对象,获取对象类的属性和方法,使service层和dao层耦合在了一起。

2.Service层没有事务控制,如果出现异常,将会使数据库数据错乱。

解决:

首先我们在bean.xml里面创建了我们需要使用的各个类以及他的唯一id,在标签内增加property标签来标明他需要调用的接口实现类是哪个。


接着进入具体处理类,通过xml解析获取文件流,通过dom4j把文件流解析成我们需要的Document对象,通过获取所有指定标签元素集合,我们就可以通过遍历这个集合,获取里面的id属性和class属性,有了class属性,就可以通过反射的方式转化成class对象,再把id和对象存入我们事先定义的map集合中待用。接着我们再遍历所有的property标签,element自带element.getParent()方法可以获取他的父级节点是谁,这样就直接从map集合中根据key和value关系获取到对应父级class对象,接着获取父对象所有方法与id拼接字符串进行判断;如果相同就直接根据ref的属性从map中获取他的实例化bean对象,再调用method.invoke把方法参数化建立依赖关系,重新放入map集合中。


接着在创建一个线程工具类,此线程用于存储数据库连接,把线程和连接绑定在一起,然后再创建一个事务管理类,获取线程工具类里面的数据库连接,进行事务统一管控,这样就可以让多次更新操作在一个线程管控的连接下面,实现事务管理。

为了将横切逻辑代码与业务逻辑代码进行分离,我们又创造了一个ProxyFactory代理工厂类,它主要的作用就是通过jdk动态代理重写invoke方法,在执行原方法之前和之后操作事务,异常里回滚事务,实现增强横切代码逻辑,也可以通过cglib方式,通过重写intercept方法对所有父类方法调用进行拦截,然后在method.invoke原方法执行之前和之后进行事务处理。

最后在实际使用通过声明对应接口类,然后直接通过set注入方式就可以从map里面获取想要的实例化bean进行具体业务的操作。


二、IOC容器设计实现及Spring源码分析
Spring框架的IOC实现三种方式
1. 纯xml方式

2. Xml加注解方式

上面两种方式加载相同:JavaEE应用通过new ClassPathXmlApplicationContext(“beas.xml”)或者new FileSystemXmlApplicationContext(“c:beans.xml”)加载;JavaWeb应用通过ContextLoaderListener(监听器去加载xml)

3. 纯注解方式

加载方式:JavaEE应用通过new AnnotationConfigApplicationContext(SpringConfig.class); JavaWeb应用通过ContextLoaderListener(监听器去加载注解配置类)

BeanFactory与ApplicationCentext区别
BeanFactory是Spring框架IOC容器顶层接口,定义了基础功能和规范,而ApplicationContect是他的子接口,不仅具备了他的所有功能,并且还对他进行扩展,支持了更多的功能,实现面向接口开发原则。

Spring IoC纯XML方式实现
Xml文件头


理解:通过引入Spring IoC容器功能,实现不需要再创建一个专门的BeanFactory来解析xml文件并返回,直接通过WebApplicationContextUtils.getWebApplicationContext就能获取到Spring工具类,再通过getBean方法获取到对应解析的全限定名转化成需要的类对象。


Bean的创建方式及属性标签
实例化Bean的三种方式

1. 使用无参构造 推荐

<bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>

2. 使用静态方法创建 需要自己去new

<bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic"/>

3. 使用实例化方法创建 需要自己去new

<bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"></bean>

<bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance"/>

Scope:定义bean的作用范围

1. singleton:单例,IOC容器中只有一个该类对象,默认为singleton,单例模式的bean对象⽣命周期与容器相同。

2. prototype:原型(多例),每次使用该类的对象(getBean),都返回给你一个新的对象,Spring只创建对象,不管理对象

id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。

class属性:⽤于指定创建Bean对象的全限定类名。

name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。

factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。

factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。

scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。

init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。

destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。

Spring DI依赖注入配置
依赖注入分类:构造函数注入和set方法注入

注入类型:基本类型和String、其他Bean类型、复杂类型(map,list等等)

构造函数注入:constructor-arg标签

依靠构造函数对类成员进行赋值,而且构造函数参数要与配置参数个数相同、类型相同

name:⽤于给构造函数中指定名称的参数赋值。

index:⽤于给构造函数中指定索引位置的参数赋值。

value:⽤于指定基本类型或者String类型的数据。

ref:⽤于指定其他Bean类型的数据。写的是其他bean的唯⼀标识。


set⽅法注⼊:property标签

name:指定注⼊时调⽤的set⽅法名称。(注:不包含set这三个字⺟,druid连接池指定属性名称)

value:指定注⼊的数据。它⽀持基本类型和String类型。

ref:指定注⼊的数据。它⽀持其他bean类型。写的是其他bean的唯⼀标识。


Spring IoC半XML半注解方式实现
1. 实际企业开发中,纯xml开发方式很少用。

2. 引入注解功能,不需要再引入额外的jar包

3. Xml+注解结合模式,xml仍然存在,所以springIOC容器仍然从Xml开始加载

4. 第三方jar的bean定义xml如druid数据库连接池,自己开发的bean定义使用注解


DI 依赖注⼊的注解实现⽅式

@Autowired: 按照类型注⼊,如果按照类型无法唯一锁定对象,结合@Qualifier告诉Spring具体去装配哪一个对象


@Resource按照 ByName ⾃动注⼊,不过在JDK11以后已经被移除,需要单独引入jar包


纯注解模式
@Configuration 注解,表名当前类是⼀个配置类

@ComponentScan 注解,替代 context:component-scan

@PropertySource,引⼊外部属性配置⽂件

@Import 引⼊其他配置类

@Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息

@Bean 将⽅法返回对象加⼊ SpringIOC 容器

注意,开启纯注解模式,需要web.xml里面配置全注解IOC启动方式,并把配置文件引用改成配置类全限定类名引用


lazy-Init 延迟加载
Spring在启动时,默认是将所有的singleton bean提前实例化,如果设置了lazy-init=”ture”的话,就可以设置这个bean在ApplicationContext启动服务器时不被实例化,在调用他的getBean索取bean时进行实例化,实现延迟加载,注意scope="pototype"时,此设置无效,会采用默认值false立即加载。这么设置的目的是对不常用的bean设置延迟加载,避免一开始启动服务器占用资源


FactoryBean 和 BeanFactory
BeanFactory接口是容器的顶级接口,定义了容器的一些基础行为和规范,而FactoryBean与前面说的ApplicationContext一样都是他的子接口,但是FactoryBean主要的作用是用它来自定义bean的创建。比如bean传了一个带分隔符号的字符串过来,需要我们进行解析,并装入一个实体类对象,我们就可以在具体工厂类里面实现FactoryBean接口,重写他的getObject,getObjectType,isSingleton方法,对复杂字符串进行处理,然后装入对应实体类中完成解析。


后置处理器
Bean的生命周期

生命周期:

1、Spring 容器根据配置中的 bean 定义中实例化 bean。

2、Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。

3、如果 bean 实现BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用setBeanName()。

4、如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。

5、如果存在与 bean 关联的任何BeanPostProcessors,则调用 preProcessBeforeInitialization() 方 法。

6、如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调用它。

7、最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。

8、如果 bean 实现DisposableBean 接口,当 spring 容器关闭 时,会调用 destory()。

9、如果为bean 指定了 destroy 方法( 的 destroy-method 属性),那么将调用它。

理解:Spring将Bean标签按照生命周期方式按顺序加载,最后类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等等一系列属性解析封装到BeanDefinition对象中,封装完成后通过getBeanDefinition获取属性,后面对bean操作其实就是对BeanDefinition进行,通过拿到类名、构造函数后就可以反射完成对象的创建

三、SpringIOC源码理解
SpringIOC容器体系
ApplicationContext是容器的高级接口,BeanFactory是顶级接口,规范和定义了容器的基础行为,Spring应用的上下文,官方称之为IOC容器,map是容器的一个成员叫做单例池,SingletonObjects。容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程。比如BeanFactory常用的getBean方法,遵循多态原则,定义了多个但参数不同的同名方法。如果需要获取整个bean需要加前缀&,通过内部FACTORY_BEAN_PREFIX来判定。

ApplicationContext继承的接口:

ListableBeanFactory接口:列出工厂可以生产的所有实例。但没有直接提供返回所有实例的方法。它可以返回指定类型的所有的实例。你可以通过getBeanDefinitionNames()得到工厂所有bean的名字,然后根据这些名字得到所有的Bean。这个工厂接口扩展了BeanFactory的功能,作为上文指出的BeanFactory二级接口,有9个独有的方法,扩展了跟BeanDefinition的功能,提供了BeanDefinition、BeanName、注解有关的各种操作。

MessageSource接口:以用于支持信息的国际化和包含参数的信息的替换。

HierarchicalBeanFactory接口:实现了Bean工厂的分层。继承自BeanFacotory,是一个二级接口,相对于父接口,它只扩展了一个重要的功能——工厂分层。

AutowireCapableBeanFactory接口:继承自BeanFacotory,它扩展了自动装配的功能,根据类定义BeanDefinition装配Bean、执行前、后处理器等。

ConfigurableBeanFactory接口:继承自HierarchicalBeanFactory 和 SingletonBeanRegistry 这两个接口,并额外独有37个方法,这37个方法包含了工厂创建、注册一个Bean的众多细节。

ResourceLoader接口:加载资源的接口,读取xml文件。

以上接口继承关系得出结论:Spring IoC 容器继承体系⾮常聪明,需要使⽤哪个层次⽤哪个层次即可,不必使⽤功能⼤⽽全的。

Bean⽣命周期关键时机点
Bean对象创建的几个关键点都在调用AbstractApplicationContext 类 的 refresh ⽅法

解析refresh ⽅法:


理解:在通过ClassPathXmlApplicationContext加载对应bean.xml文件时,首先进入到调用的构造器内,执行子类调用父类初始化方法,然后处理传入的配置文件路径,然后refresh判断,进入spring容器初始化方法内。创建工厂,把xml中的信息加载BeanDefition 并注册到 BeanDefitionRegistry(内部map结构,把BeanDefition作为value,id作为key,类似于之前自定义持久层框架中数据库javabean类中configuration类关联mappedstatement类的关系),接着初始化并执行工厂后置处理器,再注册bean的后置处理器,最后初始化bean,调用初始化方法,调用bean的后置处理器,完成容器初始化,产生bean对象。

BeanFactory获取子流程与BeanDefinition加载注册解析
BeanFactory:

理解:通过调取AbstractApplicationContext类的obtainFreshBeanFactory方法,执行调取AbstractRefreshableApplicationContext类的refreshBeanFactory方法,在这个方法内再调用自己内部的createBeanFactory方法进行实列化,并返回DefaultListableBeanFactory,而AbstractApplicationContext类再通过getBeanFactory方法把工厂对象拿回来。

BeanDefinition:


理解:通过AbstractBeanDefinitionReader类调取XmlBeanDefinitionReader内的loadBeanDefinitions方法发送消息,接着调取自己类的doLoadBeanDefinitions方法,完成xml读取为document对象,接着又调取内部的registerBeanDefinitions方法完成注册。接着在这方法内又调取DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,再在内部按吮吸调用doRegisterBeanDefinitions->parseBeanDefinitions->processBeanDefinition->调取BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法再调内部->parseBeanDefinitionElement方法完成加载注册并返回BeanDefinition对象。

Bean创建流程

理解:根据入口方法调用自己类的finishBeanFactoryInitialization方法,判断Bean的属性,接着进⼊DefaultListableBeanFactory类的preInstantiateSingletons⽅法,实例化所有立即加载的单例bean,接着声明一个list集合用于存放所有bean的id,然后循环集合,针对数据进行处理,非抽象、单列、非延迟加载的数据进入下一环节,接着判断是否工厂bean,因为工厂bean的前缀有一个&符号规范。如果不是就直接实例化Bean。接着进⼊到了AbstractBeanFactory类的doGetBean⽅法,对上面的BeanName进行解析,接着判断是否是多例bean,即prototype,如果是无法处理,抛出异常。如果不是,先检查父工厂是否存在此对象,没有就创建单例bean,调用构造函数实例化bean,再进行属性填充,最后调用初始化方法,应用BeanPostProcessor后置处理器。

lazy-init 延迟加载机制原理

理解:在上面bean创建过程中就已经说到了中间环节有一个非抽象,非单例,非延迟加载的判断。如果是延迟加载就直接不处理,就不会完成该bean的创建。

循环依赖问题
循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A。注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。

场景:

构造器的循环依赖(构造器注⼊)

Field 属性的循环依赖(set注⼊)

其中,构造器的循环依赖问题⽆法解决,因为构造函数里面必须要有东西,但是根本没法完成A的实例化放入缓存中,只能拋出 BeanCurrentlyInCreationException 异常。在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。


理解:Bean A在实例化后放入三级缓存中,接着A发现B依赖他,于是对B进行实例化,把B也放入三级缓存中,接着B又发现他依赖A,这个时候三级缓存中已经有了A对象,因此使用Bean工厂创建代理对象A放入二级缓存中,然后删除三级缓存中的A对象,B对象拿到了A的代理对象,就可以完成装配,把B放入一级缓存中,并删除三级缓存中的B对象,B实例化完之后就会返回给A,A有了B对象,就可以把二级缓存中的A对象同步到一级缓存中,并删除二级缓存中的A对象,这样就解决了循环依赖问题。

四、SpringAOP应用
AOP相关术语
AOP本质:在不改变原有业务逻辑代码的情况下运用动态代理技术(当代理对象不实现接口的情况下采用CGLIB,实现了接口采用官方JDK代理),在运行期间对需要使用的业务逻辑增强横切逻辑,横切逻辑代码往往是权限效验代码、日志代码、事务控制代码、性能监控代码。


连接点:⽅法开始时、结束时、正常运⾏完毕时、⽅法异常时等这些特殊的时机点,我们称之为连接点,项⽬中每个⽅法都有连接点,连接点是⼀种候选点。

切⼊点:指定AOP思想想要影响的具体⽅法是哪些,描述感兴趣的⽅法。

Advice增强:第⼀个层次:指的是横切逻辑;第⼆个层次:⽅位点(在某⼀些连接点上加⼊横切逻辑,那么这些连接点就叫做⽅位点,描述的是具体的特殊时机)。

Aspect切⾯:切⾯概念是对上述概念的⼀个综合。

Aspect切⾯= 切⼊点+增强= 切⼊点(锁定⽅法) + ⽅位点(锁定⽅法中的特殊时机)+ 横切逻辑。

组合以上最终的目的就是为了锁定需要在什么地方插入横切逻辑代码。

配置方式:与IOC一样,支持xml、xml+注解、全注解方式。

五种通知类型:前置通知;最终通知;正常执行通知;异常通知;环绕通知。

XML模式


注解模式


Spring声明式事务支持
编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务。

声明式事务:通过xml或者注解配置的⽅式达到事务控制的⽬的,叫做声明式事务。

概念:逻辑上的一组操作,操作各个单元,进行控制,要么全部成功,要么全部失败,保证数据的准确和安全。

四大特性之原子性:事务操作要么都发生要么都不发生。

四大特性之隔离性:事务之间互不干扰,并发事务互相隔离

四大特性之一致性:数据库从一个一致性状态到另一个一致性状态,比如A转账给B100块,完成后他们俩钱总和不能变。

四大特性之持久性:事务一旦提交,数据库的数据改变就是永久有效的,不受任何不可控因素影响结果。

事务问题:

脏读:一个线程的事务读取到了另一个线程还未提交的数据。

不可重复读:一个线程的事务读取到另一个线程已经提交update的数据(前后内容不一致)

虚读(幻读):⼀个线程的事务读到了另外⼀个线程中已经提交的insert或者delete的数据(前后条数不一致)

四种隔离级别:Mysql默认可重复读

Serializable(串⾏化):可避免脏读、不可重复读、虚读情况的发⽣。(串⾏化) 最⾼效率

Repeatable read(可重复读):可避免脏读、不可重复读情况的发⽣。(幻读有可能发⽣) 第⼆

该机制下会对要update的⾏进⾏加锁

Read committed(读已提交):可避免脏读情况发⽣。不可重复读和幻读⼀定会发⽣。 第三

Read uncommitted(读未提交):最低级别,以上情况均⽆法保证。(读未提交) 最低

事务的传播行为:两个事务控制的service层存在关系调用,进行事务的协商。


Spring声明式事务配置
导入jar包


Xml配置


接口、类、方法添加@Transactional注解


SpringAOP源码剖析
AOP代理对象创建过程


package com.example.service;
 
public class ExampleService {
    private String message;
 
    public ExampleService(String message) {
        this.message = message;
    }
 
    public String getMessage() {
        return message;
    }
}


在finishBeanFactoryInitialization方法中实例化原始对象并创建代理对象,进入方法内先创建Bean实例,调用构造方法,处理循环依赖,填充属性,完成后就开始调用初始化方法,创建BeanPostProcessor后置处理器来创建代理对象。具体源码走向:

在AbstractAutowireCapableBeanFactory类的 initializeBean方法调用自己内部applyBeanPostProcessorsAfterInitialization方法,在方法内进入AbstractAutoProxyCreator类的postProcessAfterInitialization方法(后置处理器完成bean对象创建),在调用自己内部的wrapIfNecessary方法包装代理对象,查找出和当前bean匹配的advisor,再进入createProxy方法调用栈,通过ProxyFactory创建代理对象,再把通用拦截器和增强对象合并,适配advisor,传给统一创建代理对象的工厂,进入createAopProxy方法判断是cglib代理还是jdk动态代理,最后通过Enhancer完成cglib代理对象的创建。

```python
class BertPooler(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.dense = nn.Linear(config.hidden_size, config.hidden_size)
        self.activation = nn.Tanh()

    def forward(self, hidden_states):
        # We "pool" the model by simply taking the hidden state corresponding
        # to the first token.
        first_token_tensor = hidden_states[:, 0]
        pooled_output = self.dense(first_token_tensor)
        pooled_output = self.activation(pooled_output)
        return pooled_output
from transformers.models.bert.configuration_bert import *
import torch
config = BertConfig.from_pretrained("bert-base-uncased")
bert_pooler = BertPooler(config=config)
print("input to bert pooler size: {}".format(config.hidden_size))
batch_size = 1
seq_len = 2
hidden_size = 768
x = torch.rand(batch_size, seq_len, hidden_size)
y = bert_pooler(x)
print(y.size())
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值