AEM SlingModel 概念中@postconstructor 的用途是什么?
What is the use of @postconstructor in AEM SlingModel concept?
我在 SlingModel
中使用时什么时候可以使用 @postconstructor
?
@Model(adaptables=SlingHttpServletRequest.class)
public class MyModelTest {
@Inject
private PrintWriter out;
@Inject
@Named("log")
private Logger logger;
@PostConstruct
protected void sayHelloTest() {
logger.info("hello world");
}
}
documentation 状态:
The @PostConstruct annotation can be used to add methods which are invoked upon completion of all injections:
在您提供的示例代码中,文档中提到的 "injections" 是您使用 @Inject
注释进行注释的字段。
当 Sling 模型被实例化时,这些字段将由 Sling "injected"。这意味着您不必自己设置这些字段,但 Sling 会处理它。这也称为 Dependency Injection.
回到您的问题:一旦所有这些字段都被 Sling 注入,将调用带有 @PostConstruct
注释的方法。通常,开发人员使用此方法来进一步初始化 Sling 模型。
整个过程是这样的:
- Sling 创建模型的新实例(例如
new MyModelTest()
)。
- Sling 注入您声明的所有依赖项(参见
@Inject
注释)。
- Sling 将调用带有
@PostConstruct
注释的方法。
@PostConstruct
注释基本上是构造函数的替代品。如果您为模型编写构造函数,您会注意到当调用构造函数时,所有带有 @Inject
注释的字段都尚未设置。如果您尝试对这些字段进行进一步的初始化,您将得到 NullPointerException
。
这就是引入 @PostConstruct
注释的原因。它允许您进行通常在构造函数中进行的进一步初始化。
补充说明
将依赖项注入字段称为 "field injection"。还有另一种方法可以通过构造函数注入这些依赖项。这叫做"constructor injection".
就个人而言,我喜欢在可能的情况下对 Sling 模型使用构造函数注入。使用构造函数注入有助于提高模型的不变性,有助于减少状态并提高可测试性。这是您通常应该在代码中努力实现的目标。
您的代码示例如下所示,带有构造函数注入:
@Model(adaptables=SlingHttpServletRequest.class)
public class MyModelTest {
private final Logger logger;
private final PrintWriter out;
@Inject
public MyModelTest(
@ScriptVariable @Named("log") final Logger logger,
@ScriptVariable @Named("out") final PrintWriter out
) {
this.logger = logger;
this.out = out;
}
}
现在可以设置 class 字段 logger
和 out
final
,这增加了不变性,因为它们无法再更改。
当您注入服务等以进行进一步初始化时,您也可以将结果存储在 final
class 字段中,而不是将服务引用本身作为 class 字段注入.这些引用 - 理论上 - 可能指向不存在的服务(服务可以在 OSGi 中来来去去)。例如:如果您有一个存储您需要的配置的服务,您可以只读取您感兴趣的配置值,然后将其存储在 class 字段中。
最后但同样重要的是,可测试性得到改进,因为您现在可以通过简单地调用 new MyModelTest([...])
并传递 logger
和 out
(new MyModelTest(mockLogger, mockOut)
) 的模拟来创建实例。如果您要使用字段注入,则必须使用 reflection,这通常不是您想在代码中执行的操作。虽然不得不说Sling项目包含了对使用字段注入的测试模型的支持。
我在 SlingModel
中使用时什么时候可以使用 @postconstructor
?
@Model(adaptables=SlingHttpServletRequest.class)
public class MyModelTest {
@Inject
private PrintWriter out;
@Inject
@Named("log")
private Logger logger;
@PostConstruct
protected void sayHelloTest() {
logger.info("hello world");
}
}
documentation 状态:
The @PostConstruct annotation can be used to add methods which are invoked upon completion of all injections:
在您提供的示例代码中,文档中提到的 "injections" 是您使用 @Inject
注释进行注释的字段。
当 Sling 模型被实例化时,这些字段将由 Sling "injected"。这意味着您不必自己设置这些字段,但 Sling 会处理它。这也称为 Dependency Injection.
回到您的问题:一旦所有这些字段都被 Sling 注入,将调用带有 @PostConstruct
注释的方法。通常,开发人员使用此方法来进一步初始化 Sling 模型。
整个过程是这样的:
- Sling 创建模型的新实例(例如
new MyModelTest()
)。 - Sling 注入您声明的所有依赖项(参见
@Inject
注释)。 - Sling 将调用带有
@PostConstruct
注释的方法。
@PostConstruct
注释基本上是构造函数的替代品。如果您为模型编写构造函数,您会注意到当调用构造函数时,所有带有 @Inject
注释的字段都尚未设置。如果您尝试对这些字段进行进一步的初始化,您将得到 NullPointerException
。
这就是引入 @PostConstruct
注释的原因。它允许您进行通常在构造函数中进行的进一步初始化。
补充说明
将依赖项注入字段称为 "field injection"。还有另一种方法可以通过构造函数注入这些依赖项。这叫做"constructor injection".
就个人而言,我喜欢在可能的情况下对 Sling 模型使用构造函数注入。使用构造函数注入有助于提高模型的不变性,有助于减少状态并提高可测试性。这是您通常应该在代码中努力实现的目标。
您的代码示例如下所示,带有构造函数注入:
@Model(adaptables=SlingHttpServletRequest.class)
public class MyModelTest {
private final Logger logger;
private final PrintWriter out;
@Inject
public MyModelTest(
@ScriptVariable @Named("log") final Logger logger,
@ScriptVariable @Named("out") final PrintWriter out
) {
this.logger = logger;
this.out = out;
}
}
现在可以设置 class 字段 logger
和 out
final
,这增加了不变性,因为它们无法再更改。
当您注入服务等以进行进一步初始化时,您也可以将结果存储在 final
class 字段中,而不是将服务引用本身作为 class 字段注入.这些引用 - 理论上 - 可能指向不存在的服务(服务可以在 OSGi 中来来去去)。例如:如果您有一个存储您需要的配置的服务,您可以只读取您感兴趣的配置值,然后将其存储在 class 字段中。
最后但同样重要的是,可测试性得到改进,因为您现在可以通过简单地调用 new MyModelTest([...])
并传递 logger
和 out
(new MyModelTest(mockLogger, mockOut)
) 的模拟来创建实例。如果您要使用字段注入,则必须使用 reflection,这通常不是您想在代码中执行的操作。虽然不得不说Sling项目包含了对使用字段注入的测试模型的支持。