From e3d1a1dda22723fc896bfc96c6db57c500faf208 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 20 Feb 2015 21:53:34 +0100 Subject: [PATCH] @Resource injection points support @Lazy as well Issue: SPR-12654 --- .../CommonAnnotationBeanPostProcessor.java | 49 ++++++++++- ...ommonAnnotationBeanPostProcessorTests.java | 85 ++++++++++++++++++- 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java index 592af8ef932..ffb9b55320b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,8 @@ import javax.xml.ws.Service; import javax.xml.ws.WebServiceClient; import javax.xml.ws.WebServiceRef; +import org.springframework.aop.TargetSource; +import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; @@ -414,6 +416,44 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean return new InjectionMetadata(clazz, elements); } + /** + * Obtain a lazily resolving resource proxy for the given name and type, + * delegating to {@link #getResource} on demand once a method call comes in. + * @param element the descriptor for the annotated field/method + * @param requestingBeanName the name of the requesting bean + * @return the resource object (never {@code null}) + * @since 4.2 + * @see #getResource + * @see Lazy + */ + protected Object buildLazyResourceProxy(final LookupElement element, final String requestingBeanName) { + TargetSource ts = new TargetSource() { + @Override + public Class getTargetClass() { + return element.lookupType; + } + @Override + public boolean isStatic() { + return false; + } + @Override + public Object getTarget() { + return getResource(element, requestingBeanName); + } + @Override + public void releaseTarget(Object target) { + } + }; + ProxyFactory pf = new ProxyFactory(); + pf.setTargetSource(ts); + if (element.lookupType.isInterface()) { + pf.addInterface(element.lookupType); + } + ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null); + return pf.getProxy(classLoader); + } + /** * Obtain the resource object for the given name and type. * @param element the descriptor for the annotated field/method @@ -527,6 +567,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean */ private class ResourceElement extends LookupElement { + private final boolean lazyLookup; + public ResourceElement(Member member, AnnotatedElement ae, PropertyDescriptor pd) { super(member, pd); Resource resource = ae.getAnnotation(Resource.class); @@ -552,11 +594,14 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean this.name = resourceName; this.lookupType = resourceType; this.mappedName = resource.mappedName(); + Lazy lazy = ae.getAnnotation(Lazy.class); + this.lazyLookup = (lazy != null && lazy.value()); } @Override protected Object getResourceToInject(Object target, String requestingBeanName) { - return getResource(this, requestingBeanName); + return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : + getResource(this, requestingBeanName)); } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java index c44662b418a..92c684543fe 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -437,6 +437,60 @@ public class CommonAnnotationBeanPostProcessorTests { assertTrue(bean.destroy2Called); } + @Test + public void testLazyResolutionWithResourceField() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceFieldInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + LazyResourceFieldInjectionBean bean = (LazyResourceFieldInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bf.containsSingleton("testBean")); + bean.testBean.setName("notLazyAnymore"); + assertTrue(bf.containsSingleton("testBean")); + TestBean tb = (TestBean) bf.getBean("testBean"); + assertEquals("notLazyAnymore", tb.getName()); + } + + @Test + public void testLazyResolutionWithResourceMethod() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceMethodInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + LazyResourceMethodInjectionBean bean = (LazyResourceMethodInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bf.containsSingleton("testBean")); + bean.testBean.setName("notLazyAnymore"); + assertTrue(bf.containsSingleton("testBean")); + TestBean tb = (TestBean) bf.getBean("testBean"); + assertEquals("notLazyAnymore", tb.getName()); + } + + @Test + public void testLazyResolutionWithCglibProxy() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceCglibInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + LazyResourceCglibInjectionBean bean = (LazyResourceCglibInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bf.containsSingleton("testBean")); + bean.testBean.setName("notLazyAnymore"); + assertTrue(bf.containsSingleton("testBean")); + TestBean tb = (TestBean) bf.getBean("testBean"); + assertEquals("notLazyAnymore", tb.getName()); + } + public static class AnnotatedInitDestroyBean { @@ -716,6 +770,35 @@ public class CommonAnnotationBeanPostProcessorTests { } + private static class LazyResourceFieldInjectionBean { + + @Resource @Lazy + private ITestBean testBean; + } + + + private static class LazyResourceMethodInjectionBean { + + private ITestBean testBean; + + @Resource @Lazy + public void setTestBean(ITestBean testBean) { + this.testBean = testBean; + } + } + + + private static class LazyResourceCglibInjectionBean { + + private TestBean testBean; + + @Resource @Lazy + public void setTestBean(TestBean testBean) { + this.testBean = testBean; + } + } + + @SuppressWarnings("unused") private static class NullFactory {