O que é AOP
AOP (programação orientada a aspectos, programação orientada a aspectos) pode ser considerada um suplemento e melhoria da OOP (programação orientada a objetos). A OOP introduz conceitos como encapsulamento, herança e polimorfismo para estabelecer uma hierarquia de objetos para simular uma coleção de comportamentos públicos. Quando precisamos introduzir o comportamento público em objetos dispersos, o OOP parece impotente. Ou seja, o OOP permite definir relacionamentos de cima para baixo, mas não é adequado para definir relacionamentos da esquerda para a direita. Por exemplo, a função de log. O código de log geralmente é disperso horizontalmente em todos os níveis de objeto, sem qualquer relação com a funcionalidade principal do objeto ao qual ele está disperso. O mesmo vale para outros tipos de código, como segurança, manipulação de exceções e transparência. Esse tipo de código irrelevante espalhado em todos os lugares é chamado de código cruzado. No design do OOP, causa muita duplicação de código, que não é propícia à reutilização 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, reduzindo o acoplamento entre os módulos e promovendo futuras operabilidade e manutenção. AOP representa um relacionamento horizontal. Se o "objeto" for um cilindro oco, encapsulando as propriedades e o comportamento do objeto; Em seguida, o método de programação orientado a aspectos é como uma lâmina afiada, cortando esses cilindros ocos para obter informações internas. A seção de corte é a chamada "face". Em seguida, restaurou essas seções recortadas com suas mãos inteligentes sem deixar nenhum rastro.
Usando a tecnologia "Cross-Cutting", a AOP divide o sistema de software em duas partes: preocupação central e preocupação cruzada. O principal processo de processamento de negócios é o foco principal, e a parte que pouco tem a ver com isso é o foco transversal. Uma característica das preocupações cruzadas é que elas geralmente ocorrem em múltiplas preocupações principais e são basicamente semelhantes em todos os lugares. Por exemplo, autenticação de permissão, log e processamento de transações. O papel da AOP é separar várias preocupações no sistema e separar as preocupações principais das preocupações cruzadas. Como Adam Magee, arquiteto de solução sênior da Avanade, disse, a idéia principal da AOP é "separar a lógica de negócios no aplicativo dos serviços comuns que a apoiam".
A tecnologia para implementar a AOP é dividida principalmente em duas categorias: uma é usar a tecnologia dinâmica de proxy e usar o método de interceptar mensagens para decorar a mensagem para substituir a execução do comportamento original do objeto; O outro é usar o método de tecelagem estática para introduzir sintaxe específica para criar "faces", para que o compilador possa tecer o código relacionado a "faces" durante a compilação.
Cenários de uso da AOP
AOP é usada para encapsular preocupações cruzadas, que podem ser usadas nos seguintes cenários:
Permissões de autenticação
Cache em cache
Conteúdo de passagem de contexto
Manuseio de erros
Carregamento preguiçoso
Depuração
registro, rastreamento, perfil e monitoramento
Otimização de desempenho
Persistência de persistência
Pool de recursos
Sincronização
Transações
Conceitos relacionados à AOP
Aspecto: uma modularidade de um foco que pode ainda mais cruzar vários objetos. O gerenciamento de transações é um bom exemplo de preocupações cruzadas nos aplicativos J2EE. O aspecto é implementado usando o consultor ou interceptador da Spring.
Jungpoint: um ponto claro durante a execução do programa, como uma chamada de método ou uma exceção específica sendo lançada.
Conselho: Ações executadas pela estrutura da AOP em um ponto de conexão específico. Vários tipos de notificações incluem "em torno" "antes" e "lança" notificações. Os tipos de notificação são discutidos abaixo. Muitas estruturas de AOP, incluindo a primavera, usam os interceptores como modelos de notificação para manter os pontos de conexão "redondos" da cadeia interceptores. Quatro conselhos são definidos na primavera: Beforeadvice, Afteradvice, Throwadvice e DynamicIntroductionAdvice
Pointcut: Especifica uma coleção de pontos de conexão para os quais a notificação será acionada. A estrutura da AOP deve permitir que os desenvolvedores especifiquem pontos de entrada: por exemplo, usando expressões regulares. Spring define a interface Pointcut, que é usada para combinar o MethodMatcher e o ClassFilter, que podem ser entendidos claramente através do nome. O MethodMatcher é usado para verificar se o método da classe de destino pode ser usado para aplicar essa notificação, enquanto o ClassFilter é usado para verificar se o Pointcut deve ser aplicado à classe de destino.
Introdução: Adicione um método ou campo à classe que é notificada. A primavera permite a introdução de novas interfaces em qualquer objeto notificado. Por exemplo, você pode simplificar o cache usando uma introdução que permite que qualquer objeto implemente a interface ismmodificada. Para usar a introdução na primavera, você pode usar o DelegatingIntroductionInterceptor para implementar notificações e usar o DefaultIntroductionAdvisor para configurar a interface para implementar classes de aconselhamento e proxy.
Objeto de destino: um objeto que contém o ponto de conexão. Também conhecido como objeto notificado ou proxy. Pojo
Proxy da AOP: um objeto criado pela estrutura da AOP, contendo notificações. Na primavera, o proxy AOP pode ser um proxy dinâmico do JDK ou um proxy do CGLIB.
Tecelagem: monte para criar um objeto notificado. Isso pode ser feito no momento da compilação (por exemplo, usando o compilador de aspecto) ou em tempo de execução. A primavera, como outras estruturas puras da Java AOP, completa a tecelagem em tempo de execução.
Componentes da Spring AOP
O diagrama de aula a seguir lista os principais componentes da AOP na primavera
Como usar a Spring AOP
A Spring AOP pode ser usada de arquivos de configuração ou de programação.
A configuração pode ser realizada através do arquivo XML, e existem cerca de quatro maneiras:
1. Configure proxyfactorybean, defina explicitamente consultores, conselhos, destino, etc.
2. Configure o AutoProxycreator. Dessa forma, o feijão definido ainda é usado como antes, mas o que você recebe do contêiner é na verdade um objeto proxy.
3. Configure através de <AOP: config>
4. Configure através de <DaOP: AspectJ-AutoProxy> e use as anotações de aspecto para identificar notificações e pontos de entrada
Você também pode usar o Proxyfactory diretamente para usar o Spring AOP programaticamente. Através dos métodos fornecidos pelo Proxyfactory, você pode definir objetos de destino, consultores e outras configurações relacionadas e, finalmente, obter o objeto Proxy através do método getProxy ().
Exemplos específicos de uso podem ser o Google. omitido aqui
Geração de objeto de proxy da Spring AOP
A Spring fornece duas maneiras de gerar objetos proxy: JDKProxy e CGLIB. O método específico de geração é determinado pelo AOPPROXIFFactory com base na configuração do objeto AdvisedSupport. A política padrão é usar a tecnologia de proxy dinâmica JDK se a classe de destino for uma interface, caso contrário, use o CGLIB para gerar o proxy. Vamos estudar como a primavera usa o JDK para gerar objetos proxy. O código de geração específico é colocado na classe JDkdynamicaopProxy, e o código relevante é adicionado diretamente:
/** * <ol> * <li> Obtenha a interface implementada pela classe Proxy. Além da configuração no objeto aconselhado, o SpringProxy também será adicionado, aconselhado (opacque = false) * <li> Verifique se existe uma interface que define igual ou hashcode na interface obtida acima * <li> Iffroxy.NewProxyInStance para criar um objeto de protely * <li>/ object GestProxy (logger.isdebugenabled ()) {Logger.debug ("Criando proxy dinâmico JDK: a fonte de destino é" +this.adVised.getTargetSource ()); } Classe [] proxiedInterfaces = aopProxyutils.completeProxiedInterfaces (this.adVised); FindDefinedEqualsAndHashCodemethods (ProxiedInterfaces); return proxy.newproxyInstance (Classloader, ProxiedInterfaces, este); } Então isso é realmente muito claro. Eu já escrevi os comentários claramente e não os repetirei novamente.
A pergunta a seguir é que o objeto de proxy é gerado, como a superfície cortada é tecedora?
Sabemos que o InvocationHandler é o núcleo do proxy dinâmico do JDK, e as chamadas de método de objetos de proxy geradas serão delegadas ao método InvocationHandler.invoke (). Através da assinatura do JDKDYNAMICOOPPROXIA, podemos ver que essa classe realmente implementa o InvocationHandler. Vamos dar uma olhada em como a Spring AOP entra na seção analisando o método Invoke () implementado nesta classe.
PublicObject Invoke (proxy de objeto, método do método, objeto [] args) lascas {MethodInvocation Invocation = null; Objeto OldProxy = NULL; boolean setProxyContext = false; TargetSource TargetSource = this.adVised.targetSource; Classe TargetClass = NULL; Destino de objeto = nulo; tente {// eqauls () método, o objeto de destino não implementa esse método se (! this.equalsDefined && aoputils.isequalsMethod (métod) {return (equals (args [0])? boolean.True: boolean.false); } // HASHCODE () Método, o objeto de destino não implementa esse método se (! this.hashCodEDefined && aoputils.ishashcodemethod (método)) {return newinteger (hashcode ()); } // interface aconselhada ou o método definido em sua interface pai, reflete diretamente a chamada e não usa notificação se (! This.advised.opaque && method.getDecLaringClass (). ISInterface () && Method.getDecLaringClass () .SAsSignArcRoMyc (Advised.Class) { Aoputils.inVokeJoinPointUsingReflection (this.advision, método, args); } Objeto retval = null; if (this.adVised.exposeProxy) {// disponibiliza a invocação IFNECESSARY. OldProxy = aopContext.setCurrentProxy (proxy); setProxyContext = true; } // Obtenha o destino da classe do objeto de destino = TargetSource.getTarget (); if (Target! = null) {TargetClass = Target.getClass (); } // Obtenha a lista de interceptor que pode ser aplicada a esta cadeia de listas de métodos = this.adVised.getInterceptSanddynamicInterceptionAdvice (método, TargetClass); // Se não houver notificação que possa ser aplicada a esse método (interceptador), esse método de chamada de reflexão direta. } else {// Crie MethodInvocation Invocation = newReflectiveMethodinVocation (Proxy, Target, Método, Args, TargetClass, Chain); retval = invocação.proeced (); } // Valor de retorno de massagem, se necessário. if (retVal! = null && retVal == Target && Method.getReturntype (). IsInstance (proxy) &&! RawTargetAccess.class.isassignableFrom (Method.getDecLaringClass ())) {// Caso especial: retornou "Este" e o tipo de retorno do método // Nothathat que não podemos deixar se o destino definir // uma referência a si mesma, inanOther Returned Object. retval = proxy; } retornar retval; } finalmente {if (Target! = null &&! TargetSource.isstatic ()) {// deve ter vindo do FargetSource. TargetSource.ReleasETarget (Target); } if (setProxyContext) {// Restaure o antigo proxy. AopContext.setCurrentProxy (OldProxy); }}} O processo principal pode ser descrito brevemente como: obtenção da cadeia de notificação que pode ser aplicada a esse método (cadeia interceptora). Se houver, aplique a notificação e execute o ponto de junção; Se não houver, reflita diretamente o ponto de junção. A chave aqui é como a cadeia de notificação é obtida e como é executada. Vamos analisá -lo um por um.
Primeiro de tudo, a partir do código acima, podemos ver que a cadeia de notificação é obtida através do método aconselhado. Vamos dar uma olhada na implementação deste método:
list public <ject> getInterceptSanddynamicInterceptionAdvice (método do método, classe TargetClass) {MethodCacheKeyCacheKey = new MethodCacheKey (método); List <ject> cache = this.methodcache.get (cachekey); if (em cache == null) {cache = this.advisorchainFactory.getInterceptSanddynamicInterceptionAdvice (this, método, TargetClass); this.methodcache.put (cachekey, armazenado em cache); } returncached; } Pode -se observar que o trabalho de aquisição real é realmente feito pelo AdvisorChainFactory. getInterceptoresnddynamicInterceptionAdvice () Método e os resultados obtidos serão armazenados em cache.
Vamos analisar a implementação deste método abaixo:
/*** Obtenha a lista de consultores da configuração de configuração fornecida e atravesse esses consultores. Se for uma introdução, * determine se esse consultor pode ser aplicado à classe de destino de destino. Se for um PointCutadvisor, determine * se este consultor pode ser aplicado ao método de destino. O consultor que atende às condições é convertido em uma lista de interceptores através do Advisoradaptor. */ PublicList GetInterceptSanddynamicInterceptionAdvice (Config, MethodMethod, MethodMethod, Class TargetClass) {// Isso é um pouco complicado ... Temos que processar introduções primeiro, //, mas precisamos preservar a ordem na lista final. Lista interceptorList = new ArrayList (config.getAdvisors (). Comprimento); // Verifique se as IntroductionAdvisor boolean hasintroductions = hasMatchingIntroductions (Config, TargetClass); // De fato, uma série de consultores é registrada aqui para converter o Advisor em MethodIntercept AdvisorAdapterRegistry Registry = GlobalAdvisorAdapterRegustry.getInstance (); Advisor [] Advisors = config.getAdvisors (); for (int i = 0; i <advisors.length; i ++) {Advisor Advisor = Advisors [i]; if (consultor instanceof Pointcutadvisor) {// Adicione -o condicionalmente. PointCutadvisor PointCutadvisor = (PointCutadvisor) Advisor; if (config.isprefiltered () || pointcutadvisor.getPointCut (). getClassFilter (). Matches (TargetClass)) {// TODO: As posições desses dois métodos podem ser trocadas neste local // Convert Advisor para o interceptor MethodInterceptor [] interceptors = Registry.getry.gistent.Gerents // Convert Advisor para intercetor. // Verifique se o ponto de ponto do consultor atual pode corresponder ao método atual MethodMatcher mm = PointCutadvisor.getPointCut (). GetMethodMatcher (); if (MethodMatchers.Matches (mm, método, TargetClass, hastintroductions)) {if (mm.isRuntime ()) {// criando uma instância newObject no método getIntercept () // não é um problema que normalmente criamos em cache. for (intj = 0; j <interceptores.Length; j ++) {interceptorList.add (novo interceptAnddynamicMethodMatcher (interceptores [j], mm)); }} else {interceptorList.addall (Arrays.asList (interceptores)); }}}} else if (advisor instanceOf IntructionAdvisor) {IntroductionAdvisor IA = (IntroductionAdvisor) Advisor; if (config.isprefiltered () || ia.getClassFilter (). Matches (TargetClass)) {interceptor [] interceptores = Registry.getInterceptores (Advisor); interceptorList.addall (Arrays.asList (interceptores)); }} else {interceptor [] interceptores = Registry.getInterceptores (Advisor); interceptorList.addall (Arrays.asList (interceptores)); }} retornar interceptorlist; } Depois que esse método é executado, todos os consultores configurados em aconselhamento que podem ser aplicados a pontos de conexão ou classes de destino são convertidos em MethodInterceptores.
Em seguida, vamos dar uma olhada em como a cadeia interceptadora obtida funciona.
if (Chain.isEmpty ()) {retVal = aOputils.invokeJoinPointUsUsingReflection (Target, Method, Args); } else {// Crie MethodInvocation Invocation = newReflectiveMethodinVocation (Proxy, Target, Método, Args, TargetClass, Chain); retval = invocação.proeced (); } A partir deste código, podemos ver que, se a cadeia interceptadora obtida estiver vazia, o método de destino será chamado diretamente refletido. Caso contrário, será criada uma metominvocation, seu método de processo será chamado e a execução da cadeia interceptadora será acionada. Vamos dar uma olhada no código específico
Public Object Proceed () lança jogável {// Começamos com um índice de -1 e incremento antecipadamente. if (this.currentInterceptindex == this.interceptSanddynamicMethodMatchers.size ()- 1) {// Se o interceptador terminar de executar, execute o junção de retorno InvokeJoinPoint (); } Objeto interceptorInterConsceptAdvice = this.interceptSanddynamicMethodMatchers.get (++ this.currentInterceptindex); // Se você deseja corresponder dinamicamente a JunnPoint se (interceptorInterceptionAdvice Instância do interceptoranddynamicMethodMatcher) {// Avalie o Matcher Dynamic Method aqui: A parte estática já terá // foi avaliada e encontrada para corresponder. InterceptoranddynamicmethodMatcher dm = (interceptoranddynamicmethodmatcher) interceptororInterceptionAdvice; // correspondência dinâmica: se os parâmetros de tempo de execução atendem às condições correspondentes se (dm.methodmatcher.matches (this.method, this.TargetClass, this.arguments)) {// executa o intercetpor de retorno intercetport.Invoke (this); } else {// Quando a correspondência dinâmica falhar, pule o intercetpor atual e ligue para o próximo procedimento de retorno interceptor (); }} else {// é um interceptador, então apenas o invocamos: o Pointcutwill foi avaliado estaticamente antes que esse objeto fosse construído. // Executa o retorno do intercetpor atual ((MethodIntercept) InterceptororInterceptionAdvice) .invoke (this); }}O código também é relativamente simples, então não vou entrar em detalhes aqui.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.