What I needed to do was replace
<tx:annotation-driven/>
with
<tx:annotation-driven proxy-target-class="true"/>
This forces aspectj to use CGLIB for doing aspects instead of dynamic proxies – CGLIB doesn’t lose the annotation since it extends the class, whereas dynamic proxies just expose the implemented interface.