![spring](https://i-blog.csdnimg.cn/blog_migrate/70ed6e487240ce9fd8902bea160e6721.png)
spring
每个人都听说过将单个Web应用程序组合成一个大型Web应用程序的门户。 门户软件的工作原理类似于mashup -来自多个来源的内容是在单个服务中获取的,大部分都显示在单个网页中。 门户软件还允许在嵌入到门户软件中的所有单个Web应用程序(独立模块)之间更改用户设置,例如语言或主题。 此外,预计将实现单点登录(SSO),它也应能正常工作。 这意味着一次登录即可使用户访问所有嵌入式Web应用程序。 知道在JEE世界中是否存在一个简单而轻巧的解决方案来开发模块化JSF 2应用程序,以自动收集它们并将其呈现在一个类似门户的Web应用程序中,将是很有趣的。 当然,有OSGi和复杂的Portals Bridge为JSR-168或JSR-286兼容的Portlet开发提供支持。 但是幸运的是,JSF 2已经为“幕后”提供了一种简单的可能性。 我们可以用较少的精力来构建类似门户的软件。 我们需要的只是JSF 2和CDI-Java世界中事实上的标准DI框架。
这篇文章的主题不是新的。 您可以在网上找到一些讨论和想法。 我只在这里提到两个链接。 第一个是ocpsoft博客中的文章“操作方法:具有CDI和PrettyFaces的模块化Java EE应用程序” 。 第二个“使用JSF2的模块化Web应用程序”在JBoss的Wiki中进行了介绍。 这个想法是创建包含单个Web应用程序的JAR文件,并为其提供主要的WAR文件。 WAR文件在构建过程中(例如,通过Maven依赖项)将JAR捆绑在一起。 这意味着,JAR位于WAR中的WEB-INF / lib /下。 JAR中的XHTML文件放置在/ META-INF / resources /下,并且将由JSF 2自动获取。JSF可以使用它们,就像它们在/ webapp / resources /文件夹中一样。 例如,您可以使用非常常见的ui:include来包含JAR中的facelets。 这就像一个魅力。 为了能够在运行时获取有关每个JSF模块的一般信息,我们还需要JARs文件中的空CDI的beans.xml。 它们通常位于META-INF文件夹。
现在让我们开始编码。 但是首先,让我们定义项目的结构。 您可以在GitHub上找到完整的实现示例。 这只是使用演示Web应用程序(用JSF 2.2编写)的类似于JSF 2门户的轻量级实现的概念证明。 有5个子项目:
- jsftoolkit-jar基本框架,为模块化JSF应用程序提供接口和实用程序。
- modA-jar第一个Web应用程序(模块A),它依赖于jsftoolkit-jar。
- modB-jar依赖jsftoolkit-jar的第二个Web应用程序(模块B)。
- portal-jar Java,类似于门户的软件的一部分。 它还取决于jsftoolkit-jar。
- portal-war类门户软件的Web部分。 它汇总了所有文物,并且是可部署的WAR。
基本框架(jsftoolkit-jar)具有应由每个单个模块实现的接口。 最重要的是
/**
* Interface for modular JSF applications. This interface should be implemented by every module (JSF app.)
* to allow a seamless integration into a "portal" software.
*/
public interface ModuleDescription {
/**
* Provides a human readable name of the module.
*
* @return String name of the module
*/
String getName();
/**
* Provides a description of the module.
*
* @return String description
*/
String getDescription();
/**
* Provides a module specific prefix. This is a folder below the context where all web pages and
* resources are located.
*
* @return String prefix
*/
String getPrefix();
/**
* Provides a name for a logo image, e.g. "images/logo.png" (used in h:graphicImage).
*
* @return String logo name
*/
String getLogoName();
/**
* Provides a start (home) URL to be navigated for the module.
*
* @return String URL
*/
String getUrl();
}
/**
* Any JSF app. implementing this interface can participate in an unified message handling
* when all keys and messages are merged to a map and available via "msgs" EL, e.g. as #{msgs['mykey']}.
*/
public interface MessagesProvider {
/**
* Returns all mesages (key, text) to the module this interface is implemented for.
*
* @param locale current Locale or null
* @return Map with message keys and message text.
*/
Map<String, String> getMessages(Locale locale);
}
模块A的可能实现如下所示:
/**
* Module specific implementation of the {@link ModuleDescription}.
*/
@ApplicationScoped
@Named
public class ModADescription implements ModuleDescription, Serializable {
@Inject
private MessagesProxy msgs;
@Override
public String getName() {
return msgs.get("a.modName");
}
@Override
public String getDescription() {
return msgs.get("a.modDesc");
}
@Override
public String getPrefix() {
return "moda";
}
@Override
public String getLogoName() {
return "images/logo.png";
}
@Override
public String getUrl() {
return "/moda/views/hello.jsf";
}
}
/**
* Module specific implementation of the {@link MessagesProvider}.
*/
@ApplicationScoped
@Named
public class ModAMessages implements MessagesProvider, Serializable {
@Override
public Map<String, String> getMessages(Locale locale) {
return MessageUtils.getMessages(locale, "modA");
}
}
该模块的前缀是moda。 这意味着网页和资源位于文件夹META-INF / resources / moda /下。 这样可以避免所有单个Web应用程序之间的路径冲突(相同路径)。 实用程序类MessageUtils(来自jsftoolkit-jar)不在此处公开。 我将仅显示MessagesProxy类。 MessagesProxy类是一个应用程序范围的Bean,可访问模块化JSF Web应用程序中的所有可用消息。 由于它实现了Map接口,因此可以在Java和XHTML中使用。 CDI在运行时会自动注入MessagesProvider接口的所有可用实现。 我们利用Instance <MessagesProvider>。
@ApplicationScoped
@Named(value = "msgs")
public class MessagesProxy implements Map<String, String>, Serializable {
@Inject
private UserSettingsData userSettingsData;
@Any
@Inject
private Instance<MessagesProvider> messagesProviders;
/** all cached locale specific messages */
private Map<Locale, Map<String, String>> msgs = new ConcurrentHashMap<Locale, Map<String, String>>();
@Override
public String get(Object key) {
if (key == null) {
return null;
}
Locale locale = userSettingsData.getLocale();
Map<String, String> messages = msgs.get(locale);
if (messages == null) {
// no messages to current locale are available yet
messages = new HashMap<String, String>();
msgs.put(locale, messages);
// load messages from JSF impl. first
messages.putAll(MessageUtils.getMessages(locale, MessageUtils.FACES_MESSAGES));
// load messages from providers in JARs
for (MessagesProvider messagesProvider : messagesProviders) {
messages.putAll(messagesProvider.getMessages(locale));
}
}
return messages.get(key);
}
public String getText(String key) {
return this.get(key);
}
public String getText(String key, Object... params) {
String text = this.get(key);
if ((text != null) && (params != null)) {
text = MessageFormat.format(text, params);
}
return text;
}
public FacesMessage getMessage(FacesMessage.Severity severity, String key, Object... params) {
String summary = this.get(key);
String detail = this.get(key + "_detail");
if ((summary != null) && (params != null)) {
summary = MessageFormat.format(summary, params);
}
if ((detail != null) && (params != null)) {
detail = MessageFormat.format(detail, params);
}
if (summary != null) {
return new FacesMessage(severity, summary, ((detail != null) ? detail : StringUtils.EMPTY));
}
return new FacesMessage(severity, "???" + key + "???", ((detail != null) ? detail : StringUtils.EMPTY));
}
/
// java.util.Map interface
/
public int size() {
throw new UnsupportedOperationException();
}
// other methods ...
}
好。 但是,在何处获取ModuleDescription的实例? 逻辑位于门户网站jar中。 我对CDI实例使用相同的机制。 CDI将为我们找到ModuleDescription的所有可用实现。
/**
* Collects all available JSF modules.
*/
@ApplicationScoped
@Named
public class PortalModulesFinder implements ModulesFinder {
@Any
@Inject
private Instance<ModuleDescription> moduleDescriptions;
@Inject
private MessagesProxy msgs;
private List<FluidGridItem> modules;
@Override
public List<FluidGridItem> getModules() {
if (modules != null) {
return modules;
}
modules = new ArrayList<FluidGridItem>();
for (ModuleDescription moduleDescription : moduleDescriptions) {
modules.add(new FluidGridItem(moduleDescription));
}
// sort modules by names alphabetically
Collections.sort(modules, ModuleDescriptionComparator.getInstance());
return modules;
}
}
现在,我们可以在UI中创建动态图块,这些图块表示相应Web模块的入口点。
<pe:fluidGrid id="fluidGrid" value="#{portalModulesFinder.modules}" var="modDesc"
fitWidth="true" hasImages="true">
<pe:fluidGridItem styleClass="ui-widget-header">
<h:panelGrid columns="2" styleClass="modGridEntry" columnClasses="modLogo,modTxt">
<p:commandLink process="@this" action="#{navigationContext.goToPortlet(modDesc)}">
<h:graphicImage library="#{modDesc.prefix}" name="#{modDesc.logoName}"/>
</p:commandLink>
<h:panelGroup>
<p:commandLink process="@this" action="#{navigationContext.goToPortlet(modDesc)}">
<h:outputText value="#{modDesc.name}" styleClass="linkToPortlet"/>
</p:commandLink>
<p/>
<h:outputText value="#{modDesc.description}"/>
</h:panelGroup>
</h:panelGrid>
</pe:fluidGridItem>
</pe:fluidGrid>
磁贴是由PrimeFaces Extensions中的pe:fluidGrid组件创建的。 它们具有响应能力,这意味着它们在调整浏览器窗口大小时会重新排列。 下图演示了启动后门户网站应用程序的外观。 它显示了在类路径中找到的两个模块化演示应用程序。 每个模块化Web应用程序都显示为包含徽标,名称和简短描述的图块。 徽标和名称是可单击的。 单击将重定向到相应的单个Web应用程序。
如您所见,您可以在门户的主页上切换当前语言和主题。 第二张图片显示了如果用户单击模块A会发生什么。显示了模块A的Web应用程序。 您可以看到“返回门户”按钮,因此可以向后导航到门户的主页。
最后有两个注释:
- 可以将每个模块作为独立的Web应用程序运行。 我们可以在运行时(再次通过CDI方式)检查模块是否在“门户”之内,并使用不同的主模板。 这里有一个提示。
- 我没有实现登录屏幕,但是单点登录没有问题,因为我们只有一个大型应用程序(一个WAR)。 一切都在那里交付。
翻译自: https://www.javacodegeeks.com/2013/12/using-more-than-one-property-file-in-spring-mvc.html
spring