I'm studying Spring 3 and I'm using it in a simple web-application.
Now I'm implementing a Spring MVC Controller using annotations, and I'm wondering:
Is there any best practice using #RequestMapping annotation?
I mean: I've seen that usually the URL mapped in this annotation is hardcoded in the class...
Is there a way to pass the URL in a 'loosely coupled way' (to obtain a more reusable class)?
I know that there are some wild cards that can be used, but I think that isn't the solution... Am I wrong?
EDIT:
I add an example to better explain my doubt.
Suppose I want my controller to be triggered by a request to /foo/bar/baz/mypage.htm, in my controller the handler method will be annotated with #RequestMapping("/foo/bar/baz/mypage").
Now I decide to change the URL triggering my controller into /foo/bar/otherpage.htm, so i need to edit my class, put #RequestMapping("/foo/bar/otherpage") on my handler method, recompile the project and deploy it again.
It seems to me not so practical...
Currently annotated controllers aren't very configurable.
As far as I know, the only possible approach to this problem is to use alternative HandlerMappings in order to configure "base URLs" of controllers. For example, as follows:
// Note the absense of #Controller to prevent this controller
// from being discovered by DefaultAnnotationHandlerMapping
public class FooController {
#RequestMapping("/list") public String list(...) { ... }
#ReqeustMapping("/save") public String save(...) { ... }
}
.
<bean
class = "org.springframework.web.servlet.mvc.support.ControllerBeanNameHandlerMapping" />
<bean name = "/foo" class = "FooController" />
<bean name = "/bar" class = "FooController" />
In this example two instances of FooController handle /foo/list, /foo/save, /bar/list and /bar/save respectively.
The upcoming Spring 3.1 will have an improved Spring 3.1 architecture (Spring 3.1 M2: Spring MVC Enhancements) that seems to be more flexible, though I haven't checked it yet.
I think you are trying to solve the wrong problem. If you wanted to change the pages that matched a controller you'd need to change a file somewhere. Would you rather change the file with the related code underneath it, or would you rather work with some XML files that specifies the URL and the class, and then you have to worry about the file being in the right place during runtime?
As there should be almost no code in your controller anyway, you should think of your controllers as compilable configuration files. Also, if you are using a build system like Maven or Ant and not compiling individual files by hand using Javac then compilation time shouldn't be an issue. If it becomes one, it's probably time to split your project into sub-projects.
I think you should just embrace this and see that it is probably not the issue you think it is. Also, did you know that controllers can match to expressions and not just literal strings? That gives you some flexibility in your naming.
If you really want to, you could just fall back to the Spring 2.0 style XML configuration, but I don't think anyone would recommend that.
I think that is not a best practice, but have you tryed with #PathVariable annotations?
#RequestMapping(value="/path/{word}", method=RequestMethod.GET)
public ModelAndView myRestMethod(#PathVariable String word) {
...
}
Related
So I have a standard Spring Boot web application. And I have a requirement to allow multiple authentication mechanisms. So I want to be able to have jar files that handle different authentication. One for a SAML server, CAS, OAuth, etc...
I want my "base" war file to use standard jdbc (Which it does now) but I want to be able to put my, say, CAS-security.jar in the classpath and have spring use it's AuthenticationProvider. Right now I have this in my Application.java class
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider)
.userDetailsService(userDetailsService);
}
And I #Autowire the provider and service, which are defined as #Bean as normal...
But I want to annotate (I think) those #Bean definitions to say "Don't use this if there is another one avail"
I want the exact opposite of ConditionalOnMissingBean. Now I suppose I could use that annotation, and list every possible plugin class that I could use, but that is bad form as I don't want to recompile/redeploy every time I create a new security plugin.
Is there a UseThisIfNoOtherBeanExists type annotation?
In fact, what I really want, is that I have ALL of my security stuff in say, SecurityConfiguration, annotated with #Configuration, and have the whole class replaced with something else if available.
Sounds like you're looking for ConditionalOnMissingBean. A bean annotated with this will only be used if and only if a bean of its type is not already registered in the bean factory.
If you're somehow not satisfied with that, you have options. You can peruse the entire org.springframework.boot.autoconfigure.condition package to see if there's something there that best fits your use case. Alternatively, you could also leverage #Conditional, and hand-craft a condition based on the environment your application is running in.
I want to configure CXF to auto-scan for resources, providers, features, and the lot. In my search I came across the SpringComponentScanServer, which sounds like something I need. Unfortunately, documentation about this class is virtually non-existent.
When you have classes annotated with #javax.ws.rs.ext.Provider, they get picked up by CXF as expected. When you have a feature annotated with org.apache.cxf.annotations.Provider, it will also get picked up by CXF.
But what if you have a third-party feature that does not have this annotation?
For example, the CXF LoggingFeature? You cannot, as far as I know, add an annotation to a class if you can't change it's source code.
I came up with the following, hideous, solution, but surely, there must be a better way to do this???
#Configuration
#Import(SpringComponentScanServer.class)
public class CxfConfig {
#Component
#Provider(Provider.Type.Feature)
static class MyLoggingFeature extends LoggingFeature {
}
}
Here, I basically extend the existing LoggingFeature, only to add the #Provider annotation.
I found the following example CXF JAXRS Spring Boot example, which does what I want with the Swagger2Feature. That works, but only starting from CXF 3.1.6, and only for the Swagger2Feature. Not for LoggingFeature.
So my question is: is there a less hideous way to add the LoggingFeature?
I noticed that the following is not working in a class marked as a #Controller:
#Autowired
SessionFactory sessionFactory;
#ResponseBody
#Transactional
#RequestMapping(method = RequestMethod.GET , value = "/map")
public ArrayList<PhotoDTO> getPhotos(...someParams) {
Entity result sessionFactory.getCurrentSession()... //do some manipulation
return result;
}
when I call the URL, I get an error saying that the method is not transactional (although, as you can see, it is marked as one)
If I copy this method to a another class called MyService and call it from the controller instead, it works perfectly
Is this some sort of a Spring advice (a conspiracy to make me use more classes more or less)?
Don't do transactions in your controller. Put them in your service layer classes.
Separate your code into model-view-controller.
Yes it is a conspiracy. It enables to you to share code between controllers/views without repeating code. And also stops rollbacks of transactions unnecessarily (for exceptions unrelated to the actual transaction).
It might seem like more code to begin with, but in the long run it is much more manageable and simpler to develop.
Probably you have two application contexts here: main Spring context loaded by ContextLoaderListener and a child context loaded by DispatcherServlet. You need to put <tx:annotation-driven /> in the configuration loaded by the child context too. If you show us your web.xml file maybe I can help you more.
Anyway, as #NimChimpsky says, is usually not a good practice to manage transactions in your controller layer.
I've been trying to get a sample JMX MXBean working in a Spring-configured webapp, but any basic attributes on the MXBean are coming up as UNDEFINED when I connect with jconsole.
Java interface/classes:
public interface IJmxBean { // marker interface for spring config, see below
}
public interface MgmtMXBean { // lexical convention for MXBeans - mgmt interface
public int getAttribute();
}
public class Mgmt implements IJmxBean, MgmtMXBean { // actual JMX bean
private IServiceBean serviceBean; // service bean injected by Spring
private int attribute = 0;
#Override
public int getAttribute() {
if(serviceBean != null) {
attribute = serviceBean.getRequestedAttribute();
}
return attribute;
}
public void setServiceBean(IServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
}
Spring JMX config:
<beans>
<context:component-scan base-package="...">
<context:include-filter type="assignable" expression="...IJmxBean" />
</context:component-scan>
<context:mbean-export />
</beans>
Here's what I know so far:
The element is correctly instantiating a bean named "mgmt". I've got logging in a zero-argument public constructor that indicates it gets constructed.
is correctly automatically detecting and registering the MgmtMXBean interface with my Tomcat 6.0 container. I can connect to the MBeanServer in Tomcat with jconsole and drill down to the Mgmt MXBean.
When examining the MXBean, "Attribute" is always listed as UNDEFINED, but jconsole can tell the correct type of the attribute. Further, hitting "Refresh" in jconsole does not actually invoke the getter method of "Attribute"- I have logging in the getter method to indicate if it is being invoked (similar to the constructor logging that works) and I see nothing in the logs.
At this point I'm not sure what I'm doing wrong. I've tried a number of things, including constructing an explicit Spring MBeanExporter instance and registering the MXBean by hand, but it either results in the MBean/MXBean not getting registered with Tomcat's MBean server or an Attribute value of UNDEFINED.
For various reasons, I'd prefer not to have to use Spring's #ManagedResource/#ManagedAttribute annotations.
Is there something that I'm missing in the Spring docs or MBean/MXBean specs?
ISSUE RESOLVED: Thanks to prompting by Jon Stevens (above), I went back and re-examined my code and Spring configuration files:
Throwing an exception in the getAttribute() method is a sure way to get "Unavailable" to show up as the attribute's value in JConsole. In my case:
The Spring JMX config file I was using was lacking the default-autowire="" attribute on the root <beans> element;
The code presented above checks to see if serviceBean != null. Apparently I write better code on stackoverflow.com than in my test code, since my test code wasn't checking for that. Nor did I have implements InitializingBean or #PostConstruct to check for serviceBean != null like I normally do on almost all the other beans I use;
The code invoking the service bean was before the logging, so I never saw any log messages about getter methods being entered;
JConsole doesn't report when attribute methods throw exceptions;
The NPE did not show up in the Tomcat logs.
Once I resolved the issue with serviceBean == null, everything worked perfectly. Regardless, +1 to Jon for providing a working demo, since there are literally 50 different ways to configure MBeans/MXBeans within Spring.
I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache.
It has examples for both EntityManager based access and DAO access (including transactions!). It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. There is even a SpringMVC based example servlet using annotations. Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine.
It isn't documented yet, but I'll get to that soon. Take a look at the configuration files and source code and it should be pretty clear.
The motivation behind this is that I got tired of all of the crazy blog posts with 50 different ways to set things up and finally made a single simple source that people can work from. It is up on github so feel free to fork the project and do whatever you want with it.
https://github.com/lookfirst/fallback
Is there a way to auto-cast Spring beans to the class defined in the application context XML? I'd like to avoid putting type information about the beans in 2 places.... in the xml configuration file and also in the code as a cast.
For instance, given this config file
<bean id="bean-name" class="SimpleSpringBean" scope="prototype">
<property name="myValue" value="simple value"></property>
</bean>
Can I call ApplicationContext.getBean("bean-name") in such a way as to avoid directly casting the return type to SimpleStringBean. I know I can also call ApplicationContext.getBean("bean-name", SimpleSpringBean.class) to avoid the cast itself, but I still have the type info in 2 places.
It seems that Spring can get the class info (ApplicationContext.getType) or by getting the type from the bean itself, but no way to automatically cast the type without programmer intervention.
I agree with Sii, you should avoid calling getBean as much as you can. Just wire your beans to classes that depends on them.
Still, if you have a single class that holds the application context, you can provide a wrapper generic method like the following:
class MyContextHolder{
ApplicationContext appContext;
......
#SuppressWarnings("unchecked")
public static <T> T getBean(String beanName)
{
return (T)appContext.getBean(beanName);
}
}
Then you can call it without casting
MyClass mc = MyContextHolder.getBean("myClassBean");
The answer is you shouldn't be using ApplicationContext.getBean() at all if it's possible, and bear with the one place you have to in the bootstrap code. (Generally, you should never need to use getBean() outside of your application's entry points.)
Also, what you're asking is likely impossible in the Java language at all. Casting is a compile-time feature, combined with a runtime check. The return type of getBean() simply must be known at compile time. Even if Spring can determine the type of an object, it can't change its own method signatures at runtime.
Another thing is that even if this were possible, the feature wouldn't be all that useful. Because Spring AOP is implemented using dynamic proxies, you nearly always want Spring to hand you an instance of an interface the bean implements (which could be an AOP proxy), not of the implementation class.
Another approach I also use is autowiring the bootstrapping class using:
public class Main {
#Autowired FooFacade foo;
#Autowired BarFacade bar;
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("appCtx.xml");
AutowireCapableBeanFactory bf = ctx.getAutowireCapableBeanFactory();
Object main = bf.createBean(Main.class,
AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT,
false);
((Main) main).run();
}
private void run() {
foo.doBootstrapStuff();
bar.doMoreBootstrapStuff();
}
}
(Code done from memory. Might only work if you have the Spring context configured to process wiring annotations, in that case making setters for foo and bar should work.)
The main reason for getBean being untyped is the compatibility of Spring (up to version 2.5.x) with Java 1.4. Spring 3.0 will drop that and thus offer typed getBean method then.
Nevertheless you should avoid looking up beans directly and minimize its usage as far as possible.
What if I use Spring as an object factory that my application uses extensively.
That is, instead of writing a bunch of classes that already inherit or wrap around
known Java classes I just decide to move all that into an xml file to cut down on
lines of Java code. This will mean many lines of xml but I won't need to make
skeleton Java classes which I inject with Spring or autowire. Thus making the lines
of Java code less.
For this reason and still as I am new to Spring I have just as stated in previous
posts used static Java methods which wrap around the getBeans().
I have worked with Spring but it is still new to me so forgive my question.
"Because Spring AOP is implemented using dynamic proxies, you nearly always want Spring to hand you an instance of an interface the bean implements (which could be an AOP proxy), not of the implementation class"
So there is no way to get a dynamic proxy using getBean(), then whats the best practice if there are no interfaces and its a stand alone driver class to be executed?