自从我进入CDI以来,我就探索了经典的Java EE使用。 然后,我将CDI用于我的一个宠物项目,以了解如何在Swing应用程序中使用它。 本文总结了到目前为止我从中学到的教训。
本文假定您对CDI有所了解; 如果没有,请阅读我以前有关CDI的文章(CDI概述第1部分和第2部分)或阅读文档。
在我最后一次尝试Swing(我是一名Web开发人员)时,我认为在Spring中配置大多数UI组件而不是手动编码参数可能会很好。 在此过程中,我使用了传统的XML配置方法,因为使用批注会将我的代码耦合到Spring JAR,这是我讨厌做的。 但是,使用CDI,我可以将代码绑定到标准API上:我可以更改实现。 这类似于开发一个servlet,在其中引用Servlet API,然后在所需的任何容器中运行它。
我以这种方式进行编码和编码,查看所制作的东西,进行重构,然后再进行更多编码,就像这是一个个人项目并且您没有截止日期一样。 然后,我意识到我可以吸取一些经验教训,以备将来使用。 在这里,它们没有特定的顺序。
正确的JAR
为您的Java SE项目获取所有合适的CDI JAR可能是一项艰巨的任务。 为了减轻您的痛苦,Weld为您提供了一个多合一的JAR,恰当地命名为weld-se。 不要争辩它已经过大并重新包装,只需使用它,您就可以避免很多痛苦。 对于Maven用户,这是配置:
<project>
...
<dependencies>
<dependency>
<groupId> org.jboss.weld </groupId>
<artifactId> weld-se </artifactId>
<version> 1.0.1-Final </version>
</dependency>
</dependencies>
</project>
她是一个简单的记录员
我所知道的所有日志框架(JDK,Commons Logging,Log4J和SLF4J)都使您可以轻松地声明类记录器:
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(MyClass.class);
无论如何,在大多数情况下,要使用此功能,您可以:
- 在您的IDE中创建一个模板。 然而,在每个IDE中配置模板可能会非常繁琐。
自我注意提出一项功能,可以将这样的模板(和首选项)保存在网络上并在我所有的IDE实例之间共享
- 从某处复制该行,然后将其粘贴到需要的地方。 然后,您更改要记录的类。 坦率地说,尽管这并没有增加我的传说(恰恰相反),但这就是我一直在做的事情
而且一直在进行后者,碰巧我忘记更改记录的类。 愚蠢的,不是吗? 也许吧,但是更愚蠢的是,我必须首先编写它:类是我的上下文,应该对其进行推断。 猜测一下,Weld在焊机记录器JAR的帮助下做了什么(它使用SLF4J,因此请注意或将代码用作模板以使用其他框架):
@Inject
privateLoggerlogger;
我真的不认为这会更简单! 唯一的缺点是您失去了常量:对于我现在处理的项目,我可以接受它。 要使用此功能,只需将以下依赖项添加到您的POM中:
<project>
...
<dependencies>
<dependency>
<groupId> org.jboss.weld </groupId>
<artifactId> weld-logger </artifactId>
<version> 1.0.1-Final </version>
</dependency>
</dependencies>
</project>
记住初始化
CDI当然可以注入您的依赖项。 但是注入您的依赖项并不会在屏幕上组装它们。 幸运的是,CDI还为您提供了一个钩子: @PostConstruct
注解的领域。 让我们举一个非常简单的例子:
publicclassMyPanelextendJPanel{
@Inject
privateJCheckBoxcheckbox;
@PostConstruct
publicvoidafterPropertiesSet(){
setLayout(newFlowLayout());
add(checkbox);
}
}
注入所有依赖项之后,将调用afterPropertiesSet()
方法,该方法将扮演以前在构造函数中完成的角色。
是的,该方法以在InitializingBean接口中定义的旧Spring方法命名,其版本为1。现在可以通过在XML bean定义文件中指定bean的init-method属性或使用@PostConstruct批注来替换该方法。
(不要)数量增加
使用CDI,您的课程将相互注入。 但是,这意味着每个组件都应具有自己的类。 这可能相当麻烦:例如,对于按钮,每个按钮都应在特定的基础上附加其动作。 随着时间的流逝,即使是中等大小的应用程序,其类的数量也会比标准应用程序大得多。 恕我直言,这是不理想的,因为:
- 它会使您的代码库更大,从而增加您的开发复杂性
- 在Sun的JVM中,加载更多的类意味着您将使用更多的PermGen空间,并且更有可能遇到
OutOfMemoryError
为了限制我产生的类的数量,我使用了一个装有产生器方法的组件工厂。 CDI将这些方法标识为注射提供者。
publicclassComponentFactory{
@Produces
publicJButtongetButton(){
returnnewJButton();
}
}
使用注射点
这很好,但还不够,因为必须在注入的类中完成动作(或文本和图标)关联。 我想用诸如按钮文本之类的信息注释注入的属性,并在我的生产者方法中获取该信息。 这是InjectionPoint
可选参数的目标。 如果您在方法中将其作为参数引用,CDI将免费提供给您。
publicclassComponentFactory{
@Produces
publicJButtongetButton(InjectionPointip){
JButtonbutton=newJButton();
// Do something based on annotations on the parameter
...
returnbutton;
}
}
这正是创建焊接记录器(参见上文)的方式。
尊重海格兰德规则
汉兰达的规则是:“只能有一个”。 除了被年轻人大多不知道的电影所拍摄外,它还阐明了一个基本事实。 由于注射必须是确定性的,因此注射点不能有两个候选对象。 如前所述,使用生产者方法会遇到这个问题:CDI将同时具有JButton
类和生产者方法,并且会以通常的方式大声抱怨它。
Exception in thread "main" org.jboss.weld.exceptions.DeploymentException: WELD-001409 Injection point has ambiguous dependencies..... at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280) at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:122) at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:141) at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:331) at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:317) at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:399) at org.jboss.weld.environment.se.Weld.initialize(Weld.java:81) at org.jboss.weld.environment.se.StartMain.go(StartMain.java:45) at org.jboss.weld.environment.se.StartMain.main(StartMain.java:57)
为了符合Highlander规则,您必须放弃JButton
类作为注入候选对象。 为此,我在注入点和生产者方法上都使用了限定符。 由于我不想每个注入点/生产者方法对都使用限定符,因此我将其参数化:
@Qualifier
@Target({FIELD,METHOD})
@Retention(RUNTIME)
public@interfaceJType{
publicClass<?extendsJComponent>value();
}
用JType
批注注释注入的属性和生产者方法,使我的代码符合Highlander规则!
传播这个词
我可以建议的一种好的做法是创建一个特殊的Properties
类,该类的任务是初始化标签(以及另一个用于您的首选项等),然后将其注入所需的所有客户端类中。 现在,所有课程都可以访问您的国际化标签。 真了不起!
忘记Java WebStart
焊接JAR分析过程与Java WebStart不兼容。 您可能会收到以下错误消息:
122 [javawsApplicationMain] WARN org.jboss.weld.environment.se.discovery.URLScanner - could not read entries java.io.FileNotFoundException: http:\xxxxxxxxxx\weld-logger-1.0.0-CR2.jar (La syntaxe du nom de fichier, de répertoire ou de volume est incorrecte) at java.util.zip.ZipFile.open(Native Method) at java.util.zip.ZipFile.<init>(Unknown Source) at java.util.zip.ZipFile.<init>(Unknown Source) at org.jboss.weld.environment.se.discovery.URLScanner.handleArchiveByFile(URLScanner.java:142) at org.jboss.weld.environment.se.discovery.URLScanner.handle(URLScanner.java:126) at org.jboss.weld.environment.se.discovery.URLScanner.scanResources(URLScanner.java:107) at org.jboss.weld.environment.se.discovery.SEWeldDiscovery.scan(SEWeldDiscovery.java:71) at org.jboss.weld.environment.se.discovery.SEWeldDiscovery.<init>(SEWeldDiscovery.java:45) at org.jboss.weld.environment.se.discovery.SEBeanDeploymentArchive$1.<init>(SEBeanDeploymentArchive.java:45) at org.jboss.weld.environment.se.discovery.SEBeanDeploymentArchive.<init>(SEBeanDeploymentArchive.java:44) at org.jboss.weld.environment.se.discovery.SEWeldDeployment.<init>(SEWeldDeployment.java:37) ... at com.sun.javaws.Launcher.run(Unknown Source) at java.lang.Thread.run(Unknown Source)
我希望它将在将来的版本中修复...
单身人士并不邪恶
我已经两次偶然发现一个奇怪的行为:在不应该切换按钮或状态丢失时选择切换按钮。 在疯狂调试之后,我看到@PostConstruct
方法的调用远远超出了初始调用。 看来我的代码在那之后要求再次注入。 为了对此进行补救,请使用@Singleton
注释您的类,以便在多个调用之间共享实例。 我没有做更多的调查,因为:
- 我解决了这个错误
- 我不知道为什么我不应该在Swing应用程序中使用单例
结论
我仍在开发中,所以我认为我没有看到所有可以看到的东西。 但是,对于要在Java SE上下文中使用CDI的任何项目,前面的观点都可以作为一个很好的起点。
而且请原谅双关语,由于我们😅️的这个美好的夏天,我感到很开心
翻译自: https://blog.frankel.ch/lessons-learned-from-cdi-in-swing/