Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts

Sunday, October 28, 2018

Slimming down a Spring Boot app for testing

One of my favorite things about Spring Boot is the ability to launch an application in embedded mode and do some pseudo-integration-testing (that is, it's integration testing because I'm able to call the embedded app over the loopback network, as if the test were running on a different machine).   Of course you can launch your 'real' application that lives in your 'main' source folder, and you can enable / disable parts of the application with Spring profiles.

But what if you want to create a specialized Spring Boot app, just for testing?   Well, you can!

For example, in src/test/org/example/test/app/TestServer.kt we can make an app class (btw, this is Kotlin, just so you know):

package org.example.test.app

...imports blah...

@SpringBootApplication()
@Import(SomeConfig::class)
class TestGraphQLClientApp {
    /**
     * Defines the main resolvers: Query and Mutation.
     */
    @Bean
    fun resolvers(query: GraphQLQueryResolver) = listOf(query)

}

  • This is a GraphQL server, and I'm going to test a simple GraphQL client with it.   There are more components behind the scenes.
  • I'm putting it in the app sub-package to avoid loading any component in the main app or anywhere else unintentionally.    Remember that a Spring Boot app class implies a 'component scan' of the package it lives in, and all sub-packages!
  • SomeConfig is imported, and this brings in whatever components from the main code or elsewhere.
  • Specialized test components can be defined in packages or sub-packages.
We can make a test like this:

package org.example.test

... imports blah ...

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest(classes = [TestGraphQLClientApp::class, HttpClientConfiguration::class],
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GraphQLClientTest {
    companion object : KLogging()

    @LocalServerPort
    private val port: Int = 0

    @Autowired
    private lateinit var factory: RestTemplateFactory

    // These have to be 'by lazy' because Spring will inject the fields they rely on after init.

    private val template by lazy { factory.createRestTemplate() }

    private val baseUrl by lazy { "http://localhost:$port/graphql" }

    private val client by lazy { GraphQLClient(baseUrl, template) }

    @Test
    fun basicClientTest() {
        client.query("query { foo }").also { value ->
            logger.info { prettyPrint(value) }
            assertEquals("foo", assertHasField(value, "data", "foo").asText())
        }
        client.query("query { getThing(id: \"12345-ABC\") { one two } }").also {
            logger.info { prettyPrint(it) }
        }
    }
}
  • Note that the default 'properties' will be loaded from application.properties or application.yml.  If we want to override this, we should probably make a profile and use it from the test.
So what's the problem?   The problem is that, in this particular context I have JPA and a few other Spring Boot 'starter' dependencies.   So, when the test class starts the Spring Boot app, it launches:
  1. A JDBC Data Source
  2. Liquibase (my preferred data migration tool)
  3. JPA and Hibernate
Those are all great tools, and it's super convenient to have all these 'auto starters' but they are not needed in this particular test.   How to turn them off and "slim" down my application?   There are two approaches:
  1. Create a profile, and disable some things in that profile - This works for the autostart modules that support it, but not all of them do.
  2. Use 'exclude' in SpringBootApplication to disable the autostart modules.
Using exclude is easy, and since it prevents Spring from loading the autostart modules in the first place, it can reduce startup time.   So for a simple web app, we can disable all the database modules:

@SpringBootApplication(exclude = [
    LiquibaseAutoConfiguration::class,
    DataSourceAutoConfiguration::class,
    DataSourceTransactionManagerAutoConfiguration::class,
    HibernateJpaAutoConfiguration::class])

That's all we need to do!   Now the test app starts up in about 9 seconds.

See also:



Wednesday, August 13, 2014

Spring for Java EE Developers - Part 2

The second installment in my series of blog posts about transitioning to Spring when coming from Java EE (or maybe other DI frameworks).    See  Spring for Java EE Developers for the first post.   This time I'll be diving in to some more details.

 Factories

In CDI there is @Producer, and in Guice there is the Provider<T> interface.   These are very useful when you have some run-time decisions to make about what object to produce or how to configure it.   So, how do you make a factory in Spring?

Method 1 - Make a configuration bean

One simple way to create a factory in Spring is to add a @Configuration bean.   Factory methods can be annotated with @Bean, and the factory method parameters will be injected.   You will need to add CGLIB to your (run time) dependencies if you want this to work properly.

  1. Make sure you have cglib in your dependency list.
  2. Add <context:annotation-config/> to your applicationContext.xml (or other XML configuration).
  3. Create a class in a package that is scanned for annotations, and annotate it with @Configuration.
  4. Each method in the @Configuration class that produces a bean should be annotated with @Bean.   Parameters to the @Bean methods will be injected automatically, and can have @Value and @Qualifier annotations.

Method 2 - Make a factory bean / factory method

Another way is to use factory-bean and factory-method.
  1. Register the factory bean.  For example:

    <bean id="thingFactory" class="eg.ThingFactory"/>

    Where eg.ThingFactory has a method public Thing getThing()
     
  2. Register the produced object by referencing a method on the factory bean.
     
    <bean id="thing" factory-bean="thingFactory" factory-method="getThing"/>
    
    
    Spring will then call the getThing() method on the ThingFactory to get the instance.

Injecting values vs beans

In other DI frameworks, injecting a String is the same as injecting any other component.

In the Spring bean XML format, there is a difference between injecting a "value" vs injecting another bean.    To inject a bean, use ref="someBeanId" (a.k.a. bean 'name').   To inject a value, use value="some value or Spring EL".

Using Spring annotations, you can add  @Qualified for a named bean implementation (if there are more than one), and @Value to specify a Spring EL expression.

Transactional Beans

In EJB3, there are some simple transaction annotations that allow you to declare the transaction support you want for your business logic.   Spring has a very similar feature.

@Transactional - provides transaction control.   Very similar to EJB3 - class level and method level control. 

<tx:annotation-driven/> enables the transaction annotation support.

You can also use TransactionTemplate for  programmatic control when needed.

Post-Commit Actions and Transaction Synchronization

Use TransactionSynchronizationManager to get an interface that is similar to JTA Transaction.registerSynchronization().   Something like this:

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter()
    {
           void afterCommit()
           {
                // ... do stuff ...
           }
    });

A few notes on this:
  • If this is used outside of a transaction, the method will fail.   You can have it call the 'after commit' immediately if not inside a transaction, or just let it throw an error and fix the problem.
  • TransactionSynchronizationManager is not an injectable thing.   You have to use the static methods.
  • TransactionSynchronizationAdapter is an empty implementation of TransactionSynchronization that you can use to override specific methods.   Pretty handy.
See this question on SO.

Next Time...

In the next post I'll try to cover Extended Persistence Contexts and some of the web MVC stuff. 

Sunday, June 8, 2014

Spring for Java EE Developers

Spring has been around for a long time now, and has had a significant impact on the newer Java EE standards such as JSF, CDI, and EJB3.   In some ways, Spring could be considered a 'legacy' at this point, but since it's out there it is good to know the basics in case you find yourself working with a Spring-based system (like I have).



I'll post more as I learn, but here are my initial thoughts...



1.  Transitioning to Spring - It's not that bad

In addition to influencing the newer Java EE standards, Spring itself has been influenced by the newer standards.   I'm sure there are some people who will want to argue about which came first, etc.  This is not interesting, IMO.   Both communities benefit from the influences.
  • Annotation-based configuration - Spring no longer requires all components to be defined in a separate XML file (which is considered 'old school' at this point, although IDEs make this much easier to deal with).
    • You can actually use a combination of XML config and annotations in a manner very similar to Seam 2 and CDI.
    • You can also do "Java based" configuration like Guice or Pico.   I'm not really that keen on this approach, but it could come in handy in certain cases.
    • You still need a main configuration XML file, but that's no big deal.   In CDI you need META-INF/beans.xml and Seam you need components.xml.   The main difference is that you can configure the scanning, which could be useful.
  • Supports JSR 330 @Inject and JSR-250 lifecycle annotations - If you are already familiar with CDI and EJB3, this can make the transition easier.   The Spring-specific annotations offer some additional control (the standard annotations have limitations), but these can really help ease the transition.
  • No need for a separate POJO DI mechanism - One issue that I did experience with EJB3 / CDI is that I found I needed a POJO level underneath the EJBs to share very basic services.   I used Guice for this, as at the time Guice was very small and light.   With Spring, you can use it as your POJO DI framework too, although it's significantly slower (instantiation time) and heavier (bigger jar files) than some others.   In any case, you can use it if you have POJO Java processes that are not part of your application server cluster.   'One Kind Of Stuff' and all that.
  • JSF Integration - Spring Web Flow can be configured to integrate the Spring contexts with JSF EL, similar to Seam and CDI.
  • Spring Web Flow ~= Conversation - Having a 'sub-session' concept to allow the developer to retain state between pages is essential nowdays.   A "flow" is fairly similar to a "conversation" in Seam and CDI.  There are some significant differences in how a 'flow' is controlled, but the overall concept is the same.
  • LOTS of boilerplate-code-eliminating features! - This is something that Seam2 had a bit of, but Spring has taken this much further:
    • Spring Data - Define interfaces for DAOs, and Spring Data writes all the boilerplate JPA code.
    • Defining a DAO service that provides a RESTful JSON interface can be done with hardly any code at all.
    • Spring Roo - Generate baseline code and add components easily.   Like the 'forge' stuff in JBoss.   Not sure how useful this really is with an existing project, but it could be a quick way to get the skeleton code in there. 

2. The Bad News

NOTE: This is not an anti-Spring rant.   I'm just pointing out a few facts.
  •  Spring is big - It is no longer the case that Spring is 'lighter' than Java EE - Both systems are highly modular, and very comprehensive.   There are so many Spring add-ons now, expect to spend time wading through them.  At this point, it might as well be an application server.

    On the other hand, it is well documented and very modular, so that mitigates things.
  • Spring is not a standard, it's an implementation - This is perhaps the biggest problem I have with Spring.   It is like an alternate universe where there is only one implementation of the standard, and no independent community defining the standards.   Sure JSRs and all that have their disadvantages, but Spring does have a considerable 'vendor lock in' problem (although it is OSS, so it's partially mitigated).  Sometimes it can be good to know you can pick a different vendor without re-writing the whole thing.

    On the other hand, if you use Spring, you have a "container within the container", so the idea of porting is that you would port your inner container as well.
  • Spring AoP is more complex than EJB3 and CDI - Also a big pet peeve of mine.  It's relatively easy to make interceptors in Seam, EJB3, and CDI.   Granted, Spring AoP is much more powerful, but it's also got a lot of things that seem (to me) like they wouldn't get a lot of use.   In my experience, this kind of complexity results in two problems:
    1. Longer learning curve - Developers take more time to get familiar with the techinque.
    2. A whole new kind of spaghetti code - This often happens when a developer gets through the learning curve and then proceeds to use AoP as a "golden hammer".

    On the other hand, if you really need to do fancy stuff with AoP, (um... do you really need that?), it's there if you want it.   AoP can really be great when used wisely.
  • Lots of references to JSP in the documentation - JSP is now deprecated.   It's a huge step backward from JSF 1.2 & Facelets or JSF2.

3. Things I'm Still Figuring Out

  • Transaction / Hibernate Session management - In an older version of Spring, there were some really serious problems with Hibernate Session management and JTA.   Maybe this is no longer relevant, but I do remember looking at the Spring session management code and thinking "ugh! How did this every work?" (sorry guys).   This is probably addressed, but I do want to know if the 'extended persistence context' concept exists with Spring and/or Spring Web Flow.  This is very important to making simple, transactionally sound, high performance web apps!  
  • JSF Integration - I'm wondering just how deep this is.