El tejido en tiempo de carga (LTW) se refiere al proceso de vincular aspectos de AspectJ a archivos de clase de aplicación mientras se cargan en la máquina virtual Java (JVM). Esta sección se centra en la configuración y el uso del mecanismo LTW en el contexto específico de Spring Framework. Esta sección no es una introducción general al enlace en el momento del arranque. Para obtener detalles completos sobre los detalles específicos del mecanismo de enlace en el momento del arranque y su configuración usando solo AspectJ (sin Spring involucrado en absoluto), consulte Sección sobre enlace de tiempo de carga en la Guía del entorno de desarrollo de AspectJ.
El valor de Spring
Framework para el enlace de tiempo de carga de AspectJ es que permite un control mucho más detallado del proceso
de encuadernación. El mecanismo básico de enlace en el momento del arranque de AspectJ se realiza mediante un agente
Java (5+), que se habilita especificando un argumento de máquina virtual cuando se inicia la JVM. Así que esta es
una configuración para toda la JVM que puede ser buena en algunas situaciones, pero a menudo es demasiado tosca. El
mecanismo LTW habilitado para Spring permite habilitarlo por ClassLoader
, lo cual es una configuración
más matizada y puede tener mucho más sentido en un entorno con una JVM pero muchas aplicaciones (como una típica
entorno del servidor de aplicaciones).
Además, en ciertos entornos, este soporte permite el enlace en el
momento del arranque sin realizar los cambios en el script de inicio del servidor de aplicaciones necesarios para
agregar -javaagent:path/to/aspectjweaver.jar
o (como se describe más adelante en esta sección) -javaagent:path/to/spring-instrument.jar
.
Los desarrolladores configuran el contexto de la aplicación para habilitar el enlace en el momento del arranque en
lugar de depender de los administradores que normalmente son responsables de la configuración de la implementación,
como los scripts de inicio.
Ahora que hemos terminado de exagerar, veamos un LTW rápido. ejemplo de AspectJ usando Spring y luego entre en detalles sobre los elementos presentados en el ejemplo.
Primer ejemplo
Suponga que es un desarrollador de aplicaciones encargado de diagnosticar la causa de algún rendimiento del sistema. problemas. En lugar de utilizar una herramienta de creación de perfiles, incluiremos un aspecto de creación de perfiles simple para obtener rápidamente algunas métricas de rendimiento. Inmediatamente después de esto, puede aplicar una herramienta de creación de perfiles más matizada a esa área específica.
@EnableLoadTimeWeaving
como alternativa a <context:load-time-weaver/>
.
El siguiente ejemplo muestra el aspecto menos sofisticado de la creación de perfiles. Este es un generador de perfiles controlado por tiempo que utiliza el estilo @AspectJ para declarar aspectos:
package foo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
import org.springframework.core.annotation.Order;
@Aspect
public class ProfilingAspect {
@Around("methodsToBeProfiled()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch(getClass().getSimpleName());
try {
sw.start(pjp.getSignature().getName());
return pjp.proceed();
} finally {
sw.stop();
System.out.println(sw.prettyPrint());
}
}
@Pointcut("execution(public * foo..*.*(..))")
public void methodsToBeProfiled(){}
}
package foo
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Pointcut
import org.springframework.util.StopWatch
import org.springframework.core.annotation.Order
@Aspect
class ProfilingAspect {
@Around("methodsToBeProfiled()")
fun profile(pjp: ProceedingJoinPoint): Any {
val sw = StopWatch(javaClass.simpleName)
try {
sw.start(pjp.getSignature().getName())
return pjp.proceed()
} finally {
sw.stop()
println(sw.prettyPrint())
}
}
@Pointcut("execution(public * foo..*.*(..))")
fun methodsToBeProfiled() {
}
}
También necesitamos crear un archivo META-INF/aop.xml
para indicarle a la herramienta de enlace
AspectJ que queremos vincular nuestro ProfilingAspect
a nuestras clases. Esta convención de archivos,
es decir, tener un archivo (o archivos) en la ruta de clases de Java llamado META-INF/aop.xml
, es un
estándar de AspectJ. El siguiente ejemplo muestra el archivo aop.xml
:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- we only bundle classes in our application-specific packages -->
<include within="foo.*"/>
</weaver>
<aspects>
<!-- we connect only this aspect... -->
<aspect name="foo.ProfilingAspect"/>
</aspects>
</aspectj>
Ahora podemos pasar a la parte de la configuración específica de Spring. Necesitamos configurar LoadTimeWeaver
(se explica más adelante). Esta herramienta de vinculación en tiempo de carga es el componente principal responsable
de vincular la configuración de aspectos en uno o más archivos META-INF/aop.xml
a sus clases de
aplicación. Afortunadamente, no requiere una configuración complicada (hay algunas opciones más que se pueden
configurar, pero se tratarán en detalle más adelante), como puede ver en el siguiente ejemplo:
<?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:context="http:// www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- service object; we will profile its methods -->
<bean id="entitlementCalculationService"
class="foo.StubEntitlementCalculationService"/>
<!-- this enables binding at boot time -->
<context:load-time-weaver/>
</beans>
Ahora que todos los artefactos necesarios (aspecto, archivo META-INF/aop.xml
y configuración
Spring) en su lugar, podemos crear la siguiente clase de controlador con un método main(..)
para
demostrar el mecanismo LTW en acción:
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);
EntitlementCalculationService entitlementCalculationService =
(EntitlementCalculationService) ctx.getBean("entitlementCalculationService");
// the profiled aspect "weaves" the execution of this method
entitlementCalculationService.calculateEntitlement();
}
}
package foo
import org.springframework.context.support.ClassPathXmlApplicationContext
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
val entitlementCalculationService = ctx.getBean("entitlementCalculationService") as EntitlementCalculationService
// the profiled aspect "weaves" the execution of this method
entitlementCalculationService.calculateEntitlement()
}
Solo nos queda una cosa por hacer. La introducción a esta sección indicó que con Spring puede habilitar LTW de
forma selectiva por clase de cargador, y esto es cierto. Sin embargo, en este ejemplo utilizamos el agente Java
(proporcionado con Spring) para habilitar el mecanismo LTW. Usamos el siguiente comando para ejecutar la clase
Main
mostrada anteriormente:
java-javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main
Marca -javaagent
es un indicador para configurar y activar agentes
para instrumentar programas que se ejecutan en la JVM. Spring Framework incluye un agente, InstrumentationSavingAgent
,
empaquetado en spring-instrument.jar
, que se proporcionó como el valor del argumento
-javaagent
en el ejemplo anterior.
La salida del programa Main
se parece al
siguiente ejemplo. (Introduje una declaración Thread.sleep(..)
en la implementación calculateEntitlement()
para que el generador de perfiles realmente capturara algún valor distinto de 0 milisegundos ( 01234
milisegundos no son la latencia introducida por AOP) La siguiente lista muestra el resultado que obtuvimos cuando
ejecutamos nuestro generador de perfiles:
Calculating entitlement StopWatch 'ProfilingAspect': running time (millis) = 1234 ------ ----- ---------------------------- ms % Task name ------ ----- ---------------------------- 01234 100% calculateEntitlement
Debido a que este mecanismo LTW se implementa utilizando AspectJ con todas las funciones, no nos limitamos a
brindar asesoramiento a Spring. frijoles. La siguiente ligera variación en el programa Main
da el mismo
resultado:
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("beans.xml", Main.class);
EntitlementCalculationService entitlementCalculationService =
new StubEntitlementCalculationService();
// the profiled aspect will "weave" the execution of this method
entitlementCalculationService.calculateEntitlement();
}
}
package foo
import org.springframework.context.support.ClassPathXmlApplicationContext
fun main(args: Array<String>) {
ClassPathXmlApplicationContext("beans.xml")
val entitlementCalculationService = StubEntitlementCalculationService()
// the profiled aspect will "weave" the execution of this method
entitlementCalculationService.calculateEntitlement()
}
Observe que en el programa anterior cargamos el contenedor Spring y luego creamos una nueva
instancia de StubEntitlementCalculationService
completamente fuera del contexto Spring. Las sugerencias
perfiladas todavía están vinculadas.
Por supuesto, el ejemplo está simplificado. Sin embargo, los conceptos básicos del soporte LTW de Spring se presentaron en el ejemplo anterior y el resto de esta sección detalla el significado de cada bit de configuración y aplicación.
El ProfilingAspect
usado en este ejemplo puede ser básico,
pero es bastante útil. Este es un buen ejemplo de un aspecto en tiempo de diseño que los desarrolladores pueden
utilizar durante el desarrollo y luego excluirlo fácilmente de las compilaciones de una aplicación implementada
en una prueba de aceptación del usuario (UAT) o en un entorno de producción.
Aspectos
Los aspectos que utilice en LTW deben ser aspectos de AspectJ. Puede escribirlos en el propio lenguaje AspectJ o escribir sus propios aspectos en el estilo @AspectJ. Entonces, sus aspectos serán aspectos válidos tanto de AspectJ como de Spring AOP. Además, las clases de aspecto compiladas deben estar disponibles en el classpath.
'META-INF/aop.xml'
El marco AspectJ LTW se configura utilizando uno o más archivos META-INF/aop.xml
, que se
encuentran en la ruta de clases de Java (ya sea directamente o, más típicamente, en archivos jar).
La estructura y el contenido de este archivo se describen en detalle en la documentación de referencia LTW de AspectJ.
Dado que el archivo aop.xml
está 100% escrito en AspectJ, no lo describiremos aquí.
Bibliotecas requeridas (JARS)
Para usar el motor LTW soporte Desde AspectJ a Spring Framework necesitarás al menos las siguientes bibliotecas:
spring-aop.jar
aspectjweaver.jar
Si está utilizando el agente de activación de instrumentación proporcionado por Spring, también necesitará:
spring-instrument.jar
Configuración de Spring
Un componente clave del soporte LTW de Spring es la interfaz LoadTimeWeaver
(en el paquete org.springframework.instrument.classloading
)
y sus numerosas implementaciones que se incluyen. con la distribución de primavera. LoadTimeWeaver
es responsable de agregar uno o más java.lang.instrument.ClassFileTransformers
a
ClassLoader
en tiempo de ejecución, lo que abre la puerta a todo tipo de funciones interesantes.
modos de uso, uno de los cuales es el enlace de aspecto en el momento de la carga.
java.lang.instrument
antes de
continuar. Si bien esta documentación no es completa, al menos podrá ver las interfaces y clases principales
(como referencia mientras lee esta sección).
Configuración de LoadTimeWeaver
para un específico ApplicationContext
puede ser tan
simple como agregar una sola línea. (Tenga en cuenta que es casi seguro que necesitará usar
ApplicationContext
como contenedor Spring; BeanFactory
generalmente no es suficiente ya que el soporte del motor LTW
usa BeanFactoryPostProcessors
).
Para habilitar la compatibilidad con LTW en Spring Framework, debe configurar LoadTimeWeaver
, lo que
normalmente se realiza utilizando la anotación @EnableLoadTimeWeaving
, como se muestra a
continuación:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}
Como alternativa, si prefiere la configuración basada en XML, utilice elemento <context:load-time-weaver/>
.
Tenga en cuenta que el elemento está definido en el espacio de nombres
context
. El siguiente ejemplo muestra cómo utilizar <context:load-time-weaver/>
:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver/>
</beans>
La configuración anterior detecta y registra automáticamente una cantidad de beans de infraestructura específicos
de LTW, como LoadTimeWeaver
y
AspectJWeavingEnabler
. De forma predeterminada, LoadTimeWeaver
es una clase DefaultContextLoadTimeWeaver
que intenta complementar el LoadTimeWeaver
detectado automáticamente. El tipo exacto de LoadTimeWeaver
que será "detectado automáticamente" depende de su entorno de ejecución. La siguiente tabla muestra las diversas
implementaciones de LoadTimeWeaver
:
Tiempo de ejecución | Implementación LoadTimeWeaver |
---|---|
Ejecución en Apache Tomcat |
|
Ejecución en GlassFish (limitado a la implementación de EAR) |
|
|
|
Ejecutar en WebSphere de IBM |
|
Ejecución en WebLogic de Oracle |
|
JVM comenzó con |
|
Regrese esperando que el ClassLoader subyacente siga las convenciones generales (es decir, |
|
Tenga en cuenta que la tabla enumera solo aquellos LoadTimeWeaver
que se detectan automáticamente
cuando se utiliza DefaultContextLoadTimeWeaver
. Puede especificar exactamente qué implementación de
LoadTimeWeaver
usar.
Para especificar un LoadTimeWeaver
específico usando la configuración de Java, implemente la
interfaz LoadTimeWeavingConfigurer
y anular el método getLoadTimeWeaver()
. El
siguiente ejemplo especifica ReflectiveLoadTimeWeaver
:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
@Configuration
@EnableLoadTimeWeaving
class AppConfig : LoadTimeWeavingConfigurer {
override fun getLoadTimeWeaver(): LoadTimeWeaver {
return ReflectiveLoadTimeWeaver()
}
}
Si está utilizando una configuración basada en XML, puede especificar el nombre de clase completo como el valor
de weaver-class
de atributo en el elemento <context:load-time-weaver/>
.
Nuevamente, el siguiente ejemplo especifica ReflectiveLoadTimeWeaver
:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
LoadTimeWeaver
, que está definido y registrado por la configuración, se puede recuperar más tarde
del contenedor Spring utilizando el conocido nombre loadTimeWeaver
. Recuerde que
LoadTimeWeaver
existe solo como un mecanismo para que el marco LTW de Spring agregue uno o más
ClassFileTransformers
. El ClassFileTransformer
real que hace LTW es la clase ClassPreProcessorAgentAdapter
(del paquete org.aspectj.weaver.loadtime
). Se pueden encontrar más detalles en el javadoc de la
clase ClassPreProcessorAgentAdapter
, ya que los detalles de cómo se produce el enlace están más
allá del alcance de este documento.
Queda un último atributo de configuración por resolver considere: el atributo aspectjWeaving
(o
aspectj-weaving
si está utilizando XML). Este atributo controla si el mecanismo LTW está habilitado
o no. Toma uno de los tres valores posibles, siendo el valor predeterminado autodetect
si falta el
atributo. La siguiente tabla muestra tres valores posibles:
Annotation value | XML Value | Explanation |
---|---|---|
|
|
El enlace de AspectJ está habilitado y el enlace de aspecto se produce en el momento de la carga según sea necesario. |
|
|
El enlace durante el arranque está deshabilitado. El enlace de aspecto no se produce durante la carga. |
|
|
Si el marco LTW en Spring puede encontrar al menos un archivo |
Configuración específica del entorno
Esta sección final contiene todas las configuraciones y ajustes adicionales necesarios. cuando se utiliza el soporte LTW de Spring en entornos como servidores de aplicaciones y contenedores web.
Tomcat, JBoss, WebSphere, WebLogic
Tomcat, JBoss/WildFly, IBM WebSphere Application Server y Oracle WebLogic Server todos proporcionan un ClassLoader
común para aplicaciones capaces de instrumentación local. El LTW nativo de Spring puede usar estas
implementaciones de ClassLoader para proporcionar enlace desde AspectJ. Simplemente puede activar la vinculación
en el momento del arranque. En particular, no necesita modificar el script de inicio de JVM para agregar -javaagent:path/to/spring-instrument.jar
.
Tenga en cuenta que en el caso de JBoss Es posible que deba desactivar el escaneo del servidor de aplicaciones
para que no cargue clases antes de que la aplicación se ejecute realmente. Una solución rápida es agregar un
archivo a su artefacto llamado WEB-INF/jboss-scanning.xml
con el siguiente contenido:
<scanning xmlns="urn:jboss:scanning:1.0"/>
Uso universal de Java
Si necesita instrumentar clases en entornos que no son compatibles con
implementaciones LoadTimeWeaver
específicas, una opción común La solución es el agente JVM. Para tales
casos, Spring tiene InstrumentationLoadTimeWeaver
, que requiere un agente JVM específico de Spring
(pero extremadamente genérico), spring-instrument.jar
, determinado automáticamente por la configuración
general @EnableLoadTimeWeaving
y <context:load-time-weaver/>
.
Para usarlo, necesita iniciar una máquina virtual con un agente desde Spring, especificando los siguientes parámetros para JVM:
-javaagent:/path /to/spring-instrument.jar
Tenga en cuenta que esto requiere una modificación en el script de inicio de JVM, lo que puede impedir que se utilice en entornos de servidor de aplicaciones (dependiendo de su servidor). y políticas operativas). Sin embargo, en escenarios de implementación de aplicaciones, como aplicaciones Spring Boot independientes, una aplicación por JVM, normalmente debe controlar toda la configuración de JVM de todos modos.
Recursos adicionales
Puede obtener más información sobre AspectJ. se puede encontrar en el sitio web de AspectJ.
Eclipse AspectJ de Adrian Collier et al. (Addison-Wesley, 2005) proporciona una introducción completa y una referencia al lenguaje AspectJ.
La segunda edición de AspectJ in Action es muy recomendable.por Ramnivas Laddad (Manning, 2009) El libro se centra en AspectJ, pero también se exploran muchos temas generales de AOP (con bastante profundidad).