/*
* Copyright 2002-2007 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.aspectj;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import junit.framework.TestCase;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.TestBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.MailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
* @author Adrian Colyer
* @author Rod Johnson
* @author Ramnivas Laddad
* @author Juergen Hoeller
*/
public class BeanConfigurerTests extends TestCase {
private ClassPathXmlApplicationContext context;
@Override
protected void setUp() throws Exception {
this.context =
new ClassPathXmlApplicationContext("org/springframework/beans/factory/aspectj/beanConfigurerTests.xml");
}
public void testConfigurableWithExplicitBeanName() {
ShouldBeConfiguredBySpring myObject = new ShouldBeConfiguredBySpring();
assertEquals("Rod", myObject.getName());
}
public void testInjectionAfterRefresh() {
context.refresh();
ShouldBeConfiguredBySpring myObject = new ShouldBeConfiguredBySpring();
assertEquals("Rod", myObject.getName());
}
public void testWithoutAnnotation() {
ShouldNotBeConfiguredBySpring myObject = new ShouldNotBeConfiguredBySpring();
assertNull("Name should not have been set", myObject.getName());
}
public void testConfigurableWithImplicitBeanName() {
ShouldBeConfiguredBySpringUsingTypeNameAsBeanName myObject =
new ShouldBeConfiguredBySpringUsingTypeNameAsBeanName();
assertEquals("Rob", myObject.getName());
}
public void testConfigurableUsingAutowireByType() {
ShouldBeConfiguredBySpringUsingAutowireByType myObject =
new ShouldBeConfiguredBySpringUsingAutowireByType();
assertNotNull(myObject.getFriend());
assertEquals("Ramnivas", myObject.getFriend().getName());
}
public void testConfigurableUsingAutowireByName() {
ValidAutowireByName myObject = new ValidAutowireByName();
assertNotNull(myObject.getRamnivas());
assertEquals("Ramnivas", myObject.getRamnivas().getName());
}
public void testInvalidAutowireByName() {
try {
new InvalidAutowireByName();
fail("Autowire by name cannot work");
}
catch (UnsatisfiedDependencyException ex) {
// Ok
}
}
public void testNewAspectAppliesToArbitraryNonAnnotatedPojo() {
ArbitraryExistingPojo aep = new ArbitraryExistingPojo();
assertNotNull(aep.friend);
assertEquals("Ramnivas", aep.friend.getName());
}
public void testNewAspectThatWasNotAddedToSpringContainer() {
try{
new ClassThatWillNotActuallyBeWired();
}
catch (IllegalStateException ex) {
assertTrue(ex.getMessage().indexOf("BeanFactory") != -1);
}
}
public void testInjectionOnDeserialization() throws Exception {
ShouldBeConfiguredBySpring domainObject = new ShouldBeConfiguredBySpring();
domainObject.setName("Anonymous");
ShouldBeConfiguredBySpring deserializedDomainObject =
serializeAndDeserialize(domainObject);
assertEquals("Dependency injected on deserialization","Rod",deserializedDomainObject.getName());
}
public void testInjectionOnDeserializationForClassesThatContainsPublicReadResolve() throws Exception {
ShouldBeConfiguredBySpringContainsPublicReadResolve domainObject = new ShouldBeConfiguredBySpringContainsPublicReadResolve();
domainObject.setName("Anonymous");
ShouldBeConfiguredBySpringContainsPublicReadResolve deserializedDomainObject =
serializeAndDeserialize(domainObject);
assertEquals("Dependency injected on deserialization","Rod",deserializedDomainObject.getName());
assertEquals("User readResolve should take precedence", 1, deserializedDomainObject.readResolveInvocationCount);
}
// See ShouldBeConfiguredBySpringContainsPrivateReadResolve
// public void testInjectionOnDeserializationForClassesThatContainsPrivateReadResolve() throws Exception {
// ShouldBeConfiguredBySpringContainsPrivateReadResolve domainObject = new ShouldBeConfiguredBySpringContainsPrivateReadResolve();
// domainObject.setName("Anonymous");
// ShouldBeConfiguredBySpringContainsPrivateReadResolve deserializedDomainObject =
// serializeAndDeserialize(domainObject);
// assertEquals("Dependency injected on deserialization","Rod",deserializedDomainObject.getName());
// }
public void testNonInjectionOnDeserializationForSerializedButNotConfigured() throws Exception {
SerializableThatShouldNotBeConfiguredBySpring domainObject = new SerializableThatShouldNotBeConfiguredBySpring();
domainObject.setName("Anonymous");
SerializableThatShouldNotBeConfiguredBySpring deserializedDomainObject =
serializeAndDeserialize(domainObject);
assertEquals("Dependency injected on deserialization","Anonymous",deserializedDomainObject.getName());
}
public void testSubBeanConfiguredOnlyOnce() throws Exception {
SubBean subBean = new SubBean();
assertEquals("Property injected more than once", 1, subBean.setterCount);
}
public void testSubBeanConfiguredOnlyOnceForPreConstruction() throws Exception {
SubBeanPreConstruction subBean = new SubBeanPreConstruction();
assertEquals("Property injected more than once", 1, subBean.setterCount);
}
public void testSubSerializableBeanConfiguredOnlyOnce() throws Exception {
SubSerializableBean subBean = new SubSerializableBean();
assertEquals("Property injected more than once", 1, subBean.setterCount);
subBean.setterCount = 0;
SubSerializableBean deserializedSubBean = serializeAndDeserialize(subBean);
assertEquals("Property injected more than once", 1, deserializedSubBean.setterCount);
}
public void testPreConstructionConfiguredBean() {
PreConstructionConfiguredBean bean = new PreConstructionConfiguredBean();
assertTrue("Injection didn't occur before construction", bean.preConstructionConfigured);
}
public void testPreConstructionConfiguredBeanDeserializationReinjection() throws Exception {
PreConstructionConfiguredBean bean = new PreConstructionConfiguredBean();
PreConstructionConfiguredBean deserialized = serializeAndDeserialize(bean);
assertEquals("Injection didn't occur upon deserialization", "ramnivas", deserialized.getName());
}
public void testPostConstructionConfiguredBean() {
PostConstructionConfiguredBean bean = new PostConstructionConfiguredBean();
assertFalse("Injection occurred before construction", bean.preConstructionConfigured);
}
public void testPostConstructionConfiguredBeanDeserializationReinjection() throws Exception {
PostConstructionConfiguredBean bean = new PostConstructionConfiguredBean();
PostConstructionConfiguredBean deserialized = serializeAndDeserialize(bean);
assertEquals("Injection didn't occur upon deserialization", "ramnivas", deserialized.getName());
}
public void testInterfaceDrivenDependencyInjection() {
MailClientDependencyInjectionAspect.aspectOf().setMailSender(new JavaMailSenderImpl());
Order testOrder = new Order();
assertNotNull("Interface driven injection didn't occur for direct construction", testOrder.mailSender);
}
public void testInterfaceDrivenDependencyInjectionMultipleInterfaces() {
MailClientDependencyInjectionAspect.aspectOf().setMailSender(new JavaMailSenderImpl());
PaymentProcessorDependencyInjectionAspect.aspectOf().setPaymentProcessor(new PaymentProcessor());
ShoppingCart testCart = new ShoppingCart();
assertNotNull("Interface driven injection didn't occur for direct construction", testCart.mailSender);
assertNotNull("Interface driven injection didn't occur for direct construction", testCart.paymentProcessor);
}
public void testInterfaceDrivenDependencyInjectionUponDeserialization() throws Exception {
MailClientDependencyInjectionAspect.aspectOf().setMailSender(new JavaMailSenderImpl());
Order testOrder = new Order();
Order deserializedOrder = serializeAndDeserialize(testOrder);
assertNotNull("Interface driven injection didn't occur for deserialization", testOrder.mailSender);
}
public void testFieldAutoWiredAnnotationInjection() {
FieldAutoWiredServiceBean bean = new FieldAutoWiredServiceBean();
assertNotNull(bean.testService);
}
public void testMethodAutoWiredAnnotationInjection() {
MethodAutoWiredServiceBean bean = new MethodAutoWiredServiceBean();
assertNotNull(bean.testService);
}
public void testMultiArgumentMethodAutoWiredAnnotationInjection() {
MultiArgumentMethodAutoWiredServiceBean bean = new MultiArgumentMethodAutoWiredServiceBean();
assertNotNull(bean.testService);
assertNotNull(bean.paymentService);
}
@SuppressWarnings("unchecked")
private <T> T serializeAndDeserialize(T serializable) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(serializable);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T)ois.readObject();
}
@Configurable("beanOne")
private static class ShouldBeConfiguredBySpring implements Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable("beanOne")
private static class ShouldBeConfiguredBySpringContainsPublicReadResolve implements Serializable {
private String name;
private int readResolveInvocationCount = 0;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public Object readResolve() throws ObjectStreamException {
readResolveInvocationCount++;
return this;
}
}
// Won't work until we use hasmethod() experimental pointcut in AspectJ.
// @Configurable("beanOne")
// private static class ShouldBeConfiguredBySpringContainsPrivateReadResolve implements Serializable {
//
// private String name;
//
// public void setName(String name) {
// this.name = name;
// }
//
// public String getName() {
// return this.name;
// }
//
// private Object readResolve() throws ObjectStreamException {
// return this;
// }
// }
private static class ShouldNotBeConfiguredBySpring {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
private static class SerializableThatShouldNotBeConfiguredBySpring implements Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable
private static class ShouldBeConfiguredBySpringUsingTypeNameAsBeanName {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable(autowire=Autowire.BY_TYPE)
private static class ShouldBeConfiguredBySpringUsingAutowireByType {
private TestBean friend = null;
public TestBean getFriend() {
return friend;
}
public void setFriend(TestBean friend) {
this.friend = friend;
}
}
@Configurable(autowire=Autowire.BY_NAME)
private static class ValidAutowireByName {
private TestBean friend = null;
public TestBean getRamnivas() {
return friend;
}
public void setRamnivas(TestBean friend) {
this.friend = friend;
}
}
@Configurable(autowire=Autowire.BY_NAME, dependencyCheck=true)
private static class InvalidAutowireByName {
private TestBean friend;
public TestBean getFriend() {
return friend;
}
public void setFriend(TestBean friend) {
this.friend = friend;
}
}
private static class ArbitraryExistingPojo {
private TestBean friend;
public void setFriend(TestBean f) {
this.friend = f;
}
}
public static class CircularFactoryBean implements FactoryBean{
public CircularFactoryBean() {
ValidAutowireByName autowired = new ValidAutowireByName();
assertNull(autowired.getRamnivas());
}
public Object getObject() throws Exception {
return new TestBean();
}
public Class getObjectType() {
return TestBean.class;
}
public boolean isSingleton() {
return false;
}
}
@Configurable
private static class BaseBean {
public int setterCount;
private String name;
public void setName(String name) {
this.name = name;
setterCount++;
}
}
private static class SubBean extends BaseBean {
}
@Configurable(preConstruction=true)
private static class SubBeanPreConstruction extends BaseBean {
}
@Configurable
private static class BaseSerializableBean implements Serializable {
public int setterCount;
private String name;
public void setName(String name) {
this.name = name;
setterCount++;
}
}
private static class SubSerializableBean extends BaseSerializableBean {
}
@Aspect
private static class WireArbitraryExistingPojo extends AbstractBeanConfigurerAspect {
@Pointcut("initialization(ArbitraryExistingPojo.new(..)) && this(beanInstance)")
protected void beanCreation(Object beanInstance){
}
}
@Aspect
private static class AspectThatWillNotBeUsed extends AbstractBeanConfigurerAspect {
@Pointcut("initialization(ClassThatWillNotActuallyBeWired.new(..)) && this(beanInstance)")
protected void beanCreation(Object beanInstance){
}
}
private static aspect MailClientDependencyInjectionAspect extends AbstractInterfaceDrivenDependencyInjectionAspect {
private MailSender mailSender;
public pointcut inConfigurableBean() : within(MailSenderClient+);
public void configureBean(Object bean) {
((MailSenderClient)bean).setMailSender(this.mailSender);
}
declare parents: MailSenderClient implements ConfigurableObject;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
}
private static aspect PaymentProcessorDependencyInjectionAspect extends AbstractInterfaceDrivenDependencyInjectionAspect {
private PaymentProcessor paymentProcessor;
public pointcut inConfigurableBean() : within(PaymentProcessorClient+);
public void configureBean(Object bean) {
((PaymentProcessorClient)bean).setPaymentProcessor(this.paymentProcessor);
}
declare parents: PaymentProcessorClient implements ConfigurableObject;
public void setPaymentProcessor(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
public static interface MailSenderClient {
public void setMailSender(MailSender mailSender);
}
public static interface PaymentProcessorClient {
public void setPaymentProcessor(PaymentProcessor paymentProcessor);
}
public static class PaymentProcessor {
}
public static class Order implements MailSenderClient, Serializable {
private transient MailSender mailSender;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
}
public static class ShoppingCart implements MailSenderClient, PaymentProcessorClient {
private transient MailSender mailSender;
private transient PaymentProcessor paymentProcessor;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setPaymentProcessor(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
private static class ClassThatWillNotActuallyBeWired {
}
@Configurable
private static class PreOrPostConstructionConfiguredBean implements Serializable {
private transient String name;
protected transient boolean preConstructionConfigured;
transient int count;
public PreOrPostConstructionConfiguredBean() {
preConstructionConfigured = (this.name != null);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable(preConstruction=true)
public static class PreConstructionConfiguredBean extends PreOrPostConstructionConfiguredBean {
}
@Configurable(preConstruction=false)
private static class PostConstructionConfiguredBean extends PreOrPostConstructionConfiguredBean {
}
@Configurable
public static class FieldAutoWiredServiceBean {
@Autowired transient private TestService testService;
}
@Configurable
public static class MethodAutoWiredServiceBean {
transient private TestService testService;
@Autowired
public void setTestService(TestService testService) {
this.testService = testService;
}
}
@Configurable
public static class MultiArgumentMethodAutoWiredServiceBean {
transient private TestService testService;
transient private PaymentService paymentService;
@Autowired
public void setDependencies(TestService testService, PaymentService paymentService) {
this.testService = testService;
this.paymentService = paymentService;
}
}
public static class TestService {
}
public static class PaymentService {
}
}