美团热修复Robust-源码篇

摘要:
上一篇主要分析了Robust的使用方法,这一篇就来总结一下Robust的源码分析。当它不为空时,我们可以看到它调用了PatchProxy中的isSupport方法和accessDispatch方法。我们接下来再看accessDispatch方法1publicstaticObjectaccessDispatch{23if{4RobustExtensionrobustExtension=robustExtensionThreadLocal.get();5robustExtensionThreadLocal.remove();6if(robustExtension!

上一篇主要分析了Robust的使用方法,这一篇就来总结一下Robust的源码分析。

我个人倾向于将Robust框架分为两个部分,自动插入代码和动态加载Patch。

一、Robust源码分析

目前我的分析将Robust动态加载分为两个部分,一部分是插桩后的代码逻辑,一部分是拉取Patch的逻辑。

我们首先来看插桩后的代码(这里面套用的是官方的代码,可能有些过时了)

插桩前

public longgetIndex() {
    return 100;
}

插桩后

public staticChangeQuickRedirect changeQuickRedirect;
    public longgetIndex() {
        if(changeQuickRedirect != null) {
            //PatchProxy中封装了获取当前className和methodName的逻辑,并在其内部最终调用了changeQuickRedirect的对应函数
            if(PatchProxy.isSupport(new Object[0], this, changeQuickRedirect, false)) {
                return ((Long)PatchProxy.accessDispatch(new Object[0], this, changeQuickRedirect, false)).longValue();
            }
        }
    return 100L;
}

我们可以看到Robust为我们的类添加了一个静态的ChangeQuickRedirect对象,我们可以看到当ChangeQuickRedirect为空时,证明此时没有补丁,走原逻辑。当它不为空时,我们可以看到它调用了PatchProxy中的isSupport方法和accessDispatch方法。我们具体来看一下PatchProxy中的这两个方法。

1   public static boolean isSupport(Object[] paramsArray, Object current, ChangeQuickRedirect changeQuickRedirect, boolean isStatic, intmethodNumber, Class[] paramsClassTypes, Class returnType) {
2         //Robust补丁优先执行,其他功能靠后
3         if (changeQuickRedirect == null) {
4             //不执行补丁,轮询其他监听者
5             if (registerExtensionList == null ||registerExtensionList.isEmpty()) {
6                 return false;
7 }
8             for(RobustExtension robustExtension : registerExtensionList) {
9                 if (robustExtension.isSupport(newRobustArguments(paramsArray, current, isStatic, methodNumber, paramsClassTypes, returnType))) {
10 robustExtensionThreadLocal.set(robustExtension);
11                     return true;
12 }
13 }
14             return false;
15 }
16         String classMethod =getClassMethod(isStatic, methodNumber);
17         if(TextUtils.isEmpty(classMethod)) {
18             return false;
19 }
20         Object[] objects =getObjects(paramsArray, current, isStatic);
21         try{
22             returnchangeQuickRedirect.isSupport(classMethod, objects);
23         } catch(Throwable t) {
24             return false;
25 }
26     }

我们可以看到第22行,它调用了changeQuickRedirect.isSupport方法,这个changeQuickRedirect便是我们注入的对象。

我们接下来再看accessDispatch方法

1  public static Object accessDispatch(Object[] paramsArray, Object current, ChangeQuickRedirect changeQuickRedirect, boolean isStatic, intmethodNumber, Class[] paramsClassTypes, Class returnType) {
2 
3         if (changeQuickRedirect == null) {
4             RobustExtension robustExtension =robustExtensionThreadLocal.get();
5 robustExtensionThreadLocal.remove();
6             if (robustExtension != null) {
7 notify(robustExtension.describeSelfFunction());
8                 return robustExtension.accessDispatch(newRobustArguments(paramsArray, current, isStatic, methodNumber, paramsClassTypes, returnType));
9 }
10             return null;
11 }
12         String classMethod =getClassMethod(isStatic, methodNumber);
13         if(TextUtils.isEmpty(classMethod)) {
14             return null;
15 }
16 notify(Constants.PATCH_EXECUTE);
17         Object[] objects =getObjects(paramsArray, current, isStatic);
18         return changeQuickRedirect.accessDispatch(classMethod, objects);

可以看到第18行调用了changeQuickRedirect的accseeDispatch方法。

注入后的代码我们先看到这里,我们接下来看一看,我们拉取Patch的代码

1   new PatchExecutor(getApplicationContext(), new PatchManpulateImp(), newRobustCallBack() {
2 @Override
3                     public void onPatchListFetched(boolean result, boolean isNet, List<Patch>patches) {
4                         Log.e("error-hot", "打印 onPatchListFetched:" + "isNet=" +isNet );
5 }
6 @Override
7                     public void onPatchFetched(boolean result, booleanisNet, Patch patch) {
8                         Log.e("error-hot", "打印 onPatchFetched:" + "result=" + result+"isNet="+isNet + "--->" + "patch=" +patch);
9 }
10 @Override
11                     public void onPatchApplied(booleanresult, Patch patch) {
12                         Log.e("error-hot", "打印 onPatchApplied:" + "result=" + result + "--->" + "patch=" +patch);
13 }
14 @Override
15                     public voidlogNotify(String log, String where) {
16                         Log.e("error-hot", "打印 logNotify:" + "log=" + log + "--->" + "where=" +where);
17 }
18 @Override
19                     public voidexceptionNotify(Throwable throwable, String where) {
20                         Log.e("error-hot", "打印 exceptionNotify:" + "throwable=" + throwable.toString() + "--->" + "where=" +where);
21 }
22                 }).start();

进入PatchExecutor类中看一看,我们可以发现它继承了一个线程,那么直接去run方法看一下

1 @Override
2     public voidrun() {
3         try{
4             //拉取补丁列表
5             List<Patch> patches =fetchPatchList();
6             //应用补丁列表
7 applyPatchList(patches);
8         } catch(Throwable t) {
9             Log.e("robust", "PatchExecutor run", t);
10             robustCallBack.exceptionNotify(t, "class:PatchExecutor,method:run,line:36");
11 }
12     }

可以看到run方法中做了两件事,拉取补丁列表和应用补丁列表。

我们接着进入fetchPatchList方法

1    protected List<Patch>fetchPatchList() {
2         returnpatchManipulate.fetchPatchList(context);
3     }

他返回了patchManipulate的fetchPatchList方法,这个对象便是我们在初始化的时候传进来的。我们进入看一看

1 @Override
2     protected List<Patch>fetchPatchList(Context context) {
3         Patch patch = newPatch();
4         patch.setName("test patch");
5         patch.setLocalPath(Environment.getExternalStorageDirectory().getPath()+
6                 File.separator+"robust"+File.separator+"patch");
7         patch.setPatchesInfoImplClassFullName("com.example.tyr.testrobust.PatchesInfoImpl");
8         List<Patch> patches = new ArrayList<>();
9 patches.add(patch);
10         returnpatches;
11     }

我们将这个PatchesInfoImpl拉进到列表中,那么这个PatchInfoImpl是在哪里那?我们后面再说。

接着看applyPatchList方法

protected void applyPatchList(List<Patch>patches) {
        if (null == patches ||patches.isEmpty()) {
            return;
        }
        Log.d("robust", " patchManipulate list size is " +patches.size());
        for(Patch p : patches) {
            if(p.isAppliedSuccess()) {
                Log.d("robust", "p.isAppliedSuccess() skip " +p.getLocalPath());
                continue;
            }
            if(patchManipulate.ensurePatchExist(p)) {
                boolean currentPatchResult = false;
                try{
                    currentPatchResult =patch(context, p);
                } catch(Throwable t) {
                    robustCallBack.exceptionNotify(t, "class:PatchExecutor method:applyPatchList line:69");
                }
                if(currentPatchResult) {
                    //设置patch 状态为成功
                    p.setAppliedSuccess(true);
                    //统计PATCH成功率 PATCH成功
                    robustCallBack.onPatchApplied(true, p);

                } else{
                    //统计PATCH成功率 PATCH失败
                    robustCallBack.onPatchApplied(false, p);
                }

                Log.d("robust", "patch LocalPath:" + p.getLocalPath() + ",apply result " +currentPatchResult);

            }
        }
    }

可以看到for循环patches中的每一个patch并调用patch方法。我们接着进入patch方法。

1 protected booleanpatch(Context context, Patch patch) {
2         if (!patchManipulate.verifyPatch(context, patch)) {
3             robustCallBack.logNotify("verifyPatch failure, patch info:" + "id = " + patch.getName() + ",md5 = " + patch.getMd5(), "class:PatchExecutor method:patch line:107");
4             return false;
5 }
6 
7         DexClassLoader classLoader = newDexClassLoader(patch.getTempPath(), context.getCacheDir().getAbsolutePath(),
8                 null, PatchExecutor.class.getClassLoader());
9 patch.delete(patch.getTempPath());
10 
11 Class patchClass, oldClass;
12 
13 Class patchsInfoClass;
14         PatchesInfo patchesInfo = null;
15         try{
16             Log.d("robust", "PatchsInfoImpl name:" +patch.getPatchesInfoImplClassFullName());
17             patchsInfoClass =classLoader.loadClass(patch.getPatchesInfoImplClassFullName());
18             patchesInfo =(PatchesInfo) patchsInfoClass.newInstance();
19             Log.d("robust", "PatchsInfoImpl ok");
20         } catch(Throwable t) {
21             robustCallBack.exceptionNotify(t, "class:PatchExecutor method:patch line:108");
22             Log.e("robust", "PatchsInfoImpl failed,cause of" +t.toString());
23 t.printStackTrace();
24 }
25 
26         if (patchesInfo == null) {
27             robustCallBack.logNotify("patchesInfo is null, patch info:" + "id = " + patch.getName() + ",md5 = " + patch.getMd5(), "class:PatchExecutor method:patch line:114");
28             return false;
29 }
30 
31         //classes need to patch
32         List<PatchedClassInfo> patchedClasses =patchesInfo.getPatchedClassesInfo();
33         if (null == patchedClasses ||patchedClasses.isEmpty()) {
34             robustCallBack.logNotify("patchedClasses is null or empty, patch info:" + "id = " + patch.getName() + ",md5 = " + patch.getMd5(), "class:PatchExecutor method:patch line:122");
35             return false;
36 }
37 
38         for(PatchedClassInfo patchedClassInfo : patchedClasses) {
39             String patchedClassName =patchedClassInfo.patchedClassName;
40             String patchClassName =patchedClassInfo.patchClassName;
41             if (TextUtils.isEmpty(patchedClassName) ||TextUtils.isEmpty(patchClassName)) {
42                 robustCallBack.logNotify("patchedClasses or patchClassName is empty, patch info:" + "id = " + patch.getName() + ",md5 = " + patch.getMd5(), "class:PatchExecutor method:patch line:131");
43                 continue;
44 }
45             Log.d("robust", "current path:" +patchedClassName);
46             try{
47                 oldClass =classLoader.loadClass(patchedClassName.trim());
48                 Field[] fields =oldClass.getDeclaredFields();
49                 Log.d("robust", "oldClass :" + oldClass + "     fields " +fields.length);
50                 Field changeQuickRedirectField = null;
51                 for(Field field : fields) {
52                     if (TextUtils.equals(field.getType().getCanonicalName(), ChangeQuickRedirect.class.getCanonicalName()) &&TextUtils.equals(field.getDeclaringClass().getCanonicalName(), oldClass.getCanonicalName())) {
53                         changeQuickRedirectField =field;
54                         break;
55 }
56 }
57                 if (changeQuickRedirectField == null) {
58                     robustCallBack.logNotify("changeQuickRedirectField  is null, patch info:" + "id = " + patch.getName() + ",md5 = " + patch.getMd5(), "class:PatchExecutor method:patch line:147");
59                     Log.d("robust", "current path:" + patchedClassName + " something wrong !! can  not find:ChangeQuickRedirect in" +patchClassName);
60                     continue;
61 }
62                 Log.d("robust", "current path:" + patchedClassName + " find:ChangeQuickRedirect " +patchClassName);
63                 try{
64                     patchClass =classLoader.loadClass(patchClassName);
65                     Object patchObject =patchClass.newInstance();
66                     changeQuickRedirectField.setAccessible(true);
67                     changeQuickRedirectField.set(null, patchObject);
68                     Log.d("robust", "changeQuickRedirectField set sucess " +patchClassName);
69                 } catch(Throwable t) {
70                     Log.e("robust", "patch failed! ");
71 t.printStackTrace();
72                     robustCallBack.exceptionNotify(t, "class:PatchExecutor method:patch line:163");
73 }
74             } catch(Throwable t) {
75                 Log.e("robust", "patch failed! ");
76 t.printStackTrace();
77                 robustCallBack.exceptionNotify(t, "class:PatchExecutor method:patch line:169");
78 }
79 }
80         Log.d("robust", "patch finished ");
81         return true;
82     }

可以看到我们的类加载器在这里加载了我们的patch,我们接下来可以看到classloader加载了我们的PatchInfoImpl类。在这个类中继承了Robust的PatchInfo接口,这里只有一个方法

1 public interfacePatchesInfo {
2     List<PatchedClassInfo>getPatchedClassesInfo();
3 }  

他拉取了我们需要修改的类的信息。

这里面的PatchedClassInfo中保存了两个类的信息,一个是我们需要修改的类PatchedClass和修改他的类PatchClass。

第39,40行Robust拿到了这两个类的名字。

第48行通过反射获取了我们需要修改的类的所有field

接下来是一个for循环获取到我们注入代码中的静态ChangeQuickRedirect对象。

获取到对象后我们看第64行他加载了我们PatchClass的类

接下来的65,66,67三行,我们可以看到他通过反射将我们PatchedClass即oldClass中的changeQuickRedirect字段赋值为我们的PatchClass。至于这个PatchClass是什么。我们接下来说。

到目前为止,我们可以看到,插桩后的逻辑已经说完了,不得不说Robust的原理还是比较通俗易懂的。我们接下来回答前面的两个剩余问题,PatchInfoImpl和PatchClass在哪里。我们顺着我们的Patch.jar去寻找。反编译后得到如下列表。

美团热修复Robust-源码篇第1张

找到了我们的PatchesInfoImpl,而我们的PatchClass就是RobustActivityPatchControl了

我们先来看一看PatchesInfoImpl做了什么

1 importcom.meituan.robust.PatchedClassInfo;
2 importcom.meituan.robust.PatchesInfo;
3 importjava.util.ArrayList;
4 importjava.util.List;
5 
6 public classPatchesInfoImpl
7   implementsPatchesInfo
8 {
9   publicList getPatchedClassesInfo()
10 {
11     ArrayList localArrayList = newArrayList();
12     localArrayList.add(new PatchedClassInfo("com.example.tyr.testrobust.RobustActivity", "com.example.tyr.testrobust.RobustActivityPatchControl"));
13     com.meituan.robust.utils.EnhancedRobustUtils.isThrowable = false;
14     returnlocalArrayList;
15 }
16 }

可以看到他把我们的patchedClass和patchClass加入了list中,也就是上面返回的信息。

我们接着看我们注入的这个patchClass中的方法

1 public classRobustActivityPatchControl
2   implementsChangeQuickRedirect
3 {
4   public static final String MATCH_ALL_PARAMETER = "(\w*\.)*\w*";
5   private static final Map<Object, Object> keyToValueRelation = newWeakHashMap();
6 
7   private staticObject fixObj(Object paramObject)
8 {
9     Object localObject =paramObject;
10     if ((paramObject instanceofByte))
11       if (((Byte)paramObject).byteValue() == 0)
12         breaklabel32;
13     label32: for (boolean bool = true; ; bool = false)
14 {
15       localObject = newBoolean(bool);
16       returnlocalObject;
17 }
18 }
19 
20   publicObject accessDispatch(String paramString, Object[] paramArrayOfObject)
21 {
22 label134: 
23     while (true)
24       try
25 {
26         if (!paramString.split(":")[2].equals("false"))
27           continue;
28         if (keyToValueRelation.get(paramArrayOfObject[(paramArrayOfObject.length - 1)]) != null)
29           continue;
30         RobustActivityPatch localRobustActivityPatch = new RobustActivityPatch(paramArrayOfObject[(paramArrayOfObject.length - 1)]);
31         keyToValueRelation.put(paramArrayOfObject[(paramArrayOfObject.length - 1)], null);
32         breaklabel134;
33         if (!"12".equals(paramString.split(":")[3]))
34           break;
35         localRobustActivityPatch.onCreate((Bundle)paramArrayOfObject[0]);
36         return null;
37         localRobustActivityPatch = (RobustActivityPatch)keyToValueRelation.get(paramArrayOfObject[(paramArrayOfObject.length - 1)]);
38         breaklabel134;
39         localRobustActivityPatch = new RobustActivityPatch(null);
40         continue;
41 }
42       catch(Throwable paramString)
43 {
44 paramString.printStackTrace();
45         return null;
46 }
47     return null;
48 }
49 
50   publicObject getRealParameter(Object paramObject)
51 {
52     Object localObject =paramObject;
53     if ((paramObject instanceofRobustActivity))
54       localObject = newRobustActivityPatch(paramObject);
55     returnlocalObject;
56 }
57 
58   public booleanisSupport(String paramString, Object[] paramArrayOfObject)
59 {
60     paramString = paramString.split(":")[3];
61     return ":12:".contains(":" + paramString + ":");
62 }
63 }

我们可以看到它实现了Robust的ChangeQuickRedirect接口,并实现了他们的两个方法accessDispatch和isSupport的两个方法,也就是PatchProxy中调用的这两个方法。

先说isSupport方法

这里的isSupport方法是混淆后的,我们可以看到在PatchProxy类中,他传入了classMethod的名字和这个方法所需要的参数。校验后进行判断。

在PatchProxy中他传入了的classMethod格式为className:methodName:isStatic:methodNumber。这里只校验了方法的number。这里是在accessDispatch中传入,目测插桩后的代码有所改动。

继续说accessPatch方法。

第26行校验了是否为静态方法。将参数数组传给了RobustActivityPatch这个类,并调用了它的onCreat方法,莫名的熟悉感,这个就是我们标注为Modify标签的那个类。

我们接下来看一看RobustActivityPatch这个类

1 importandroid.os.Bundle;
2 importandroid.support.v7.app.c;
3 importandroid.view.View;
4 importandroid.widget.TextView;
5 importcom.meituan.robust.utils.EnhancedRobustUtils;
6 
7 public classRobustActivityPatch
8 {
9 RobustActivity originClass;
10 
11   publicRobustActivityPatch(Object paramObject)
12 {
13     this.originClass =((RobustActivity)paramObject);
14 }
15 
16   public static voidstaticRobustonCreate(RobustActivityPatch paramRobustActivityPatch, RobustActivity paramRobustActivity, Bundle paramBundle)
17 {
18 RobustActivityPatchRobustAssist.staticRobustonCreate(paramRobustActivityPatch, paramRobustActivity, paramBundle);
19 }
20 
21   publicObject[] getRealParameter(Object[] paramArrayOfObject)
22 {
23     if ((paramArrayOfObject == null) || (paramArrayOfObject.length < 1))
24       returnparamArrayOfObject;
25     Object[] arrayOfObject = newObject[paramArrayOfObject.length];
26     int i = 0;
27     if (i <paramArrayOfObject.length)
28 {
29       if ((paramArrayOfObject[i] instanceofObject[]))
30         arrayOfObject[i] =getRealParameter((Object[])paramArrayOfObject[i]);
31       while (true)
32 {
33         i += 1;
34         break;
35         if (paramArrayOfObject[i] == this)
36 {
37           arrayOfObject[i] = this.originClass;
38           continue;
39 }
40         arrayOfObject[i] =paramArrayOfObject[i];
41 }
42 }
43     returnarrayOfObject;
44 }
45 
46   protected voidonCreate(Bundle paramBundle)
47 {
48     staticRobustonCreate(this, this.originClass, paramBundle);
49     EnhancedRobustUtils.invokeReflectMethod("setContentView", ((RobustActivityPatch)this).originClass, getRealParameter(new Object[] { new Integer(2131296284) }), new Class[] { Integer.TYPE }, c.class);
50     paramBundle = (View)EnhancedRobustUtils.invokeReflectMethod("findViewById", ((RobustActivityPatch)this).originClass, getRealParameter(new Object[] { new Integer(2131165307) }), new Class[] { Integer.TYPE }, c.class);
51     if (paramBundle == this);
52     for (paramBundle = ((RobustActivityPatch)paramBundle).originClass; ; paramBundle =(TextView)paramBundle)
53 {
54       String str = (String)EnhancedRobustUtils.invokeReflectMethod("RobustPublicgetString", new RobustActivityInLinePatch(getRealParameter(new Object[] { this })[0]), getRealParameter(new Object[0]), null, null);
55       Object localObject =paramBundle;
56       if (paramBundle == this)
57         localObject =((RobustActivityPatch)paramBundle).originClass;
58       EnhancedRobustUtils.invokeReflectMethod("setText", localObject, getRealParameter(new Object[] { str }), new Class[] { CharSequence.class }, TextView.class);
59       return;
60 }
61 }
62 }

看到onCreate方法,第48行,这里我们可以看到他特殊处理了一下我们在RobustActivity的onCreate方法,感觉有点怪怪的,这里似乎是又执行了一边RobustActivity的OnCreate方法,而不是super.onCreate。

ps:看了看美团官方关于super的解析,似乎是这样的,他通过调用RobustActivity的OnCreate,将class文件中的invokevirtual指令替换为invokesuper指令,从而达到super的效果,这里面还有个问题,如果这样调用会出现这样的问题

Caused by: java.lang.NoSuchMethodError: No super method thisIsSuper()V in class Lcom/meituan/sample/TestSuperClass; or its super classes (declaration of 'com.meituan.sample.TestSuperClass' appears in /data/app/com.meituan.robust.sample-3/base.apk)

Robust的解决方案是使这个类也继承RobustActivity的父类,我们可以看到RobustActivityPatchRobustAssist类果然继承了一个类,但是由于混淆我们看到的是他继承了一个c的类,猜测它应该就是RobustActivity的父类AppCompatActivity。

验证一下打印dex文件

美团热修复Robust-源码篇第2张

看到invoke-super指令,现在可以确定了

再看一下RobustActivityPatchRobustAssist的父类,这绝对就是android.support.v7.app.AppcompatActivity了。

美团热修复Robust-源码篇第3张

然后我们可以看到他执行了setContentView,findViewById这里传入的两串数字便是我们的布局和空间在R类的数字。

然后我们可以看到它执行到了我们修改代码的地方。

第54行它调用了RobustPublicgetString方法,又是莫名的熟悉感,,

进入RobustActivityInLinePatch看一看。

1 public classRobustActivityInLinePatch
2 {
3 RobustActivity originClass;
4 
5   publicRobustActivityInLinePatch(Object paramObject)
6 {
7     this.originClass =((RobustActivity)paramObject);
8 }
9 
10   privateString getString()
11 {
12     return "hello robust";
13 }
14 
15   publicString RobustPublicgetString()
16 {
17     returngetString();
18 }
19 
20   publicObject[] getRealParameter(Object[] paramArrayOfObject)
21 {
22     if ((paramArrayOfObject == null) || (paramArrayOfObject.length < 1))
23       returnparamArrayOfObject;
24     Object[] arrayOfObject = newObject[paramArrayOfObject.length];
25     int i = 0;
26     if (i <paramArrayOfObject.length)
27 {
28       if ((paramArrayOfObject[i] instanceofObject[]))
29         arrayOfObject[i] =getRealParameter((Object[])paramArrayOfObject[i]);
30       while (true)
31 {
32         i += 1;
33         break;
34         if (paramArrayOfObject[i] == this)
35 {
36           arrayOfObject[i] = this.originClass;
37           continue;
38 }
39         arrayOfObject[i] =paramArrayOfObject[i];
40 }
41 }
42     returnarrayOfObject;
43 }
44 }

可以看到我们传入的getString方法出现在了这里。

二、总结

到目前为止,Robust的逻辑算是走通了。

目前为止,我认为Robust的核心应该算是它自动插桩的那一部分,目前暂时不涉及了,下一篇将会了解一下热修复背后的动态加载。

参考资料:

Android中热修复框架Robust原理解析+并将框架代码从"闭源"变成"开源"(上篇)

Android热更新方案Robust

免责声明:文章转载自《美团热修复Robust-源码篇》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇yum 程序包管理简介Linux内存管理(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

CentOS安装mysql源码包

1.# cd /usr/local/src 2.上传mysql.tar.gz文件 3.# tar -zxvf mysql-5.6.36-linux-glibc2.5-x86_64.tar.gz 4.# mv mysql-5.6.36-linux-glibc2.5-x86_64.tar /usr/local/mysql 5.# useradd -s /sbi...

Spring Boot配置文件详解:自定义属性、随机数、多环境配置

自定义属性与加载 我们在使用Spring Boot的时候,通常也需要定义一些自己使用的属性,我们可以如下方式直接定义: application-dev.yml 1 com.didispace.blog: 2 3 name: 程序猿DD 4 5 title: Spring Boot教程 6 7 desc: ${com.didispace.blog.na...

Java高并发,如何解决,什么方式解决

  对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题,但话又说回来了,既然逃避不掉, 那我们就坦然面对吧~今天就让我们一起来研究一下常见的并发和同步吧。   为了更好的理解并发和同步,我们需要先明白两个重要的概念:同步和异步    1、同步和异步的区别和联系    所谓同步,可以...

SpringXML方式配置bean的集合注入:list,map,properties

新建一个bean,设置相应的集合属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 publicclassCollections {     privateSet<String> sets;     privateList<String> l...

函数对象与仿函数(function object and functor)

part 1. 仿函数在STL组件中的关系   如下图:   # 仿函数配合算法完成不同的策略变化。   # 适配器套接仿函数。 part 2. 仿函数介绍   传递给算法的“函数型实参”不一定得是函数,可以是行为类似函数的对象。这种对象称为函数对象(function object),或称为仿函数(functor)。——《STL标准库(第2版)》 P23...

Java若不为空则取其值的lambda表达式

  原本的写法是: Map<String, Object> map = new HashMap<>(); String text = ""; if(map.get("text")!=null){ text = map.get("text").toString().trim(); } System.out.prin...