Skip to main content

Spring Core

Please refer to spring-framework under org.springframework.mylearntest package for code related to this article (from official source spring-test module).

Specify a shorter one in a long lifecycle Bean

lookup-method

Assuming a singleton Bean A needs to reference another non-singleton Bean B, generally we use set method to populate dependency. But here singleton Bean A and non-singleton Bean B have different lifecycles, how to get a new Bean B every time Bean A logic is executed? Is it possible every time? Using lookup-method can solve this problem. lookup-method has a prerequisite: Bean A needs to be abstract or corresponding method is abstract.

XML Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="newsBean" class="org.springframework.mylearntest.ioc.lookupMethod.FXNewsBean" scope="prototype">
</bean>

<bean id="mockNewsProvider" class="org.springframework.mylearntest.ioc.lookupMethod.MockNewsPersister">
<lookup-method name="getNewsBean" bean="newsBean"/>
</bean>

</beans>
Test Class
package org.springframework.mylearntest.ioc.lookupMethod;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test4lookupMethod {
public static void main(String[] args) {
ApplicationContext beanFactory = new ClassPathXmlApplicationContext("ioc/lookup-method.xml");
MockNewsPersister mockNewsPersister = (MockNewsPersister) beanFactory.getBean("mockNewsProvider");
mockNewsPersister.persistNews();
mockNewsPersister.persistNews();
}
}

Spring will use CGLIB dynamically generated subclass to override getNewsBean method. Since it is dynamically generated, we naturally can't see the code, but we can verify it by printing class name: System.out.println(mockNewsPersister.getClass().getName()); result is org.springframework.mylearntest.ioc.lookupMethod.MockNewsPersister$$EnhancerBySpringCGLIB$$.... This indicates that MockNewsPersister generated by Spring is indeed CGLIB subclass.

Actually there is another way, which is to implement BeanFactoryAware interface. When a Bean implements BeanFactoryAware interface, Spring will inject BeanFactory into this Bean during instantiation. Then we can use BeanFactory to get Bean B.

Use BeanFactoryAware
package org.springframework.mylearntest.ioc.lookupMethod;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

public class MockNewsPersister implements BeanFactoryAware {
private BeanFactory beanFactory;

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}

public void persistNews() {
System.out.println("persist news");
// Dispatcher
getNewsBean();
}

public FXNewsBean getNewsBean() {
return (FXNewsBean) beanFactory.getBean("newsBean");
}
}

Prototype bean in singleton Bean

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class Prototype {

}

@Component
public class Singleton {

@Autowired
private Prototype prototype;

public void print() {
System.out.println(prototype);
}
}

Since Singleton Bean is a singleton, it is only initialized once, so Prototype Bean will also initialize once. This causes Prototype Bean to actually become a singleton. If we want Prototype to be initialized anew every time print is called, we can use ObjectFactory.

@Component
public class Singleton {

@Autowired
private ObjectFactory<Prototype> prototypeObjectFactory;

public void print() {
System.out.println(prototypeObjectFactory.getObject());
}
}

ObjectFactory is a functional interface, its implementation class is implicitly implemented by Spring. Its getObject method will trigger Spring container to get logic of Bean. If Target Bean is singleton, it returns same instance; if it is prototype, it creates a new instance. There is another way ObjectProvider, inherited from ObjectFactory, providing more functions (lazy loading, optional injection, stream processing: stream()).

@Component
public class Singleton {
@Autowired
private ObjectProvider<Prototype> prototypeObjectProvider; // Inherits ObjectFactory

public void print() {
System.out.println(prototypeObjectProvider.getIfAvailable()); // Safe consumption
prototypeObjectProvider.stream().forEach(System.out::println);
}
}

@Autowired field underlying implementation

@Autowired annotation is handled by AutowiredAnnotationBeanPostProcessor. This Post Processor handles @Autowired and @Value annotations in postProcessProperties method (or postProcessPropertyValues in old versions).

  1. Lookup Injection Metadata (InjectionMetadata): When Spring creates Bean, it parses all fields and methods of target class via findAutowiringMetadata, finding those marked with @Autowired. These metadata will be cached to avoid repeated parsing.
  2. Execute Injection (InjectedElement): Traverse parsed fields (AutowiredFieldElement) and methods (AutowiredMethodElement), call inject method.
  3. Dependency Lookup (resolveDependency): Internal call DefaultListableBeanFactory.resolveDependency:
    • First lookup Bean definition by type (Type).
    • If find multiple candidates: determine priority based on @Primary, @Priority. If still unable to determine, fallback to matching by field name (Name).
    • If still cannot find or ambiguous, throw NoSuchBeanDefinitionException or NoUniqueBeanDefinitionException (unless marked required=false).
  4. Reflection/MethodHandle Assignment: Finally use Java reflection field.set(bean, value) or method.invoke(bean, value) to complete injection.

static @Bean

If using @Bean in @Configuration class, it is usually instance method. But if this method is static, it has special meaning.

Avoid triggering Configuration Class initialization early: Common @Bean method requires container to instantiate @Configuration class first, and intercept method calls via CGLIB proxy (to ensure singleton symantics). Static @Bean method does not depend on instance of containing class. It can load and generate Bean definition without instantiating Configuration class. This is very careful for initializing early infrastructure Beans (like BeanPostProcessor, BeanFactoryPostProcessor). If these Beans are defined as instance methods, it will force Configuration class and other Beans it depends on to initialize prematurely during container startup, which may bypass some PostProcessors processing.

@Configuration
public class AppConfig {

// Common Bean: requires AppConfig to be instantiated first, controlled by CGLIB proxy
@Bean
public MyService myService() {
return new MyService();
}

// Infrastructure Bean: should define as static
// Container loads it without instantiating AppConfig, preventing AppConfig from being processed by existing PostProcessors too early
@Bean
public static BeanPostProcessor myBeanPostProcessor() {
return new MyBeanPostProcessor();
}
}
  1. CGLIB enhancement failure: static method call will not be intercepted by CGLIB subclass inheriting @Configuration, so calling this static method inside another @Bean method will strictly execute standard Java method call logic (return new instance directly), instead of getting existing Singleton Bean from container. (Usually we do not call static @Bean directly between internal methods, but inject dependencies via method parameters)
@Configuration
public class InfraConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

@Autowired or @Resource specify implementation class

@Autowired defaults to matching by type (byType). If there are multiple candidates, try to match by name (byName). @Resource defaults to matching by name (byName). If not specified name, fallback to field name matching, then type matching.

How to specify implementation class precisely?

  1. @Qualifier + @Autowired: @Qualifier("impl1")
  2. @Resource(name="impl1"):
  3. @Primary:
  4. Generic Injection: List<Interface>, Map<String, Interface>

Constructor Injection

Spring 4.3+ recommends constructor injection as best practice.

  1. Immutability: final field can be safely assigned in constructor.
  2. Integrity: Ensure dependent objects are not null when created (Fail Fast).
  3. Testability: Easy to manually new object and pass mock dependencies in Unit Test, no need for reflection or Spring container startup.
  4. If there is only one constructor, @Autowired can be omitted, Spring will automatically use it directly.
@Service
public class OrderService {
private final UserService userService; // final, ensures immutability

// @Autowired // Only one constructor, can emit
public OrderService(UserService userService) {
this.userService = userService;
}
}

Is it better to use AspectJ annotation for AOP configuration or xml

Closure and Callback

This part is very abstract, for example HibernateTemplate, JdbcTemplate etc., they all use Template Method Pattern inside, but they are not pure Template Method Pattern, they use Callback.

Template Method Pattern: Define steps of an algorithm in parent class, allowing subclasses to provide implementation for one or more steps. HibernateTemplate is not abstract class, but uses Callback to inject change parts into Template. Why do this? Because Java doesn't support passing method as parameter (like C++ function pointer), so can only define an interface, then pass implementation class of this interface.

public void doSomething(Callback callback) {
// ... Common logic part 1 (Get connection, start transaction)

callback.execute(); // Execute customized logic

// ... Common logic part 2 (Commit transaction, close connection, handle exception)
}

public interface Callback {
void execute();
}

HibernateTemplate Example

// HibernateTemplate ensure session open/close
public <T> T execute(HibernateCallback<T> action) {
Session session = getSession();
try {
return action.doInHibernate(session); // Callback logic
} finally {
releaseSession(session);
}
}

// User usages: focus on business logic only
template.execute(new HibernateCallback<Object>() {
public Object doInHibernate(Session session) {
return session.load(Person.class, 1);
}
});

Using Callback interface + Anonymous Inner Class (or Lambda) simulates effect of Closure, allowing internal logic to access external variables (in Java need to be final or effectively final)

@Configurable and @Configuration

@Configuration: Standard annotation for Spring Java Config. Indicates this class is a configuration class, containing @Bean definition methods. Container will process this class via CGLIB proxy to ensure Bean singleton semantics. @Configurable: This is an annotation provided by Spring AOP (relying on AspectJ Load-Time Weaving or Compile-Time Weaving). Its purpose is to: Allow objects NOT managed by Spring (e.g. objects created via new manually) to still inject Spring Dependencies! When you new an object marked with @Configurable, AspectJ aspect will intercept constructor call, and automatically pull dependencies from Spring context to inject into this new object. This is widely used in Domain Driven Design (DDD) Entity injection Service.

Spring Boot Customize an Event Publication

  1. Define Event

    Inherit ApplicationEvent or directly POJO (Spring 4.2+ supports generic event).

    public class MyEvent extends ApplicationEvent {
    private String content;
    public MyEvent(Object source, String content) {
    super(source);
    this.content = content;
    }
    // getters
    }
  2. Publish Event Inject ApplicationEventPublisher.

    @Service
    public class MyPublisher {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void publish(String msg) {
    publisher.publishEvent(new MyEvent(this, msg));
    }
    }
  3. Listen Event Use @EventListener annotation on method.

    @Component
    public class MyListener {
    @EventListener
    public void handleMyEvent(MyEvent event) {
    System.out.println("Received: " + event.getContent());
    }

    // Support Async processing (requires @EnableAsync)
    @Async
    @EventListener
    public void handleAsync(MyEvent event) { ... }
    }

    @TransactionalEventListener: Special listener, can trigger event only after current transaction commits/rollbacks. E.g. send email notification after database save succeeds.

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(MyEvent event) { ... }
Agreement
The code part of this work is licensed under Apache License 2.0 . You may freely modify and redistribute the code, and use it for commercial purposes, provided that you comply with the license. However, you are required to:
  • Attribution: Retain the original author's signature and code source information in the original and derivative code.
  • Preserve License: Retain the Apache 2.0 license file in the original and derivative code.
The documentation part of this work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . You may freely share, including copying and distributing this work in any medium or format, and freely adapt, remix, transform, and build upon the material. However, you are required to:
  • Attribution: Give appropriate credit, provide a link to the license, and indicate if changes were made.
  • NonCommercial: You may not use the material for commercial purposes. For commercial use, please contact the author.
  • ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.