ATL WTL 实现分析(五)

CDialogImpl

对话框本质上是布局和行为受约束的窗口。最原始的模态对话框API是:

1:  WINUSERAPI
2:  INT_PTR
3:  WINAPI
4:  DialogBoxParamW(
5:      __in_opt HINSTANCE hInstance,     //applicaton instance
6:      __in LPCWSTR lpTemplateName,      //IDD : dialog template resource identifies
7:      __in_opt HWND hWndParent,         //hWndParent
8:      __in_opt DLGPROC lpDialogFunc,    //WndProc   ::StartDialogProc
9:      __in LPARAM dwInitParam);         //initialization value

非模态对话框的唯一区别在于返回值:

1:  WINUSERAPI
2:  HWND
3:  WINAPI
4:  CreateDialogIndirectParamW 。。。

对话框的Win32API编程步骤,首先用资源编辑器布局窗口,然后写一个DlgProc,组后创建(模态或非模态)。ATL对对话框的封装和Window类似,类图参考分析一,在实现DlgProc时,同样用到了两级跳转以及thunk,从StartDiallgProc到实际的ProcessWindowMessage跳转。而窗口的布局则使用资源描述符IDD所预先布置好的位置图来得到。

CSimpleDlg是一个简化版本的对话框,用以简单的弹出式对话框。

 

DataExchange和验证

对话框使用时还是比较复杂的,主要原因有将数据写给对话框所包含的子控件,对话框从子控件读取数据。

模态数据交互流程如下:

1、创建继承自CDialogImpl的类实例;

2、将数据拷贝进对话框类的数据成员;

3、调用DoModal。

4、在WM_INITDIALOG时将数据传递给子控件;

5、当对话框处理OK键消息时,对子控件获得的数据进行验证;

6、当数据有效,把数据拷贝到对话框的数据成员,结束对话框;

7、应用从对话框DoModal返回获得IDOK,并从中把数据返回。

非模态对话框的交互流程类似,不同在于第6、7步:

6、当数据有效,并把数据拷贝到对话框的数据成员,应用接受到通知并从对话框数据成员获取这些数据;

7、应用在收到通知后接收数据,拷贝到应用自己的数据成员中。

不同在于,模态时对数据的验证工作室对话框自己做,非模态则将接收数据的消息传给了应用,由应用进行处理。消息处理的位置不同。

ATL没有提供类似MFC中DDX、DDV的功能,不过很容易模仿实现;而在WTL中随机对这一功能进行了补充。

 

Windows Control Wrappers

Child Window Management

操作一个子控件时,例如设定edit control的文本显示或禁用OK按钮等,我们使用到的函数都是来自CWindow,在CWindow中提供了一系列的helper function来操作子控件:

 1:  class CWindow
 2:  {
 3:  public:
 4:      ...
 5:      // Dialog-Box Item Functions
 6:  
 7:      BOOL CheckDlgButton(int nIDButton, UINT nCheck) throw()
 8:      {
 9:          ATLASSERT(::IsWindow(m_hWnd));
10:          return ::CheckDlgButton(m_hWnd, nIDButton, nCheck);
11:      }
12:   
13:      BOOL CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton) throw()
14:      {
15:          ATLASSERT(::IsWindow(m_hWnd));
16:          return ::CheckRadioButton(m_hWnd, nIDFirstButton, nIDLastButton, nIDCheckButton);
17:      }
18:   
19:      int DlgDirList(_Inout_z_ LPTSTR lpPathSpec, _In_ int nIDListBox, _In_ int nIDStaticPath, _In_ UINT nFileType) throw()
20:      {
21:          ATLASSERT(::IsWindow(m_hWnd));
22:          return ::DlgDirList(m_hWnd, lpPathSpec, nIDListBox, nIDStaticPath, nFileType);
23:      }
24:      ...
25:      BOOL SetDlgItemText(int nID, LPCTSTR lpszString) throw()
26:      {
27:          ATLASSERT(::IsWindow(m_hWnd));
28:          return ::SetDlgItemText(m_hWnd, nID, lpszString);
29:      }
30:  };

这种实现效率上不高,CWindow是一个庞大的类,包含了很多的help functions。如,每次传入一个子控件ID,windows将会查找以得到HWND,然后再调用实际的处理函数,SetDlgItemText的【API】实现“应该”如下:

1:  BOOL SetDlgItemText(HWND hwndParent, int nID, LPCTSTR lpszString) 
2:  {
3:      HWND hwndChild = ::GetDlgItem(hwndParent, nID);
4:      if (!hwndChild) return FALSE;
5:      return ::SetWindowText(HWND, lpszString);
6:  }

也即,首先根据子控件ID找到HWND, 然后调用实际的窗口方法。所以,在CWindow中实现的这个help function并不高效,每次调用都有一个查找的步骤。更好的方法是在使用时首先缓存每个控件的HWND,然后调用时就省去这个查找的步骤。使用时如下:

 1:  LRESULT CStringDlg :: OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
 2:  {
 3:      ::CenterWindow();
 4:   
 5:      //cache the HWNDs
 6:      m_edit.Attach(GetDlgItem(IDC_STRING));
 7:      m_ok.Attach(GetDlgItem(IDOK));
 8:   
 9:      //then we can use it like this
10:      m_edit.SetWindowText(m_sz);
11:      ...
12:      return 1;
13:  }
14:   
15:  LRESULT CStringDlg :: OnOK(WORD, UINT, HWND, BOOL&)
16:  {
17:      m_edit.GetWindowText(m_sz, lengthof(m_sz));
18:   
19:      ::EndDialog(IDOK);
20:      return 0;
21:  }

在OnInitDialog中绑定HWNDs,然后在其他方法中就可以直接调用了。【当然,以上方法需要在类中具体实现,实现很简单,就是直接调用win32API了】

 

A Better Class of Wrappers

带edit control的对话框无论怎么写,实现起来都很简单。而当使用listbox等控件时,就不只是简单调用::SetWindowText了。操作listbox就要用到Windows messages了。例如填充一个listbox并选中的代码如下:

 1:  LRESULT CStringListDlg :: OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
 2:  {
 3:      ::CenterWindow();
 4:   
 5:      //cache list box HWND
 6:      m_lb.Attach(GetDlgItem(IDC_LIST));
 7:   
 8:      //fullfill the listbox
 9:      m_lb.SendMessage(LB_ADDSTRING, 0, (LPARAM)__T("Hello, ATL"));
10:      m_lb.SendMessage(LB_ADDSTRING, 0, (LPARAM)__T("Ain't ATL cool?"));
11:      m_lb.SendMessage(LB_ADDSTRING, 0, (LPARAM)__T("ATL foooooo"));
12:   
13:      //Set initial selection
14:      int n = m_lb.SendMessage(LB_FINDSTRING, 0, (LPARAM)m_sz);
15:      if (n == LB_ERR) n = 0;
16:      m_lb.SendMessage(LB_SETCURSEL, n);
17:   
18:      return 1;
19:  }

ATL中CWindows确实提供了很多wrapper函数,但是对于windows内置的控件却并没有提供,如listbox等。然而,ATL非官方的提供了一些这样的类,在atlcontrols.h中。例如CListBox,其实现如下:

 1:  //取自WTL8.0实现
 2:  template <class TBase>
 3:  class CListBoxT : public TBase
 4:  {
 5:  public:
 6:      // Constructors
 7:      CListBoxT(HWND hWnd = NULL) : TBase(hWnd)
 8:      { }
 9:   
10:      CListBoxT< TBase >& operator =(HWND hWnd)
11:      {
12:          m_hWnd = hWnd;
13:          return *this;
14:      }
15:   
16:      HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
17:          DWORD dwStyle = 0, DWORD dwExStyle = 0,
18:          ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
19:      {
20:          return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);
21:      }
22:   
23:      // Attributes
24:      static LPCTSTR GetWndClassName()
25:      {
26:          return _T("LISTBOX");
27:      }
28:   
29:      // for entire listbox
30:      int GetCount() const
31:      {
32:          ATLASSERT(::IsWindow(m_hWnd));
33:          return (int)::SendMessage(m_hWnd, LB_GETCOUNT, 0, 0L);
34:      }
35:   
36:  #ifndef _WIN32_WCE
37:      int SetCount(int cItems)
38:      {
39:          ATLASSERT(::IsWindow(m_hWnd));
40:          ATLASSERT(((GetStyle() & LBS_NODATA) != 0) && ((GetStyle() & LBS_HASSTRINGS) == 0));
41:          return (int)::SendMessage(m_hWnd, LB_SETCOUNT, cItems, 0L);
42:      }

这样前面的listbox填充并选中的代码可以改写如下:

 1:  LRESULT CStringListDlg :: OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
 2:  {
 3:      ::CenterWindow();
 4:   
 5:      //cache list box HWND
 6:      m_lb.Attach(GetDlgItem(IDC_LIST));
 7:   
 8:      //fullfill the listbox
 9:      m_lb.AddString(__T("Hello,ATL"));
10:      //m_lb.SendMessage(LB_ADDSTRING, 0, (LPARAM)__T("Hello, ATL"));
11:      ...
12:      //Set initial selection
13:      int n = m_lb.FindString(0, m_sz);
14:      if(n ==LB_ERR)  n = 0;
15:      m_lb.SetCurSel(n);
16:   
17:      return 1;
18:  }

而WTL则对这部分进行了完善,实现了所有的控件类。

 

CContainedWindow

CContainedWindow可以使父窗口处理其子窗口传递过来的消息,从而可以将消息处理函数集中放到父窗口中。父窗口即可以主动创建这样的子窗口,也可以使用已创建好的然后将其子类化(subclassing)实现父窗口处理子窗口消息的方法是使用Altmessage maps。每个CContainedWindow都包含了一个message map ID,由此实现将消息转移到父窗口的消息映射中。

 1:  template <class TBase /* = CWindow */, class TWinTraits /* = CControlWinTraits */>
 2:  class CContainedWindowT : public TBase
 3:  {
 4:  public:
 5:      CWndProcThunk m_thunk;
 6:      LPCTSTR m_lpszClassName;
 7:      WNDPROC m_pfnSuperWindowProc;
 8:      CMessageMap* m_pObject;
 9:      DWORD m_dwMsgMapID;
10:      const _ATL_MSG* m_pCurrentMsg;
11:   
12:      // If you use this constructor you must supply
13:      // the Window Class Name, Object* and Message Map ID
14:      // Later to the Create call
15:      CContainedWindowT() : m_pCurrentMsg(NULL)
16:      { }
17:   
18:      CContainedWindowT(LPTSTR lpszClassName, CMessageMap* pObject, DWORD dwMsgMapID = 0)
19:          : m_lpszClassName(lpszClassName),
20:          m_pfnSuperWindowProc(::DefWindowProc),
21:          m_pObject(pObject), m_dwMsgMapID(dwMsgMapID),
22:          m_pCurrentMsg(NULL)
23:      { }
24:      ...

CContainedWindow即非继承自CWindowImpl也没有继承CMessageMap,所以CContainedWindow对象没有消息映射,而是通过WindowProc静态成员函数传递给了父窗口,message map ID是由构造函数活着Create函数提供。CContainedWindow提供了很多构造函数和Create方法。举个应用例子,我们是一个edit control只接收字母,实现如下:

 1:  class CMainWindow :
 2:      public CWindowImpl<CMainWindow, CWindow, CMainWindowTraits>
 3:  {
 4:  public:
 5:      ...
 6:      BEGIN_MESSAGE_MAP(CMainWindow)
 7:          ...
 8:          //Handle the child edit controls' messages
 9:          ALT_MSG_MAP(2013)    //message map ID
10:              MESSAGE_HANDLER(WM_CHAR, OnEditChar)
11:      END_MSG_MAP()
12:   
13:      LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
14:      {
15:          //Create the contained window, routing its message to us
16:          if(m_edit.Create("edit", this, 2013, m_hWnd, &CWindow::rcDefault)) {
17:              return 0;
18:          }
19:    /* 第一个参数是控件名,第二个参数是CMessageMap的指针,这样子窗口的的消息才能找到消息处理映射,第三个是消息映射ID, 第四个是HWND。*/
20:          return -1;
21:      }
22:   
23:      //child edit message handler
24:       LRESULT OnEditChar(UINT, WPARAM, LPARAM, BOOL &bHandled)
25:      {
26:          if (isalpha((TCHAR)wparam)) bHandled = FALSE;
27:          else return 0;
28:      }
29:  private:
30:      CContainedWindow m_edit;
31:  };

注意在OnEditChar中,将是字母的输入设为消息未处理,从而可以让CContainedWindow对应控件的默认消息处理procedure来进行响应;而对于非字母输入,则bHandled为TRUE,消息不再传递下去,表现出来就是该字符被忽略。

Subclassing Contained Windows

如果是包含一个已经创建的子控件,就需要用到子类化功能。之前已经讲到了超类化实现了窗口类的继承,而子类化功能则更加温和并且更加常用。子类化不需要完全创建一个新类,而只是对原有窗口的一些消息进行hack,功能表现更像一个filter。子类化的实现是通过创建一个特定的窗口对象,然后替换掉其窗口过程SetWindowLong(GWL_WNDPROC)。替换的窗口过程首先接收所有的消息,然后再决定是否让原有的窗口过程是否也处理。可以认为,超类化是一个类的特化,而子类化是一个对象实例的特化。子类化常用在窗口子控件上,如:

 1:  class CLetterDlg : public CDialogImpl<CLetterDlg>
 2:  {
 3:  public:
 4:      //Set the CMessageMap* and the message map ID
 5:      CLetterDlg(): m_edit(this, 2013) {}
 6:   
 7:      BEGIN_MESSAGE_MAP(CLetterDlg)
 8:          ...
 9:          ALT_MSG_MAP(2013)
10:              MESSAGE_HANDLER(WM_CHAR, OnEditChar)
11:      END_MSG_MAP()
12:   
13:      enum {IDD = IDD_LETTER_ONLY};
14:   
15:      LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
16:      {
17:          //Subclass the exiting child edit control
18:          m_edit.SubclassWindow(GetDlgItem(IDC_EDIT));
19:   
20:          return 1;
21:      }
22:      ...
23:   
24:  private:
25:      CContainedWindow m_edit;
26:   
27:  };

因为没有窗口创建的步骤,所以只能在构造函数中将CMessageMap指针和message map ID传递给CContainedWindow对象(CContainedWindow就是一个辅助类),这样在WM_INITDIALOG中只需要获得窗口句柄即可了。那么子类化过程是如何实现的?

 1:  BOOL SubclassWindow(HWND hWnd)
 2:  {
 3:      BOOL result;
 4:      ATLASSUME(m_hWnd == NULL);
 5:      ATLASSERT(::IsWindow(hWnd));
 6:   
 7:      result = m_thunk.Init(WindowProc, this);
 8:      if (result == FALSE) 
 9:      {
10:          return result;
11:      }
12:   
13:      WNDPROC pProc = m_thunk.GetWNDPROC();
14:      WNDPROC pfnWndProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
15:      if(pfnWndProc == NULL)
16:          return FALSE;
17:      m_pfnSuperWindowProc = pfnWnd
Proc;
18:      m_hWnd = hWdnd;
19:      return TRUE;
20:  }

首先使用thunk技术获得当前父窗口的WndProc保存其中,然后使用SetWindowLongPtr把父窗口的WndProc植入,这样子窗口的消息首先进入父窗口的消息映射中。同时还cache了子窗口的WndProc,可以用来处理父窗口未处理的消息。

Containing the Windows Control Wrappers

在CContainedWindow中默认把CWindow当作了基类,当然不必非这样做。同样可以对ATL Windows control wrapper classes作为基类来创建contained window。如:

1:  CContainedWindowT<ATLControls::CEdit> m_edit;

这对于使用Create来创建子类窗口时比较方便,若不这样,就需要在Create中传入window class的字符串名。而是用模板类,则可以自动获取,通过基类(如CEdit)所提供的GetWndClassName成员函数来获得,其实现如下:

1:  static LPCTSTR GetWndClassName()
2:  {
3:      return _T("LISTBOX");
4:  }

这样,创建子类化控件时,无需那么多参数了:

1:  LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
2:  {
3:      //Create the contained window, routing its message to us
4:      if(m_edit.Create( this, 2013, m_hWnd, &CWindow::rcDefault)) {
5:          return 0;
6:      }
7:   
8:      return -1;
9:  }

 

后记:基本是《ATL技术内幕》第九章的读书笔记(翻译)。可以看到ATL所提供的窗口框架基本已经成型,WTL只是后续做了一些扩展和完善。通过阅读这一部分,对中间的消息流算是比较清楚了,还有各种类为何如此设计,整个过程有了比较清晰的理解。由于本人对Windows窗口编程本身的知识掌握的都不甚完善,所以只是尽最大努力写出自己的理解和见解。后续若有空,再进行WTL中代码的分析,以及自定义控件等进行学习。

转载于:https://www.cnblogs.com/macrolee/archive/2013/04/22/3036609.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/433654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python的read函数_Python Pandas pandas.read_sql函数方法的使用

pandas.read_sql&#xff08;sql&#xff0c;con&#xff0c;index_col None&#xff0c;coerce_float True&#xff0c;params None&#xff0c;parse_dates None&#xff0c;columns None&#xff0c;chunksize None)源代码 将SQL查询或数据库表读入DataFrame。 此功能…

android 4.2以上版本永不休眠

android4.2以上版本比android4.2以前的版本framework层框架部分有了很大的改变&#xff0c;androd4.2以前版本framework层给上的接口函数与android4.2以上版本的接口函数也有了些变化&#xff0c;以前让机器永不休眠的方法用在android4.2以上版本已经不起作用了。现将android4.…

peoplesoft系统与sap_sap顾问或者peoplesoft顾问转行做自研系统顾问的为什么那么少?...

我就是从SAP实施转做互联网供应链电商开放平台自研产品的。说一点体会。不愿意转行&#xff0c;是有舒适区(个人认为税后年收入30万可成为舒适区标准)。国内资深顾问收入超过这个数比较容易&#xff0c;工作强度也比互联网行业低&#xff0c;能力达到一定程度后可以摸鱼更显“惬…

Mybatis笔记——Mybatis入门

&#xff08;一&#xff09;MyBatis快速入门 通过一个案例快速入门Mybatis框架案例&#xff1a;查询user表中所有数据1) 创建user表&#xff0c;添加数据2) 创建模块&#xff0c;导入坐标3) 编写MyBatis核心配置文件-->替换连接信息&#xff0c;解决硬编码问题4) 编写SQL映…

POJ-3693 Maximum repetition substring 后缀数组

题目链接&#xff1a;http://poj.org/problem?id3693 求字符串的重复次数最多的且字典序最小的字串。 很不错的题目。罗穗骞大牛论文的模板题&#xff0c;摘了Neo / Add ~0U>>1大牛的详细题解&#xff0c;如下&#xff1a; 首先求第一问最大重复数。从N的范围来看O(N^2)…

asterisk1.8 for mipsel mysql

在astersik上要使用mysql数据库的话&#xff0c;需要加载跟mysql相关的3个模块 res_config_mysql.so app_mysql.so cdr_mysql.so 但是这个三个模块一直加载不上&#xff0c;自己写了程序用dlopen依然加载不上&#xff0c;用dlerror函数将错误信息打印出来&#xff1a; fil…

uln2003驱动蜂鸣器_让蜂鸣器发声

预备知识&#xff1a;蜂鸣器介绍开发板原理图上面蜂鸣器的电路图是这样的&#xff0c;根据视频知道它是一个无源蜂鸣器。可以看到蜂鸣器的一端和电阻相连&#xff0c;另一端和引脚BZ相连&#xff0c;注意这个BZ并不是元气名称&#xff0c;有图可知&#xff0c;元气名称都是蓝色…

python nlp_Python NLP入门教程

本文简要介绍Python自然语言处理(NLP)&#xff0c;使用Python的NLTK库。NLTK是Python的自然语言处理工具包&#xff0c;在NLP领域中&#xff0c;最常使用的一个Python库。 什么是NLP&#xff1f; 简单来说&#xff0c;自然语言处理(NLP)就是开发能够理解人类语言的应用程序或服…

asterisk1.8 拨号方案 mysql存储(动态)

1. 在asterisk库中创建下表 DROP TABLE IF EXISTS extensions; CREATE TABLE extensions ( id int(11) NOT NULL auto_increment, context varchar(20) NOT NULL default , exten varchar(20) NOT NULL default , priority tinyint(4) NOT NULL default 0, …

执行异常ORA-00918: column ambiguously defined

oracle查询报错信息&#xff1a;执行异常ORA-00918: column ambiguously defined 该错误信息意为有一列定义模糊&#xff0c;不知道是哪张表的。 通常在字段名前面加上前缀就可以解决。比如column改成a.column 在使用其他方案提供的查询工具时&#xff0c;外围可能包裹了类似se…

cesium 隐藏entity_cesium entity创建各类实体

html>创建实体import url(../Build/Cesium/Widgets/widgets.css);html, body, #cesiumContainer {width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;}.cesium-viewer .cesium-widget-credits{ display:none }var myurl "http://mt1.google.cn/vt/…

python安装后cmd找不到_关于Python3.6环境中,virtualenv找不到命令的解决方法

今天收到一个网友的提问,说是,已经安装好了,Python3.6,系统环境变量也检查过,没有问题,在系统CMD命令行窗口,输入python可以返回当前安装的python环境的版本号,如下图python3.6 也测试了pip安装程序,也是正常的,然后用pip install virtualenv 安装虚拟环境,整个过程也没有报错,…

Mybatis占位符问题—ReflectionException: There is no getter for property named xxx

使用${}占位符时&#xff08;PS&#xff1a;一般都使用#{}&#xff0c;不建议使用${}&#xff09;&#xff0c;发现xml文件中的SQL语句使用了$后会报错&#xff1a;具体情况如下&#xff1a;解决方案如下&#xff1a;在参数前加上Param("id")注解

kamailio,asteisk,realtime 实现负载均衡(集群)完整配置笔记

环境 负载均衡数据库服务器 Centos-5.4 kamailio-3.1.4 mysql-5.0.77-4.el5_6.6 IP:192.168.1.30 asterisk服务器1 Centos-5.4 asterisk-1.8 IP:192.168.1.31 asterisk服务器2 Centos-5.4 asterisk-1.8 IP:192.168.1.32 目的 1&#xff1a;假设一个简易的asterisk 集…

maven依赖 spark sql_window环境运行spark-xgboost 8.1踩到的坑

在window 环境下使用spark - xgboost会出现一些问题&#xff0c;这里记录一下。环境&#xff1a;window 7 spark 2.31 xgboost 8.1 idea maven一.依赖以及代码数据集下载地址UCI Machine Learning Repository: Iris Data Set​archive.ics.uci.edupom依赖<!-- https://m…

linux文件目录的操作

1、查看目录信息 opendir readdir stat,lstat,fstat 将目录按字母排序,alphasort,scandir 一般的用法是将alphasort作为scandir的参数&#xff0c;比如 struct dirent **namelist; scandir("/",&namelist,0,alphasort); 2、复习了一遍popen,pclose 3、明…

python书籍排行榜前十名_学习Python,这些书你看过几本?

Python是一门简单&#xff0c;强大的计算机编程语言。Python书也是种类繁多&#xff0c;对于初学者选择合适的学习书籍是很有必要的。 1、Python编程&#xff1a;从入门到实践 全书分两部分&#xff1a;第一部分介绍用Python编程所必须了解的基本概念&#xff1b;第二部分将理论…

Spring 5的新特性

Spring Framework 5.0 是在 2017 年发布的一个主要版本&#xff0c;它带来了许多改进和新特性&#xff0c;相比于 Spring Framework 4.x&#xff0c;主要的升级包括&#xff1a; 基于 Java 8 的基线&#xff1a; Spring 5 需要 Java 8 或更高版本&#xff0c;这使得框架得以利用…

MyBatis笔记——配置文件完成增删改查

l 完成品牌数据的增删改查操作 要完成的功能列表清单&#xff1a;□ 查询 查询所有数据 查看详情 条件查询□ 添加□ 修改 修改全部字段 修改动态字段□ 删除 删除一个 批量删除准备环境&#xff1a; 数据库表tb_branddrop table if exists tb_brand;CREATE TABLE tb_brand(--…

linux新的API signalfd、timerfd、eventfd使用说明——eventfd

好久没更新了&#xff0c;今天看一下第三种新的fd&#xff1a;eventfd类似于管道的概念&#xff0c;可以实现线程间的事件通知&#xff0c;所不同的是eventfd的缓冲区大小是sizeof(uint64_t)也就是8字节&#xff0c;它是一个64位的计数器&#xff0c;写入递增计数器&#xff0c…