this is nice. what i do like about it * you can inject anything anywhere
what i dont like is * post constructor injection like youve mentioned - delegate or not it still sucks, a different pointcut is needed * you have to keep your variables transient - very easy mistake to make, otherwise big boo boo might happen if the dependency is serializable and you wont know until much later * you have to inject everything - ie i cant take an instance of injected service and pass it to some other component to use * i dont like @Configure on the entire object, i like per-field annotations on the fields * you have to have access to the java runtime args to install the weaver what is needed to fix this * a different aspect that wraps the bean in the wicket-proxy just like we do now - that should give you the ultimate freedom, but then what worries me is if tomcat will let you cluster objects loaded through aspectj classloader. -Igor Leszek Gawron-2 wrote: > > Hello, > I just wanted to share another way of injecting spring services into > wicket code. This one uses AOP. > > - o - Why another approach? - o - > > Using wicket-spring along with wicket-spring-annot works nicely for > components (althought you have to remember not initializing it yourself) > but does not work for other parts of application - models. Just ask your > self how many times you have put a spring service into wicket page only > to pass it to model constructed: > >> public class RankingPanel extends Panel { >> @SpringBean >> private LeagueService leagueService; >> >> public RankingPanel( String id, IModel leagueModel ) { >> super( id ); >> add( new ListView( "ranking", new RankingModel( leagueService, >> leagueModel ) ) { >> @Override >> protected void populateItem( ListItem listItem ) { >> PlayerRank rank = (PlayerRank) listItem.getModelObject(); >> listItem.add( new Label( "position", String.valueOf( >> rank.getPosition() ) ) ); >> listItem.add( new Label( "player", >> rank.getPlayer().getFullName() ) ); >> } >> } ); >> } >> } > > If you could have your model injected with appropriate service this > would probably be: > >> public class RankingPanel extends Panel { >> public RankingPanel( String id, IModel leagueModel ) { >> super( id ); >> add( new ListView( "ranking", new RankingModel( leagueModel ) ) { >> @Override >> protected void populateItem( ListItem listItem ) { >> PlayerRank rank = (PlayerRank) listItem.getModelObject(); >> listItem.add( new Label( "position", String.valueOf( >> rank.getPosition() ) ) ); >> listItem.add( new Label( "player", >> rank.getPlayer().getFullName() ) ); >> } >> } ); >> } >> } > > - o - What you'll need - o - > > - your current fancy project > - one fresh spring (at least 2.0 M1). I have used 2.0-rc2. > - one ripe aspectj (http://ibiblio.org/maven2/aspectj/) especially > aspectjrt and aspectjweaver. I have used 1.5.2. > - java 1.5 (there are probably some ways to do it with 1.4 - didn't > bother to try) > > - o - Implementation - o - > > Spring 2.0 M1 introduced @Configurable annotation [1]. We can use it to > inject spring beans into ANY object (not only created by spring but also > simply instantiated with new MyObject() ). > > Let's create a simple spring context: > >> <?xml version="1.0" encoding="UTF-8"?> >> <beans xmlns="http://www.springframework.org/schema/beans" >> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >> xmlns:aop="http://www.springframework.org/schema/aop" >> xmlns:tx="http://www.springframework.org/schema/tx" >> xsi:schemaLocation=" >> http://www.springframework.org/schema/beans >> http://www.springframework.org/schema/beans/spring-beans.xsd >> http://www.springframework.org/schema/tx >> http://www.springframework.org/schema/tx/spring-tx.xsd >> http://www.springframework.org/schema/aop >> http://www.springframework.org/schema/aop/spring-aop.xsd"> >> <bean >> class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/> >> >> <aop:spring-configured/> >> >> <bean id="leagueService" >> class="com.mobilebox.squasher.service.impl.LeagueServiceImpl >> autowire="byName"/> >> </beans> > > We have a single service declared here. What's more important is > <aop:spring-configured/> which simply saying turns on Spring AspectJ > machinery. Please refer to [1] for additional info. > > Now let's build a wicket model: > >> @Configurable(autowire = Autowire.BY_NAME, dependencyCheck = true) >> public class RankingModel extends LoadableDetachableModel { >> protected transient LeagueService leagueService; >> >> public void setLeagueService( LeagueService leagueService ) { >> this.leagueService = leagueService; >> } >> >> public RankingModel( IModel master ) { >> this.master = master; >> } >> >> @SuppressWarnings("unchecked") >> @Override >> protected Object load() { >> return new LinkedList( leagueService.getRanking( >> leagueService.loadRound( (Long) master.getObject( null ) ) ) ); >> } >> } > > Nothing shocking. Just remember to annotate your class with > @Configurable and make your reference to LeagueService transient. > > You need to put an additional file on the classpath to make it all work. > The location should be META-INF/aop.xml. Mine states: > >> <aspectj> >> <weaver options="-showWeaveInfo >> -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler"> >> <include within="com.mobilebox.squasher..*"/> >> </weaver> >> <aspects> >> <include >> within="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"/> >> </aspects> >> </aspectj> > > You can drop aspectj/weaver/@options. This is only for debugging > purposes. Remember to change aspectj/weaver/include/@within attribute to > match the package (and subpackages) you want weaved. > > That's all for coding. Let's run it. > > - o - Running - o - > > Put aspectjrt.jar on classpath. Create you JVM with additional option > (adjust aspectjweaver location): > >> java -javaagent:lib/aspectjweaver.jar >> com.mobilebox.squasher.launcher.JettyRunner > > If you run your container from withing Eclipse IDE put > '-javaagent:lib/aspectjweaver.jar' in 'VM arguments' editbox. > > That's all. AspectJ will weave your RankingModel class on load and > inject leagueService reference. In case of my project that was starting > in 5.5 seconds it added 2 more seconds. You can skip Load-Time Weaving > (LTW) if you just used aspectj compiler or weaved your classes offline. > > - o - Serialization - o - > > Exactly. Wicket is know with it's I-serialize-everything-like-crazy. > Turns out this is already handled. You need to make references to your > services *transient* so they are not serialized. Fortunately when > deserializing and object from stream AOP also kicks in so you have your > service reference reinjected. Pure fun! > > - o - Gotchas - o - > Every programming solutions got one. This one also. With current > @Configurable implementation services get injected AFTER the injectee is > created (after all constructors got invoked). That means this won't work: > >> public class LeaguePage extends BaseSquasherPage { >> private LeagueService leagueService; >> >> public LeaguePage( PageParameters parameters ) throws >> StringValueConversionException { >> Long leagueId = parameters.getLong( "id" ); >> >> // nasty NPE here! >> League league = leagueService.loadLeague( leagueId ); >> >> add( new Label( "leagueName", league.getName() ) ); >> } >> } > > You can do 2 things: > - implement another AOP pointcut so services get injected before > construction (do not know if that is possible, will dig further) > - be smarter than AOP itself: > >> @Configurable(autowire = Autowire.BY_NAME, dependencyCheck = true) >> public class LeagueServiceDelegate implements Serializable, LeagueService >> { >> private transient LeagueService leagueService; >> >> public void setLeagueService( LeagueService leagueService ) { >> this.leagueService = leagueService; >> } >> >> public League loadLeague( Long id ) { >> return leagueService.loadLeague( id ); >> } >> } > >> public class LeaguePage extends BaseSquasherPage { >> private LeagueServiceDelegate leagueService = new >> LeagueServiceDelegate(); >> >> public LeaguePage( PageParameters parameters ) throws >> StringValueConversionException { >> Long leagueId = parameters.getLong( "id" ); >> >> // the delegate is already up and running >> League league = leagueService.loadLeague( leagueId ); >> >> add( new Label( "leagueName", league.getName() ) ); >> } >> } > > With eclipse you can create such delegate in 30 seconds no matter how > big the target service is. (generate setter, generate delegates). > > - o - Post scriptum - o - > > If anyone is interested I will blogify this entry along with sample > working project to test. Just let me know. > > [1] > http://www.springframework.org/docs/reference/aop.html#aop-atconfigurable > > -- > Leszek Gawron > > ------------------------------------------------------------------------- > Take Surveys. Earn Cash. Influence the Future of IT > Join SourceForge.net's Techsay panel and you'll get the chance to share > your > opinions on IT & business topics through brief surveys -- and earn cash > http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV > _______________________________________________ > Wicket-user mailing list > Wicket-user@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/wicket-user > > -- View this message in context: http://www.nabble.com/-tutorial--Wicket-%2B-Spring-integration---revisited-tf2375652.html#a6622688 Sent from the Wicket - User mailing list archive at Nabble.com. ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys -- and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ Wicket-user mailing list Wicket-user@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/wicket-user