Spring Tips & Tricks


Spring-boot
Spring-boot specifies and manages its dependencies' version:
check https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml. 
Don't specify and overwrite the version again in our project's pom.xml.
Don't use same property value to overwrite the version by accident.

Properties
@Bean public static PropertySourcesPlaceholderConfigurer propertyConfig(){}

Bind properties to a list or a map
First create ConversionService bean
@Bean public ConversionService conversionService() {
    return new DefaultConversionService();
}

my.list.of.ints=1,2,3
@Value("${my.list.of.ints}")
private List<Integer> myList

redis.expires={typeA:900, typeB: 86400}
@Value("#{${redis.expires}}")

private Map<String, Long> expires;

Spring Cache
Support Spring Expression Language in Spring AOP
In contrast to the @Cacheable annotation, @CachePut does not cause the advised method to be skipped. Rather, it always causes the method to be invoked and its result to be stored in the associated cache. 
We can use #result as key in @CachePut.

CacheAspectSupport.execute
CacheAspectSupport.generateKey
SpringCacheAnnotationParser.parseCacheAnnotations
CacheOperationExpressionEvaluator.createEvaluationContext
if (result != NO_RESULT) evaluationContext.setVariable(RESULT_VARIABLE, result);

- To support #result, we just put method return result value into the variable: result.

Synchronized caching
@Cacheable(sync="true")
@CacheConfig(cacheNames = "cacheA")
@Cacheable(key= ("#p0['name']")) // p0 is a map
@CachePut(key = "#p0.name") // p0 is an instance
@Cacheable(key = "'somePrefix-'+#p0")

public static final String PREFIX = "'" + CACHE_NAME + "-'+";
@Cacheable(key = PREFIX + "#p0.toString()") // p0 is UUID

@CachePut(key = PREFIX + "#result.uuid.toString()", condition = "#p2 == 'typeA'")

Compared with condition, unless expressions are evaluated after the method has been called.
unless = "#result != null"

Not cache null value explicitly
@Cacheable(unless = "#result == null")

Change cache log level to trace
logger name="org.springframework.cache" level="trace"
No cache entry for key
Cache entry for key found in cache

Redis Cache
- Cache null values
- Use properties to set different expires for different cache regions
public CacheManager cacheManager(final StringRedisTemplate redisTemplate) {
    final RedisCacheManager cacheManager =
            new RedisCacheManager(redisTemplate, Collections.<String>emptyList(), true);
    cacheManager.setDefaultExpiration(86400);
    cacheManager.setExpires(expires);
    return cacheManager;
}
configKeys = stringRedisTemplate.opsForZSet().range(cacheKey, 0, -1)
redisTemplate.opsForValue().multiGet(configKeys);
stringRedisTemplate.opsForZSet().size(cacheKey)

Bind Collection of Beans
@Autowired(required = false)
private List<XInterface> instances;

WebApplicationInitializer
AbstractAnnotationConfigDispatcherServletInitializer

Using Spring managed Bean in non-managed object
SpringContextBridge

Spring EL
@Value("${property:default_vaule}")
@Value("#{'${my.list.of.strings}'.split(',')}")
private List myList;
@Value("#{someBean.someMethod()}")

Parse annotation's value using Spring EL
// instance are thread safe
private final ExpressionParser parser = new SpelExpressionParser(); 
final XAnnotation xAnnotation = method.getAnnotation(XAnnotation.class);
final Expression exp = parser.parseExpression(xAnnotation.propertyA());
final EvaluationContext context = new MethodBasedEvaluationContext(invocation.getThis(), invocation.getMethod(),
        invocation.getArguments(), new LocalVariableTableParameterNameDiscoverer());


final Object value = exp.getValue(context);

Spring EL
#root, #root.methodName, #root.method.name, #result


@PostConstruct
@PreDestroy

BeanUtils.copyProperties - copy properties while ignoring null values
@EnableRetry
@Retryable(value = {FooException.class, BarException.class}, maxAttempts = 5)


Spring-Security
Use AbstractSecurityWebApplicationInitializer
Configure it by extends WebSecurityConfigurerAdapter

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

Integrate In-Memory Authentication for Test Automation

Use inMemoryAuthentication in dev lines for automation test

Extend UsernamePasswordAuthenticationFilter
Build Multi-Tenant Application

Spring Test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)

Spring test doesn't work with JUnit 4.11
InitializationError When Use Spring Test + JUnit

Spring data
How pring-data-cassandra runs query
org.springframework.aop.framework.JdkDynamicAopProxy.invoke
org.springframework.data.repository.core.support.RepositoryFactorySupport.QueryExecutorMethodInterceptor.doInvoke(MethodInvocation)

org.springframework.data.cassandra.repository.query.AbstractCassandraQuery.execute(Object[])

SimpleCassandraRepository

Add @NoRepositoryBean to intermediate repository interface.
Spring-data-solr

@Score private Float score;

Spring-sftp
com.jcraft.jsch.JSchException: UnknownHostKey
ssh-keyscan server-ip-address > my_known_hosts
new DefaultSftpSessionFactory().setKnownHosts(new ClassPathResource("my_known_hosts").getFile().getAbsolutePath());

Spring-mvc
PathVariable contains special characters 
@GetMapping("/path/{variable:.+}/")

@PathVariable(value = "variable")

Return raw json string
- Add StringHttpMessageConverter before MappingJackson2HttpMessageConverter in method configureMessageConverters of WebMvcConfigurationSupport

Misc
Injecting Bean Dependencies as Method Parameters
@Bean
public Foo foo(@Qualifier("bar1") Bar bar1) {
 return new Foo(bar1);
}
@Bean public Bar bar1() {
 return new Bar("bar1");
}
This approach is better than the following one as there is only one bean(bar) created.
@Bean
public Foo foo() {
 return new Foo(bar());
}
-Dspring.profiles.active=p1,p2

afterPropertiesSet
If we create a bean manually in code and it implements InitializingBean interface which will be called automatically after the bean is created by Spring, we may have to call afterPropertiesSet manually.

@Autowired(required = false)
@Qualifier("aConf")

private Configuration aConf;

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)