Mybatis Porxy 동적 에이전트 와 sql 해석 교체 문제 분석

JDK 상용 핵심 원리
개술
Mybatis 에서 자주 사용 하 는 역할 은 데이터베이스 에 있 는 표 의 필드 맵 을 대상 으로 하 는 속성 을 말 하 는 것 입 니 다.Mybatis 에 들 어가 기 전에 원생 JDBC 는 몇 가지 절차 가 있 습 니 다.JDBC 드라이버 패 키 지 를 가 져 오고 DriverManager 를 통 해 구동 을 등록 하고 연결 을 만 들 며 Statement 을 만 들 고 삭제 하고 검사 하 며 결과 집합 을 조작 하고 연결 을 닫 습 니 다.
프로 세 스 상세 설명
먼저 클래스 로 딩 을 진행 하고 DriverManager 를 통 해 구동 을 등록 합 니 다.

Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("");
왜 여기에 직접 등록 할 수 있 습 니까?com.my sql.jdbc.Driver 는 Driver.class 에 불 러 옵 니 다.DriverManager 에서 먼저 정적 코드 블록 이 있어 서 드라이버 를 초기 화 합 니 다.

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
loadInitialDrivers()를 통 해 Driver 를 불 러 오고 jdbc.drivers 를 꺼 내 며 ServiceLoader 를 통 해 Driver.class 를 읽 고 driver 와 모든 교체 기 를 읽 으 며 교체 합 니 다.

private static void loadInitialDrivers() {
    String drivers;
    //      ,     jdbc.drivers    
    try {
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            //      driver        
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            
            //       
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
            // Do nothing
            }
            return null;
        }
    });

    println("DriverManager.initialize: jdbc.drivers = " + drivers);

    if (drivers == null || drivers.equals("")) {
        return;
    }
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
        try {
            println("DriverManager.Initialize: loading " + aDriver);
            Class.forName(aDriver, true,
                    ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
            println("DriverManager.Initialize: load failed: " + ex);
        }
    }
}
Driver 를 불 러 온 후에 데이터베이스 와 의 연결 connection 을 얻 을 수 있 습 니 다.connection 은 Statement 을 만 들 수 있 습 니 다.Statement 은 sql 문 구 를 실행 할 수 있 습 니 다.결 과 를 결과 집합 으로 되 돌려 주 고 가 져 온 결과 집합 을 List 집합 에 옮 겨 놓 을 수 있 습 니 다.

Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from mybatis.user");
    while (resultSet.next()) {
      int id = resultSet.getInt(1);
      String username = resultSet.getString(2);
      list.add(new User(id,username));
}
원생 JDBC 직접 조작 에 서 는 번 거 로 운 절차 가 업무 코드 에 서 는 사용 되 지 않 으 며,Mybatis 는 더 편리 하 게 사용 할 수 있다.
JDK 동적 에이전트
sql 구문 분석 교체
JDK 동적 에이전트 에 서 는 프 록 시 라 는 클래스 를 이용 하여 이 루어 집 니 다.프 록 시 에 서 는 new Proxy Instance()방법 이 있 습 니 다.동적 프 록 시 인 스 턴 스 를 만 듭 니 다.

interface UserMapper {
  @Select("select * from mybatis.user where id =#{id}")
  List<User> selectUserList();
}

public static void main(String[] args) {
  UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(
    JDKMybatis.class.getClassLoader(),
    new Class<?>[]{UserMapper.class},
    new InvocationHandler() {
      /**
       *   invoke()            method,args
       * @param proxy      
       * @param method   
       * @param args     
       */
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //       ,              
        Select annotation = method.getAnnotation(Select.class);
        if (annotation != null) {
          String[] value = annotation.value();
          System.out.println(Arrays.toString(value));
        }
        return null;
      }
    });

  userMapper.selectUserList(1);
}
new Proxy Instance()의 생 성 은 세 개의 인자 가 필요 합 니 다.원본 코드 를 보면 ClassLoader 류 로 더,interfaces 인터페이스(Mapper 인터페이스),InvocationHandler 프로세서 가 필요 하 다 는 것 을 알 수 있 습 니 다.

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

sql 문장의 인 자 를 꺼 내 args 에 넣 으 려 면 맵 이 필요 합 니 다.
문제.
반사 획득 방법의 매개 변수 이름,method.getParameters()에서 얻 은 매개 변 수 는 모두 arg 0,arg 1...의미 없 는 매개 변수 입 니 다.

자바 8 이전에 코드 를 클 라 스 파일 로 컴 파일 한 후 방법 매개 변수 유형 은 고정 되 어 있 으 나 매개 변수 이름 은 잃 어 버 렸 습 니 다.컴 파일 할 때 컴 파일 옵션 이 필요 합 니 다.자바 c-parameters 는 기본적으로 닫 혀 있 습 니 다.아이디어 에서 열 려 야 합 니 다.열 린 후에 컴 파일 원본 파일 을 다시 시작 해 야 합 니 다.

이 방식 은 현재 환경 설정 만 임시로 해결 할 수 있 고 다른 사람 이 코드 를 실행 할 때 다시 설정 해 야 합 니 다.
다른 해결 방법 은 pom 파일 에 컴 파일 파 라 메 터 를 추가 합 니 다.

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <compilerArgument>-parameters</compilerArgument>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
</plugins>
컴 파일 이 완료 되면 다시 실행 하고 method.getParameters()를 통 해 인 자 를 가 져 옵 니 다.

원래 sql 을 분석 하려 면\#{}을 교체 해 야 합 니 다.이 럴 때 StringBuffer 클래스 를 사용 하여 교체 할 수 있 습 니 다.

private static String parseSql(String sql, Map<String, Object> argsNameMap) {
  //        
  char[] str = {'#', '{'};
  StringBuilder stringBuilder = new StringBuilder();
  for (int i = 0; i < sql.length(); i++) {
    char aloneParseSql = sql.charAt(i);

    if (str[0] == aloneParseSql) {
      int nextIndex = i + 1;
      char nextChar = sql.charAt(nextIndex);

      // #      { ,         
      if (str[1] != nextChar) {
        throw new RuntimeException(String.format(
          "     :#{
sql:%s
index:%d", stringBuilder.toString(), nextIndex)); } /* 1 2 #{} 3 argsName argsValue 4 stringBuilder sql */ StringBuilder partStringBuilder = new StringBuilder(); i = partParseSql(partStringBuilder, sql, nextIndex); String argsName = partStringBuilder.toString(); Object argsValue = argsNameMap.get(argsName); stringBuilder.append(argsValue.toString()); } // , stringBuilder.append(aloneParseSql); } return stringBuilder.toString(); }
그 중에서 교체 할 값 을 StringBuffer 클래스 로 구현 해 야 합 니 다.

private static int partParseSql(StringBuilder partStringBuilder, String sql, int nextIndex) {
  //    nextIndex          {       ,       
  nextIndex++;
  char[] str = {'}'};
  for (; nextIndex < sql.length(); nextIndex++) {
    char indexSql = sql.charAt(nextIndex);
    if (str[0] != indexSql) {
      partStringBuilder.append(indexSql);
    }
    if (str[0] == indexSql) {
      return nextIndex;
    }
  }
  throw new RuntimeException(String.format(
    "  :}
index:%d", nextIndex)); }
invoke 방법 에서 다시 호출 하여 sql 문장의 동적 조합 을 완성 합 니 다.

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //       ,              
  Select annotation = method.getAnnotation(Select.class);
  Map<String, Object> argsNameMap = MapBuildArgsName(method, args);
  if (annotation != null) {
    String[] value = annotation.value();
    String sql = value[0];
    sql = parseSql(sql, argsNameMap);
    System.out.println(sql);
  }
  return null;
}

Mybatis(1)Porxy 동적 에이전트 와 sql 해석 교체 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 Mybatis(1)Porxy 동적 에이전트 와 sql 해석 교체 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 지원 바 랍 니 다!

좋은 웹페이지 즐겨찾기