Master Spring Bean Injection: Prototype in Singleton, Lazy, Transactions & More
This guide explains how to handle various Spring bean injection and transaction challenges, including injecting prototype beans into singletons, using @Lazy, registering abstract beans, resolving multiple bean conflicts, forcing rollback without exceptions, injecting static fields, ensuring transaction consistency, enabling non‑public transactional methods, custom component scanning, and adding interfaces via AOP.
Question 1: How to correctly inject a prototype bean into a singleton bean?
Spring injects a bean only once at container startup; to obtain a new instance each time you can use one of the following approaches.
Method 1: Use a scoped proxy
<code>// Define prototype bean with proxyMode = TARGET_CLASS
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
static class HttpSecurity {}
static class SecurityFilterChain {
@Resource
private HttpSecurity httpSecurity;
@Override
public String toString() {
return "SecurityFilterChain [httpSecurity=" + httpSecurity + "]";
}
}
</code>Method 2: Inject lazily
<code>static class SecurityFilterChain {
@Resource
// @Lazy makes the bean a proxy that is created only when first used
@Lazy
private HttpSecurity httpSecurity;
@Override
public String toString() {
return "SecurityFilterChain [httpSecurity=" + httpSecurity + "]";
}
}
</code>Method 3: Retrieve from ApplicationContext each use
<code>static class SecurityFilterChain {
@Resource
private ApplicationContext context;
@Override
public String toString() {
return "SecurityFilterChain [httpSecurity=" + httpSecurity + "]";
}
public void test() {
// Get a fresh instance on each call
HttpSecurity httpSecurity = context.getBean(HttpSecurity.class);
}
}
</code>Method 4: Use @Lookup (proxy‑based, omitted for brevity)
See the section “Question 3”.
Question 2: What happens when a bean class is annotated with @Lazy?
If a class is marked with @Lazy, Spring does not instantiate it during container startup; the bean is created only when it is first requested, after which it is placed in the singleton pool.
<code>public void refresh() {
finishBeanFactoryInitialization(beanFactory);
}
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
beanFactory.preInstantiateSingletons();
}
public void preInstantiateSingletons() {
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// Skip beans that are lazy‑initialized
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
getBean(beanName);
}
}
}
</code>Question 3: Can an abstract class be registered as a bean?
Yes, if the abstract class contains a method annotated with @Lookup or if you manually set a method on its BeanDefinition.
<code>@Component
static abstract class SecurityManager {
public void execute() {
HttpSecurity httpSecurity = httpSecurity();
System.out.println(httpSecurity);
}
@Lookup
protected abstract HttpSecurity httpSecurity();
}
</code>Manual registration example:
<code>static abstract class SecurityProvider {
public void authority() {}
protected void httpSecurity() {}
}
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(SecurityProvider.class, bd -> {
AbstractBeanDefinition definition = (AbstractBeanDefinition) bd;
Method method = SecurityProvider.class.getDeclaredMethod("httpSecurity");
LookupOverride override = new LookupOverride(method, "");
definition.getMethodOverrides().addOverride(override);
});
</code>Question 4: How to inject multiple beans of the same type?
When several beans implement the same interface, Spring throws NoUniqueBeanDefinitionException . Resolve it by:
Specifying the bean name with @Resource(name="a") .
Using @Qualifier on the desired bean and the injection point.
Assigning @Priority (lower value = higher priority).
Marking one bean with @Primary .
<code>// By name
@Component static class A implements DAO {}
@Component static class B implements DAO {}
@Component static class Service {
@Resource(name = "a")
private DAO d;
}
// By qualifier
@Component static class A implements DAO {}
@Component @Qualifier static class B implements DAO {}
@Component static class Service {
@Resource
@Qualifier
private DAO d;
}
// By priority
@Component @Priority(2) static class A implements DAO {}
@Component @Priority(1) static class B implements DAO {}
@Component static class Service {
@Resource
private DAO d; // B is injected because it has higher priority
}
// By primary
@Component @Primary static class A implements DAO {}
</code>Question 5: How to trigger a transaction rollback without throwing an exception?
<code>@Transactional
public void save() {
Person person = new Person();
person.setAge(36);
person.setName("张三");
int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());
System.out.printf("保存Person 结果:%d%n", result);
// Mark the transaction for rollback
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
</code>Question 6: Can static fields be injected?
By default Spring does not support injection into static fields, but you can work around it.
Method 1: Manual injection after context startup
<code>private static PersonDAO dao;
@Resource private ApplicationContext context;
@PostConstruct
public void init() {
dao = context.getBean(PersonDAO.class);
}
</code>Method 2: Modify Spring source to allow static @Resource
<code>public class CommonAnnotationBeanPostProcessor {
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
if (field.isAnnotationPresent(Resource.class)) {
// Comment out the static‑field check in the source code
/* if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
} */
}
}
}
</code>Question 7: How to ensure transaction consistency in multithreaded scenarios?
Refer to the detailed article “Spring multithreading transaction consistency”. (The link and illustrative image have been omitted as they are not core content.)
Question 8: Does Spring only support @Transactional on public methods?
By default yes, but you can override this by providing a custom TransactionAttributeSource bean.
<code>@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// Setting false allows protected and package‑private methods to be proxied
return new AnnotationTransactionAttributeSource(false);
}
</code>Make sure the allowBeanDefinitionOverriding property of the BeanFactory is set to true .
Question 9: How to make classes annotated with a custom annotation managed by Spring?
Method 1: Custom @ComponentScan include filter
<code>@ComponentScan(useDefaultFilters = false, includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {PackComponent.class})
})
static class AppConfig {}
</code>Method 2: Custom type filter
<code>@ComponentScan(useDefaultFilters = false, includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {PackTypeFilter.class})
})
static class AppConfig {}
static class PackTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return metadataReader.getAnnotationMetadata().hasAnnotation(PackCount.class.getName());
}
}
</code>Question 10: How to give a class an additional interface without modifying its source?
Use an introduction interceptor (AOP) to add the interface at runtime.
<code>static interface CommonDAO { void play(); }
static class CustomIntroductionInterceptor implements IntroductionInterceptor, CommonDAO {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (this.implementsInterface(invocation.getMethod().getDeclaringClass())) {
return invocation.getMethod().invoke(this, invocation.getArguments());
}
return invocation.proceed();
}
@Override
public boolean implementsInterface(Class<?> intf) { return intf.isAssignableFrom(this.getClass()); }
@Override
public void play() { System.out.println("通用方法..."); }
}
@Component
static class CustomAopCreator extends AbstractAutoProxyCreator {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) {
return new Object[] { new DefaultIntroductionAdvisor(new CustomIntroductionInterceptor(), CommonDAO.class) };
}
}
// Test
try (GenericApplicationContext context = new GenericApplicationContext()) {
context.registerBean(CustomAopCreator.class);
context.registerBean("us", UserService.class);
context.refresh();
Object us = context.getBean("us");
System.out.println(Arrays.toString(us.getClass().getInterfaces()));
if (us instanceof CommonDAO) {
((CommonDAO) us).play();
}
((UserService) us).save();
}
</code>All questions are answered with code snippets and explanations.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.