Que es AOP
Se puede decir que AOP (programación orientada al aspecto, programación orientada a los aspectos) es un suplemento y una mejora de la OOP (programación orientada a objetos). OOP introduce conceptos como encapsulación, herencia y polimorfismo para establecer una jerarquía de objetos para simular una colección de comportamientos públicos. Cuando necesitamos introducir el comportamiento público en objetos dispersos, OOP parece impotente. Es decir, OOP le permite definir relaciones de arriba a abajo, pero no es adecuada para definir las relaciones de izquierda a derecha. Por ejemplo, función de registro. El código de registro a menudo se dispersa horizontalmente en todos los niveles de objeto sin ninguna relación con la funcionalidad central del objeto al que está disperso. Lo mismo es cierto para otros tipos de código, como seguridad, manejo de excepciones y transparencia. Este tipo de código irrelevante disperso en todas partes se llama código transversal. En el diseño de OOP, causa mucha duplicación de código, que no es propicio para la reutilización de cada módulo.
AOP technology, on the contrary, uses a technique called "crosscutting" to dissect the inside of the encapsulated object and encapsulate the common behaviors that affect multiple classes into a reusable module and name it "Aspect", i.e. The so-called "aspect", simply put, encapsulates logic or responsibilities that are not related to the business but are called jointly by the business module, which facilitates reducing the system's duplicate code, Reducción del acoplamiento entre módulos y promover la operabilidad futura y la mantenibilidad. AOP representa una relación horizontal. Si el "objeto" es un cilindro hueco, encapsulando las propiedades y el comportamiento del objeto; Luego, el método de programación orientado al aspecto es como una cuchilla afilada, cortando estos cilindros huecos para obtener información interna. La sección de corte es la llamada "cara". Luego restauró estas secciones recortadas con sus manos inteligentes sin dejar ningún rastro.
Utilizando la tecnología "transversal", AOP divide el sistema de software en dos partes: preocupación central y preocupación transversal. El principal proceso de procesamiento comercial es el enfoque central, y la parte que tiene poco que ver con él es el enfoque transversal. Una característica de las preocupaciones transversales es que a menudo ocurren en múltiples preocupaciones del núcleo, y son básicamente similares en todas partes. Por ejemplo, autenticación de permisos, registro y procesamiento de transacciones. El papel de AOP es separar diversas preocupaciones en el sistema y separar las preocupaciones centrales de las preocupaciones transversales. Como dijo Adam Magee, arquitecto senior de soluciones en Avanade, la idea central de AOP es "separar la lógica comercial en la aplicación de los servicios comunes que lo respaldan".
La tecnología para implementar AOP se divide principalmente en dos categorías: una es usar tecnología proxy dinámica y usar el método de interceptar mensajes para decorar el mensaje para reemplazar la ejecución del comportamiento del objeto original; El otro es utilizar el método de tejido estático para introducir una sintaxis específica para crear "caras", de modo que el compilador pueda tejer el código relacionado con "caras" durante la compilación.
Escenarios de uso de AOP
AOP se usa para encapsular las preocupaciones transversales, que se pueden usar en los siguientes escenarios:
Permisos de autenticación
Caché de almacenamiento en caché
Contexto que pasa contenido
Manejo de errores
Carga perezosa
Depuración
Registro, rastreo, perfiles y monitoreo
Optimización del rendimiento
Persistencia
Agrupación de recursos
Sincronización
Actas
Conceptos relacionados con AOP
Aspecto: una modularidad de un enfoque que puede obtener más objetos transversales. La gestión de transacciones es un buen ejemplo de preocupaciones transversales en aplicaciones J2EE. El aspecto se implementa utilizando el asesor o interceptor de Spring.
Punto de unión: un punto claro durante la ejecución del programa, como una llamada de método o una excepción específica que se está lanzando.
Consejo: Acciones realizadas por el marco AOP en un punto de conexión específico. Varios tipos de notificaciones incluyen notificaciones "alrededor", "antes" y "arrojan". Los tipos de notificación se analizan a continuación. Muchos marcos de AEOP, incluidos Spring, usan interceptores como modelos de notificación para mantener un punto de conexión "redondo" de la cadena de interceptor. Cuatro consejos se definen en primavera: BeforeAdVice, después de la ventaja, ThrowAdvice y DynamicIntroductionAdvice
PointCut: especifica una colección de puntos de conexión a los que se activará la notificación. El marco AOP debe permitir a los desarrolladores especificar puntos de entrada: por ejemplo, utilizando expresiones regulares. Spring define la interfaz de PointCut, que se utiliza para combinar MethodMatcher y ClassFilter, que se puede entender claramente a través del nombre. MethodMatcher se usa para verificar si el método de la clase de destino se puede usar para aplicar esta notificación, mientras que ClassFilter se usa para verificar si PointCut debe aplicarse a la clase de destino.
Introducción: Agregue un método o campo a la clase que se notifica. Spring permite la introducción de nuevas interfaces en cualquier objeto notificado. Por ejemplo, puede simplificar el almacenamiento en caché utilizando una introducción que permita a cualquier objeto implementar la interfaz ISModified. Para utilizar la introducción en Spring, puede usar DelegatingInTroDductionInterceptor para implementar notificaciones y utilizar defaultInTroDuctionAdVisor para configurar la interfaz para implementar clases de asesoramiento y proxy.
Objeto objetivo: un objeto que contiene el punto de conexión. También conocido como objeto notificado o proxy. Pojo
AOP proxy: un objeto creado por el marco AOP, que contiene notificaciones. En primavera, el proxy AOP puede ser un proxy dinámico JDK o un proxy CGLIB.
Tejido: ensamblar para crear un objeto notificado. Esto se puede hacer en el momento de la compilación (por ejemplo, usando el compilador SUPSJ) o en tiempo de ejecución. La primavera, como otros marcos Pure Java AOP, completa el tejido en tiempo de ejecución.
Componentes de AOP de resorte
El siguiente diagrama de clase enumera los componentes principales de AOP en primavera
Cómo usar Spring AOP
Spring AOP se puede usar en archivos de configuración o formas de programación.
La configuración se puede realizar a través del archivo XML, y hay alrededor de cuatro maneras:
1. Configurar proxyFactoryBean, establecer explícitamente asesores, asesoramiento, objetivo, etc.
2. Configurar AutoProxyCreator. De esta manera, el frijol definido todavía se usa como antes, pero lo que obtienes del contenedor es en realidad un objeto proxy.
3. Configurar a través de <aop: config>
4. Configurar a través de <AOP: SuppeJ-AutoProxy>, y use anotaciones de SuppeJ para identificar notificaciones y puntos de entrada
También puede usar proxyFactory directamente para usar Spring AOP programáticamente. A través de los métodos proporcionados por ProxyFactory, puede establecer objetos de destino, asesores y otras configuraciones relacionadas, y finalmente obtener el objeto proxy a través del método getProxy ().
Ejemplos específicos de uso pueden ser Google. omitido aquí
Generación del objeto proxy de resorte AOP
Spring proporciona dos formas de generar objetos proxy: JDKProxy y CGLIB. El método específico de generación está determinado por AOPProxyFactory en función de la configuración del objeto AsesedSupport. La política predeterminada es utilizar la tecnología de proxy dinámica JDK si la clase de destino es una interfaz, de lo contrario, use CGLIB para generar el proxy. Estudiemos cómo Spring usa JDK para generar objetos proxy. El código de generación específico se coloca en la clase jdkdynamicaopproxy, y el código relevante se agrega directamente:
/** * <ol> * <li> Obtenga la interfaz que la clase proxy implementa. Además de la configuración en el objeto recomendado, SpringProxy también se agregará, aconsejará (OPAQUE = FALSE) * <li> Verifique si hay una interfaz que define iguales o hashcode en la interfaz obtenida anteriormente * <li> Llamando proxy.newproxyInstance para crear un objeto proxy * </ ol> */ objeto público getProxy (classloader) (logger.isDebugeNabled ()) {logger.debug ("Creación de proxy dinámico JDK: la fuente de destino es" +this.advised.gettargetSource ()); } Class [] proxiedInterfaces = aopproxyutil.completeproxiedInterfaces (this.AdVised); finddefinedequals yhashcodemetods (proxiedInterfaces); return proxy.newproxyInstance (classloader, proxiedInterfaces, esto); } Entonces esto es realmente muy claro. Ya he escrito los comentarios con claridad y no los repetiré nuevamente.
La siguiente pregunta es, se genera el objeto proxy, ¿cómo se teje la superficie de corte?
Sabemos que InvocationHandler es el núcleo del proxy dinámico JDK, y las llamadas de método de los objetos proxy generados se delegarán al método InvocationHandler.invoke (). A través de la firma de JdkdynamicaopProxy, podemos ver que esta clase realmente implementa InvocationHandler. Echemos un vistazo a cómo Spring AOP se mueve en la sección analizando el método Invoke () implementado en esta clase.
PublicObject Invoke (Object Proxy, Método, Método, Object [] Args) ShowSsThrowable {MethodInVocation Invocation = NULL; Objeto OldProxy = nulo; boolean setProxyContext = false; TargetSource TargetSource = this.Advised.TargetSource; Clase TargetClass = NULL; Objeto Target = null; Pruebe el método {// eqauls (), el objeto de destino no implementa este método si (! this.equalsdefined && aoputils.isequalsmethod (método)) {return (igual (args [0])? boolean.true: boolean.false); } // método hashcode (), el objeto de destino no implementa este método si (! this.hashCodeDefined && aoputils.ishashCodemethod (método)) {return newInteger (hashcode ()); } // La interfaz asesorada o el método definido en su interfaz principal, refleja directamente la llamada y no usa la notificación if (! This.AdVised.Opaque && Method.getDecLaringClass (). ISIERTERFACE () && Method.getDecLassSss (). ISASSIGNABLEFROM (aconsejado Aoputils.invokojoinpointusingreflection (this.s.es aconsejados, método, args); } Objeto retval = nulo; if (this.AdVised.EXPOSEPROXY) {// Haga que la invocación esté disponible IfNecessary. OldProxy = aopContext.setCurrentProxy (proxy); setProxyContext = true; } // Obtener la clase de objeto de destino Target = TargetSource.GetTarget (); if (target! = null) {targetClass = target.getClass (); } // Obtenga la lista de interceptor que se puede aplicar a esta cadena de lista de métodos = this.Advised.getInterceptorsandDynamicInterceptionAdVice (método, TargetClass); // Si no hay una notificación que se pueda aplicar a este método (Interceptor), este método de llamada de reflexión directa. } else {// Crear Invocación de MethodInVocation = NewReflectivemethodInVocation (proxy, Target, Method, Args, TargetClass, Chain); retval = invocation.proced (); } // Valor de retorno de masaje si es necesario. if (retval! = null && retval == Target && Method.getReturnType (). IsInstance (proxy) &&! RawTargetAccess.class.isassignableFrom (método.getDeClaringClass ()) {// Especial: devolvió "este" y el tipo de retorno del método // es tipo compatible. Nogios no podemos ayudar si el objetivo establece // una referencia a sí misma sin otro objeto devuelto. retval = proxy; } return retval; } Finalmente {if (Target! = Null &&! TargetSource.Isstatic ()) {// debe haber venido deTargetSource. TargetSource.RelEASETARGET (Target); } if (setProxyContext) {// restaurar el proxy antiguo. AopContext.setCurrentProxy (OldProxy); }}} El proceso principal se puede describir brevemente como: obtener la cadena de notificación que puede aplicarse a este método (cadena de interceptor). Si lo hay, aplique la notificación y ejecute el punto de unión; Si no hay no, refleje directamente el punto de unión. La clave aquí es cómo se obtiene la cadena de notificaciones y cómo se ejecuta. Analicémoslo uno por uno.
En primer lugar, del código anterior, podemos ver que la cadena de notificación se obtiene a través del método recomendado. Echemos un vistazo a la implementación de este método:
Lista pública <S Object> GetInterceptorsAnd DynamicInterceptionAdVice (Método del método, clase TargetClass) {MethodCacheKeyCacheKey = new MethodCacheKey (método); Lista <S Object> Cached = this.methodcache.get (cachekey); if (Cached == null) {Cached = this.AdVisorchainFactory.getInterceptorsandDynamicInterceptionAdVice (this, Method, TargetClass); this.methodcache.put (Cachekey, caché); } returnCached; } Se puede ver que el trabajo de adquisición real es realizado por AdvisorChainFactory. GetInterceptorsand DynamicInterceptionAdVice () Método, y los resultados obtenidos se almacenarán en caché.
Analicemos la implementación de este método a continuación:
/*** Obtenga la lista de asesores de la configuración de instancia de configuración proporcionada y atraviese estos asesores. Si se trata de un IntroductionAdvisor, * entonces determine si este asesor puede aplicarse a la clase TargetClass de la clase de destino. Si se trata de un PointCutAdvisor, determine * si este asesor se puede aplicar al método del método de destino. El asesor que cumple con las condiciones se convierte en una lista de interceptores a través del AdvisorAdaptor. */ PublicList GetInterceptorsand DynamicInterceptionAdVice (Consejado Config, MethodMethod, Class TargetClass) {// Esto es algo complicado ... Tenemos que procesar las presentaciones primero, // pero necesitamos preservar el orden en la lista final. List InterceptorList = new ArrayList (config.getAdvisors (). Longitud); // Verifique si el IntroductionAdvisor boolean haintroductions = HasmatchingInTroDductions (config, TargetClass); // De hecho, una serie de AdvisorAdapters se registra aquí para convertir el asesor en MethodInterceptor AdvisorAdapterRegistry Registry = GlobalAdvisorAdapterRegistry.getInstance (); Advisor [] Advisors = config.getAdvisors (); for (int i = 0; i <advisors.length; i ++) {advisor advisor = advisors [i]; if (asesor instanceo de puntos de puntos) {// agrégalo condicionalmente. PointCutAdvisor PointCutAdvisor = (PointCutAdvisor) Asesor; if (config.isprefiltered () || PointCuTAdVisor.getPointCut (). // Verifique si el punto de vista del asesor actual puede coincidir con el método actual del método MM = PointCutAdvisor.getPointCut (). GetMethodMatcher (); if (MethodMatchers.Matches (MM, Method, TargetClass, HasInTroDuctions)) {if (mm.isRunTime ()) {// Creación de una instancia de NewObject en el método getinterceptors () // no es un problema que normalmente almacenamos en caché creamos cadenas. for (intj = 0; j <interceptors.length; j ++) {interceptorList.add (nuevo interceptoranddynamicmethodmatcher (interceptores [j], mm)); }} else {interceptorList.addall (arrays.aslist (interceptores)); }}}} else if (asesor instancia de introductionAdVisor) {introductionAdVisor ia = (introductionAdvisor) asesor; if (config.isprefiltered () || ia.getClassFilter (). Matches (TargetClass)) {Interceptor [] Interceptors = Registry.getInterceptors (asesor); interceptorList.addall (arrays.aslist (interceptores)); }} else {interceptor [] interceptores = registry.getInterceptors (asesor); interceptorList.addall (arrays.aslist (interceptores)); }} return interceptorList; } Después de ejecutar este método, todos los asesores configurados se aconsejaron que se pueden aplicar a los puntos de conexión o las clases de destino se convierten en MethodInterceptor.
A continuación, echemos un vistazo a cómo funciona la cadena de interceptor obtenida.
if (chain.isEmpty ()) {retVal = aoputils.invokejoinpointUsingReflection (objetivo, método, args); } else {// Crear Invocación de MethodInVocation = NewReflectivemethodInVocation (proxy, Target, Method, Args, TargetClass, Chain); retval = invocation.proced (); } A partir de este código, podemos ver que si la cadena de interceptor obtenida está vacía, el método de destino se llamará directamente reflejado. De lo contrario, se creará un método de Invocación, se llamará a su método de proceso y se activará la ejecución de la cadena de interceptor. Echemos un vistazo al código específico
Public Object Proceed () lanza Throwable {// Comenzamos con un índice de -1 e incremento temprano. if (this.CurrentInterceptorIndex == this.interceptorsanddDynamicMethodMatchers.size ()- 1) {// Si el interceptor está terminado de ejecutar, ejecute unión de retorno de unión invokeJoinPoint (); } Object InterceptororRinterceptionAdVice = this.interceptorsandddynamicMethodMatchers.get (++ this.CurrentIntercepeptorIndex); // Si desea hacer coincidir dinámicamente unión de unión if (InterceptororRinterceptionAdVice instancia de InterceptorAndDynamicMethodMatcher) {// Evalúe el método dinámico Matcher aquí: La parte estática ya se habrá evaluado y se ha encontrado para que coincida. InterceptorAndDynamicMethodMatcher DM = (InterceptorAndDynamicMethodmatcher) InterceptororRInterceptionAdvice; // coincidencia dinámica: si los parámetros de tiempo de ejecución cumplen con las condiciones de coincidencia si (dm.methodmatcher.matches (this.method, this.targetClass, this.arguments)) {// ejecutar el retorno de interceptor actual actualdm.interceptor.invoke (this); } else {// Cuando falla la coincidencia dinámica, omita el intercetpor actual y llame al siguiente procedimiento de retorno del interceptor (); }} else {// Es un interceptor, por lo que simplemente lo invocamos: el punto de punto se ha evaluado // estábrado estáticamente antes de que se construyera este objeto. // Ejecutar el retorno InterCetpor actual ((MethodInterceptor) InterceptororRInterceptionAdvice) .invoke (esto); }}El código también es relativamente simple, por lo que no entraré en detalles aquí.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.