6
votes

I've been trying to get this to work for a few days and have been failing miserably at it. I cannot seem to get my class cast properly to an Introduced interface. I'm using Spring 3.0.5.

Does anyone have a complete working example of a project that uses @DeclareParents? Or the XML equivalent? I have found a bunch of snippets on the web, but haven't had success in getting those to work either.

This is my first time trying Introductions, but am having a lot of difficulty getting this to work. I've read through the Spring docs and the AspectJ docs as well as well as the forums but still cannot get this to work. I thought created the classes/interfaces correctly, but when I try to cast the Advised object to the interface, I get a cast error.

Target:

@Entity
@Table(name = "item")
public class Item implements java.io.Serializable {

    private long id;
    private Integer mainType;
    private Integer subType;
    private String brandName;
    private String itemName;
    private Date releaseDate;
    private Date retireDate;
    private String fileName;
    private String thumbnailName;
    private String dimension;
    private boolean active;
    private boolean unlocked;
    private Integer unlockCost;
    private boolean owned;

    public Item() {
    }

    ...
    // bunch of getters and setters
    ...
}

Interface:

public interface AbstractBaseEntity {

    /**
     * @return the customAttributes
     */
    public Object getCustomAttribute(String name);

    /**
     * @param customAttributes the customAttributes to set
     */
    public void setCustomAttribute(String name, Object value);

}

Implementation:

public class AbstractBaseEntityImpl implements AbstractBaseEntity {

    /**
     * Map of custom attributes that can be mapped for the specific entity
     */
    @Transient
    protected Map customAttributes = new ConcurrentHashMap();

    /**
     * @return the customAttributes
     */
    public Object getCustomAttribute( String name ) {
        return customAttributes.get(name);
    }

    /**
     * @param customAttributes the customAttributes to set
     */
    public void setCustomAttribute(String name, Object value) {
        this.customAttributes.put(name, value);
    }

}

Aspect:

    @DeclareParents(value="com.fwl.domain.model.*+", defaultImpl=AbstractBaseEntityImpl.class)
    public AbstractBaseEntity mixin;

However, when I pass my introduced object to a method as an Object parameter, checking if it is an instanceof AbstractBaseEntity, it returns false.

    public void localize(Object entity, Locale locale) {
        List cachedFields;
            if (entity == null)
                // do nothing
                return;

            // check if the entity is already localized
            if( entity instanceof AbstractBaseEntity)
                // already localized so nothing to do
                return;

...
...
}

Is there anyway to ensure that the introduction is being done properly? Anyway to determine why I cannot cast it as an AbstractBaseEntity?

Any help would be greatly appreciated.

Thanks,

Eric

1

1 Answers

5
votes

I have working example, but I know that the question is very old.
Basic Interface:

package pl.beans;

public interface Performance {
    public void perform();
}

implementation of Performance:

package pl.beans;

import java.util.Random;

import org.springframework.stereotype.Component;

@Component
public class Actor implements Performance {

private static final String WHO = "Actor: ";

@Override
public void perform() {
    System.out.println(WHO+"Making some strange things on scene");
    int result = new Random().nextInt(5);
    if(result == 0) {
        throw new IllegalArgumentException(WHO+"Actor falsified");
    }

}

}

New interface:

package pl.introduction;

public interface Crazy {

    public void doSomeCrazyThings();

}

Implementation of new interface:

package pl.introduction;

public class CrazyActor implements  Crazy {

@Override
public void doSomeCrazyThings() {
    System.out.println("Ługabuga oooo  'Performer goes crazy!'");

    }

}

Aspect:

package pl.introduction;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

@Aspect
public class CrazyIntroducer {

    @DeclareParents(value="pl.beans.Performance+", defaultImpl=pl.introduction.CrazyActor.class)
    public static Crazy shoutable;

}

JavaConfig:

package pl.introduction;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import pl.beans.Actor;
import pl.beans.Performance;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class Config {

    @Bean
    public CrazyIntroducer introducer () {
        return new CrazyIntroducer();
    }

    @Bean
    public Performance performance() {
        return new Actor();
    }
}

And test which shows that introduction working:

package pl.introduction;

import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import pl.beans.Performance;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = pl.introduction.Config.class)
public class IntroductionTest {

@Autowired
private Performance performance;

@Test
public void shoutTest() {
    try {
        performance.perform();

    } catch (IllegalArgumentException e) {
        System.out.println(e);
    }

    assertTrue(performance instanceof Crazy);
    ((Crazy) performance).doSomeCrazyThings();

    }
}