直接开搞,接着上一章没说完的。之前XML配置文件形式的注入一方面是实现了对象的构造和对象的使用相分离的理念,另一方面还将对象之间的关系配置移到了配置文件中,这样在实现一个工具库的时候可以使用它的配置能力极大程度提升灵活性(可参考McLogQt)。而声明式注入则去掉了配置文件,直接在类的声明时就指定了需要获取哪一个bean,这里就直接对之前XML注入中的几个类加入几个声明代码即可。
- 首先在类R中加入
MC_COMPONENT("r")
,将此类声明为一个组件,并指定beanName为"r"。 - 同样的在类C中加入
MC_COMPONENT("c")
;然后加入一行新代码MC_AUTOWIRED("r", "r")
,第一个参数为类C中的属性,第二个参数为容器中已经存在的beanName,即类R中MC_COMPONENT("r")
宏指定的名字。如果容器中的beanName和对象中的属性名一样,比如现在这种情况,则可以直接使用MC_AUTOWIRED("r")声明即可。这里有一个遗憾是暂时只支持注入对象类型,即QList,QMap等容器无法注入。 - 这里为了表现信号槽,所以需要在C.cpp中引入头文件
#include <McIoc/ApplicationContext/McContainerGlobal.h>
,同时在静态代码块中加入MC_CONNECT("this", "signal_send()", "r", "slot_recv()");
- main中引入
#include <McIoc/ApplicationContext/impl/McAnnotationApplicationContext.h>
,并将auto appContext = McLocalPathApplicationContextPtr::create(R"(E:\Code\QtCreator\McIocBoot\IocTest\myspring.xml)");
改为auto appContext = McAnnotationApplicationContextPtr::create();
完整代码如下: C.h
#pragma once
#include <McIoc/McGlobal.h>
class R : public QObject
{
Q_OBJECT
MC_DECL_INIT(R)
MC_COMPONENT("r")
Q_PROPERTY(QString text READ text WRITE setText);
public:
Q_INVOKABLE R(){}
QString text() const noexcept;
void setText(const QString &val) noexcept;
public slots:
void slot_recv() noexcept;
private:
QString m_text;
};
MC_DECL_METATYPE(R);
class IA
{
public:
virtual ~IA() = default; //!< C++中超类析构函数必须是virtual
virtual void a() noexcept = 0;
};
MC_DECL_METATYPE(IA); //!< 这里必须使用该宏声明,否则无法从C转换到该接口。
class IB : public IA
{
MC_INTERFACES(IA); //!< 由于本接口有一个父接口,并且可能存在从IB转换到IA,所以这里需要使用这个宏保存父接口
public:
};
MC_DECL_METATYPE(IB);
class C : public QObject, public IB
{
Q_OBJECT
MC_DECL_INIT(C) //!< 这个宏主要用来实现一个类似于java静态代码块的功能。这里只是声明,真正实现在cpp中
//! 同理,由于C实现至IB接口,并且可能转换到IB,所以这里需要使用该宏。
//! 这里不需要额外指定QOBject,容器会自动指定。但如果C继承至其他类,比如QWidget,那么需要先使用MC_DECL_POINTER声明QWidget,再使用MC_TYPELIST(QWidget, IB),
//! 当然,如果不需要从C转换到QWidget,也就不需要额外声明QWidget
MC_INTERFACES(IB)
MC_COMPONENT("c")
Q_PROPERTY(QString text READ text WRITE setText) //!< 使用getter和setter形式
MC_AUTOWIRED("r") //!< 也可以使用MC_AUTOWIRED("r = r")
Q_PROPERTY(RPtr r MEMBER m_r) //!< 如果外界并不需要使用对象r,则可以直接使用MEMBER形式。具体请查阅QT官方文档
Q_PROPERTY(QList<QString> texts MEMBER m_texts)
Q_PROPERTY(QVector<RPtr> rs MEMBER m_rs)
typedef QMap<QString, QString> StringMap; //!< 由于QMap在Q_PROPERTY宏中有错误提示,所以这里先重定义一下
Q_PROPERTY(StringMap mtexts MEMBER m_mtexts)
typedef QHash<QString, RPtr> RHash;
Q_PROPERTY(RHash hrs MEMBER m_hrs)
public:
Q_INVOKABLE C(){}
void a() noexcept override;
QString text() const noexcept;
void setText(const QString &val) noexcept;
/*!
* \brief start
*
* 被MC_BEAN_START宏标记的函数将会在C被构造后,属性未被注入前调用,即m_r等所有属性都是默认值
*/
Q_INVOKABLE
MC_STARTED
void start() noexcept;
/*!
* \brief finished
*
* 当所有属性都注入完成后调用
*/
Q_INVOKABLE
MC_FINISHED
void finished() noexcept;
/*!
* \brief threadFinished
*
* 如果调用过本对象的moveToThread函数移动过生存线程,则移动之后调用此函数,否则不调用
*/
Q_INVOKABLE
MC_THREAD_MOVED
void threadFinished() noexcept;
signals:
void signal_send();
private:
QString m_text; //!< 普通字符串
RPtr m_r; //!< 对象
QList<QString> m_texts; //!< 字符串列表
QVector<RPtr> m_rs; //!< 对象数组
QMap<QString, QString> m_mtexts; //!< 字符串映射表
QHash<QString, RPtr> m_hrs; //!< 对象哈希表
};
MC_DECL_METATYPE(C);
C.cpp
#include "C.h"
#include <QThread>
#include <QDebug>
MC_INIT(R)
MC_REGISTER_BEAN_FACTORY(R);
MC_INIT_END
QString R::text() const noexcept
{
return m_text;
}
void R::setText(const QString &val) noexcept
{
m_text = val;
}
void R::slot_recv() noexcept
{
qDebug() << "r slot recv";
}
#include <McIoc/ApplicationContext/McContainerGlobal.h>
MC_INIT(C)
MC_REGISTER_BEAN_FACTORY(C);
MC_REGISTER_CONTAINER_CONVERTER(QList<QString>); //!< 容器需要额外注册,只需注册一次即可到处使用,此宏多次调用只生效一次
MC_REGISTER_LIST_CONVERTER(QVector<RPtr>); //!< 和MC_REGISTER_CONTAINER_CONVERTER效果一样
MC_REGISTER_MAP_CONVERTER(StringMap); //!< 重定义之后需要使用重定义之后的类型
MC_REGISTER_CONTAINER_CONVERTER(RHash); //!< 和MC_REGISTER_MAP_CONVERTER效果一样
MC_CONNECT("this", "signal_send()", "r", "slot_recv()");
//!< 可以做更多事情,此代码块中的功能将在main函数之前被调用,以后可能会在QCoreApplication构造时调用
//!< 所以建议其他正常操作都放在QCoreApplication构造后
MC_INIT_END
void C::a() noexcept
{
qDebug() << "m_text:" << m_text
<< "m_r:" << m_r << m_r->text()
<< "m_texts:" << m_texts
<< "m_rs:" << m_rs
<< "m_mtexts:" << m_mtexts
<< "m_hrs:" << m_hrs
<< "obj thread:" << thread()
<< "cur thread:" << QThread::currentThread();
emit signal_send();
}
QString C::text() const noexcept
{
return m_text;
}
void C::setText(const QString &val) noexcept
{
m_text = val;
}
void C::start() noexcept
{
qDebug() << "start";
}
void C::finished() noexcept
{
qDebug() << "finished";
}
void C::threadFinished() noexcept
{
qDebug() << "thread finished";
}
main.cpp
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <McIoc/ApplicationContext/impl/McLocalPathApplicationContext.h>
#include <McIoc/ApplicationContext/impl/McAnnotationApplicationContext.h>
#include "C.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// auto appContext = McLocalPathApplicationContextPtr::create(R"(E:\Code\QtCreator\McIocBoot\IocTest\myspring.xml)");
auto appContext = McAnnotationApplicationContextPtr::create();
QThread *t = new QThread(&a);
t->start();
qDebug() << "t:" << t;
auto ia = appContext->getBean<IA>("c", t);
ia->a();
return a.exec();
}
运行效果如下:
这里可以看出只有属性m_r有值,其他属性用声明式暂时无法注入。
其他
这种声明式注入目前在McBoot项目中有使用,该库会自动帮你构造满足某些条件的对象,具体将在讲解该项目时具体说明。