Mybatis 차단기 실행 과정 분석
17929 단어 mybatis
intercept
방법 을 투철 하 게 분석 할 것 이다.동료 들 은 먼저 문장 내용 에 따라
전체 차단기 의 집행 과정 을 파악 하고 종이 에 각 점 을 그 려 서
의 소스 코드 를 읽 고 이런 점 을 선 으로 연결 시 켜 하나님 의 시각 에 서서 더욱 깊이 이해 했다.차단기 발견
홈 페이지 설명 에 따 르 면 저 희 는
org.apache.ibatis.plugin.Interceptor
인터페이스 사용자 정의 차단 기 를 실현 함으로써 사용자 정의 차단 기 를 Mybatis configuration 에 추가 하 는 두 가지 방법 이 있 습 니 다.설정 파일 방식
mybatis-config.xml
에 plugin 추가
XMLConfigBuilder
Mybatis 전역 프로필 을 분석 하 는 데 pluginElement
방법 이 있 습 니 다. private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
이 방법 에서 알 수 있 듯 이 XML
XNode
을 옮 겨 다 니 며 이 node 에 interceptor
속성 이 있다 면 차단 기 를 설정 하고 configuration.addInterceptor(interceptorInstance);
를 통 해 차단 기 인 스 턴 스 를 Mybatis Configuration 에 추가 합 니 다.주해 방식
글 Mybatis 차단기 의 데이터 암호 화 복호화 에서 제 가 사용자 정의 차단기 류 에
@Component
주 해 를 추가 한 것 을 보 았 습 니 다. 현재 마이크로 서비스 프레임 워 크 에는 Spring Boot 에 Mybatis Starter 의존 을 추가 하 는 형식 이 많이 존재 합 니 다. MybatisAutoConfiguration.java
의 구조 방법 을 보십시오.public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider databaseIdProvider,
ObjectProvider> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
}
구조 방법
interceptorsProvider.getIfAvailable();
에서 모든 주입 인 터 셉 터 를 가 져 오고 구축 SqlSessionFactory
할 때 차단 기 를 추가 합 니 다.if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
호출 프로 세 스 분석
Configuration 류 는 Mybatis 의 모든 설정 정 보 를 포함 하고 있 습 니 다. 그 안에 4 가지 중요 한 방법 이 있 습 니 다. 또한 차단기 차단 방법 입 니 다.
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.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);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
그들의 집행 순 서 는
newExecutor
- > StatementHandler
- > ParameterHandler
- > ResultSetHandler
- > StatementHandler
입 니 다. 왜 이 순서 인지 보 세 요. MyBatis 에서 SqlSession Factory 를 사용 하여 SqlSession 을 만 드 는 지 알 고 있 습 니 다.세 션 이 있 으 면 맵 문 구 를 실행 하고 제출 하거나 스크롤 백 연결 을 할 수 있 습 니 다. 마지막 으로 필요 하지 않 을 때 세 션 을 닫 습 니 다.참고 로 DefaultSqlSessionFactory.java
클래스 의 openSessionFromDataSource
방법 에서 Configuration 클래스 의 newExecutor
방법 을 호출 합 니 다.private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// Configuration newExecutor
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
SqlSessionTemplate.java
은 SqlSession
인 터 페 이 스 를 실현 했다. 그 안에 개인 내부 류 SqlSessionInterceptor
가 있 고 InvocationHandler
실현 되 었 다. 이것 은 자바 동적 에이전트 의 실현 방식 으로 재 작성 invoke
방법 에 관심 을 가진다. 여 기 는 방법 이 진정 으로 호출 된 곳 이다. 스 택 을 추적 한 결과 Configuration 류 에 최종 적 으로 호출 된 new Statement Handler 방법 을 발견 했다. @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// , , Configuration newStatementHandler
Object result = method.invoke(sqlSession, args);
...
} catch (Throwable t) {
...
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
Configuration 류 의 new StatementHandler 방법 에서
new RoutingStatementHandler(...)
방법 으로 StatementHandler 를 구축 하고 이 방법 에서 statement Type 에 따라 어떤 StatementHandler 를 생 성 하 는 지 판단 합 니 다. public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
세 가지 유형 Statement Handler 가 모두 계승 되 었 습 니 다
BaseStatementHandler.java
. 아래 의 관계 도 를 보 세 요.구체 적 인 StatementHandler 를 실례 화 할 때 부모 클래스 BaseStatementHandler 의 구조 기 를 먼저 호출 하고 부모 클래스 의 구조 기 에서 Configuration 클래스 의
newParameterHandler
와 newResultSetHandler
방법 을 각각 순서대로 호출 합 니 다.this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
그 러 니까 전체 호출 과정 은 다음 과 같다.
newExecutor
- > StatementHandler
- > ParameterHandler
- > ResultSetHandler
- > StatementHandler
이렇게 많은 말 을 했 지만 차단기 가 어떻게 실행 되 는 지 에 대해 서 는 말 하지 못 했 습 니 다. 서 두 르 지 마 세 요. 앞 에 있 는 것들 은 모두 깔 려 있 습 니 다. 세심 한 동료 들 이 Configuratin 류 의 네 가지 방법 중 똑 같은 코드 가 있다 는 것 을 발 견 했 을 수도 있 습 니 다.interceptorChain.pluginAll(...)
맞습니다. 이름 을 통 해 우 리 는 이것 이 차단기 의 관건 이 라 고 추측 할 수 있 습 니 다. interceptorChain 은 Configuration 류 의 구성원 변수 입 니 다.
InterceptorChain.java
류 를 보 세 요.public class InterceptorChain {
private final List interceptors = new ArrayList();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
plugin All 방법 에서 모든 차단 기 를 옮 겨 다 니 는 plugin 방법 은 사용자 정의 차단기 에서 plugin 방법 을 다시 씁 니 다. 여 기 는 유 니 버 설 페이지 차단기 로 호출 차단기 과정 을 설명 합 니 다. 관건 적 인 코드 를 보십시오.
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class PageInterceptor implements Interceptor {
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
Plugin.java
은 InvocationHandler
인 터 페 이 스 를 실 현 했 고 자바 동적 에이전트 로 정적 방법 wrap
을 호출 했다.public static Object wrap(Object target, Interceptor interceptor) {
Map, Set> signatureMap = getSignatureMap(interceptor);
Class> type = target.getClass();
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
interfaces.length > 0
도 target 에 프 록 시 대상 을 생 성 합 니 다. 즉, Configuration 류 네 가지 방법 으로 호출 된 executor / parameter Handler / resultSetHandler / statementHandler 에 프 록 시 대상 을 생 성 합 니 다. 여기 서 두 가지 중요 한 방법 getSignatureMap(interceptor)
과 getAllInterfaces(type, signatureMap)
을 따로 분석 해 야 합 니 다.private static Map, Set> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map, Set> signatureMap = new HashMap, Set>();
for (Signature sig : sigs) {
Set methods = signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet();
signatureMap.put(sig.type(), methods);
}
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
이 방법 은 자바 반 사 를 통 해 차단기 류 의 주석 정 보 를 읽 고 Type 을 key 로 하고 Method 집합 을 Value 로 하 는 HashMap 을 되 돌려 줍 니 다. 위의 페이지 차단 기 를 예 로 들 면 key 는
getSignatureMap(interceptor)
이 고 Value 는 두 개의 org.apache.ibatis.executor.Executor
query 방법 으로 다시 봅 니 다
.private static Class>[] getAllInterfaces(Class> type, Map, Set> signatureMap) {
Set> interfaces = new HashSet>();
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()]);
}
이 방법 은 대상 인 스 턴 스 target 과 부모 클래스 의 인터페이스 배열 에 따라 되 돌아 갑 니 다.
getAllInterfaces(type, signatureMap)
방법 은 인터페이스 배열 의 길이 가 0 보다 크 면 target 에 프 록 시 대상 을 생 성 합 니 다. 마지막 으로 DefaultSqlSession 에서 구체 적 인 실행 을 수행 할 때 selectList 방법 과 같이 이때 executor 는 방금 생 성 된 프 록 시 대상 입 니 다.return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
executor 호출 방법 은 plugin 재 작성 invoke 방법 을 실행 합 니 다.
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
최종 적 으로 사용자 정의 차단기 의 intercept 방법 을 실행 합 니 다. 차단 기 는 이렇게 실 행 됩 니 다. Mybatis 프레임 워 크 에서 자바 동적 대 리 를 대량으로 사 용 했 습 니 다. 예 를 들 어 Mapper 인터페이스 에서 방법 만 정의 하고 구체 적 인 실현 류 가 없 는 것 을 발 견 했 습 니 다. 이 모든 것 은 자바 동적 대 리 를 사용 해 야 하기 때문에 동적 대 리 를 이해 합 니 다.전체 집행 과정 을 더욱 잘 이해 할 수 있다.
차단기 설명
본 논문 에서 페이지 차단기 의 일부 핵심 코드 를 캡 처 했 는데 이 차단기 의 설명 내용 은 다음 과 같다.
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
Mybatis 차단기 의 데이터 암호 화 복호화 에서 매개 변수 차단 기 를 요청 하고 결과 집합 차단 기 를 되 돌려 주 는 내용 은 다음 과 같 습 니 다.
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class})
})
모든 차단기 차단 방법 서명 (Signature) 이 다 릅 니 다. 어떻게 써 야 하나 요?사실 간단 합 니 다. 이것 은 모두 인터페이스 Executor / Parameter Handler / ResultSetHandler 의 방법 입 니 다. 해당 하 는 인터페이스 방법 에 따라 여기에 설정 하면 됩 니 다. 차단 기 를 반사 적 으로 해석 할 때 해당 하 는 방법 으로 서명 할 수 있 는 지 판단 합 니 다. 회의 보고
Plugin.wrap
이상 예 를 들 어 Executor 인 터 페 이 스 를 찾 지 못 하면 두 개의 과부하 query 방법 이 있 습 니 다.주해 중의 내용 을 다시 보 니 갑자기 밝 아 지지 않 습 니까? List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
글 의 시작 부분 에 있 는 깔개 와 결합 하여 Configuration 류 의 네 가지 방법 을 차단 합 니 다. 맞습니다. 바로 Executor / Parameter Handler / ResultSetHandler / StatementHandler 입 니 다. Mybatis 차단기 의 데이터 암호 화 복호화 시작
NoSuchMethodException
내용 을 계속 보고 Executor / parameter Handler / ResultSetHandler / StatementHandler 의 역할 을 충분히 이해 합 니 다.우 리 는 차단 기 를 이용 해 우리 만 의 수작 을 부 릴 수 있다.문제 달걀
우 리 는 호출 차단 기 를 보 았 을 때 interceptor Chain 을 통 해 호출 되 었 습 니 다. 직역 하면
입 니 다. 사실은 이것 은 디자인 모델 의
입 니 다.효율 향상 도구
공중 번호 에 관심 을 가지 고 답장
을 통 해 우리 의 효율 적 인 작업 을 도 울 수 있 는 도 구 를 더 많이 얻 을 수 있 습 니 다.Free Mybatis Plugin
Mybatis 를 사용 하고 손 으로 SQL 을 써 야 할 때 Mapper 인터페이스 에서 방법 을 정의 하 는 동시에 XML 에서 같은 이름 의 statementId 의 SQL 을 정의 해 야 합 니 다. 이 Intellij IDEA 플러그 인 은 빠 른 포 지 셔 닝 방법 과 XML 을 도와 주 고 자바 에서 SQL 로 전환 합 니 다.
SQL 에서 자바 까지
유인원 은 왜 원본 코드 를 봐 야 합 니까?
대중 번호 에 관심 을 가지 고 coding 에 관 한 이 야 기 를 나 누 며 실력 을 향상 시 키 는 것 을 환영 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Mybatis가 키 id를 삽입하는 방법을 되돌려줍니다.mapper의 xml 파일에useGeneratedKeys 구성 KeyProperty를 사용하여 Id로 돌아가면 됩니다. PS: Mybatis의 insert에서 키 ID를 반환하는 방법 1、XyzMapper.xml 또...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.