Android 轻量级ORM数据库开源框架ActiveAndroid 源码分析

ActiveAndroid 项目地址在https://github.com/pardom/ActiveAndroid

关于他的详细介绍和使用步骤 可以看下面两篇文章:

https://github.com/pardom/ActiveAndroid/wiki

http://www.future-processing.pl/blog/persist-your-data-activeandroid-and-parse/

请确保你在阅读本文下面的内容之前 熟读上面的2篇文章。我不会再讲一遍如何使用这个框架。

另外由于这个项目的作者已经多时不更新,所以在android studio 里直接引用的话会有些麻烦

请确保使用我的代码 保证你可以正常引用并使用这个框架。

 1 repositories {
 2     mavenCentral()
 3     maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
 4 }
 5
 6
 7 dependencies {
 8     compile fileTree(dir: ‘libs‘, include: [‘*.jar‘])
 9     compile ‘com.android.support:appcompat-v7:22.2.1‘
10     compile ‘com.michaelpardo:activeandroid:3.1.0-SNAPSHOT‘
11     //compile ‘:ActiveAndroid‘
12 }

然后我们来剖析他的源码,看看他的工作机理是什么。

首先看数据库是如何创建的?

 1 public static void initialize(Context context) {
 2         initialize(new Configuration.Builder(context).create());
 3     }
 4
 5     public static void initialize(Configuration configuration) {
 6         initialize(configuration, true);
 7     }
 8
 9     public static void initialize(Context context, boolean loggingEnabled) {
10         initialize(new Configuration.Builder(context).create(), loggingEnabled);
11     }
12
13     public static void initialize(Configuration configuration, boolean loggingEnabled) {
14         // Set logging enabled first
15         setLoggingEnabled(loggingEnabled);
16         Cache.initialize(configuration);
17     }

看第10行,这个地方有一个Configuration的类 还有个create方法。

我们贴以下create方法

 1   public Configuration create() {
 2             Configuration configuration = new Configuration(mContext);
 3             configuration.mCacheSize = mCacheSize;
 4
 5             // Get database name from meta-data
 6             if (mDatabaseName != null) {
 7                 configuration.mDatabaseName = mDatabaseName;
 8             } else {
 9                 configuration.mDatabaseName = getMetaDataDatabaseNameOrDefault();
10             }
12
13             // Get database version from meta-data
14             if (mDatabaseVersion != null) {
15                 configuration.mDatabaseVersion = mDatabaseVersion;
16             } else {
17                 configuration.mDatabaseVersion = getMetaDataDatabaseVersionOrDefault();
18             }
20
21             // Get SQL parser from meta-data
22             if (mSqlParser != null) {
23                 configuration.mSqlParser = mSqlParser;
24             } else {
25                 configuration.mSqlParser = getMetaDataSqlParserOrDefault();
26             }
27
28             // Get model classes from meta-data
29             if (mModelClasses != null) {
30                 configuration.mModelClasses = mModelClasses;
31             } else {
32                 final String modelList = ReflectionUtils.getMetaData(mContext, AA_MODELS);
34                 if (modelList != null) {
35                     configuration.mModelClasses = loadModelList(modelList.split(","));
36                 }
37             }
38
39             // Get type serializer classes from meta-data
40             if (mTypeSerializers != null) {
41                 configuration.mTypeSerializers = mTypeSerializers;
42             } else {
43                 final String serializerList = ReflectionUtils.getMetaData(mContext, AA_SERIALIZERS);
44                 if (serializerList != null) {
45                     configuration.mTypeSerializers = loadSerializerList(serializerList.split(","));
46                 }
47             }
48
49             return configuration;
50         }

所以就能看出来 这个create方法 就是去读取我们的配置文件里的内容的。比如

但实际上个人认为这样做并不是特别好的方案,比如AA_MODELS,如果你的app 需要的表比较多,那这里android:value那边里面因为要写model的全名就是包名+类名

这么做会导致这个value里的字符串非常的长,而且难以维护,比较好的写法 我认为是要把这个配置文件单独放在assets目录下面 新建一个xml文件,去读取比较好。

有兴趣的读者可以尝试修改一下这边的逻辑。

当我们读取结束配置文件以后 我们会继续调用cache.init这个方法

 1  public static synchronized void initialize(Configuration configuration) {
 2         if (sIsInitialized) {
 3             return;
 4         }
 5         sContext = configuration.getContext();
 6         sModelInfo = new ModelInfo(configuration);
 7         sDatabaseHelper = new DatabaseHelper(configuration);
 8
 9         // TODO: It would be nice to override sizeOf here and calculate the memory
10         // actually used, however at this point it seems like the reflection
11         // required would be too costly to be of any benefit. We‘ll just set a max
12         // object size instead.
13         sEntities = new LruCache<String, Model>(configuration.getCacheSize());
14
15         openDatabase();
16
17         sIsInitialized = true;
18
19         Log.v("ActiveAndroid initialized successfully.");
20     }

在这个里面 我们找到了databaseHelper。看来建立数据库和表的 关键部分就在这里面了。而且要注意databasehelper是用config 我们读取出来的那些配置数据来初始化的。

1  @Override
2     public void onCreate(SQLiteDatabase db) {
3         executePragmas(db);
4         executeCreate(db);
5         executeMigrations(db, -1, db.getVersion());
6         executeCreateIndex(db);
7     }
private void executeCreate(SQLiteDatabase db) {
        db.beginTransaction();
        try {
            for (TableInfo tableInfo : Cache.getTableInfos()) {
                String sql = SQLiteUtils.createTableDefinition(tableInfo);
                db.execSQL(sql);
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

实际上 在这个地方就已经把我们取得的config数据 遍历以后 组成我们的sql语句 去初始化我们的数据库以及表了。

除此之外,我们当然也可以用sql脚本的方式来建表,尤其是在数据库升级的时候,直接导入脚本来升级也是非常简便的。

此框架,也要求我们将sql脚本放在app assets migrations 目录下.此脚本必须.sql为结尾。前缀可以用阿拉伯数字

还表示脚本的版本号。

我们来走一遍数据库升级时的脚本流程。

 1 private boolean executeMigrations(SQLiteDatabase db, int oldVersion, int newVersion) {
 2         boolean migrationExecuted = false;
 3         try {
 4             final List<String> files = Arrays.asList(Cache.getContext().getAssets().list(MIGRATION_PATH));
 5             Collections.sort(files, new NaturalOrderComparator());
 6
 7             db.beginTransaction();
 8             try {
 9                 for (String file : files) {
10                     try {
11                         final int version = Integer.valueOf(file.replace(".sql", ""));
12                         if (version > oldVersion && version <= newVersion) {
13                             executeSqlScript(db, file);
14                             migrationExecuted = true;
15
16                             Log.i(file + " executed succesfully.");
17                         }
18                     } catch (NumberFormatException e) {
19                         Log.w("Skipping invalidly named file: " + file, e);
20                     }
21                 }
22                 db.setTransactionSuccessful();
23             } finally {
24                 db.endTransaction();
25             }
26         } catch (IOException e) {
27             Log.e("Failed to execute migrations.", e);
28         }
29
30         return migrationExecuted;
31     }

4行 拿到assets下mig路径下的所有文件 并且排序以后进行遍历 符合条件的会在13行执行脚本。

12行就是判断 是否需要进行数据库升级脚本操作的判断条件。可以自己体会下为什么要这么写,

当然我个人意见是你在使用此框架的时候 关于数据库升级的部分 可以自定义。尤其是在做一些

复杂的电商,聊天之类的app时候,表关系复杂,并且时有升级。

到目前为止,我们已经过了一遍此框架 建立数据库 建立 升级表的过程。

然后我们来看一下 这个框架是如何执行crud 操作的。我们只分析add和selet操作。其他操作留给读者自己分析。

一般我们建立的model都如下

1     public Model() {
2         mTableInfo = Cache.getTableInfo(getClass());
3         idName = mTableInfo.getIdName();
4     }

实际上我们的model 在初始化的时候 会把自己的class 在构造函数里面 传到cache里,这样我们在model里面就能拿到我们对应的表的信息了。

这个model的构造函数就是做这件事的。 那拿到这个表信息以后 进行save操作 就是非常简单的了。

 1  public final Long save() {
 2         final SQLiteDatabase db = Cache.openDatabase();
 3         final ContentValues values = new ContentValues();
 4
 5         for (Field field : mTableInfo.getFields()) {
 6             final String fieldName = mTableInfo.getColumnName(field);
 7             Class<?> fieldType = field.getType();
 8             field.setAccessible(true);
 9
10             try {
11                 Object value = field.get(this);
12
13                 if (value != null) {
14                     final TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
15                     if (typeSerializer != null) {
16                         // serialize data
17                         value = typeSerializer.serialize(value);
18                         // set new object type
19                         if (value != null) {
20                             fieldType = value.getClass();
21                             // check that the serializer returned what it promised
22                             if (!fieldType.equals(typeSerializer.getSerializedType())) {
23                                 Log.w(String.format("TypeSerializer returned wrong type: expected a %s but got a %s",
24                                         typeSerializer.getSerializedType(), fieldType));
25                             }
26                         }
27                     }
28                 }
29
30                 // TODO: Find a smarter way to do this? This if block is necessary because we
31                 // can‘t know the type until runtime.
32                 if (value == null) {
33                     values.putNull(fieldName);
34                 } else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
35                     values.put(fieldName, (Byte) value);
36                 } else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
37                     values.put(fieldName, (Short) value);
38                 } else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
39                     values.put(fieldName, (Integer) value);
40                 } else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
41                     values.put(fieldName, (Long) value);
42                 } else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
43                     values.put(fieldName, (Float) value);
44                 } else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
45                     values.put(fieldName, (Double) value);
46                 } else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
47                     values.put(fieldName, (Boolean) value);
48                 } else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
49                     values.put(fieldName, value.toString());
50                 } else if (fieldType.equals(String.class)) {
51                     values.put(fieldName, value.toString());
52                 } else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
53                     values.put(fieldName, (byte[]) value);
54                 } else if (ReflectionUtils.isModel(fieldType)) {
55                     values.put(fieldName, ((Model) value).getId());
56                 } else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
57                     values.put(fieldName, ((Enum<?>) value).name());
58                 }
59             } catch (IllegalArgumentException e) {
60                 Log.e(e.getClass().getName(), e);
61             } catch (IllegalAccessException e) {
62                 Log.e(e.getClass().getName(), e);
63             }
64         }
65
66         if (mId == null) {
67             mId = db.insert(mTableInfo.getTableName(), null, values);
68         } else {
69             db.update(mTableInfo.getTableName(), values, idName + "=" + mId, null);
70         }
71
72         Cache.getContext().getContentResolver()
73                 .notifyChange(ContentProvider.createUri(mTableInfo.getType(), mId), null);
74         return mId;
75     }

思路就是利用反射 来拿到值以后 进行数据库操作,10-29行有一段关于序列化的代码 我放在后面再讲。先继续看select操作吧。

比如说 我们一般的select操作 都是这样的

来看一下select的from方法。

public From from(Class<? extends Model> table) {
        return new From(table, this);
    }

原来是利用我们传进去的class名字来构造真正的查询对象。

 1 public final class From implements Sqlable {
 2     private Sqlable mQueryBase;
 3
 4     private Class<? extends Model> mType;
 5     private String mAlias;
 6     private List<Join> mJoins;
 7     private final StringBuilder mWhere = new StringBuilder();
 8     private String mGroupBy;
 9     private String mHaving;
10     private String mOrderBy;
11     private String mLimit;
12     private String mOffset;
13
14     private List<Object> mArguments;
15
16     public From(Class<? extends Model> table, Sqlable queryBase) {
17         mType = table;
18         mJoins = new ArrayList<Join>();
19         mQueryBase = queryBase;
20
21         mJoins = new ArrayList<Join>();
22         mArguments = new ArrayList<Object>();
23     }

最终的excute方法

 1 public <T extends Model> List<T> execute() {
 2         if (mQueryBase instanceof Select) {
 3             return SQLiteUtils.rawQuery(mType, toSql(), getArguments());
 4
 5         } else {
 6             SQLiteUtils.execSql(toSql(), getArguments());
 7             Cache.getContext().getContentResolver().notifyChange(ContentProvider.createUri(mType, null), null);
 8             return null;
 9
10         }
11     }

query这边的代码 我认为写的非常巧妙清晰,篇幅所限 我无法讲的太细,你们可以观察下代码结构 自己回去多翻翻

回到 我们刚才讲save操作时候 看到有几行序列化的代码,实际上 那边的代码 是用来建立 对象和 数据库原始数据关系的。

比如我们有个学生model,我们希望有个字段来存放这个学生的手机 pad以及电脑的品牌,当然为了表示简洁,我们这个字段

可以用一段json String来表示。但是我们在model里又不希望直接用string,我们希望用一个对象来表示。就可以用

“序列化” 和“反序列化” 来表示他们之间的关系。

我们首先来建立一个 学生物品类

 1 package com.example.administrator.myapplication5;
 2
 3 import org.json.JSONException;
 4 import org.json.JSONObject;
 5
 6 /**
 7  * Created by Administrator on 2015/8/28.
 8  */
 9 public class PersonalItems {
10     private String computerBand;
11     private String phoneBand;
12     private String padBand;
13
14     public String getComputerBand() {
15         return computerBand;
16     }
17
18     public void setComputerBand(String computerBand) {
19         this.computerBand = computerBand;
20     }
21
22     public String getPhoneBand() {
23         return phoneBand;
24     }
25
26     public void setPhoneBand(String phoneBand) {
27         this.phoneBand = phoneBand;
28     }
29
30     public String getPadBand() {
31         return padBand;
32     }
33
34     public void setPadBand(String padBand) {
35         this.padBand = padBand;
36     }
37
38     @Override
39     public String toString() {
40
41         JSONObject jsonObject = new JSONObject();
42         try {
43             jsonObject.put("computerBand", computerBand);
44             jsonObject.put("padBand", padBand);
45             jsonObject.put("phoneBand", phoneBand);
46
47         } catch (JSONException e) {
48             e.printStackTrace();
49         }
50
51
52         return jsonObject.toString();
53     }
54 }

然后我们在model里 也建立这个对象。

 1 package com.example.administrator.myapplication5;
 2
 3 import com.activeandroid.Model;
 4 import com.activeandroid.annotation.Column;
 5 import com.activeandroid.annotation.Table;
 6
 7 import java.util.Date;
 8
 9 /**
10  * Created by Administrator on 2015/8/27.
11  */
12 @Table(name = "Student")
13 public class Student extends Model {
14     @Column(name = "Name")
15     public String name;
16     @Column(name = "No")
17     public String no;
18     @Column(name = "sex")
19     public int sex;
20     @Column(name = "date")
21     public Date date;
22
23     @Column(name = "personalItems")
24     public PersonalItems personalItems;
25
26     @Override
27     public String toString() {
28         return "Student{" +
29                 "name=‘" + name + ‘\‘‘ +
30                 ", no=‘" + no + ‘\‘‘ +
31                 ", sex=" + sex +
32                 ", date=" + date +
33                 ", personalItems=" + personalItems +
34                 ‘}‘;
35     }
36 }

然后我们来建立两者之间的关系类!这个是核心。

 1 package com.activeandroid.serializer;
 2
 3 import com.example.administrator.myapplication5.PersonalItems;
 4
 5 import org.json.JSONException;
 6 import org.json.JSONObject;
 7
 8 /**
 9  * Created by Administrator on 2015/8/28.
10  */
11 public class PersonalItemsSerializer extends TypeSerializer {
12
13     @Override
14     public Class<?> getDeserializedType() {
15         return PersonalItems.class;
16     }
17
18     @Override
19     public Class<?> getSerializedType() {
20         return String.class;
21     }
22
23     @Override
24     public String serialize(Object data) {
25         if (data == null) {
26             return null;
27         }
28         String str = data.toString();
29
30         return str;
31     }
32
33     @Override
34     public PersonalItems deserialize(Object data) {
35         if (data == null) {
36             return null;
37         }
38         PersonalItems personalItems = new PersonalItems();
39         try {
40             JSONObject dataJson = new JSONObject(data.toString());
41             personalItems.setComputerBand(dataJson.getString("computerBand"));
42             personalItems.setPadBand(dataJson.getString("padBand"));
43             personalItems.setPhoneBand(dataJson.getString("phoneBand"));
44         } catch (JSONException e) {
45             e.printStackTrace();
46         }
47         return personalItems;
48     }
49 }

代码还是很清楚的,复写的几个方法就是要求你 写一下序列和反序列化的过程罢了。(对象----数据库字段 的关系 就是在这建立的)

当然你写完了 还要在配置文件里配置一下。不然系统会找不到这个序列化类的。这个过程我就不分析了,留着大家有兴趣可以自己写一个。

我们主要看一下 那个save操作里的过程

 1 for (Field field : mTableInfo.getFields()) {
 2             final String fieldName = mTableInfo.getColumnName(field);
 3             Class<?> fieldType = field.getType();
 4             field.setAccessible(true);
 5
 6             try {
 7                 Object value = field.get(this);
 8
 9                 if (value != null) {
10                     final TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
11                     if (typeSerializer != null) {
12                         // serialize data
13                         value = typeSerializer.serialize(value);
14                         // set new object type
15                         if (value != null) {
16                             fieldType = value.getClass();
17                             // check that the serializer returned what it promised
18                             if (!fieldType.equals(typeSerializer.getSerializedType())) {
19                                 Log.w(String.format("TypeSerializer returned wrong type: expected a %s but got a %s",
20                                         typeSerializer.getSerializedType(), fieldType));
21                             }
22                         }
23                     }
24                 }

第三行 我们拿到了 fieldType 注意此时他的值 实际上 就是我们对象所属的类的名字!

第10行  去cache里 找对应关系类,看看有没有对应的这个类的 序列化工作器。

第13行 表明如果找到这个序列化工作器的话  就用他把这个value的值转换成 数据库能接受的值!

第15行  表明如果序列化成功的话,fieldType的值也要改成 数据库也就是sqlite能接受的类型!

然后 才能用下面的代码 进行插入操作!

 1 if (value == null) {
 2                     values.putNull(fieldName);
 3                 } else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
 4                     values.put(fieldName, (Byte) value);
 5                 } else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
 6                     values.put(fieldName, (Short) value);
 7                 } else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
 8                     values.put(fieldName, (Integer) value);
 9                 } else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
10                     values.put(fieldName, (Long) value);
11                 } else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
12                     values.put(fieldName, (Float) value);
13                 } else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
14                     values.put(fieldName, (Double) value);
15                 } else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
16                     values.put(fieldName, (Boolean) value);
17                 } else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
18                     values.put(fieldName, value.toString());
19                 } else if (fieldType.equals(String.class)) {
20                     values.put(fieldName, value.toString());
21                 } else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
22                     values.put(fieldName, (byte[]) value);
23                 } else if (ReflectionUtils.isModel(fieldType)) {
24                     values.put(fieldName, ((Model) value).getId());
25                 } else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
26                     values.put(fieldName, ((Enum<?>) value).name());
27                 }

至于selcet操作时 反序列化的操作 就留给读者自己分析了。

其实这个框架本身已经提供了不少好用的工作器给我们使用了(第四个是我刚才定义的 请大家忽略掉 哈哈)。

最后由于这个框架大量使用的注解 反射 导致效率肯定比不上自己直接写sql,不过可以接受,尽管如此 还是请尽量在子线程里去操作数据库!重要的话 只说一遍!

时间: 2024-08-08 17:37:09

Android 轻量级ORM数据库开源框架ActiveAndroid 源码分析的相关文章

android 在线升级借助开源中国App源码

http://www.cnblogs.com/luomingui/p/3949429.html android 在线升级借助开源中国App源码分析如下: 1: checkAppUpdate 检查是或需要升级 // 网络连接判断         if (appContext.isNetworkConnected()) {             // 检查新版本             if (appContext.isCheckUp()) {                    UpdateM

ym——Android仿网易新闻导航栏PagerSlidingTabStrip源码分析

转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103),谢谢支持! 前言 最近工作比较忙,所以现在才更新博文,对不住大家了~!言归正传,我们来说说这个PagerSlidingTabStrip,它是配合ViewPager使用的导航栏,网易新闻就是用的这个导航,我们仔细观察这个导航栏不仅他是跟着ViewPager滑动而滑动,而且指示器还会随着标题的长度而动态的变化长度,还可以改变多种样式哦~! · 下载地址: Github:https://github.

Android异步消息处理机制详解及源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 最近相对来说比较闲,加上养病,所以没事干就撸些自己之前的知识点为博客,方便自己也方便别人. 1 背景 之所以选择这个知识点来分析有以下几个原因: 逛GitHub时发现关注的isuss中有人不停的在讨论Android中的Looper , Handler , Me

Android异步任务处理框架AsyncTask源码分析

[转载请注明出处:http://blog.csdn.net/feiduclear_up CSDN 废墟的树] 引言 在平时项目开发中难免会遇到异步耗时的任务(比如最常见的网络请求).遇到这种问题,我们可以自己通过Handler+Message+Thread/ThreadPool来构造一个异步耗时任务框架.当你下次项目中又遇到一个网络请求,你又不得不重写异步耗时任务处理框架.出于避免开发者重复搬砖工作,Google工程师给开发者搭建了一个通用的异步耗时任务处理框架--AsyncTask. Asyn

Android 4.2 Wifi Display 之 Settings 源码分析(一)

一.简单背景 简单背景:随着无线互联的深入,不管是蓝牙.WIFI或者各种基于此的规范不管是UPNP还是DLNA都随着用户的需求得到了很大的发展,google 自从android 4.0引入wifi direct后,又在11月份公布的android 4.2中引入了Miracast无线显示共享,其协议在此可以下载.具体的协议部分内容比较多,本人由于水平有限,就不在这里罗列协议的内容了,只附上一份架构图供大家对其有个大致的印象. 英文缩写对应如下: HIDC: Human Interface Devi

Android异步消息处理 Handler Looper Message关系源码分析

# 标签: 读博客 对于Handler Looper Message 之前一直只是知道理论,知其然不知所以然,看了hongyang大神的源码分析,写个总结帖. 一.概念.. Handler . Looper .Message 这三者都与Android异步消息处理线程相关的概念. 异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环.若消息队列为空,线程则会阻塞等待. 说了这一堆,那么和Handle

skynet 框架snax源码分析1---变量注入

skynet为了简化服务的编写,推出了snax框架,源码里也有一个例子pingserver.这是snax原创文章的第一篇,所以先就分析snax框架里的interface.lua源码,它的实现应用了一个闭包中的upvalue注入技巧. 凡是框架都得遵循框架的约定,snax有两个大的约定,一是约定了一组预置的接口init/exit/hotfix:二是accept/response这两组用来编写服务的接口.本文,并不涉及这些,而是谈accept/response是如何注入给snax服务的. snax框

Django框架 --CBV源码分析、restful规范、restframework框架

一.CBV源码分析 1.url层的使用CBV from app01 import views url(r'book/',views.Book.as_view()) 2.as_view方法 as_view是一个类方法,实际上是一个闭包函数(内层函数包含对外层作用域的使用) 请求来了以后,调用as_view方法,调用函数中的view方法,view方法是调用了dispatch方法 @classonlymethod def as_view(cls, **initkwargs): def view(req

Flask框架 —— session源码分析

目录 session源码分析 1.请求来了,执行__call__方法 2.__call__方法 3.调用__call__方法 3.1.ctx = self.request_context(environ) --- 得到空的session 3.2.ctx.push() --- 调用open_session方法 3.3.self.full_dispatch_request() --- 路由分发,执行函数,写入session session源码分析 1.请求来了,执行__call__方法 # 请求来了