Introdução ao Mybatis Interceptor
Mybatis fornece uma função de plug -in. Embora seja chamado de plug -in, na verdade é uma função interceptadora. Então, o que o interceptador intercepta mybatis?
Vamos ao site oficial para dar uma olhada:
O Mybatis permite interceptar chamadas em um determinado ponto durante a execução de uma declaração mapeada. Por padrão, o método chama Mybatis permite que os plugins interceptem incluem:
Vimos alguns métodos que podem interceptar a interface do executor, como atualização, consulta, compromisso, reversão e outros métodos, bem como alguns métodos de outras interfaces
espere.
O resumo geral é:
Uso de interceptores
Introdução e configuração do interceptador
Primeiro, vejamos a definição da interface do Mybatis Interceptor:
interface pública Interceptor {Object Intercept (Invocation Invocation) lança Throwable; Plugin de objeto (destino do objeto); Void SetProperties (Propriedades Propriedades);}É relativamente simples, existem apenas 3 métodos. O Mybatis não possui uma classe de implementação de interface interceptores por padrão, e os desenvolvedores podem implementar interceptores que atendam às suas necessidades.
Aqui está um exemplo de interceptador do site oficial do Mybatis:
@Intercepts ({@Signature (type = executor.class, method = "update", args = {mappedstatement.class, object.class})}) public classe Exemplugin implementa interceptor {public Object Intercept (invocação de invocação) lança lança {retorno invocação.proeced (); } plug -in de objeto público (destino do objeto) {return plugin.wrap (Target, este); } public void setProperties (Propriedades Propriedades) {}}Configuração XML global:
<flugins> <plugin interceptor = "org.format.mybatis.cache.intercept.exampleplugin"> </plugin> </plugins>
Esse interceptador intercepta o método de atualização da interface do executor (na verdade, é as operações de adição, exclusão e modificação do SQLSession). Todos os métodos de atualização que executam executor serão interceptados pelo interceptador.
Análise do código -fonte
Vamos analisar o código -fonte por trás desse código.
Primeiro, inicie a análise do arquivo de origem-> Config:
Xmlconfigbuilder analisa o método privado do pluginement do arquivo de configuração global mybatis:
Pluginelement de void privado (pai Xnode) lança a exceção {if (parent! = null) {for (xnode Child: parent.getChildren ()) {string interceptor = child.getStringAttribute ("interceptor"); Propriedades Propriedades = Child.getChildrenasProperties (); Interceptador interceptorInstance = (interceptor) resolveclass (interceptor) .NewInstance (); interceptorInstance.SetProperties (Propriedades); Configuration.addintercept (InterceptInstance); }}}O código de análise específico é realmente relativamente simples, então não o postarei. Ele instancia principalmente a classe representada pelo atributo interceptador no nó do plug -in por reflexão. Em seguida, ligue para o método AddInterceptor da configuração da classe de configuração global.
public void addintercept (interceptador interceptador) {interceptorchain.addinterceptor (interceptor); }Esse interceptorchain é uma propriedade interna da configuração e seu tipo é interceptchain, que é uma cadeia interceptadora. Vamos dar uma olhada em sua definição:
classe pública interceptorchain {private final List <Soceptor> interceptores = new ArrayList <leroptorTor> (); public Object Pluginall (Objeto Target) {for (Interceptor Interceptor: Interceptores) {Target = Interceptor.plugin (Target); } retornar o destino; } public void addinterceptor (interceptador interceptador) {interceptores.add (interceptor); } Lista pública <Soceptor> getInterceptores () {return collects.unmodifiablelist (interceptores); }}Agora que entendemos a análise da configuração do interceptador e a propriedade do interceptador, agora olhamos para o motivo pelo qual o interceptador intercepta esses métodos (métodos parciais do executor, ParameterHandler, ResultetHandler):
Public ParameterHandler NewParameterHandler (MappEdStatement MappedStatement, Objeto ParameterObject, Boundsql Boundsql) {parameterHandHandler parameterHandler = MappEdStatement.getLang (). ParameterHandler = (ParameterHandler) InterceptChain.pluginall (ParameterHandler); return parameterHandler;}public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, ResultHandler, Boundsql, Rowbounds); Resultados e (Resultados) InterceptChain.Pluginall (Resultados e); Retorno Resultados dondler;} Public DeclarationHandler NewStatementHandler (Executor Executor, MappEdStatement MappEdStatement, Objeto ParameterObject, RowBounds RowBounds, ResultHandler ResutorTHandler, Boundsql Boundsql) {DeclarationHandHandler = RoutingStateMementMementMementMemEndler, MAPPEDSPEDSPEDSTSQL) {DeclarationHandler = RoutingStemementMement (Recorrer, MAPPEDSQL) { Boundsql); DeclarationHandler = (DeclarationHandler) InterceptChain.Pluginall (DeclarationHandler); Return DeclarationHandler;} Executor público NewExecutor (transação de transação, ExecutyType ExecutyType, Boolean AutoCommit) {ExecutyType = ExecutOnType == NULL? defaultExecutyType: ExecutOnType; executOrtype = ExecutOnType == NULL? Executortype.simple: ExecutOnType; Executor executor; if (ExecutOnType.Batch == ExecutyType) {Executor = new BatchExecutor (esta, transação); } else if (executOrtype.reuse == ExecutyType) {executor = new Reuseexecutor (esta, transação); } else {executor = new SimpleExector (esta, transação); } if (Cacheenabled) {executor = new CachingExecutor (executor, autocomit); } executora = (executor) interceptochain.pluginall (executor); Retornar executor;}Os 4 métodos acima são todos métodos de configuração. Esses métodos serão executados em uma operação de Mybatis (add, excluir, modificar e consultar). The order of execution is Executor, ParameterHandler, ResultSetHandler, StatementHandler (where ParameterHandler and ResultSetHandler are created when creating StatementHandler [3 available implementation classes CallableStatementHandler, PreparedStatementHandler, SimpleStatementHandler], and the constructor calls the constructor [the constructors of these three implementation classes actually call the constructor of the parent class BaseStatementHandler]).
Após esses 4 métodos instanciados o objeto correspondente, eles chamarão o método pluginall de interceptorchain. Como mencionado anteriormente, foi introduzido o pluginall do InterceptChain, que é atravessar todos os interceptores e chamar o método do plug -in de cada interceptador. Nota: O valor de retorno do método do plug -in do interceptador será atribuído diretamente ao objeto original.
Como o DeclarationHandler pode ser interceptado, essa interface lida principalmente com a construção da sintaxe do SQL. Portanto, por exemplo, a função da paginação pode ser implementada com um interceptador. Você só precisa processar o SQL na classe de implementação da interface DeclarationHandler no método do plug -in do interceptador e pode usar a reflexão para implementá -lo.
O Mybatis também fornece anotações para @Intercepts e @Signature sobre os interceptores. O exemplo do site oficial é usar essas duas anotações, incluindo o uso da classe de plug -in:
@OverridePublic Object Plugin (Objeto Target) {return plugin.wrap (Target, este);}Vamos analisar os códigos de origem dessas três "novas combinações". Primeiro, vejamos o método WRAP da aula de plug -in:
Public Static Object Wrap (destino do objeto, interceptador interceptador) {map <classe <?>, set <odhod>> signaturemap = getSignaturemap (interceptor); Classe <?> Type = Target.getClass (); Classe <?> [] Interfaces = getAllInterfaces (tipo, signotemap); if (interfaces.length> 0) {return proxy.newproxyInstance (type.getclassloader (), interfaces, novo plug -in (destino, interface, signotemap)); } retornar o destino;}A classe do plug -in implementa a interface InvocationHandler. Obviamente, vemos que uma classe dinâmica de proxy fornecida pelo JDK é devolvida aqui. Vamos dissecar outros métodos chamados por este método:
Método GetSignatureMap:
mapa estático privado <classe <?>, set <odhod>> getSignaturemap (interceptador interceptador) {intercepts interceptSannotation = interceptor.getClass (). getAnnotation (intercepts.class); if (interceptSannotation == null) {// Edição #251 lança nova pluginexception ("Não foi encontrada anotação @Intercepts no interceptador" + interceptor.getClass (). getName ()); } Assinatura [] sigs = interceptSannotation.value (); Mapa <classe <?>, Set <odhod>> SignatureMap = new Hashmap <classe <?>, Set <odhod>> (); para (Signature sig: sigs) {set <odhod> methods = signaturemap.get (sig.type ()); if (métodos == null) {métodos = new HashSet <Sodheth> (); SignatureMap.put (sig.type (), métodos); } tente {método método = sig.type (). getMethod (sig.method (), sig.args ()); métodos.add (método); } catch (noschmethodException e) {tiro a nova pluginexception ("não conseguiu encontrar o método" + sig.type () + "nomeado" + sig.method () + ". Causa:" + e, e); }} Retorne Signaturemap;}A explicação do método GetSignatureMap: Primeiro, você receberá a anotação @Interceptores da classe interceptadora e, em seguida, obterá a coleção de atributos @Signature Anotation de atributos desta anotação, depois atravesse esta coleção, retire o atributo de tipo (tipo de classe) do tipo @Signature e obtenha o método Attribute e Args baseado em base. Como o atributo @Signature anotado por @Interceptores é uma propriedade, ele eventualmente retornará um mapa com o tipo como chave e valor como definido <odhod>.
@Intercepts ({@assinatura (type = executor.class, métod = "update", args = {mapedstatement.class, object.class})})Por exemplo, a anotação @Intercepts retornará uma chave como executor e valor como uma coleção (existe apenas um elemento nesta coleção, ou seja, a instância do método, essa instância do método é o método de atualização da interface do executor, e esse método possui parâmetros de tipo mapedstatement e objeto). Esta instância do método é obtida com base nos atributos do método e args de @Signature. Se o parâmetro ARGS não corresponder ao método do método do tipo, uma exceção será lançada.
Método GetAllInterfaces:
Classe estática privada <?> [] getAllInterfaces (classe <?> type, map <classe <?>, set <odhod>> SignatureMap) {set <class <? >> interfaces = new HashSet <classe <? >> (); while (type! = null) {for (class <?> c: type.getInterfaces ()) {if (signaturemap.containsKey (c)) {interfaces.add (c); }} type = type.getSuperclass (); } retornar interfaces.toArray (nova classe <?> [interfaces.size ()]);}A explicação do método GetAllInterfaces: de acordo com o destino da instância de destino (esse alvo é a classe que o interceptador Mybatis pode interceptar, conforme mencionado anteriormente, Executor, ParameterHandler, Resultsethandler, DeclarationHandler) e suas classes parentais, retornam a matriz de interface que contém a implementação de destino no SignatureMap.
Portanto, a função da classe do plug -in é obter o atributo Attribute @Signature dos atributos anotados com base na anotação @Intercepts e, em seguida, usar a reflexão para encontrar o método correspondente de acordo com o método, o método e o AGRS de cada @Signature anotado. Por fim, com base na interface implementada pelo objeto de destino chamado, decida se retornará um objeto proxy para substituir o objeto de destino original.
Por exemplo, no site oficial do Mybatis, quando a configuração chama o método NewExecutor, o método Atualização (MappedStatement MS, Parâmetro do Objeto) da interface do executor é interceptado pelo interceptor. Portanto, o final é retornado com um plug -in de classe proxy, não um executor. Ao chamar o método dessa maneira, se for uma classe de proxy, ele será executado:
Public Object Invoke (proxy do objeto, método do método, objeto [] args) lança jogável {try {set <Sodhod> methods = signaturemap.get (métod.getDecLaringClass ()); if (métodos! = null && methods.contains (método)) {return interceptor.intercept (nova invocação (destino, método, args)); } retornar métod.invoke (Target, args); } catch (Exceção e) {tiro excepcionutil.unwraphrowable (e); }}É isso mesmo, se o método correspondente for encontrado e for proxyed, o método interceptador da interface interceptador será executado.
Esta aula de invocação é a seguinte:
classe pública Invocation {destino de objeto privado; método privado método; objeto privado [] args; Invocação pública (destino do objeto, método do método, objeto [] args) {this.target = Target; this.method = método; this.args = args; } public Object getTarget () {return Target; } método público getMethod () {Método de retorno; } public object [] getArgs () {return args; } public object prossegu () lança InvocationTargeTexception, ilegalAccessException {return métod.invoke (Target, args); }}Seu método de processo é chamar o método original (sem proxy).
Resumir
Entre os três métodos fornecidos pela interface Mybatis Interceptor, o método do plug -in é usado no processo de construção de determinados processadores (manipuladores). O método interceptador é usado para lidar com a execução da classe proxy. O método do setProperties é usado para definir as propriedades do interceptador.
De fato, os métodos fornecidos pelo site oficial do MYBATIS para usar as anotações e as classes de plug -in @Interceptors e @Signature para lidar com interceptores não são necessariamente usadas diretamente dessa maneira. Também podemos abandonar essas três classes e executar diretamente operações correspondentes com base no tipo de instância de destino no método do plug -in.
No geral, o Mybatis Interceptor ainda é muito simples. O próprio interceptador não requer muitos pontos de conhecimento, mas aprender o interceptador requer familiaridade com cada interface no mybatis, porque o interceptador envolve os pontos de conhecimento de cada interface.
Resumir
O exposto acima é uma exploração dos princípios do Mybatis Interceptor introduzido pelo editor. Espero que seja útil para todos. Se você tiver alguma dúvida, deixe -me uma mensagem e o editor responderá a todos a tempo. Muito obrigado pelo seu apoio ao site wulin.com!