过完五一长假,花了四天的时间来学习Hibernate框架的使用,作为门外汉,先是从sourceforg.net下载 Hibernate3.2,先看官方文档,只有一个提纲,了解了一下各个包的结构,便开始根据Toturial实践,基本掌握了它的使用方法之后,盟生了 实现自己的SessionFactory和ConnectionProvider的想法。
闲话少说,要实现我的SessionFactory和ConnectionProvider,不深入了解该体系结构是不行的,先从源代码分析开始: 首先从org.hibernate.cfg.Configuration.java开始,使用Hibernate框架实现应用程序,首先就要与org.hibernate.cfg.Configuration打交道,要使用
Configuration.buildSessionFactory()方法获得一个SessionFactory,截取代码片段如下:
上面的代码片断省略了读取Hibernate配置的代码,从这个我们可以知道,Configuration类buildSessionFactory()方法实际上返回了SessionFactory接口的实现SessionFactoryImpl。
当我们得到了一个SessionFactory接口的实现SessionFactoryImpl,就要调用它的getCurrentSession()方 法来获得一个Session,接下来转到org.hibernate.impl.SessionFactoryImpl.java,来看看 getCurrentSession()方法的实现,代码片段如下:
在该方法中,SessionFactoryImpl将获得Session的工作委托给了currentSessionContext.currentSession(),currentSessionContext为何物?其定义为:
org.hibernate.context.CurrentSessionContext;在SessionFactoryImpl的构造函数中,可以看到:
currentSessionContext
=
buildCurrentSessionContext();
马上追溯到buildCurrentSessionContext()方法,代码片段如下:
从这里可以发现,SessionFactoryImpl用反射实现了CurrentSessionContext接口的动态装配。
接下来,暂时将视线从SessionFactoryImpl移开,以org.hibernate.context.JTASessionContext为代表,看CurrentSessionContext接口是如何实现
的currentSession()方法的,打开org.hibernate.context.JTASessionContext.java,找到currentSession(),代码片段如下:
转移到buildOrObtainSession()方法,
闲话少说,要实现我的SessionFactory和ConnectionProvider,不深入了解该体系结构是不行的,先从源代码分析开始: 首先从org.hibernate.cfg.Configuration.java开始,使用Hibernate框架实现应用程序,首先就要与org.hibernate.cfg.Configuration打交道,要使用
Configuration.buildSessionFactory()方法获得一个SessionFactory,截取代码片段如下:
1
public
SessionFactory buildSessionFactory()
throws
HibernateException
{
2
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
3
return new SessionFactoryImpl(
4
this,
5
mapping,
6
settings,
7
getInitializedEventListeners()
8
);
9
}
![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/ca75c07623e1b494fee67e8f316fc310.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
上面的代码片断省略了读取Hibernate配置的代码,从这个我们可以知道,Configuration类buildSessionFactory()方法实际上返回了SessionFactory接口的实现SessionFactoryImpl。
当我们得到了一个SessionFactory接口的实现SessionFactoryImpl,就要调用它的getCurrentSession()方 法来获得一个Session,接下来转到org.hibernate.impl.SessionFactoryImpl.java,来看看 getCurrentSession()方法的实现,代码片段如下:
1
public
org.hibernate.classic.Session getCurrentSession()
throws
HibernateException
{
2![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if ( currentSessionContext == null )
{
3
throw new HibernateException( "No CurrentSessionContext configured!" );
4
}
5
return currentSessionContext.currentSession();
6
}
![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/ca75c07623e1b494fee67e8f316fc310.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
在该方法中,SessionFactoryImpl将获得Session的工作委托给了currentSessionContext.currentSession(),currentSessionContext为何物?其定义为:
org.hibernate.context.CurrentSessionContext;在SessionFactoryImpl的构造函数中,可以看到:
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
马上追溯到buildCurrentSessionContext()方法,代码片段如下:
1
private
CurrentSessionContext buildCurrentSessionContext()
{
2
String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS );
3
// for backward-compatability
4![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if ( impl == null && transactionManager != null )
{
5
impl = "jta";
6
}
7![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
8![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if ( impl == null )
{
9
return null;
10
}
11![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
else if ( "jta".equals( impl ) )
{
12![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if ( settings.getTransactionFactory().areCallbacksLocalToHibernateTransactions() )
{
13
log.warn( "JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()" );
14
}
15
return new JTASessionContext( this );
16
}
17![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
else if ( "thread".equals( impl ) )
{
18
return new ThreadLocalSessionContext( this );
19
}
20![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
else if ( "managed".equals( impl ) )
{
21
return new ManagedSessionContext( this );
22
}
23![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
else
{
24![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
try
{
25
Class implClass = ReflectHelper.classForName( impl );
26
return ( CurrentSessionContext ) implClass
27![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
.getConstructor( new Class[]
{ SessionFactoryImplementor.class } )
28![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
.newInstance( new Object[]
{ this } );
29
}
30![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
catch( Throwable t )
{
31
log.error( "Unable to construct current session context [" + impl + "]", t );
32
return null;
33
}
34
}
35
}
36
![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/ca75c07623e1b494fee67e8f316fc310.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
10
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
11
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
12
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
13
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
14
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
15
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
16
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
17
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
18
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
19
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
20
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
21
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
22
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
23
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
24
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
25
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
26
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
27
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
28
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
29
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
30
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
31
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
32
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
33
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
34
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
35
![](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
36
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
从这里可以发现,SessionFactoryImpl用反射实现了CurrentSessionContext接口的动态装配。
接下来,暂时将视线从SessionFactoryImpl移开,以org.hibernate.context.JTASessionContext为代表,看CurrentSessionContext接口是如何实现
的currentSession()方法的,打开org.hibernate.context.JTASessionContext.java,找到currentSession(),代码片段如下:
1
public
Session currentSession()
throws
HibernateException
{
2![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
3![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if ( currentSession == null )
{
4
currentSession = buildOrObtainSession();
5![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
6
}
7![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
8
return currentSession;
9
}
![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/ca75c07623e1b494fee67e8f316fc310.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3c6cafce68eb941a00f1998f1d3d3aa6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
转移到buildOrObtainSession()方法,
1
protected
Session buildOrObtainSession()
{
2
return factory.openSession(
3
null,
4
isAutoFlushEnabled(),
5
isAutoCloseEnabled(),
6
getConnectionReleaseMode()
7
);
8
}
![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/ca75c07623e1b494fee67e8f316fc310.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
前面SessionFactoryImpl.buildCurrentSessionContext()方法有new JTASessionContext( this ),而此时的factory.openSessio()就是SessionFactoryImpl.openSessio()了。
将目光焦点回到org.hibernate.impl.SessionFactoryImpl.java, SessionFactoryImpl.openSessio()的实现如下:
1
private
SessionImpl openSession(
2
Connection connection,
3
boolean
autoClose,
4
long
timestamp,
5
Interceptor sessionLocalInterceptor
6![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
)
{
7
return new SessionImpl(
8
connection,
9
this,
10
autoClose,
11
timestamp,
12
sessionLocalInterceptor == null ? interceptor : sessionLocalInterceptor,
13
settings.getDefaultEntityMode(),
14
settings.isFlushBeforeCompletionEnabled(),
15
settings.isAutoCloseSessionEnabled(),
16
settings.getConnectionReleaseMode()
17
);
18
}
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/ca75c07623e1b494fee67e8f316fc310.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/9b8a8a44dd1c74ae49c20a7cd451974e.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
10
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
11
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
12
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
13
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
14
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
15
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
16
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
17
![](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
18
![](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)