The Way to Pro Full Stack

A seed project based on Spring Framework family, including Spring Data JPA, Spring Data Redis and so on.

Why this project

During Java web development, we usually have to work on several projects at the same time and we try to make those applications follow the same structure,
including directory structure, annotation based configuration over xml based configuration, naming convensions and so on.
This is really useful when we need to create a new project. For example, the new project should share the same directory structure, third party dependencies and version (Spring, Hibernate, Redis..),
build scripts & tools and so on.
So I try to create a common seed project including all those things to avoid repeating.

The quick structure overview

Just like other Spring frameworks, annotation based configuration is heavily used, like @Enable-* annotations.

The build tool is gradle. Of course you can use Maven but then you need to write your own build scripts instead of those provided.

cd ../..
./gradlew :domain:flywayMigrate -Dflyway.extLocation=src/migration/data
#Will print success at the end.
#You can use any Mysql client to check the database. The root password is root.

Finally, run our tomcat server.

./gradlew web:tomcatRun

Go to http://localhost:8080/api/swagger-ui.html to check and test all the Rest Apis.

Implementation details

Project modules

Just like the sample project, we recommended you seperate your J2EE project into three different modules:

Domain module. All the core business logic code should put here, such as database operations, core entity definations, core business services. The name domain came from the Domain Driven Design.

Web module. All controllers should put here, and it depends on Domain module.

Batch module. All batch job codes should put here, and it also depends on Domain module.

Project profile and property

The project should also support profile and property file settings, such as local, develop and production profile. Under different profile, different property files will be read. And every module should have its own property file, for example:

Domain property files. This folder includes different profile properties, such as domain-default.xml, domain-develop.xml, domain-production.xml.

The profle feature is based on Spring profile and use the same system property spring.profiles.active to set profile. Predefined profiles are default, local, develop, integration and production. The default profile is the default profile if none is set and the default property file is always read.
Upon Spring context initialized, those property files will be read to Spring Environment object automatically and you can also use @Value("${}") to retrive those values. The core implemention code is under profile package.

Domain module features

JPA support

@EnableSpringSeedJpa(propertyPrefix="jdbc.basicDataSource", baseEntityClasses = {Group.class}). This annotation will read all the properties start with propertyPrefix to new a DBCP2 BasicDataSource object as datasource. This feature use Java reflection to call the set method given the property name. For example, in the default profile property file, we have

Then we will automatically call setDriverClassName on basicDataSource object. This reduce a lot of code and we can set any value in the basicDataSource object.
This annotation also import an EntityManagerFactory bean and a JpaTransactionManager bean.

Multiple datasources is also supported, you can set the beanNamePrefix to defined the bean name. The implementation is more complex than I thought. Because @Bean annotation only accept static name value, but we need to generate bean name at runtime. After reading some source code from the Spring Data JPA’s @Enable-* annotation, I find the BeanDefinition object defines a Spring bean at runtime and we can use BeanDefinitionRegistry to register it. Check the code here.

Redis cluster support

This annotation will import jedisConnectionFactory, redisTemplate and ExpirableRedisCacheManager.

The most important feature is to add expiration support on Spring Cache abstraction. This is the known unsupported feature in Spring @Cacheable. So we provide another implementation, @ExpirableCacheable:

A new keyGenerator, the set the default key name to className + methodName + args to avoid a globle unique key set.

A distributed lock implemantation SimpleRedisDlmLock based on Redis. Check here for more details.

Batch module

@EnableBatchProcessing is the native Spring Batch configuration, but you need to set up the batch database to use it for Spring batch need somewhere to store its metadata. Sometimes we don’t want to set up a batch db, so @EnableSpringSeedBatchProcessing is the replacement. It uses a in-memory Map to store the metadata, and a ResourcelessTransaction to handle transactions.

Writing a simple batch job could also be complex, expecially when you just need a simple one step job. You need to define a job, a step and a tasklet. To simplify this process, you only need to extend the AbstractTaskletJob.