Spring’s
annotations: Proxy
José Canfrán (Osoco)
September 2015
Programming Gotchas & Pitfalls Series
@Transactional1
Ejemplo
package es.osoco.lightTalk
import org.springframework.transaction.annotation.Transactional
class TransactionalService {
@Transactional
def firstMethod() {
anotherMethod()
// code
}
@Transactional // @Transactional(propagation = Propagation.REQUIRES_NEW)
private anotherMethod() {
// code
}
}
Spring: Proxy
La anotación Transactional de Spring crea un proxy en tiempo de ejecución de la clase, el cual tiene a
su vez una instancia de la clase original.
Todas las llamadas a métodos transaccionales son interceptadas por el proxy, que es el encargado
de iniciar una transacción, unirse a la existente… y llamar al método del servicio.
Pero si se hace una llamada a métodos del mismo servicio, el proxy no podrá interceptarla al
encontrarnos por debajo de él, en su instancia del servicio y no se ejecutará la condiciones de
transacción que hayamos puesto.
Posibles soluciones
● Grails < 2.3.1
○ Usar .withTransaction()
● Grails > 2.3.1
○ Usar @grails.transaction.Transactional: Durante la compilación
mediante una transformación AST, se crea un nuevo método por cada
método anotado dentro de una GrailsTransactionTemplate que ejecuta
el código del original con los ajustes de transacción.Ya no existe proxy y
como se ha hecho la misma operación con todos los métodos no hay
problemas con las llamadas directas. (Puede fallar con la anotación
@CompileStatic)
@Cacheable2
Ejemplo
package es.osoco.lightTalk
import org.springframework.cache.annotation.Cacheable
class CacheableService {
def firstMethod() {
anotherMethod(param1, param2)
// code
}
@Cacheable
private anotherMethod(param1, param2) {
// code
}
}
Solución3
Obtener proxy: En el servicio
package es.osoco.lightTalk
import org.springframework.cache.annotation.Cacheable
class CacheableService {
def firstMethod() {
proxy.anotherMethod(param1, param2)
// code
}
@Cacheable
private anotherMethod(param1, param2) {
// code
}
private getProxy() {
def proxy = grailsApplication.mainContext.cacheableService
}
}
De este modo obtenemos el proxy
para llamar al método a través de él.
Obtener proxy: Genérico
class BootStrap {
def grailsApplication
def init = { servletContext ->
for( sc in grailsApplication.serviceClasses) {
sc.clazz.metaClass.getMyProxy = { ->
grailsApplication.mainContext.getBean(sc.propertyName)
}
}
}
De este modo estará disponible la variable myProxy con el proxy correspondiente en cada
servicio.
Bibliografía
◉ Programming Grails (0’Reilly)
Burt Beckwith, ISBN: 9 781449 323936
◉ Documentación Grails
https://grails.org/documentation.html
Any questions ?
You can find me at
◉ @jose_cg
Thanks!
This work is licensed under a Creative Commons
Attribution 4.0 International License.

Spring Annotations: Proxy

  • 1.
    Spring’s annotations: Proxy José Canfrán(Osoco) September 2015 Programming Gotchas & Pitfalls Series
  • 2.
  • 3.
    Ejemplo package es.osoco.lightTalk import org.springframework.transaction.annotation.Transactional classTransactionalService { @Transactional def firstMethod() { anotherMethod() // code } @Transactional // @Transactional(propagation = Propagation.REQUIRES_NEW) private anotherMethod() { // code } }
  • 4.
    Spring: Proxy La anotaciónTransactional de Spring crea un proxy en tiempo de ejecución de la clase, el cual tiene a su vez una instancia de la clase original. Todas las llamadas a métodos transaccionales son interceptadas por el proxy, que es el encargado de iniciar una transacción, unirse a la existente… y llamar al método del servicio. Pero si se hace una llamada a métodos del mismo servicio, el proxy no podrá interceptarla al encontrarnos por debajo de él, en su instancia del servicio y no se ejecutará la condiciones de transacción que hayamos puesto.
  • 5.
    Posibles soluciones ● Grails< 2.3.1 ○ Usar .withTransaction() ● Grails > 2.3.1 ○ Usar @grails.transaction.Transactional: Durante la compilación mediante una transformación AST, se crea un nuevo método por cada método anotado dentro de una GrailsTransactionTemplate que ejecuta el código del original con los ajustes de transacción.Ya no existe proxy y como se ha hecho la misma operación con todos los métodos no hay problemas con las llamadas directas. (Puede fallar con la anotación @CompileStatic)
  • 6.
  • 7.
    Ejemplo package es.osoco.lightTalk import org.springframework.cache.annotation.Cacheable classCacheableService { def firstMethod() { anotherMethod(param1, param2) // code } @Cacheable private anotherMethod(param1, param2) { // code } }
  • 8.
  • 9.
    Obtener proxy: Enel servicio package es.osoco.lightTalk import org.springframework.cache.annotation.Cacheable class CacheableService { def firstMethod() { proxy.anotherMethod(param1, param2) // code } @Cacheable private anotherMethod(param1, param2) { // code } private getProxy() { def proxy = grailsApplication.mainContext.cacheableService } } De este modo obtenemos el proxy para llamar al método a través de él.
  • 10.
    Obtener proxy: Genérico classBootStrap { def grailsApplication def init = { servletContext -> for( sc in grailsApplication.serviceClasses) { sc.clazz.metaClass.getMyProxy = { -> grailsApplication.mainContext.getBean(sc.propertyName) } } } De este modo estará disponible la variable myProxy con el proxy correspondiente en cada servicio.
  • 11.
    Bibliografía ◉ Programming Grails(0’Reilly) Burt Beckwith, ISBN: 9 781449 323936 ◉ Documentación Grails https://grails.org/documentation.html
  • 12.
    Any questions ? Youcan find me at ◉ @jose_cg Thanks! This work is licensed under a Creative Commons Attribution 4.0 International License.