모듈 파싱을 구문 분석의 MyBatis 출처 : 플러그인 모듈

그림 삽입 설명 여기

소개

좀 플러그인 구현 원리의 MyBatis에 대한 에세이를 작성하기 전에

MyBatis로 쓰기 전용 인터페이스, 왜 실행?

책임 패턴 및 동적 에이전트 주쇄와 플러그인 Mybaits 달성

아래 몇 가지 점 절편에서 SQL 문을 실행할 수있는 동적 프록시 프로세스는 여러 개의 플러그 인을 구성 할 때, 책임 패턴의 체인은 여러 차단, 책임 패턴 UML 다이어그램의 체인을 만들 수 있습니다
그림 삽입 설명 여기
당신은 책임의 체인을 볼 수있는 각 처리기, 객체 책임 전체 체인이 종료 될 때까지 등등 처리 핸들러 오브젝트 등을 계속해서 다음에 전달 된 요청 메시지를 처리 한 다음 핸들러 오브젝트 핸들러 객체에 대한 참조를 포함한다. 그리고 우리가 핸들러의 실행 순서를 변경할 수 있습니다, 개방 및 폐쇄의 원칙에 맞춰, 추가 또는 핸들러 삭제

의 MyBatis 플러그를 쓰기

MyBatis로 캔 절편은 다음과 같은 방법으로 호출

  1. 집행자 (갱신, 쿼리, flushStatements, 커밋, 롤백, getTransaction, 가까운, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. StatementHandler (파라미터 화, 배치, 업데이트 쿼리, 준비)

이러한 개체는 나중에 언급 될 이유에 관해서

인쇄 플러그인을 작성하는 SQL 실행 시간

@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = { Statement.class, ResultHandler.class }),
        @Signature(type = StatementHandler.class, method = "update", args = { Statement.class }),
        @Signature(type = StatementHandler.class, method = "batch", args = { Statement.class })})
public class SqlCostTimeInterceptor implements Interceptor {

    public static final Logger logger = LoggerFactory.getLogger(SqlCostTimeInterceptor.class);

    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        long start = System.currentTimeMillis();
        try {
            // 执行被拦截的方法
            return invocation.proceed();
        } finally {
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            long end = System.currentTimeMillis();
            long cost = end - start;
            logger.info("{}, cost is {}", sql, cost);
        }
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {

    }
}

플러그인 구성 파일 MyBatis로 구성

<plugins>
	<plugin interceptor="com.javashitang.part1.plugins.SqlCostTimeInterceptor"></plugin>
</plugins>

그럼 당신은 시간이 많이 걸리는 SQL과 실행, 다음과 같은 효과를 인쇄 할 수 있습니다

select id, role_name as roleName, note from role where id = ?, cost is 35

원리 분석

우리의 방법은 대상체 원래 방법 대신 프록시 객체의 메소드를 호출 취하고 있으므로, 부가 기능을 동적 프록시 접근 방식을 통해 상기 이전의 MyBatis

말하자면 당신은 지정된 오브젝트의 프록시 클래스를 생성하는 라인에서의 InvocationHandler 인터페이스를 구현할 필요가 알고 요격하고, 다음 메소드의 InvocationHandler 호출 지정된 방법을 개선.

그러나의 InvocationHandler를 상속 한 후, 프록시 클래스를 생성하고,이 그렇게 어렵지 않다을 향상시키는 방법을 지정, 프레임은 다음 아를 패키지 도움이 될 수 있습니다

그래서 주로 더 @Signature 메모, 주석을 넣고 @Signature이 요격에 클래스와 메소드를 정의 메모를,이 @Intercepts,

그리고 편리한 플러그인 클래스 인터셉터 인터페이스를 제공하고, 당신은 그들이 그와 함께 사용하는 방법을보고, 동적 에이전트를 실현

우리는 때문에에, 인터셉터 인터페이스 분석을 시작 플러그인 인터셉터를 구현해야합니다

public interface Interceptor {

  /** 执行拦截逻辑的方法,Invocation只是将动态代理中获取到的一些参数封装成一个对象 */
  Object intercept(Invocation invocation) throws Throwable;

  /**
   * target是被拦截的对象,它的作用是给被拦截对象生成一个代理对象,并返回它。
   * 为了方便,可以直接使用Mybatis中org.apache.ibatis.plugin.Plugin类的wrap方法(是静态方法)生成代理对象
   */
  Object plugin(Object target);

  /** 根据配置初始化Interceptor对象 */
  void setProperties(Properties properties);

}

프록시 객체 생성 방법, 플러그인 클래스 내부 모습을 주요 방법이다만큼 다음, 본 방법은 프록시 플러그인 객체를 생성하는 데있어서, 일반적인 관행은 (이 타겟) Plugin.wrap를 호출하는 것

public class Plugin implements InvocationHandler {

  /** 目标对象 */
  private final Object target;
  /** Interceptor对象 */
  private final Interceptor interceptor;
  /** 记录了@Signature注解中的信息 */
  /** 被拦截的type->被拦截的方法 */
  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<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    // 取得要改变行为的类 (ParameterHandler | ResultSetHandler | StatementHandler | Executor)
    Class<?> type = target.getClass();
    // 拿到被代理对象的拦截方法,所实现的接口
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    // 如果当前传入的Target的接口中有@Intercepts注解中定义的接口,那么为之生成代理,否则原Target返回
    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 {
      // 获取当前方法所在类或接口中,可被当前 Interceptor 拦截的方法
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      // 如果当前调用的方法需要被拦截,则调用interceptor.intercept()方法进行拦截处理
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      // 如果当前调用的方法不能被拦截,则调用target对象的相应方法
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
}

지금까지, 프록시 클래스와는 얻을 절편에 Plugin.wrap () 메소드를 사용하는 특정 방법, 쉽게 도둑을 얻을 수 있습니다.

Plugin.invoke () 메소드 인터셉터 인터셉트 법 인터페이스 호출 및 대상 클래스, 오브젝트에있어서의 말단은, 오브젝트 파라미터는 호출에 포장

return interceptor.intercept(new Invocation(target, method, args));

그런 다음 호출의 정의를 보면

/**
 * @author Clinton Begin
 * 将要调用的类,方法,参数封装成一个对象,方便传递给拦截器
 */
public class Invocation {

  private final Object target;
  private final Method method;
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget() {
    return target;
  }

  public Method getMethod() {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }

  /** 这个方法是给拦截器调用的,拦截器最后会调用这个方法来执行本来要执行的方法,这样就可以在方法前后加上拦截的逻辑了 */
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }

}

하나의 방법은 () 메소드를 진행하고, 마지막 호출의 호출 객체가 진행 () (만 차단하는 다음 명확하게 바로 쓰기 강화 논리를 운영하는 차단 방법 인터셉터 개체에서 수행되어야한다 메소드 실행을 진행합니다 ) 방법이 될 수있다

지금까지 우리는 아마 마지막 단계에서만 다른 플러그인의 작동 원리를 이해 한, 대상 객체는 프록시 객체를 생성하기 위해, 우리는 MyBatis로 초기화에서 답변을 찾을

플러그, 인터셉터 채우기 전체 클래스 이름, 키 인터셉터 인터페이스 후에 달성 값 복수의 다음 값을 쓸 수 있고, 이러한 특성은, 프로퍼티 오브젝트 값으로 캡슐화 제공하는 방법 SetProperties를가 구성 파일 형식으로 배치 올

<plugin interceptor="">
	<property name="" value=""/>
	<property name="" value=""/>
</plugin>

우리가 볼 곳 구문 분석 parseConfiguration의 구성 파일이 방법 XMLConfigBuilder, 확인 프로세스를 MyBatis로 플러그인

pluginElement(root.evalNode("plugins"));
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        // 解析拦截器中配置的属性,并封装成一个Properties对象
        Properties properties = child.getChildrenAsProperties();
        // 通过类名示例化一个Interceptor对象
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        // 可以给拦截器的Properties属性赋值
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

객체의 인터셉터의 좋은 예는 객체 속성 인터셉터 InterceptorChain 배치됩니다

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  /** 这里有个特别有意思的地方,先添加的拦截器最后才会执行,因为代理是一层一层套上去的,就像这个函数f(f(f(x))) */
  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<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

pluginAll 방법 InterceptorChain 객체는 프록시 객체 DO를 생성하는 데 사용되지 않는 이유는 무엇입니까? 소위에서 봐

parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
executor = (Executor) interceptorChain.pluginAll(executor);

특정 클래스가 할 이유는 MyBatis로 이전 할 수있는 유일한 절편을 언급하는 것이 아닌가? 이러한 클래스는 에이전트를 할 수 있기 때문에.

당신이 무엇을해야하는지 등의 방법 ParameterHandler과 다른 일을 이해하기 때문에 지금까지 분석의 원칙에 MyBatis로 플러그가 완료, 또는 매우 간단하지만, 실제의 MyBatis 플러그를 작성하는 것은 쉽지 않다, 어떻게 향상 할

서명 주석이 주로 차단에 대한 방법을 클래스와 절편에 그것의 방법 및 플러그인 인터셉터 인터페이스를 정의하고 지정된 개체에 대한 프록시 객체를 생성 대처하고 지정하는 데 사용됩니다 @ 당신이 이전과 이후 몇 가지 추가 작업을 할 수 있도록, 요약하면

에 오신 것을 환영합니다 관심

그림 삽입 설명 여기

참고 블로그

기록의 MyBatis 플러그
[1] https://www.cnblogs.com/xrq730/p/6972268.html
MyBatis로 구현 플러그인 원리
[2] https://www.cnblogs.com/xrq730/p/6984982.html
[3] https://juejin.im/post/5abe12f5f265da237411177f

게시 된 385 개 원래 기사 · 원 찬양 1471 ·은 90 + 조회수

추천

출처blog.csdn.net/zzti_erlie/article/details/104416622