Get rid of *-portlet.xml using annotations. Possible? How?
The documentation here says:
The framework will, on initialization of a DispatcherPortlet, look for a file named [portlet-name]-portlet.xml in the WEB-INF directory of your web application and create the beans defined there (overriding the definitions of any beans defined with the same name in the global scope).
I do the configuration using annotations if I can, because it feels easier to keep configuration and the actual code in sync. So this [portlet-name]-portlet.xml in my project looks like this (and this file exists maaany time... One for each portlet):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<bean class="some.path.to.a.Class" />
</beans>
That is a lot of XML just for a tiny piece of information: That some.path.to.a.Class should be used to handle requests to [portlet-name]. It would be a lot easier to put a @ForPortlet("[portlet-name]") annotation or something like that on some.path.to.a.Class and forget about this XML file completely. Is anything like that possible? This bug report may hint at a "no"/"not yet"?
SOLUTION
Thanks to the super helpful hints by OlliS (Thanks a lot!) I figured out a way to do it. I dived at the points that OlliS gave me into spring's source and after taking a looooong time to figure out how things work together I wrote the following class:
public class MyPortletContext extends
AbstractRefreshablePortletApplicationContext
{
private static final String PORTLET_PACKAGE = "package.where.my.portlets.are.";
private static final String REMOVE_FROM_NAMESPACE_FOR_PORTLETNAME = "-portlet";
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException
{
// The following line does the same thing as specifying
// <context:annotation-config /> in the xml file:
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// Figure out the portlet name:
final String portletName = StringUtils.removeEnd(getNamespace(),
REMOVE_FROM_NAMESPACE_FOR_PORTLETNAME);
// Derive the controller from the portlet name:
final String beanClassName = PORTLET_PACKAGE + portletName;
// Tell spring about the bean:
final GenericBeanDefinition beanD开发者_JAVA技巧efinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
final String beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, beanFactory);
final BeanDefinitionHolder bdHolder = new BeanDefinitionHolder(
beanDefinition, beanName, new String[] { beanClassName });
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, beanFactory);
}
}
Then I registered this class as contextClass in the portlet.xml file using an init-param, just like OlliS figured in his answer. And that's it. No *-portlet.xml file needed any more. Just a single class configuring all my portlets.
Of course one could still improve this class, making it more flexible, reading the portlet package from somewhere instead of a constant. Maybe an init-param. Or one could scan for annotations, perhaps the mentioned @ForPortlet annotation, which would create the possibility to register more than one bean. But for now I am happy :-).
Have you tried using @Configuration annotated classes for configuration? It is a feature in the spring framework as of version 3.0
See the reference documentation here:
http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-java
For example context:component-scan can be enbaled:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
Java based configuration can also be combined with XML; mentioned in the documentation.
Hope it helps.
EDIT: So you would want to replace the portlet specific context with a java class. I am not sure if the DispatcherPortlet supports java configuration though. You could try adding something like this which is used in normal webapps:
<portlet>
<portlet-name>portlet</portlet-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<!-- Configure DispatcherPortlet to use AnnotationConfigWebApplicationContext
instead of the default XmlPortletApplicationContext ? -->
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.PortletConfig</param-value>
</init-param>
<!-- ... -->
</portlet>
Some documentation about AnnotationConfigWebApplicationContext: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.html
From the portlet reference docs:
DispatcherPortlet initialization parameters
contextClass
Class that implements WebApplicationContext, which will be used to instantiate the context used by this portlet. If this parameter isn't specified, the XmlPortletApplicationContext will be used.
Implementations of WebApplicationContext can be found in the docs:
http://static.springsource.org/spring/docs/current/api/org/springframework/web/context/WebApplicationContext.html
Doesn't seem like there's a portlet specific WebApplicationContext implementation class though, at least I didnt find any. One could always make one :)
精彩评论