How SpringBoot #ConditionalOnClass work? - spring-boot

For example, HttpEncodingAutoConfiguration is annotated with #ConditionalOnClass(CharacterEncodingFilter.class).
What I know about #ConditionalOnClass is to promise the CharacterEncodingFilter in classpath, but if the class not in classpath how could it get through at compile or class loading time.
Thanks a lot.

The javadoc on ConditionalOnClass.value gives the answer:
The classes that must be present. Since this annotation is parsed by
loading class bytecode, it is safe to specify classes here that may
ultimately not be on the classpath, only if this annotation is
directly on the affected component and not if this annotation is used
as a composed, meta-annotation. In order to use this annotation as a
meta-annotation, only use the name attribute.
Also at compile time you do have to have such classes on the classpath to use the value attribute of ConditionalOnClass.
Spring-boot uses optional dependencies to achieve this. In maven this looks like this:
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
<version>4.0.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
In gradle you usually use compileOnly to achieve this.

Related

Spring-boot bean name conflict between two dependent retrofit clients

My maven springboot app has a dependency on two Retrofit clients whose code I have no control over. Both of these clients have a #Configuration class called ClientConfig. When I try to run my application, I get this error:
ConflictingBeanDefinitionException: Annotation-specified bean name 'clientConfig' for bean class [a.b.c.client.config.ClientConfig] conflicts with existing, non-compatible bean definition of same name and class [a.b.c.config.ClientConfig]
How can I fix this? Is there any way I can override the bean names for these classes? This is my maven configuration:
<dependency>
<groupId>a.b</groupId>
<artifactId>lib1-client</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>a.b</groupId>
<artifactId>lib2-client</artifactId>
<version>1.2.55</version>
</dependency>
Only thing I could think of is to exclude them from component scan:
#SpringBootApplication
#ComponentScan(excludeFilters = {"a.b.lib1-client",...})

How to inject a bean into JAX RS resource?

I am sure this is most probably a silly question but I am not familiar with JAX RS (and Jersey).
We've had a standalone Java application that basically starts a RESTful service. As part of a refactoring, we've moved this application to be just a thread within another application. That other application uses Spring beans that are defined in an application-context.xml. So, I need to inject some of those beans to the resource class (if that's the correct name for it: the one with #Path annotations, etc.). The problem is I don't know what instantiates this particular class. There is a main class of the legacy app that is creating a (jetty) Server instance with ServletContexthandler to which a ServletHolder is added to which a ResourceConfig is set. Something like that.
So, I can inject my stuff from Spring to this main class but can't see how exactly I can pass those objects to the JAX RS resource?
I am sure I miss something pretty simple.
Edit: I have added a better explanation to my problem and a solution I found below.
Jersey has integration with Spring support. For this case, there are really only two things you need to do:
Make sure you have the integration dependency. You'll also need to the commons logging, so it doesn't complain
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring4</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Just add a ContextLoaderListener along with a WebApplicationContext containing your Spring context configuration.
ServletContextHandler context
= new ServletContextHandler(ServletContextHandler.SESSIONS);
AnnotationConfigWebApplicationContext wac
= new AnnotationConfigWebApplicationContext();
wac.register(SpringConfig.class);
context.addEventListener(new ContextLoaderListener(wac));
Here the SpringConfig is just a "Java config" Spring configuration class. If you wanted you could use an XML application context, but the example I used in the below link uses a Java config class, but also show how to easily import an XML into the class if you just want to use your XML config. You can combine two.
That's pretty much it. Once you have this configured, you should be able to #Autowired your Spring beans into your Jersey resources.
For a complete example, check out this GitHub repo
Maybe I wasn't able to explain well my problem, so basically it was a problem of how to inject beans into JAX-RS resource classes when the actual JAX-RS app is not being instantiated through its own DI-mechanism but from somewhere else. In my case I already had a Spring container that creates those beans and there was no easy way to link the Spring's own bean application context to the JAX-RS's one. A better solution would have been the one already answered but additional problem is that our existing Spring solution is XML-based, whereas the #Injected annotation in JAX-RS won't work with it (at least that's what I've read in their documentation).
So, JAX-RS supports #Injected annotations and in order for it to know where to get bean definitions from, I had to go to the class that defines the ResourceConfig and add the following lines to it:
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(beanImpl1).to(BeanInterface1.class);
bind(beanImpl2).to(BeanInterface2.class);
}
})
The actual beanImpl1 and beanImpl2 bean instances were coming through the constructor of that class, which in turn was instantiated from our Spring through the XML configuration.

SpatialRepository not autowiring

Does anyone know how to get SpatialRepository #Autowiring in a spring boot app? I have put the additional dependency in my classpath
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-spatial</artifactId>
<version>0.9</version>
</dependency>
with the following configuration options
#SuppressWarnings("unused")
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableNeo4jRepositories(basePackages = {"com.eanda.prototype", "test.com.eanda.prototype"})
#ComponentScan({"com.erranda.prototype", "org.springframework.data.neo4j"})
I have tried it all but no avail. My domain class is this:
public interface ErrandRepository extends GraphRepository<Errand>, SpatialRepository<Errand> {}
I get the following exception when running a query on the spatial repo
java.lang.IllegalArgumentException: No index provider 'spatial' found. Maybe the intended provider (or one more of its dependencies) aren't on the classpath or it failed to load.
Are you introducing the spatial engine to an existing database?
Have you installed the spatial extension in the plugin directory?
Lorenzo

Is it possible to autowire dependencies that are created outside of an ApplicationContext?

I've got an application which uses JAXRS to map Restlet resources using annotations. However, the only entry point I have is essentially defining a list of resource classes in the application configuration. These classes are instantiated by Restlet or JAXRS, so I have no way to put them in my ApplicationContext. Is there a way to have Spring scan the classpath and autowire new instances as necessary? I've already tried using something like below:
#Autowired
private SessionFactory sessionFactory;
Unfortunately, it doesn't really work. Is there a way to do what I'm talking about here?
You can use AspectJ to dependency inject your beans that are created out of your control, or if you create objects using new. You can read more on Springs documentation: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-using-aspectj
Essentially what you will do is add #Configurable annotation to the class that you want to be target of injection. You also have to enable it in Spring by having in your Spring xml. Lastly you have to decide between compile time weaving or runtime weaving. Again you can get help from spring documentation.
Loadtime weaving: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-aj-ltw
If you use maven you can check this Stackoverflow question for setting up compile time AspectJ: Why doesn't AspectJ compile-time weaving of Spring's #Configurable work?
ApplicationContext.getAutowireCapableBeanFactory().autowireBean(object) will inject all dependencies into the object.

Spring MVC: Adding JAXB to the classpath so that it automatically serializes XML

According to Spring MVC documentation, <mvc:annotation-driven/> configures support for JSON if Jackson is in the classpath, and support for XML if JAXB is present in the classpath. Simply by adding a Jackson dependency to my pom.xml, I get JSON support to work! (see: Ajax Simplification in Spring 3.0)
However, after trying to access the same service with accept header "application/xml", I get a 406 Not Acceptable response. What's the simplest way to get JAXB in the classpath? What is necessary to enable support for XML MarshallingHttpMessageConverter?
Update
Taking a look at AnnotationDrivenBeanDefinitionParser, I can see what defines if "jaxb2Present". I set a breakpoint around line 179 to see if the Jaxb2RootElementHttpMessageConverter is indeed being registered like the MappingJacksonHttpMessageConverter is. It isn't...
What's the simplest way to add JAXB to the classpath to make it automatically serialize my XML requests?
It should work. Make sure that the object being returned has #XmlRootElement annotation as required by JAXB.
If you're using Java 6, JAXB is already on the classpath. If you're using Java 5, you'll need to add a reference implementation yourself.
If you're using Maven, you can add to your pom.xml:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2</version>
</dependency>

Resources