Mybatis 인터셉터 소개
Mybatis는 플러그인 기능을 제공합니다. 플러그인이라고하지만 실제로는 인터셉터 기능입니다. 인터셉터가 mybatis를 차단하는 것은 무엇입니까?
공식 웹 사이트로 이동하여 다음을 살펴 보겠습니다.
MyBatis를 사용하면 매핑 된 문을 실행하는 동안 특정 지점에서 통화를 가로 채 릴 수 있습니다. 기본적으로 메소드 호출 MyBatis는 플러그인을 가로 채도록 허용합니다.
업데이트, 쿼리, 커밋, 롤백 및 기타 메소드와 같은 다른 인터페이스의 메소드와 같은 실행자 인터페이스를 가로 채울 수있는 몇 가지 방법을 보았습니다.
기다리다.
전체 요약은 다음과 같습니다.
인터셉터 사용
인터셉터 소개 및 구성
먼저 MyBatis 인터셉터의 인터페이스 정의를 살펴 보겠습니다.
공개 인터페이스 인터셉터 {객체 인터셉트 (호출 호출)는 던질 수 있습니다. 객체 플러그인 (객체 대상); void setProperties (속성 속성);}비교적 간단하며 3 가지 방법 만 있습니다. Mybatis에는 기본적으로 인터셉터 인터페이스 구현 클래스가 없으며 개발자는 자신의 요구를 충족시키는 인터셉터를 구현할 수 있습니다.
다음은 Mybatis 공식 웹 사이트의 인터셉터의 예입니다.
@InterCepts ({@signature (type = executor.class, method = "update", args = {mappedStatement.class, object.class})) public class exampleplugin interceptor {public 객체 intercept (invocation invocation) throws trownable {return invocation.proceed (); } public Object Plugin (Object Target) {return Plugin.wrap (Target, this); } public void setProperties (속성 속성) {}}글로벌 XML 구성 :
<플러그인> <플러그인 인터셉터 = "org.format.mybatis.cache.interceptor.exampleplugin"> </plugin> </plugins>
이 인터셉터는 Executor 인터페이스의 업데이트 방법을 가로 채 웁니다 (실제로 SQLSESSION의 추가, 삭제 및 수정 작업). 실행자를 실행하는 모든 업데이트 방법은 인터셉터에 의해 차단됩니다.
소스 코드 분석
이 코드의 소스 코드를 분석하겠습니다.
먼저 소스-> 구성 파일에서 분석을 시작합니다.
XMLCONFIGBUILDER parses pluginElement MyBatis 글로벌 구성 파일의 개인 메소드 :
private void pluginElement (xnode parent)는 예외를 던졌습니다 {if (parent! = null) {for (xnode child : parent.getchildren ()) {String interceptor = child.getStringattribute ( "interceptor"); 속성 속성 = child.getchildrenasproperties (); 인터셉터 인터셉터 로린 스탠스 = (인터셉터) resolveClass (interceptor) .newinstance (); InterceptorInstance.setProperties (속성); configuration.addinterceptor (interceptorinstance); }}}특정 구문 분석 코드는 실제로 비교적 간단하므로 게시하지 않습니다. 주로 플러그인 노드의 인터셉터 속성으로 표시되는 클래스를 반사로 인스턴스화합니다. 그런 다음 글로벌 구성 클래스 구성의 addinterceptor 메소드를 호출하십시오.
public void addinterceptor (인터셉터 인터셉터) {interceptorchain.addinterceptor (인터셉터); }이 interceptorchain은 구성의 내부 특성이며, 그 유형은 인터셉터 체인 인 인터셉트토 체인입니다. 정의를 살펴 보겠습니다.
공개 클래스 인터셉터 체인 {개인 최종 목록 <인터셉터> 인터셉터 = 새로운 ArrayList <인터셉터> (); public Object Pluginall (객체 대상) {for (인터셉터 인터셉터 : 인터셉터) {target = interceptor.plugin (target); } 반환 대상; } public void addinterceptor (인터셉터 인터셉터) {interceptor.add (인터셉터); } public list <인터셉터> getInterceptors () {return collections.unmodifiablelist (인터셉터); }}이제 인터셉터 구성의 구문 분석과 인터셉터의 소유권을 이해함으로써, 인터셉터가 이러한 방법 (Executor, ParameterHandler, resultesThandler의 부분적 방법)을 가로 채는 이유를 되돌아 봅니다.
public parameterHandler NewParameterAndler (MappedStatement MappedStatement, Object ParameterObject, BoundSQL BoundSQL) {ParameterAndler ParameterAndler = MappedStatement.getLang (). CreateParameterAndler (MappedStatement, ParameterObject, BoundSQL); ParameterAndler = (ParameterAndler) interceptorchain.pluginall (ParameterHandler); return parameterHandler;} public resultsEtandler newResultSethandler (집행자, 맵핑 스테이트 맵핑 스테이트, 행 바운드로 바운드, 파라미터 핸들러 매개 변수 핸들러, 레이 술란 더 resulthandler, boundsql boundsql) {resultEthandler resultShaultEdThernsultsler (executor, renstatement, mappedstatement, mappedstatement, mappedstatement) BONDSQL, ROWBOUNDS); resultSetHandler = (resultSethandler) interceptorchain.pluginall (resultesthandler); return resultSetHandler;} public statement hewStatementHandler (집행자, 맵핑 스테이트 매핑 스테이트, 객체 매개 변수, 행 바운드, rowbounds rowbounds, resulthandler rigsulthandler, boundsql boundsql) {statehandler stateHandler = 새로운 라우팅 스테이트 핸드 러 (exectingStatementHandler) (매개 변수, 매개 변수, rowbounds, rowbounds, rowbounds, rowbounds. boundsql); StateHandler = (StateHandler) interceptorchain.pluginall (StateHandler); return stateHandler;} 공개 집행자 NewExecutor (트랜잭션 트랜잭션, ExecutOptype Executiple, Boolean Autocommit) {ExecutOple = ExecutOptype == NULL? defaultexecutortype : executipe; ExecutOple = ExecutOptype == NULL? ExecutOptype.simple : Executipe; 집행자 집행자; if (ExecutOple.batch == ExecutOptype) {executor = new BatchExecutor (this, transaction); } else if (executorType.reuse == executOptype) {executor = new reuseexecutor (this, transaction); } else {executor = new SimpleExexecutor (이, 트랜잭션); } if (Cacheenabled) {Executor = New CachingExecutor (Executor, AutoCommit); } executor = (executor) interceptorchain.pluginall (executor); 귀환 집행자;}위의 4 가지 방법은 모두 구성 방법입니다. 이 방법은 mybatis (추가, 삭제, 수정 및 쿼리)의 작동에서 실행됩니다. 실행 순서는 Executor, ParameterHandler, resultSENDLER, StateHandler (ParameterAndler 및 ResultsEndler가 StateHandler [3 구현 클래스 CallableStatemAndler, PrepartatementHandler, SimpleStatementHandler]를 만들 때 생성되는 곳입니다. 생성자는 제작자를 Calluctor를 호출합니다.
이 4 가지 방법이 해당 객체를 인스턴스화 한 후에는 interceptorchain의 플러그인 몰 메소드를 호출합니다. 앞에서 언급했듯이 인터셉트 토 체인 플러그인이 도입되었으며, 이는 모든 인터셉터를 가로지고 각 인터셉터의 플러그인 메소드를 호출하는 것입니다. 참고 : 인터셉터의 플러그인 메소드의 반환 값은 원래 객체에 직접 할당됩니다.
StateHandler를 가로 채울 수 있으므로이 인터페이스는 주로 SQL 구문의 구성을 처리합니다. 따라서 예를 들어, 페이징의 기능은 인터셉터로 구현 될 수 있습니다. 인터셉터의 플러그인 메소드에서 StateHandler Interface 구현 클래스에서 SQL을 처리하면 반사를 사용하여 구현할 수 있습니다.
Mybatis는 또한 @intercepts 및 @Signature에 대한 주석을 제공합니다. 공식 웹 사이트의 예는 플러그인 클래스 사용을 포함 하여이 두 가지 주석을 사용하는 것입니다.
@overridepublic 객체 플러그인 (객체 대상) {return plugin.wrap (target, this);}이 3 개의 "새로운 조합"의 소스 코드를 분석하겠습니다. 먼저 플러그인 클래스의 랩 방법을 살펴 보겠습니다.
공개 정적 객체 랩 (객체 대상, 인터셉터 인터셉터) {map <class <?>, set <method >> signaturemap = getSignaturemap (interceptor); class <?> type = target.getClass (); class <?> [] interfaces = getAllInterfaces (type, signaturemap); if (interfaces.length> 0) {return proxy.newproxyInstance (type.getClassLoader (), 인터페이스, 새 플러그인 (대상, 인터페이스, SignaturemAP)); } 반환 대상;}플러그인 클래스는 InvocationHandler 인터페이스를 구현합니다. 분명히, 우리는 JDK 자체가 제공하는 동적 프록시 클래스가 여기에서 반환된다는 것을 알 수 있습니다. 이 방법에 의해 불리는 다른 방법을 해부합시다.
getSignaturemap 메소드 :
개인 정적 맵 <class <?>, set <메소드 >> getSignaturemap (인터셉터 인터셉터) {intercepts interceptSannotation = interceptor.getCrass (). getAnnotation (interannotation (intercepts.class); if (interceptSannotation == null) {// issue #251 새 pluginexception 던지기 ( "아니요 @intercepts 주석"은 인터셉터에서 발견되었습니다. } signature [] sigs = interceptSannotation.value (); map <class <?>, set <method >> signaturemap = new Hashmap <class <?>, set <method >> (); for (signature sig : sigs) {set <method> method = signaturemap.get (sig.type ()); if (method == null) {method = new Hashset <메소드> (); signaturemap.put (sig.type (), 메소드); } try {method method = sig.type (). getMethod (sig.method (), sig.args ()); 방법 .add (메소드); } catch (nosuchmethodexception e) {throw new pluginexception ( "" + sig.type () + "명명 된" + sig.method () + "에서 메소드를 찾을 수 없습니다. 원인 :" + e, e); }} Return SignatureMap;}getSignaturemap 메소드 설명 : 먼저, 인터셉터 클래스의 @Interceptors 주석을 얻은 다음이 주석의 @Signature 주석 모음을 얻은 다음이 컬렉션의 속성을 통과 한 다음 @Signature Annotation의 유형 속성 (클래스 유형)을 가져간 다음이 유형을 기반으로하는 메소드 속성 및 Args 속성으로 메소드를 얻습니다. @Interceptors에 의해 주석이 붙은 @Signature 속성은 속성이므로 결국 <메소드> 세트로 유형의 맵을 키와 값으로 반환합니다.
}
예를 들어, @interceptors 주석은 키를 executor로 반환하고 컬렉션으로 값을 반환합니다 (이 컬렉션에는 하나의 요소 만 있습니다. 이 메소드 인스턴스는 @Signature의 메소드 및 ARGS 속성에 기초하여 얻습니다. ARGS 매개 변수가 유형 유형의 메소드 메소드에 해당하지 않으면 예외가 발생됩니다.
getAllInterfaces 메소드 :
private static class <?> [] getAllInterfaces (class <?> 유형, map <class <?>, set <method >> signaturemap) {set <class <? >> 인터페이스 = new Hashset <class <? >> (); while (type! = null) {for (class <?> c : type.getInterfaces ()) {if (signaturemap.containskey (c)) {interfaces.add (c); }} type = type.getSuperClass (); } return interfaces.toArray (new class <?> [interfaces.size ()];}GetAllInterfaces 메소드 설명 : 대상 인스턴스 대상 (이 대상은 MyBatis 인터셉터가 앞에서 언급 한 것처럼 가로 채는 클래스, Executor, ParameterHandler, ResultesThandler, StateHandler) 및 상위 클래스 및 SignatureMap에서 대상 구현을 포함하는 인터페이스 어레이를 반환합니다.
따라서 플러그인 클래스의 기능은 @interceptors 주석을 기반으로 주석이 달린 속성의 @Signature 배열을 얻은 다음 반사를 사용하여 각 @Signature의 유형, 메소드 및 Args 속성에 따라 해당 메소드를 찾는 것입니다. 마지막으로, 호출 된 대상 객체가 구현 한 인터페이스를 기반으로 원래 대상 객체를 교체하기 위해 프록시 객체를 반환할지 여부를 결정하십시오.
예를 들어, MyBatis 공식 웹 사이트에서 구성이 NewExecutor 메소드를 호출 할 때 Executor 인터페이스의 업데이트 (MappedStatement MS, Object Parameter) 메소드는 인터셉터에 의해 차단됩니다. 따라서 엔드는 집행자가 아닌 프록시 클래스 플러그인으로 반환됩니다. 이 방법으로 메소드를 호출 할 때 프록시 클래스 인 경우 실행됩니다.
공개 객체 호출 (개체 프록시, 메소드 메소드, 개체 [] args) 던지기 가능 {try {set <메소드> 메서드 = signaturemap.get (method.getDeclaringClass ()); if (methods! = null && methods.contains (method)) {return interceptor.intercept (새 호출 (target, method, args)); } return method.invoke (target, args); } catch (예외 e) {throw exception.unwrapThrowable (e); }}맞습니다. 해당 메소드가 발견되고 근접한 경우 인터셉터 인터페이스의 인터셉터 메소드가 실행됩니다.
이 호출 클래스는 다음과 같습니다.
공개 클래스 호출 {개인 객체 대상; 개인 방법 방법; 개인 대상 [] args; 공개 호출 (객체 대상, 메소드 메소드, 개체 [] args) {this.target = target; this.method = 메소드; this.args = args; } public Object getTarget () {return target; } public method getMethod () {return method; } public Object [] getArgs () {return args; } public Object Proceed ()는 invoctionTargetexception, 불법 행사 {return method.invoke (target, args); }}프로세스 방법은 원래 방법 (프록시 없음)을 호출하는 것입니다.
요약
Mybatis 인터셉터 인터페이스가 제공하는 3 가지 방법 중 플러그인 방법은 특정 프로세서 (핸들러)의 구성 프로세스에 사용됩니다. 인터셉터 방법은 프록시 클래스의 실행을 처리하는 데 사용됩니다. SetProperties 방법은 인터셉터 특성을 설정하는 데 사용됩니다.
실제로 Mybatis 공식 웹 사이트에서 @interceptors 및 @signature 주석 및 플러그인 클래스를 사용하여 인터셉터를 처리하는 방법이 이러한 방식으로 직접 사용되는 것은 아닙니다. 또한이 세 가지 클래스를 포기하고 플러그인 방법의 대상 인스턴스 유형에 따라 해당 작업을 직접 수행 할 수 있습니다.
전반적으로, Mybatis 인터셉터는 여전히 매우 간단합니다. 인터셉터 자체는 너무 많은 지식 포인트가 필요하지 않지만 인터셉터를 배우려면 MyBatis의 각 인터페이스에 익숙해야합니다. 인터셉터에는 각 인터페이스의 지식 지점이 포함되기 때문입니다.
요약
위는 편집자가 소개 한 Mybatis 인터셉터의 원리를 탐구하는 것입니다. 모든 사람에게 도움이되기를 바랍니다. 궁금한 점이 있으면 메시지를 남겨 주시면 편집자가 제 시간에 모든 사람에게 답장을 드리겠습니다. Wulin.com 웹 사이트를 지원해 주셔서 대단히 감사합니다!