Analyzing Circular Bean Dependencies in Spring

It has been well documented that circular dependencies are an anti-pattern. It results in tight coupling and is a smell that your application might be utilizing the big ball of mud architecture. However, it is very rarely as simple as ClassA depends on ClassB and ClassB depends on ClassA. In medium-large Spring projects, these cycles might be 10+ beans in size and difficult to find.

Moving to constructor injection is seen as a best-practice for many reasons but one is that it doesn’t allow for circular dependencies to exist. In field/setter-injection, Spring can first construct all of the beans and then wire them up after. Constructor injection combines those two steps and requires all dependencies to already be instantiated before it can inject/create the bean. And while moving to constructor injection can be great for new projects, it doesn’t directly solve the problem of first identifying the circular dependencies in an existing application.

To find the cycles, first we need to identify all of the beans and their dependencies. That can be accomplished by iterating over all the bean definitions in the ListableBeanFactory:

public class DepGraph {
    private final Map<String, List<String>> nodes;

    private DepGraph(Map<String, Set<String>> nodes) {
        this.nodes = nodes;
    }

    public static DepGraph buildDependencyGraph(GenericApplicationContext applicationContext) {
        ListableBeanFactory factory = applicationContext.getBeanFactory();
        Map<String, Set<String>> beanDeps = Arrays.stream(factory.getBeanDefinitionNames())
            .filter(beanName -> !factory.getBeanDefinition(beanName).isAbstract())
            .collect(Collectors.toMap(
                Function.identity(),
                beanName -> Arrays.asList(factory.getDependenciesForBean(beanName))
            ));

        return new DepGraph(beanDeps);
    }
}

Next, we will perform a depth-first search of the graph. Is this the cleanest, most elegant DFS on the internet? No, but it worked for my use case. If anyone has any suggestions on how I can make stylish, please let me know in the comments below:

public Set<String> calculateCycles() {
    Set<String> visited =  new HashSet<>();
    return nodes.keySet().stream()
        .map(node -> calculateCycles(node, visited, Collections.emptyList()))
        .flatMap(Set::stream)
        .collect(Collectors.toSet());
}

private Set<String> calculateCycles(String node, Set<String> visited, List<String> path) {
    if (visited.contains(node)) {
        return Collections.emptySet();
    }

    List<String> newPath = new LinkedList<>(path);
    newPath.add(node);
    visited.add(node);

    if (path.contains(node)) {
        List<String> cycle = newPath.subList(path.indexOf(node), path.size());
        return Collections.singleton(String.join("->", cycle));
    }
    List<String> deps = nodes.getOrDefault(node, Collections.emptyList());
    return deps.stream()
        .map(dep -> calculateCycles(dep, visited, newPath))
        .flatMap(Set::stream)
        .collect(Collectors.toSet());
}

We begin by creating a HashSet to keep track of all the bean names that have been already visited in the search. Since the graph likely has cycles, we wouldn’t want to get stuck in an endless loop. We then begin iterating through the map invoking the next method for each entry. For each entry, we recursively explore all of its dependencies while keeping track of the path we took to get there. If the new node is already in that path, then we have found a cycle.

I’m just returning a collection of String (ie "A->B->C->D->A") but you can adapt that to return a List or real class depending on your use case case.

I found this to be very helpful in identifying problematic portions of my codebase. My team is slowly working on untangling this web of beans and working to avoid these in the future.

Using Java 8 Time within JAXB with XJC

When we left off on our Gradle JAXB project, we were using a simple Gradle configuration to generate Java classes from an XML schema using JAXB and XJC. However, once you start to use the generated classes, you will notice something about date fields that might be difficult to use. Once again, we are using the ISO 20022 schemas in the sample.

If you don’t want to follow the entire tutorial, you can jump right to the complete code on GitHub.

Before we begin, let’s take a look at the issue. Notice the FinancialInstrumentAttributes79 class:

public class FinancialInstrumentAttributes79 {

  @XmlElement(name = "FinInstrmId", required = true)
  protected SecurityIdentification19 finInstrmId;
  @XmlElement(name = "PlcOfListg")
  protected MarketIdentification3Choice plcOfListg;
  @XmlElement(name = "DayCntBsis")
  protected InterestComputationMethodFormat4Choice dayCntBsis;
  @XmlElement(name = "ClssfctnTp")
  protected ClassificationType32Choice clssfctnTp;
  @XmlElement(name = "OptnStyle")
  protected OptionStyle8Choice optnStyle;
  @XmlElement(name = "DnmtnCcy")
  protected String dnmtnCcy;
  @XmlElement(name = "NxtCpnDt")
  protected XMLGregorianCalendar nxtCpnDt;
  @XmlElement(name = "XpryDt")
  protected XMLGregorianCalendar xpryDt;
  @XmlElement(name = "FltgRateFxgDt")
  protected XMLGregorianCalendar fltgRateFxgDt;
  @XmlElement(name = "MtrtyDt")
  protected XMLGregorianCalendar mtrtyDt;
  @XmlElement(name = "IsseDt")
  protected XMLGregorianCalendar isseDt;

  ...

}

While some of those fields use simple strings, the default XMLGregorianCalendar class is not the easiest to work with. Some of these fields do not even store time information and would be much better suited with the Java 8 LocalDate class. Luckily, with a bit of xml configuration in the bindings file, we can tell XJC to use type adapters for Java 8’s Date and Time API (aka JSR-310).

Getting Started

At its core, JAXB uses the XmlAdapter<ValueType,BoundType> class to convert between the XML string and the Java class. We could extend this class ourselves but I found an open source project jaxb-java-time-adapters that did the job quite well. To start, we will need to add this dependency to gradle so that jaxb can use the classes:

dependencies {
  compile(files(genJaxb.classesDir).builtBy(genJaxb))
  jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7"
  jaxb "com.migesok:jaxb-java-time-adapters:1.1.3"
}

Next, we will need to add the xjc namespace to the top of the binding.xml file:

<bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1"
          xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
  ...
</bindings>

However, you’re not done yet. If you were to try to run gradle clean genJaxb right now, you would see an error similar to the following:

[ant:xjc] [ERROR] vendor extension bindings (jaxb:extensionBindingPrefixes) are not allowed in the strict mode. Use -extension.

The error is pretty self-explanatory but it can be difficult to act on if you don’t know where to look. We just need to add one simple line to the xjc ant configuration in the build.gradle file:

xjc(destdir: sourcesDir, binding: "${projectDir}/src/main/resources/binding.xml") {
  schema(dir: "${projectDir}/src/main/resources", includes: '**/*.xsd')
  arg(value: "-extension")
  produces(dir: sourcesDir, includes: '**/*.java')
}

Adding the Type Adapter

After adding the dependency and enabling extension bindings, we can configure our new type adapters. Just add the following to the top of the binding.xml file:

<bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1"
          xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">

  <globalBindings>
    <xjc:javaType name="java.time.LocalDate" xmlType="xs:date"
      adapter="com.migesok.jaxb.adapter.javatime.LocalDateXmlAdapter" />
    <xjc:javaType name="java.time.LocalDateTime" xmlType="xs:dateTime"
      adapter="com.migesok.jaxb.adapter.javatime.LocalDateTimeXmlAdapter" />
  </globalBindings>

  ...

</bindings>

We’re just going to configure the adapters for LocalDate and LocalDateTime (binding them to the xs:date and xs:dateTime XML types) but you can add as many as you would like.

Result

Now let’s look back at that same FinancialInstrumentAttributes79 class from above:


public class FinancialInstrumentAttributes79 {

  @XmlElement(name = "FinInstrmId", required = true)
  protected SecurityIdentification19 finInstrmId;
  @XmlElement(name = "PlcOfListg")
  protected MarketIdentification3Choice plcOfListg;
  @XmlElement(name = "DayCntBsis")
  protected InterestComputationMethodFormat4Choice dayCntBsis;
  @XmlElement(name = "ClssfctnTp")
  protected ClassificationType32Choice clssfctnTp;
  @XmlElement(name = "OptnStyle")
  protected OptionStyle8Choice optnStyle;
  @XmlElement(name = "DnmtnCcy")
  protected String dnmtnCcy;
  @XmlElement(name = "NxtCpnDt", type = String.class)
  @XmlJavaTypeAdapter(LocalDateXmlAdapter.class)
  protected LocalDate nxtCpnDt;
  @XmlElement(name = "XpryDt", type = String.class)
  @XmlJavaTypeAdapter(LocalDateXmlAdapter.class)
  protected LocalDate xpryDt;
  @XmlElement(name = "FltgRateFxgDt", type = String.class)
  @XmlJavaTypeAdapter(LocalDateXmlAdapter.class)
  protected LocalDate fltgRateFxgDt;
  @XmlElement(name = "MtrtyDt", type = String.class)
  @XmlJavaTypeAdapter(LocalDateXmlAdapter.class)
  protected LocalDate mtrtyDt;
  @XmlElement(name = "IsseDt", type = String.class)
  @XmlJavaTypeAdapter(LocalDateXmlAdapter.class)
  protected LocalDate isseDt;

  ...

}

Now the generated classes are using LocalDate instead of XMLGregorianCalendar for these dates. Notice the @XMLJavaType annotation on the field–that is how the JAXB marshaller knows how to marshal and unmarshal the XML and these generated classes.

A Simple Gradle JAXB Configuration

Working with any XML schema is often a daunting task and when using Java it is common to use a library such as JAXB to turn that schema into Java classes and then marshal (or unmarshal) the XML using those classes. The XJC tool will convert XML schemas into the Java classes but unfortunately due to the age of the project, integrating with gradle is not clear. However, there is an Ant plugin that we can invoke from Gradle to make generating these classes easy.

Because this is just a Gradle task, it can be included in any project. However, I’ve found placing the generated classes in its own jar to work well on any medium to large project. This way, the classes don’t have to be constantly regenerated each time gradle clean is run and it allows any consumers to be consistently versioned to the correct schema.

For this example, we will be using the ISO 20022 schemas to generate Java classes. These schemas are especially complex with a single XSD file including hundreds of classes.

If you don’t want to follow the entire tutorial, you can jump right to the complete code on GitHub.

Getting Started

To get started, we are going to want to add the jaxb dependency and a basic task with our details:

configurations {
    jaxb
}
dependencies {
    jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7"
}
task genJaxb {
  ext.sourcesDir = "${buildDir}/generated-sources/jaxb"
  ext.classesDir = "${buildDir}/classes/jaxb"
  outputs.dir classesDir
}

Working with a Single Schema

We want configure the genJaxb task to generate the classes:

task genJaxb {
  ext.sourcesDir = "${buildDir}/generated-sources/jaxb"
  ext.classesDir = "${buildDir}/classes/jaxb"
  ext.schema = "${projectDir}/src/main/resources/seev.031.001.07.xsd"
  outputs.dir classesDir

  doLast() {
    project.ant {
      // Create output directories
      mkdir(dir: sourcesDir)
      mkdir(dir: classesDir)

      taskdef name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath

      xjc(destdir: sourcesDir, schema: schema, 'package': 'com.gdpotter.sample.iso_20022.seev_031_01_07') {
        produces(dir: sourcesDir, includes: '**/*.java')
      }

      javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true,
            debugLevel: 'lines,vars,source',
            includeantruntime: false,
            classpath: configurations.jaxb.asPath) {
        src(path: sourcesDir)
        include(name: '**/*.java')
        include(name: '*.java')
      }

      copy(todir: classesDir) {
        fileset(dir: sourcesDir, erroronmissingdir: false) {
          exclude(name: '**/*.java')
        }
      }
    }
  }
}

In this example, I’ve placed the schema xsd file in src/main/resources but you could have also pointed it to an online address.

Finally, we just need to make sure that the compiled sources are included in the jar and can referenced by the code. We do this by adding the classesDir to the jar task and also including them as a dependency:

dependencies {
  compile(files(genJaxb.classesDir).builtBy(genJaxb))
  jaxb 'com.sun.xml.bind:jaxb-xjc:2.1.7'
}

compileJava.dependsOn 'genJaxb'

jar {
  from genJaxb.classesDir
}

To generate the classes, you can run the jar task which depends on genJaxb:

$ ./gradlew jar

You can see the results in the /build/ directory:

├── build
│   ├── classes
│   │   └── jaxb
│   │       └── (compiled .class files)
│   ├── generated-sources
│   │   └── jaxb
│   │       └── (generated .java files)
│   ├── libs
│   │   └── gradle-jaxb-example-1.0-SNAPSHOT.jar
...

Adding Multiple Schemas

Let’s now take a look at what we would need to do add multiple schemas. We have the seev_031 schema added above, but let’s add the seev_039 schema as well and just change it to load all *.xsd files:

xjc(destdir: sourcesDir, 'package': 'com.gdpotter.sample.iso_20022') {
  schema(dir: "${projectDir}/src/main/resources", includes: '**/*.xsd')
  produces(dir: sourcesDir, includes: '**/*.java')
}

However, when trying to run the genJaxb Gradle task, we get an errors like the following:

[ant:xjc] [ERROR] A class/interface with the same name "com.gdpotter.sample.iso_20022.seev_031_01_07.EventConfirmationStatus1Code" is already in use. Use a class customization to resolve this conflict.

This is because the two xsd files declare some of the same classes. You’ll also notice that both schemas have to be configured for the same package. Even though we can configure multiple schemas, we can only configure a single package. However, by using a binding.xml file, we are able to customize the package of each schema.

The binding.xml file

The binding file allows for the customization how how the jaxb binding process occurs. We are going to use it to change the package on a per-schema basis as follows:

<bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1">
  <bindings schemaLocation="seev.031.001.07.xsd">
    <schemaBindings>
      <package name="com.gdpotter.sample.iso_20022.seev_031_01_07" />
    </schemaBindings>
  </bindings>
  <bindings schemaLocation="seev.039.001.07.xsd">
    <schemaBindings>
      <package name="com.gdpotter.sample.iso_20022.seev_039_01_07" />
    </schemaBindings>
  </bindings>
</bindings>

And then we can just configure our genJaxb task to use that file:

xjc(destdir: sourcesDir, binding: "${projectDir}/src/main/resources/binding.xml") {
  schema(dir: "${projectDir}/src/main/resources", includes: '**/*.xsd')
  produces(dir: sourcesDir, includes: '**/*.java')
}

Custom Jackson Config for Spring Boot

Spring Boot comes with some really great and usually sensible defaults for creating a Spring Application, but sometimes these defaults need to be tweaked slightly. I recently found myself needing to change some of the Jackson settings, but only for Web Requests. We wanted to use snake_case for the JSON fields in our request and response bodies.

The simplest way to solve this is to just create a custom ObjectMapper bean which would get picked up automatically by Spring Boot. This can be accomplished by doing something like the following:

@Configuration
public class JacksonConfig {

  @Autowired
  public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();

    // Some other custom configuration for supporting Java 8 features
    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule());

    // Use property
    objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

    return objectMapper;
  }
}

The issue with this approach is that it will change the default ObjectMapper for the rest of the application. We only want to change it for Spring MVC. To make a change, we will need to extend the WebMvcConfigurerAdapter:

@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

  @Autowired
  private ObjectMapper objectMapper;

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    ObjectMapper webObjectMapper = objectMapper.copy();
    webObjectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    converters.add(new MappingJackson2HttpMessageConverter(webObjectMapper));
  }
}

Here we have injected the existing ObjectMapper and created a copy. On that copy we are able to set the desired property naming strategy (and any other configuration) and then added a custom message converter with it.

If you have not defined your own ObjectMapper bean, then the default one will be injected (see JacksonAutoConfiguration)

Spring Filter Ordering

The internet is littered with examples of how to add Cross Origin Resource Sharing (CORS) support to a Spring Boot application. Almost all of these examples use the SimpleCORSFilter which looks something like this:

@Component
public class SimpleCORSFilter implements Filter {

   	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
       	HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
   	    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
       	response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Origin, x-requested-with, Content-Type, Accept");
   	    chain.doFilter(req, res);
    }
   	public void init(FilterConfig filterConfig) {}
    public void destroy() {}
}

When working on an AngularJS project, I got stuck trying to determine why my requests were failing the CORS check but my filter was never being called.

After some investigation I found that Spring Security had its own filter that ended the FilterChain when authentication failed. I wasn’t passing the authentication header in the OPTIONS request and so when Spring Security couldn’t authorize the request, the rest of the chain stopped and thus never got to my filter.

As of Spring 2.5, fixing this issue is quite simple. Just add the @Order annotation to the filter like so:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {

The JavaDoc for @Order states:

The default value is Ordered.LOWEST_PRECEDENCE, indicating lowest priority (losing to any other specified order value).

Unless you pass the Oredered.HIGHEST_PRECEDENCE (or some other integer), the filter will be towards the end of the chain.