Mybatis 플러그 인 시스템 에 정통 합 니 다 (중간 부품 과 의 사고)
이것 은 my batis 시리즈 에 정통 한 마지막 글 일 것 입 니 다. 플러그 인 시스템 이 설명 한 후에 여러분 이 모 르 는 부분 이 있 으 면 같이 토론 할 수 있 습 니 다. 지난 블 로그 에서 Mybatis 의 Configuration 설정 체계 에 정통 하여 차단기 체인 을 이야기 할 때 작은 편집 자 는 주로 플러그 인 체 계 를 맞 추고 네 곳 에서 해당 하 는 강 화 를 했다 고 말 한 적 이 있 습 니 다.그 는 아직 op 과 조금 다 르 고 더욱 간결 하고 명료 하 죠? 그래서 오늘 은 마 이 바 티 스 의 플러그 인 시스템 을 보 여 드 리 겠 습 니 다.
my batis 플러그 인의 4 대 구성 요소 입구
플러그 인 메커니즘 은 MyBatis 기 존 시스템 을 확장 하기 위해 제 공 된 입구 입 니 다.밑바닥 은 동적 대 리 를 통 해 실현된다.프 록 시 차단 이 가능 한 인 터 페 이 스 는 네 개 입 니 다.
소스 코드 읽 기 가 왜 상기 네 개의 입구 인지 우 리 는 눈 으로 확인 할 수 있 습 니 다. Configuration 에서 방법:
//Executor
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);
}
// interceptorChain plugin
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
//ParameterHandler
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
//ResultSetHandler
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;
}
//StatementHandler
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;
}
중간물 실현 사고.
하나의 프레임 워 크 를 확장 하려 면 좋 은 디자인 사고방식 과 디자인 모델 이 필요 하 다. 이것 은 업무 코드 를 쓰 는 것 보다 더욱 도전 적 이 고 경험 이 없 으 면 상세 하고 전면적으로 생각 하기 어렵다.여기 서 편집장 은 자신의 사고 와 총 결 된 경험 을 설명 한다.
이상 은 작은 편집 사고의 몇 가지 점 입 니 다. 사실은 my batis 의 플러그 인 실현 도 중간 부품 의 실현 에 해당 합 니 다. 여러분 들 이 얻 기 를 바 랍 니 다.
페이지 별 구현
먼저 마 이 바 티 스 의 plugin 류 를 테스트 합 니 다.
public class PluginTest {
@Test
public void testMyPlugin(){
MyPlugin myPlugin = msg -> msg + " MyPlugin";
Interceptor interceptor = new MyPluginInterceptor();
MyPlugin wrap = (MyPlugin)Plugin.wrap(myPlugin, interceptor);
System.out.println(wrap.wrappedString("hello"));
}
public interface MyPlugin{
String wrappedString(String msg);
}
@Intercepts({
@Signature(type = MyPlugin.class, method = "wrappedString", args = {
String.class})})
public static class MyPluginInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept");
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return null;
}
@Override
public void setProperties(Properties properties) {
}
}
}
테스트 결과:
intercept
hello MyPlugin
여기 서 plugin 의 소스 코드 를 먼저 보 세 요.
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
//
public static Object wrap(Object target, Interceptor interceptor) {
// map
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(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
//
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> 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);
}
}
// @Intercepts({
// @Signature(type = MyPlugin.class, method = "wrappedString", args = {String.class})})
//
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
// @Signature, map,key ,values set
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
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;
}
//
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> 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()]);
}
}
Interceptor 의 소스 코드
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
위 에서 보 셨 듯 이 Plugin, Interceptor 의 역할 을 알 고 계 시 죠? 저 희 는 사실 페이지 플러그 인 을 만 드 는 것 은 Interceptor 인 터 페 이 스 를 실현 하고 설정 하 는 것 입 니 다.
간이 판 페이지 플러그 인 원리
페이지 플러그 인 원 리 는 먼저 페이지 클래스 를 설정 합 니 다. total, size, index 3 개의 속성 을 포함 하고 Mapper 인터페이스 에서 이 매개 변 수 는 자동 페이지 논 리 를 실행 해 야 한 다 는 것 을 표시 합 니 다.전체 실현 절 차 는 3 개 를 포함한다.
페이지 종류:
public class Page {
private Integer pageNum;
private Integer pageSize;
private Integer total;
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
}
PageInterceptor 클래스
@Intercepts(@Signature(type = StatementHandler.class,
method = "prepare", args = {
Connection.class, Integer.class}))
public class PageInterceptor implements Interceptor {
@Override
@SuppressWarnings("unchecked")
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
Page page = null;
if (parameterObject instanceof Page) {
page = (Page) parameterObject;
} else if (parameterObject instanceof Map) {
page = (Page) ((Map) parameterObject).values().stream().
filter(item -> item instanceof Page).findFirst().orElse(null);
}
if (page != null) {
int total = getTotalSize(invocation,statementHandler);
page.setTotal(total);
String limitSql = String.format("%s limit %s , %s",boundSql.getSql(),page.getPageNum(),page.getPageSize());
SystemMetaObject.forObject(boundSql).setValue("sql",limitSql);
}
return invocation.proceed();
}
private int getTotalSize(Invocation invocation, StatementHandler statementHandler) throws SQLException {
int count = 0;
BoundSql boundSql = statementHandler.getBoundSql();
String countSql = String.format("select count(*) from (%s) as _page",boundSql.getSql());
Connection connection = (Connection) invocation.getArgs()[0];
PreparedStatement preparedStatement = connection.prepareStatement(countSql);
statementHandler.getParameterHandler().setParameters(preparedStatement);
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
count = resultSet.getInt(1);
}
resultSet.close();
preparedStatement.close();;
return count;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
테스트 클래스:
@Before
public void init() throws SQLException {
//
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
// XML
factory = factoryBuilder.build(ExecutorTest.class.getResourceAsStream("/mybatis-config.xml"));
factory.getConfiguration().addInterceptor(new PageInterceptor());
sqlSession = factory.openSession();
}
@Test
public void selectByPage() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Page page = new Page();
page.setPageNum(1);
page.setPageSize(10);
List<User> users = mapper.selectByPage(page);
System.out.println(" :" + page.getTotal() + ", :" + users.size());
}
그리고 그 sql 은 사실 select * from users 입 니 다.
<select id="selectByPage" resultMap="result_user">
select * from users
select>
결과:
22:39:40,562 DEBUG selectByPage:54 - ==> Preparing: select count(*) from (select * from users) as _page
22:39:40,601 DEBUG selectByPage:54 - ==> Parameters:
22:39:40,631 DEBUG selectByPage:54 - ==> Preparing: select * from users limit 1 , 10
22:39:40,631 DEBUG selectByPage:54 - ==> Parameters:
22:39:40,641 DEBUG selectByPage:54 - <== Total: 10
:12, :10
이것 은 분명 완선 되 지 않 을 것 입 니 다. 여러분 은 my batis - plus 의 페이지 별 실현 이나 PageHelper 의 실현 을 볼 수 있 습 니 다. 물론 원 리 를 파악 하 는 것 이 가장 중요 합 니 다.
다른 플러그 인의 확장
sql 모니터링: sql 소모 시간 통계, sql 호출 횟수 통계, sql 로그 등.
작은 매듭
여러분 은 인터넷 에서 많은 플러그 인 을 볼 수 있 습 니 다. 사실은 위 에서 말 한 원 리 를 바탕 으로 각 구성 요소 에 대한 확장 입 니 다. 여러분 이 관심 이 있 으 면 주석 연 구 를 할 수 있 습 니 다.
총결산
한 달 여 동안 의 학습 총화 에 들 어가 면 작은 편 의 my batis 여행 이 일 단락 되 었 고 끝 이 났 습 니 다. 그리고 시간 이 있 으 면 my batis - plus 가 어떻게 확장 되 었 는 지 설명 하려 고 합 니 다. 그리고 my batis 의 간단 한 sql 조회 패키지 도 my batis 의 플러그 인 시스템 입 니 다.마지막 으로 소 편 과 소 편 문장 을 읽 는 여러분 께 감 사 드 립 니 다. 더욱 분발 하 세 요. 우 리 는 다음 기 에 새로운 내용 을 이어서 이야기 하 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
MySQL/마이바티스 | 동적 쿼리 사용A라는 서비스에 해당하는 테이블을 조인하고 조회하는 데 사용됩니다. 나중에 공통화를 위해 B 및 C 서비스도 추가됩니다. A, B, C 서비스는 모두 단일 쿼리에서 작동할 수 있도록 공통화되어야 합니다. 테이블에 각...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.