Spring, aspekty, rzutowanie beana

2015-07-19 22:42
D3X
0

Klasa main:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("com/springinaction/springidol/spring-idol.xml");

        Instrumentalist performer  = (Instrumentalist) context.getBean("eddie");
        try {
            performer.perform();
        } catch (PerformanceException e) {
            e.printStackTrace();
        }
    }
}

Interfejs Performer:

public interface Performer {
  void perform() throws PerformanceException;
}

Interfejs Instrument:

public interface Instrument {
  public void play();
}

Klasa Instrumentalist:

public class Instrumentalist implements Performer {
  public void perform() throws PerformanceException {
    instrument.play();
  }

  private Instrument instrument;

  public void setInstrument(Instrument instrument) {
    this.instrument = instrument;
  }

  public Instrument getInstrument() {
    return instrument;
  }
}

Klasa Gitara:

public class Guitar implements Instrument {
  public void play() {
    System.out.println("Strum strum strum");
  }
}
  <bean id="eddie"
      class="com.springinaction.springidol.Instrumentalist">
    <property ref="gitara" name="instrument"/>
  </bean>

    <bean id="gitara" class="com.springinaction.springidol.Guitar" />

I taki kod działa.

Dodaję sobie klasę Audience:

public class Audience {
  public void takeSeats() { 
    System.out.println("The audience is taking their seats.");
  }

  public void turnOffCellPhones() { 
    System.out.println("The audience is turning off their cellphones");
  }

  public void applaud() { 
    System.out.println("CLAP CLAP CLAP CLAP CLAP");
  }

  public void demandRefund() { 
    System.out.println("Boo! We want our money back!");
  }
}

i dodaję do configuracji springa aspekty:

<aop:config>
  <aop:aspect ref="audience"><!--<co id="co_refAudienceBean"/>-->

    <aop:before pointcut=
         "execution(* com.springinaction.springidol.Performer.perform(..))"
      method="takeSeats" /> <!--<co id="co_beforePointcut"/>-->

    <aop:before pointcut=
         "execution(* com.springinaction.springidol.Performer.perform(..))"
      method="turnOffCellPhones" /> <!--<co id="co_beforePointcut2"/>-->

    <aop:after-returning pointcut=
         "execution(* com.springinaction.springidol.Performer.perform(..))"
      method="applaud" /> <!--<co id="co_afterPointcut"/>-->

    <aop:after-throwing pointcut=
         "execution(* com.springinaction.springidol.Performer.perform(..))"
      method="demandRefund" /> <!--<co id="co_afterThrowingPointcut"/>-->

 </aop:aspect>
</aop:config>

I aplikacja się wywala:

INFO: Pre-instantiating singletons in org.s[email protected]61b4607f: defining beans [eddie,gitara,audience,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,org.springframework.aop.aspectj.AspectJPointcutAdvisor#1,org.springframework.aop.aspectj.AspectJPointcutAdvisor#2,org.springframework.aop.aspectj.AspectJPointcutAdvisor#3]; root of factory hierarchy
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.springinaction.springidol.Instrumentalist
    at com.springinaction.springidol.Main.main(Main.java:13)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

Jeśli zamienię na taką linikę w Main:

Performer performer  = (Performer) context.getBean("eddie");

to działa. Czy mógłby to ktoś wyjaśnić?

edytowany 1x, ostatnio: D3X, 2015-07-19 22:42

Pozostało 580 znaków

2015-07-19 23:05
  1. Czemu nie uzywasz adnotacji tylko piszesz jak w Springu 2.X?
  2. Problem polega na tym, że aspekty to nie jest żadna "magia". Aspekty działają tak, że Spring tworzy sobie obiekt "proxy" który ma w sobie referencje do twojego prawdziwego obiektu i np. dla before robi
public void pointcutowaMetoda(){
    twojeInstrukcjeBefore();
    prawdziwyObiekt.pointcutowaMeotda();
}

Jak widać taki obiekt proxy musi imitować interfejs klasy którą obsługuje. I tak też standardowo robi (tzn jest klasą która implementuje dany interfejs). Problem w tym że ty próbujesz go przypisać do referencji na konkretną klasę a nie interfejs a tego zrobić już nie wolno. Załóżmy że robisz AOP dla klasy ArrayList. To wygenerowane AOP wygląda wtedy tak:

class AOPArrayListProxy implements List{}

I przypisać takiego obiektu do ArrayList nie możesz, ale do List już jak najbardziej.

Szerzej ten problem jest opisany tutaj: http://docs.spring.io/spring/[...]html#aop-introduction-proxies

Można wymusić na Springu wykorzystanie cgliba do generowania class-proxy zamiast interface-proxy, wtedy wygenerowana klasa będzie wyglądała tak:

class AOPArrayListProxy extends ArrayList{}

i będzie można ją przypisać już do referencji na konkretną klasę.

Generalnie ten problem jest bardziej powszechnie spotykany kiedy używa się np. @Transactional które jest implementowane za pomocą AOP.


Masz problem? Pisz na forum, nie do mnie. Nie masz problemów? Kup komputer...
edytowany 3x, ostatnio: furious programming, 2015-07-19 23:29
Pokaż pozostałe 2 komentarze
@Shalom z tym @Transactional jaki problem masz na myśli? - karolinaa 2015-07-19 23:41
że metoda bez transakcji wywołująca w tej samej klasie inną z @Transactional nie zadziała czy o czym? - karolinaa 2015-07-19 23:41
@karolinaa o tym samym co tutaj teraz opisujemy. Transactional to jest aop proxy typu around i próba wstrzykiwania klasy z metodami oznaczonymi jako @Transactional poprzez klasę a nie interfejs w standardowej konfiguracji nie zadziała. - Shalom 2015-07-19 23:43
dzięki dobrze wiedzieć - karolinaa 2015-07-19 23:46
@Shalom To samo. Deklaracje beanów i wstrzykiwanie przez xml. Ponieważ przechodzę przez książkę. I wiem wiem, że są adnotacje @Autowired (@Inject itd). - D3X 2015-07-20 10:53

Pozostało 580 znaków

2015-07-20 13:27
D3X
0

@Shalom Skoro już poruszyliśmy ten temat.

To masz na myśli, żeby wszystko robić za pomocą adnotacji? Ustawić tylko taki "czysty" xml springowy i wstrzyknięcia czy konfiguracje i skanowanie komponentów "jechać" na adnotacjach?

Pozostało 580 znaków

2015-07-20 13:36
0

// uwaga protip:
.. A czy wiesz, że jak wpiszesz w google "spring konfiguracja xml vs adnotacje 4programmers" , "spring annotations or xml" , "spring anotations vs xml" to znajdzies to co chcesz
samemu i szybciej i więcej i wgl? Tego się naucz, bo to też ważne!


PROGRAMY NA ZAMÓWIENIE, ZALICZENIA STUDENCKIE, KONFIGURACJA SERWERÓW, SYSTEMÓW I BAZ DANYCH, STRONY INTERNETOWE, POMOC W PROGRAMOWANIU, POPRAWIENIE I OPTYMALIZACJA APLIKACJI
JAVA, C++, LINUX, WWW, SQL, PYTHON
POSIADAM KOMERCYJNE DOŚWIADCZENIE
TANIO, SZYBKO I PORZĄDNIE
Z KOMENTARZAMI OBJAŚNIAJĄCYMI KOD
PISZ NA PRYWATNĄ WIADOMOŚĆ
CENY JUŻ OD 49,99ZŁ ZA PROGRAM
ZAJMIJ SIĘ TYM CO CIĘ NAPRAWDĘ INTERESUJE!
Święta prawda. Praca nad samodzielnością to u mnie najważniejsza sprawa :) - D3X 2015-07-20 14:24

Pozostało 580 znaków

2015-07-20 14:26
D3X
0

@karolinaa o i mam.

@Shalom napisał:

A ja tam jestem oldschoolowy i uważam że główną konfiguracje w xmlu a reszta adnotacjami w kodzie

no widzisz. pięknie - karolinaa 2015-07-20 14:28

Pozostało 580 znaków

Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0