This week I discovered why @Transactional(readOnly=true) is not cost free.
The whole concept of read only transaction is platform-dependent and confusing to start with.
A read-only transaction with proxies / aspects will still grab a connection and install it in a thread local to implement the readonly transaction. While this fulfils what you said, it often will not be what you want - it leads to connection pool thrashing if you put in onto a 'hot' method.
In the case I saw this week, we had put the annotation on a cache-backed database-free method in 'sympathy' while adding the annotation for a couple of new write-only api methods. This cache method turned out to be called 14 times per page and thrashed the database pool so much that even with pool retries enabled we were getting pool errors and throughput losses.
So, my current thinking is not to annotate read-only transactions and only annotate the top-level transaction entry points, with proxies around all your Daos to ensure you only get one transaction. And never add annotations in sympathy without a good reason other than 'it can't hurt'. Because maybe it will!