<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>이모씨</title>
    <link>https://yimoc.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 15 Jul 2026 05:00:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>yimoc</managingEditor>
    <item>
      <title>Graalvm native image</title>
      <link>https://yimoc.tistory.com/23</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.설치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Graalvm 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.graalvm.org/docs/getting-started/#install-graalvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.graalvm.org/docs/getting-started/#install-graalvm&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/graalvm/graalvm-ce-builds/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github.com/graalvm/graalvm-ce-builds/releases&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;os : windows 10 pro&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당하는 파일 다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setx환경변수 설정이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 cmd.exe에서 설정함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set&amp;nbsp;PATH=D:\develop\program\graalvm-ce-java8-21.0.0.2\bin;%PATH% &lt;br /&gt;set&amp;nbsp;JAVA_HOME=D:\develop\program\graalvm-ce-java8-21.0.0.2&lt;/p&gt;
&lt;pre id=&quot;code_1617848562689&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;set&amp;nbsp;PATH=D:\develop\program\graalvm-ce-java8-21.0.0.2\bin;%PATH%
set&amp;nbsp;JAVA_HOME=D:\develop\program\graalvm-ce-java8-21.0.0.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;setx 환경변수 설정방법&lt;br /&gt;setx&amp;nbsp;/M&amp;nbsp;PATH&amp;nbsp;&quot;D:\develop\program\graalvm-ce-java8-21.0.0.2\bin;%PATH%&quot; &lt;br /&gt;setx&amp;nbsp;/M&amp;nbsp;JAVA_HOME&amp;nbsp;&quot;D:\develop\program\graalvm-ce-java8-21.0.0.2&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 추가설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Download the SDK file&lt;span&gt;&amp;nbsp;&lt;/span&gt;GRMSDKX_EN_DVD.iso&lt;span&gt;&amp;nbsp;&lt;/span&gt;for from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.microsoft.com/en-gb/download&quot;&gt;Microsoft&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=8442&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.microsoft.com/en-us/download/details.aspx?id=8442&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mount the image by opening&lt;span&gt;&amp;nbsp;&lt;/span&gt;F:\Setup\SDKSetup.exe&lt;span&gt;&amp;nbsp;&lt;/span&gt;directly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;visaul studio Build Tools 2019 설치&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Start&amp;nbsp;-&amp;gt;&amp;nbsp;Visual&amp;nbsp;Studio&amp;nbsp;2019&amp;nbsp;-&amp;gt;x64&amp;nbsp;Native&amp;nbsp;Tools&amp;nbsp;Command&amp;nbsp;Prompt&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 실행해야함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않는 경우 아래 2가지 오류가 발생함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) Error: Default native-compiler executable 'cl.exe' not found via environment varialbe PATH&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) Error:&amp;nbsp;Native-image&amp;nbsp;building&amp;nbsp;on&amp;nbsp;Windows&amp;nbsp;currently&amp;nbsp;only&amp;nbsp;supports&amp;nbsp;target&amp;nbsp;architecture:&amp;nbsp;AMD64&amp;nbsp;((x64)&amp;nbsp;unsupported)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Code작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HelloWorld.java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. native image 빌드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D:\develop\graalvm_ws&amp;gt;native-image&amp;nbsp;HelloWorld &lt;br /&gt;[helloworld:5400]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;classlist:&amp;nbsp;&amp;nbsp;&amp;nbsp;1,195.32&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.14&amp;nbsp;GB &lt;br /&gt;[helloworld:5400]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setup:&amp;nbsp;&amp;nbsp;&amp;nbsp;1,413.75&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.14&amp;nbsp;GB &lt;br /&gt;Error:&amp;nbsp;Native-image&amp;nbsp;building&amp;nbsp;on&amp;nbsp;Windows&amp;nbsp;currently&amp;nbsp;only&amp;nbsp;supports&amp;nbsp;target&amp;nbsp;architecture:&amp;nbsp;AMD64&amp;nbsp;((x64)&amp;nbsp;unsupported) &lt;br /&gt;Error:&amp;nbsp;To&amp;nbsp;prevent&amp;nbsp;native-toolchain&amp;nbsp;checking&amp;nbsp;provide&amp;nbsp;command-line&amp;nbsp;option&amp;nbsp;-H:-CheckToolchain &lt;br /&gt;Error:&amp;nbsp;Use&amp;nbsp;-H:+ReportExceptionStackTraces&amp;nbsp;to&amp;nbsp;print&amp;nbsp;stacktrace&amp;nbsp;of&amp;nbsp;underlying&amp;nbsp;exception &lt;br /&gt;Error:&amp;nbsp;Image&amp;nbsp;build&amp;nbsp;request&amp;nbsp;failed&amp;nbsp;with&amp;nbsp;exit&amp;nbsp;status&amp;nbsp;1 &lt;br /&gt;&lt;br /&gt;D:\develop\graalvm_ws&amp;gt;native-image&amp;nbsp;-H:-CheckToolchain&amp;nbsp;HelloWorld &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;classlist:&amp;nbsp;&amp;nbsp;&amp;nbsp;1,432.10&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.15&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(cap):&amp;nbsp;&amp;nbsp;&amp;nbsp;4,629.42&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.15&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setup:&amp;nbsp;&amp;nbsp;&amp;nbsp;6,745.06&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.15&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(clinit):&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;132.44&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.61&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;(typeflow):&amp;nbsp;&amp;nbsp;&amp;nbsp;3,455.79&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.61&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(objects):&amp;nbsp;&amp;nbsp;&amp;nbsp;2,803.94&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.61&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;(features):&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;145.35&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.61&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis:&amp;nbsp;&amp;nbsp;&amp;nbsp;6,665.44&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.61&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;universe:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;324.37&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.61&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(parse):&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;736.25&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.64&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(inline):&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;870.19&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.64&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(compile):&amp;nbsp;&amp;nbsp;&amp;nbsp;4,358.17&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.78&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;compile:&amp;nbsp;&amp;nbsp;&amp;nbsp;6,352.63&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.78&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;image:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;568.52&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.78&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;write:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;725.91&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.78&amp;nbsp;GB &lt;br /&gt;[helloworld:40832]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[total]:&amp;nbsp;&amp;nbsp;23,002.35&amp;nbsp;ms,&amp;nbsp;&amp;nbsp;1.78&amp;nbsp;GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 참고사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blogs.oracle.com/javakr/graalvm-nativeimage-revisited&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;blogs.oracle.com/javakr/graalvm-nativeimage-revisited&lt;/a&gt;&lt;/p&gt;</description>
      <category>GraalVM</category>
      <category>native image</category>
      <category>native-image</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/23</guid>
      <comments>https://yimoc.tistory.com/23#entry23comment</comments>
      <pubDate>Wed, 7 Apr 2021 14:52:06 +0900</pubDate>
    </item>
    <item>
      <title>Storage 비교 설명</title>
      <link>https://yimoc.tistory.com/22</link>
      <description>&lt;p&gt;출처&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/storage/comparing-your-on-premises-storage-patterns-with-aws-storage-services/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;aws.amazon.com/ko/blogs/storage/comparing-your-on-premises-storage-patterns-with-aws-storage-services/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1617672577388&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Comparing your on-premises storage patterns with AWS Storage services | Amazon Web Services&quot; data-og-description=&quot;Many companies want to move to the cloud, and for most of those companies, moving to the cloud starts with an assessment of existing storage infrastructure. It is useful to know how to map existing storage with AWS Storage options to assess the existing st&quot; data-og-host=&quot;aws.amazon.com&quot; data-og-source-url=&quot;https://aws.amazon.com/ko/blogs/storage/comparing-your-on-premises-storage-patterns-with-aws-storage-services/&quot; data-og-url=&quot;https://aws.amazon.com/blogs/storage/comparing-your-on-premises-storage-patterns-with-aws-storage-services/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wvtLu/hyJMjheiw0/1rSPOIpX1hSTQdP6pqFS61/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/WisfE/hyJNTVwQ73/RuHgZO65GUjmH3HbWkCa1k/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/b1kwno/hyJN1TyUDo/OCVh3wCHRCzu8qjarVD650/img.png?width=2120&amp;amp;height=1148&amp;amp;face=0_0_2120_1148&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/storage/comparing-your-on-premises-storage-patterns-with-aws-storage-services/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aws.amazon.com/ko/blogs/storage/comparing-your-on-premises-storage-patterns-with-aws-storage-services/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wvtLu/hyJMjheiw0/1rSPOIpX1hSTQdP6pqFS61/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/WisfE/hyJNTVwQ73/RuHgZO65GUjmH3HbWkCa1k/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/b1kwno/hyJN1TyUDo/OCVh3wCHRCzu8qjarVD650/img.png?width=2120&amp;amp;height=1148&amp;amp;face=0_0_2120_1148');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Comparing your on-premises storage patterns with AWS Storage services | Amazon Web Services&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Many companies want to move to the cloud, and for most of those companies, moving to the cloud starts with an assessment of existing storage infrastructure. It is useful to know how to map existing storage with AWS Storage options to assess the existing st&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. On-premises stroage pattern&lt;/h2&gt;
&lt;p&gt;일반적인 On-promise 스토리지는 FC SAN, iSCSI SAN, NAS, Object Storage 를 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) FC(Fibre Channel) SAN Storage&lt;/h3&gt;
&lt;p&gt;SAN은 전용 고속 FC네트워크를 통해 블록스토리지리소스를 공유하는 기술이다.&lt;/p&gt;
&lt;p&gt;FCP(Fibre Channel Protocol)는 FC네트워크를 통한 SCSI프로토콜의 매핑입니다. (? -좀더 조사 필요)&lt;/p&gt;
&lt;p&gt;DC SAN을 사용하는 애플리케이션은 ERP, CRM, SAP, SQL, Oarcle, DB2, MSSQL등이 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2)iSCSI SAN stroage&lt;/h3&gt;
&lt;p&gt;iSCSI는 IP네트워크를 통해 스토리지리소스를 공유하는 기술이다.&lt;/p&gt;
&lt;p&gt;종종, 디스크리소스는 iSCSI SAN에서 공유되는 저장소 리소스이다.&lt;/p&gt;
&lt;p&gt;iSCSI는 TCP/IP를 통한 SCSI 프로토콜 매핑이다.&lt;/p&gt;
&lt;p&gt;SCSI가 FC와 같은 다른 전송을 통해 매핑 된 것과 동일한 방식으로, 이론적으로 모든 SCSI 장치가 iSCSI SAN을 통해 공유 될 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;iSCSI를 사용하는 애플리케이션의 예는 FC SAN을 사용하는 애플리케이션 유형과 동일하지만 IP 네트워킹 인프라 (예 : ERP, CRM, SAP, SQL, Oracle 등)를 사용하는 애플리케이션입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3)Network attached stroage(NAS)&lt;/h3&gt;
&lt;p&gt;NAS파일시스템은 공유 파일스토리지 자산이며 비정형 혹은 파일 스토리지라고도 합니다.&lt;/p&gt;
&lt;p&gt;Linux용 NFS(Network File Storage)와 Windows용 SMB(Server Message Block) 또는 CIFS(Common Internet File System)와 같은 프로토콜을 사용합니다. NSA는 사용자 및 애플리케이션을 위한 공유 폴더 및 문서 저장소에 자주 사용됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4)Object Storage&lt;/h3&gt;
&lt;p&gt;Object Storage르 통해 모든 유형의 데이터를 저장할수 있습니다. 객체는 고유의 식별자와 임의의 해싱체계로 식별합니다. 대부분의 객체는 계층구조 없이 하나의 크고 평평한 네임스페이스에 보관됩니다. 이러한 flat name space는 오브젝트 스토리지 시스템에 내재된 대규모 확정성을 가능하게 합니다.(??) 객체 저장장치(OSD)는 REST, SOAP, XAM과 같은 API 작업을 통해 액세스 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.AWS Storage Service&lt;/h2&gt;
&lt;p&gt;기존 온프라이스 스토리지를 대체하는 AWS에 사용할 수 있는 Storage Service를 소개한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Amazon EBS(Elastic Block Store)&lt;/h3&gt;
&lt;p&gt;Amzon EC2와 함께 사용하게 설계되었다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;relational and non-relational databases, enterprise applications, containerized applications, big data analytics engines, file systems, and media workflows처럼 광범위한 워크로드가 EBS에 배포되어사용한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;EBS는 온프레미스의 iSCSI, FC SAN Storage Arrary에서 서비스하던 어플리케이션 워크로드를 대체할수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Amazon EFS(Elastic File System)&lt;/h3&gt;
&lt;p&gt;NFS파일시스템을 제공한다. 애플리케이션을 중단하지 않고 페타바이트까지 확장되며, 파일을 추가 제거할때 자동적으로 확장 축소되며, 관리가 가능하다.&lt;/p&gt;
&lt;p&gt;최소 3개의 AZ(가용영역)에 데이터를 저장하면서 한자리수의 밀리초의 지연시간을 제공하는 지역서비스이며 99.9999..(11 9)의 내구성을 설계가 되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;EFS는 온프레미스의 NFS 프로토콜기반의 NAS Array에서 서비스하던 애플리케이션 및 사용자 워크로드를 대체할수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) Amzon FSx(&lt;span style=&quot;color: #333333;&quot;&gt;Windows 파일 서버용&lt;span&gt; )&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;SMB프로토콜을 통해 액세스 할 수 있는 스토리지이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4)Amazon S3(Simple Storage Service)&lt;/h3&gt;
&lt;p&gt;객체 스토리지 서비스이다. 확장성, 데이터가용성, 보안 및 성능이 유지된다다.&lt;/p&gt;
&lt;p&gt;웹사이트, 모바일어플리케이션, 백업 및 복원, 아카이브, 엔터프라이즈 애플리케이션, Iot 장치 및 빅데티어 분석 등 다양한 사례가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다양한 사용 사례를 위핸 스토리지 클래스(&lt;a href=&quot;https://aws.amazon.com/ko/s3/storage-classes/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;aws.amazon.com/ko/s3/storage-classes/&lt;/a&gt;)를 제공한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;온프레미스의 객체기반 및 파일 기반 스토리지 Array를 대체가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Figure-4-represents-the-storage-decision-matrix-for-applications-that-are-already-migrated-to-AWS-application-to-access-protocols-to-AWS-service.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;595&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI0oVO/btq1U7Y57xJ/s4F5hMLNoT0i7W8Kzwj4o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI0oVO/btq1U7Y57xJ/s4F5hMLNoT0i7W8Kzwj4o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI0oVO/btq1U7Y57xJ/s4F5hMLNoT0i7W8Kzwj4o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI0oVO%2Fbtq1U7Y57xJ%2Fs4F5hMLNoT0i7W8Kzwj4o1%2Fimg.png&quot; data-filename=&quot;Figure-4-represents-the-storage-decision-matrix-for-applications-that-are-already-migrated-to-AWS-application-to-access-protocols-to-AWS-service.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;595&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud/AWS</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/22</guid>
      <comments>https://yimoc.tistory.com/22#entry22comment</comments>
      <pubDate>Tue, 6 Apr 2021 11:06:28 +0900</pubDate>
    </item>
    <item>
      <title>chorme 의 OPTION Method호출</title>
      <link>https://yimoc.tistory.com/20</link>
      <description>&lt;p&gt;Chorme 브라우져에서 POST를 요청할때 OPTION으로 바뀌어서 서버로 호출하는 경우가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;nexacro studio에서 개발툴의 webserver로 구동하고 STS에서 tomcat구동시 발생한다.&lt;/p&gt;
&lt;p&gt;console 이경우 아래 처럼 오류가 나온다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;a href=&quot;SystemBase_HTML5.js:8133&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SystemBase_HTML5.js:8133&lt;/a&gt;&amp;nbsp;OPTIONS&amp;nbsp;http://localhost:8080/nexup/&lt;a href=&quot;simpleresponse.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;simpleresponse.do&lt;/a&gt;&amp;nbsp;500&amp;nbsp;(Internal&amp;nbsp;Server&amp;nbsp;Error)&lt;br /&gt;nexacro.__startCommunication&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;SystemBase_HTML5.js:8133&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SystemBase_HTML5.js:8133&lt;/a&gt;&lt;br /&gt;nexacro._startCommunication&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;SystemBase.js:6128&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SystemBase.js:6128&lt;/a&gt;&lt;br /&gt;nexacro._loadData&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;SystemBase.js:6035&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SystemBase.js:6035&lt;/a&gt;&lt;br /&gt;__pLoadManager.loadDataModule&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;Platform.js:1157&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Platform.js:1157&lt;/a&gt;&lt;br /&gt;_pForm.transaction&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;FormBase.js:3814&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;FormBase.js:3814&lt;/a&gt;&lt;br /&gt;Button00_onclick&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;tt.xfdl.js:49&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tt.xfdl.js:49&lt;/a&gt;&lt;br /&gt;_pEventListener._fireEvent&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;SystemBase.js:2632&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SystemBase.js:2632&lt;/a&gt;&lt;br /&gt;_pComponent.on_fire_onclick&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;CompEventBase.js:3832&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CompEventBase.js:3832&lt;/a&gt;&lt;br /&gt;_pComponent._on_click&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;CompEventBase.js:526&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CompEventBase.js:526&lt;/a&gt;&lt;br /&gt;__pWindow._on_default_sys_lbuttonup&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;Platform.js:3462&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Platform.js:3462&lt;/a&gt;&lt;br /&gt;nexacro._syshandler_lock_onmouseup&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;Platform_HTML5.js:1481&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Platform_HTML5.js:1481&lt;/a&gt;&lt;br /&gt;_cur_win._syshandler_lock_onmouseup_forward&amp;nbsp;@&amp;nbsp;&lt;a href=&quot;Platform_HTML5.js:491&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Platform_HTML5.js:491&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl:1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl:1&lt;/a&gt;&amp;nbsp;Access&amp;nbsp;to&amp;nbsp;XMLHttpRequest&amp;nbsp;at&amp;nbsp;'http://localhost:8080/nexup/&lt;a href=&quot;simpleresponse.do'&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;simpleresponse.do'&lt;/a&gt;&amp;nbsp;from&amp;nbsp;origin&amp;nbsp;'http://127.0.0.1:4098'&amp;nbsp;has&amp;nbsp;been&amp;nbsp;blocked&amp;nbsp;by&amp;nbsp;CORS&amp;nbsp;policy:&amp;nbsp;Response&amp;nbsp;to&amp;nbsp;preflight&amp;nbsp;request&amp;nbsp;doesn't&amp;nbsp;pass&amp;nbsp;access&amp;nbsp;control&amp;nbsp;check:&amp;nbsp;No&amp;nbsp;'Access-Control-Allow-Origin'&amp;nbsp;header&amp;nbsp;is&amp;nbsp;present&amp;nbsp;on&amp;nbsp;the&amp;nbsp;requested&amp;nbsp;resource.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래처럼 Invalid CORS request가 발생된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;OPTIONS&amp;nbsp;http://localhost:8080/nexup/&lt;a href=&quot;simpleresponse.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;simpleresponse.do&lt;/a&gt;&amp;nbsp;HTTP/1.1 &lt;br /&gt;Host:&amp;nbsp;localhost:8080 &lt;br /&gt;Connection:&amp;nbsp;keep-alive &lt;br /&gt;Access-Control-Request-Method:&amp;nbsp;POST &lt;br /&gt;Origin:&amp;nbsp;http://127.0.0.1:4098 &lt;br /&gt;User-Agent:&amp;nbsp;Mozilla/5.0&amp;nbsp;(Windows&amp;nbsp;NT&amp;nbsp;6.1;&amp;nbsp;Win64;&amp;nbsp;x64)&amp;nbsp;AppleWebKit/537.36&amp;nbsp;(KHTML,&amp;nbsp;like&amp;nbsp;Gecko)&amp;nbsp;Chrome/75.0.3770.100&amp;nbsp;Safari/537.36 &lt;br /&gt;Access-Control-Request-Headers:&amp;nbsp;cache-control,content-type,x-requested-with &lt;br /&gt;Accept:&amp;nbsp;*/* &lt;br /&gt;Referer:&amp;nbsp;http://127.0.0.1:4098/&lt;a href=&quot;quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl&lt;/a&gt; &lt;br /&gt;Accept-Encoding:&amp;nbsp;gzip,&amp;nbsp;deflate,&amp;nbsp;br &lt;br /&gt;Accept-Language:&amp;nbsp;ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTTP/1.1&amp;nbsp;403&amp;nbsp;Forbidden&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Server:&amp;nbsp;Apache-Coyote/1.1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Allow:&amp;nbsp;GET,&amp;nbsp;HEAD,&amp;nbsp;POST,&amp;nbsp;PUT,&amp;nbsp;DELETE,&amp;nbsp;TRACE,&amp;nbsp;OPTIONS,&amp;nbsp;PATCH&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Content-Length:&amp;nbsp;20&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Date:&amp;nbsp;Thu,&amp;nbsp;27&amp;nbsp;Jun&amp;nbsp;2019&amp;nbsp;06:24:08&amp;nbsp;GMT&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Invalid&amp;nbsp;CORS&amp;nbsp;request&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해결하기위해서는 CORS 위해 세팅해야한다.&lt;/p&gt;
&lt;p&gt;&amp;lt;mvc:cors&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;lt;mvc:mapping path=&quot;/**/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/mvc:cors&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 PreFlightHandler 처리를 위해서 HttpHttpRequestHandlerAdapter가 필요하다.&lt;/p&gt;
&lt;p&gt;&amp;lt;bean class =&quot;org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter&quot;&amp;nbsp;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 Controller에서&amp;nbsp;@CrossOrigin 설정&lt;/p&gt;
&lt;p&gt;하지 않는경우 Status Code:&lt;span&gt;403 Forbidden 으로 응답처리 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;설정하면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OPTIONS&amp;nbsp;http://localhost:8080/nexup/&lt;/span&gt;&lt;a href=&quot;https://yimoc.tistory.com/manage/newpost/simpleresponse.do&quot;&gt;simpleresponse.do&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;HTTP/1.1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Host:&amp;nbsp;localhost:8080&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Connection:&amp;nbsp;keep-alive&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Request-Method:&amp;nbsp;POST&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Origin:&amp;nbsp;http://127.0.0.1:4098&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;User-Agent:&amp;nbsp;Mozilla/5.0&amp;nbsp;(Windows&amp;nbsp;NT&amp;nbsp;6.1;&amp;nbsp;Win64;&amp;nbsp;x64)&amp;nbsp;AppleWebKit/537.36&amp;nbsp;(KHTML,&amp;nbsp;like&amp;nbsp;Gecko)&amp;nbsp;Chrome/75.0.3770.100&amp;nbsp;Safari/537.36&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Request-Headers:&amp;nbsp;cache-control,content-type,x-requested-with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Accept:&amp;nbsp;*/*&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Referer:&amp;nbsp;http://127.0.0.1:4098/&lt;/span&gt;&lt;a href=&quot;https://yimoc.tistory.com/manage/newpost/quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl&quot;&gt;quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Accept-Encoding:&amp;nbsp;gzip,&amp;nbsp;deflate,&amp;nbsp;br&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Accept-Language:&amp;nbsp;ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTTP/1.1&amp;nbsp;200&amp;nbsp;OK&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Server:&amp;nbsp;Apache-Coyote/1.1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Allow-Origin:&amp;nbsp;http://127.0.0.1:4098&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Vary:&amp;nbsp;Origin&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Allow-Methods:&amp;nbsp;GET,HEAD,POST&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Allow-Headers:&amp;nbsp;cache-control,&amp;nbsp;content-type,&amp;nbsp;x-requested-with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Allow-Credentials:&amp;nbsp;true&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Max-Age:&amp;nbsp;1800&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Allow:&amp;nbsp;GET,&amp;nbsp;HEAD,&amp;nbsp;POST,&amp;nbsp;PUT,&amp;nbsp;DELETE,&amp;nbsp;TRACE,&amp;nbsp;OPTIONS,&amp;nbsp;PATCH&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Content-Length:&amp;nbsp;0&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Date:&amp;nbsp;Thu,&amp;nbsp;27&amp;nbsp;Jun&amp;nbsp;2019&amp;nbsp;06:19:08&amp;nbsp;GMT&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;POST&amp;nbsp;http://localhost:8080/nexup/&lt;/span&gt;&lt;a href=&quot;https://yimoc.tistory.com/manage/newpost/simpleresponse.do&quot;&gt;simpleresponse.do&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;HTTP/1.1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Host:&amp;nbsp;localhost:8080&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Connection:&amp;nbsp;keep-alive&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Content-Length:&amp;nbsp;0&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Accept:&amp;nbsp;application/xml,&amp;nbsp;text/xml,&amp;nbsp;*/*&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;cache-control:&amp;nbsp;no-cache&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Origin:&amp;nbsp;http://127.0.0.1:4098&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;X-Requested-With:&amp;nbsp;XMLHttpRequest&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;User-Agent:&amp;nbsp;Mozilla/5.0&amp;nbsp;(Windows&amp;nbsp;NT&amp;nbsp;6.1;&amp;nbsp;Win64;&amp;nbsp;x64)&amp;nbsp;AppleWebKit/537.36&amp;nbsp;(KHTML,&amp;nbsp;like&amp;nbsp;Gecko)&amp;nbsp;Chrome/75.0.3770.100&amp;nbsp;Safari/537.36&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Content-Type:&amp;nbsp;text/xml&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Referer:&amp;nbsp;http://127.0.0.1:4098/&lt;/span&gt;&lt;a href=&quot;https://yimoc.tistory.com/manage/newpost/quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl&quot;&gt;quickview.html?screenid=Desktop_screen&amp;amp;formname=Base::tt.xfdl&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Accept-Encoding:&amp;nbsp;gzip,&amp;nbsp;deflate,&amp;nbsp;br&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Accept-Language:&amp;nbsp;ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTTP/1.1&amp;nbsp;200&amp;nbsp;OK&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Server:&amp;nbsp;Apache-Coyote/1.1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Allow-Origin:&amp;nbsp;http://127.0.0.1:4098&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Vary:&amp;nbsp;Origin&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Access-Control-Allow-Credentials:&amp;nbsp;true&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Content-Type:&amp;nbsp;text/html;charset=UTF-8&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Content-Language:&amp;nbsp;ko-KR&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Transfer-Encoding:&amp;nbsp;chunked&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Date:&amp;nbsp;Thu,&amp;nbsp;27&amp;nbsp;Jun&amp;nbsp;2019&amp;nbsp;06:19:08&amp;nbsp;GMT&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;df&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SSV:UTF-8 ErrorCode:int=0 Dataset:ds1 _RowType_ col2:string(32) col1:string(32) N &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;0&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;다시 문제점 발생 재요청시 다시 요청하지 않는다??&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>chrome</category>
      <category>cors</category>
      <category>nexacro</category>
      <category>spring</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/20</guid>
      <comments>https://yimoc.tistory.com/20#entry20comment</comments>
      <pubDate>Thu, 27 Jun 2019 15:27:49 +0900</pubDate>
    </item>
    <item>
      <title>too many files opens 오류</title>
      <link>https://yimoc.tistory.com/19</link>
      <description>&lt;p&gt;오류 메시지 : jar파일은 바뀜&lt;/p&gt;
&lt;p&gt;java.io.FileNotFoundException : /usr~~~/ .jar (열린 파일이 너무 많음)&lt;/p&gt;
&lt;p&gt;too many files opens&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 오류 발생 원인&lt;br /&gt;오류는 Too many open files로 제한된 file descriptor의 개수를 초과하여 발생&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;ulimit -a 로 현재 설정된 open file 한계치 확인&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 발생 원인 분석&lt;br /&gt;2-1) 일시적으로 과부하에 의한 것인지?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;설정된 FD가 적고 일시적 과부인 경우는 FD 개수를 늘려서 해결&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2-2) File descriptor가 누적되어 발생한 것인지 ?&lt;br /&gt;통신(socket..), input/out stream , 파일 등에서 close가 제대로 되지 않아 발생합니다. &lt;br /&gt;Close가 되지 않으므로 누적되어 제한된 FD개수를 초과한 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;선행적으로 FD의 정보를 확인하시기 바랍니다. 해지 되지 않고 지속적으로 증가되는지 파악이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;물론, 의심되는 서비스를 호출하여 FD가 증가되지는 확인하는 것이 빠릅니다.&lt;br /&gt;지속적으로 fd 수가 증가할 경우는 코드에서 문제가 발생한 것입니다.&lt;/p&gt;
&lt;p&gt;이 경우는 해당 원인이 되는 코드를 수정해야 합니다.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;테스트 및 체크 방법&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. File not close 서비스 개발&lt;br /&gt;- 일부러 파일을 close하지 않음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;@RequestMapping&lt;/span&gt;(&lt;span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;notclosefile&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;) &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;void doService() &lt;span&gt;throws&lt;/span&gt; ServiceException {&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;String&lt;/span&gt; path = axb2ServiceManager.getServicePath(); &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;System&lt;/span&gt;.out.println(&lt;span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;path=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;+path); &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;File&lt;/span&gt; file = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;File&lt;/span&gt;(path+&lt;span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/h2_sample/merge.xml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;); &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;try&lt;/span&gt; { &lt;span&gt;FileInputStream&lt;/span&gt; fileInputStream = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;FileInputStream&lt;/span&gt;(file); &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;} &lt;span&gt;catch&lt;/span&gt; (&lt;span&gt;FileNotFoundException&lt;/span&gt; e) { &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;// TODO Auto-generated catch block&lt;/span&gt; e.printStackTrace(); &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;} &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. centos에서 FD 체크&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; background-color: #c1c1c1;&quot;&gt;$ ls -l /proc/19586/fd&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; background-color: #c1c1c1;&quot;&gt;total 0 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;lr-x------ 1 tobesoft tobesoft 64 Jun 18 10:21 0 -&amp;gt; /dev/null ~~~ &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;l-wx------ 1 tobesoft tobesoft 64 Jun 18 10:21 1 -&amp;gt; /home/tobesoft/apache-tomcat-7.0.33/logs/catalina.out lr-x------ 1 tobesoft tobesoft 64 Jun 18 10:22 118 -&amp;gt; /home/tobesoft/apache-tomcat-7.0.33/webapps/nexup/WEB-INF/service/h2_sample/merge.xml &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;lr-x------ 1 tobesoft tobesoft 64 Jun 18 10:23 119 -&amp;gt; /home/tobesoft/apache-tomcat-7.0.33/webapps/nexup/WEB-INF/service/h2_sample/merge.xml ~~ &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;lr-x------ 1 tobesoft tobesoft 64 Jun 18 10:22 52 -&amp;gt; /home/tobesoft/apache-tomcat-7.0.33/webapps/nexup/WEB-INF/service/h2_sample/merge.xml &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;중복 체크 확인 함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. File Leack Detector 로 체크&lt;/p&gt;
&lt;p&gt;opensource로 제공됨 : &lt;a href=&quot;http://file-leak-detector.kohsuke.org/&quot;&gt;http://file-leak-detector.kohsuke.org/&lt;/a&gt;&lt;br /&gt;Usage :&lt;br /&gt;java -javaagent:path/to/file-leak-detector.jar=http=19999 &lt;br /&gt;&amp;gt; 19999 port 로 로그가 나옴&lt;/p&gt;
&lt;p&gt;Browser로 &lt;a&gt;http://ip:19999로&lt;/a&gt; 연결시 열려 있는 file 정보를 알려준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 실제 정보 정리&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1;&quot;&gt;#17 /home/tobesoft/apache-tomcat-7.0.33/webapps/nexup/WEB-INF/service/h2_sample/merge.xml by thread:http-bio-8081-exec-5 on Tue Jun 18 10:23:23 KST 2019 at java.io.FileInputStream.&amp;lt;init&amp;gt;(FileInputStream.java:139) at nexacro.sample.NotCloseFileController.doService(NotCloseFileController.java:31) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1;&quot;&gt;~~ &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1;&quot;&gt;#29 /home/tobesoft/apache-tomcat-7.0.33/webapps/nexup/WEB-INF/service/h2_sample/merge.xml by thread:http-bio-8081-exec-10 on Tue Jun 18 10:22:17 KST 2019 at java.io.FileInputStream.&amp;lt;init&amp;gt;(FileInputStream.java:139) at nexacro.sample.NotCloseFileController.doService(NotCloseFileController.java:31) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1;&quot;&gt;~~ &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.94em;&quot;&gt;&lt;span style=&quot;background-color: #c1c1c1;&quot;&gt;#32 /home/tobesoft/apache-tomcat-7.0.33/webapps/nexup/WEB-INF/service/h2_sample/merge.xml by thread:http-bio-8081-exec-10 on Tue Jun 18 10:22:03 KST 2019 at java.io.FileInputStream.&amp;lt;init&amp;gt;(FileInputStream.java:139) at nexacro.sample.NotCloseFileController.doService(NotCloseFileController.java:31)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;file-leak-detector option&lt;/p&gt;
&lt;p&gt;&lt;span&gt;File leak detector arguments (to specify multiple values, separate them by ',': help - show the help screen. trace - log every open/close operation to stderr. trace=FILE - log every open/close operation to the given file. error=FILE - if 'too many open files' error is detected, send the dump here. by default it goes to stderr. threshold=N - instead of waiting until 'too many open files', dump once we have N descriptors open. http=PORT - Run a mini HTTP server that you can access to get stats on demand Specify 0 to choose random available port, -1 to disable, which is default. strong - Don't let GC auto-close leaking file descriptors listener=S - Specify the fully qualified name of ActivityListener class to activate from beginning dumpatshutdown- Dump open file handles at shutdown excludes=FILE - Ignore files opened directly/indirectly in specific methods. File lists 'some.pkg.ClassName.methodName' patterns. &lt;/span&gt;&lt;/p&gt;</description>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/19</guid>
      <comments>https://yimoc.tistory.com/19#entry19comment</comments>
      <pubDate>Wed, 19 Jun 2019 17:53:55 +0900</pubDate>
    </item>
    <item>
      <title>도서 대출 챗봇 2</title>
      <link>https://yimoc.tistory.com/14</link>
      <description>&lt;p&gt;환경&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Python 3.5&lt;/p&gt;&lt;p&gt;개발 환경 : MacOs + Pycharm&lt;/p&gt;&lt;p&gt;배포 환경 :&amp;nbsp;CentOS release 6.10&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1. 기본 Bot Server 개발&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1) Bot 신청&lt;/p&gt;&lt;p&gt;2) BotServer 개발&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;설치 package :&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;library&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; zbar : 바코드 인식용&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;python package&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;python-telegram-bot : telegram bot 개발 패키지&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;pyzbar : 바코드 인식용&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;requests, urllib3 : api 서버 통신용&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;---------------------------------------------------------&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;TODO&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;KoNLPy : 현재 macOS에서 설치 오류가 남.. libc++이 오류 남 stdlibc++로 copy해서 대체해보았즈만 실패&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;tensorflow : KoNLPy 설치 후 진행 할 예정&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;gt;&amp;gt; 주요목적이 사용자가 입력한 메시지를 파싱 -&amp;gt; 문맥파악 -&amp;gt; 의도 파악 -&amp;gt; 처리 -&amp;gt; 결과문 생성 할랬는데 나중에 해야겠군..&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;&lt;p&gt;2.테스트&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;테스트 환경 구성 필요&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3.배포&lt;/p&gt;&lt;p&gt;서버설정(CentOS 3.5 )진행&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Python 3.5 설치&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;package 가져오기&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;# curl -O https://www.python.org/ftp/python/3.5.6/Python-3.5.6.tgz&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;설치&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;#configure&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;#make&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;#make install&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&amp;nbsp; &amp;gt; Error need TLS/SSL 발생&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;#openssl-devel 설치&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;#yum install openssl-devel&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Zbar설치&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;https://www.tecmint.com/how-to-enable-epel-repository-for-rhel-centos-6-5/&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;# rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;#yum repolist&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;#yum install zbar zbar-devel&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Python package 설치&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;pip3 upgrade 한번 하고 진행한다.&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;#pip3 install --upgrade pip&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;Python 패키지는 개발환경과 동일하게 설치를 위해 아래로 진행한다.&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;# pip3 freeze pip-requriement.txt로 사용&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;# pip3 install -r pip-requriement.txt --user&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;code 배포&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;이번엔 그냥 sftp로 전송&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;gt; 각종 패키지 설치가 이원화 됨으로 문제가 발생한다.&lt;/p&gt;&lt;p&gt;도커를 사용하여 개발환경과 배포 환경을 일원화 하는것이 낫다고 판단된다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;하지만 PyCharm community는 지원하지 않는다..돈주고 사야할듯..&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>북어회</category>
      <category>chatbot</category>
      <category>대출</category>
      <category>도서</category>
      <category>챗봇</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/14</guid>
      <comments>https://yimoc.tistory.com/14#entry14comment</comments>
      <pubDate>Wed, 16 Jan 2019 16:50:58 +0900</pubDate>
    </item>
    <item>
      <title>도서 대출 챗봇 1</title>
      <link>https://yimoc.tistory.com/13</link>
      <description>&lt;p&gt;회사 사내 동우회인 북어회를 위해 대출시스템을 개인적으로 구축하고 있다.&lt;/p&gt;&lt;p&gt;해당 자료를 정리하기 위해 작성한다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;1. 구성&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;ASIS)&lt;/span&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 820px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E40A375C3EDA6715&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E40A375C3EDA6715&quot; width=&quot;820&quot; height=&quot;242&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;추가로 iPhone Native App을 개발 하려 했으나 게으름으로 스킵 중..&lt;/p&gt;&lt;p&gt;2019년을 한해는 챗봇이나 한번 해보려고 추가적인 작업을 계획하였다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;TOBE)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 820px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D375345C3EDA961C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D375345C3EDA961C&quot; width=&quot;820&quot; height=&quot;421&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;2. 상세&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Bot Server 상세&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;모듈 구성 :&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1) BotServer&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;기본 구성 : telegram chat bot 과 연동하는 기본 모듈&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2) Command처리 모듈&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;v1) 현재는 기본 command를 기준으로 처리하기&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Rule base 로 처리 : &quot;내대출&quot;, &quot;사용자등록&quot;...&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;v2) 머신러닝으로 텍스트기반으로 처리하기&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1) 자연어 처리&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 2) 분석&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 3) 의도&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 4) 결과말 생성&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3) Barcode 처리&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Image 처리 : 전송받은 이미지 저장&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Barcode 읽기 : 전송 받은 이미지에서 Barcode값을 읽음&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4) Backend API 서버 연동 모듈&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; Booker 서버에 접속하여 대출,반납등 기본 호출 하는 모듈&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3. 기본 flow&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;*대출 / 반납&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 이미지 전송 -&amp;gt; 이미지저장 -&amp;gt; 바코드 인식 -&amp;gt; API server에서 대출요청 -&amp;gt; 대출 및 반납 처리 -&amp;gt; DB 저장 -&amp;gt; 결과 전송&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>북어회</category>
      <category>Barcode</category>
      <category>chatbot</category>
      <category>OpenCV</category>
      <category>telegram</category>
      <category>대출</category>
      <category>도서</category>
      <category>챗봇</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/13</guid>
      <comments>https://yimoc.tistory.com/13#entry13comment</comments>
      <pubDate>Wed, 9 Jan 2019 17:16:59 +0900</pubDate>
    </item>
    <item>
      <title>ASM User Guide 번역 4</title>
      <link>https://yimoc.tistory.com/9</link>
      <description>&lt;p&gt;&lt;a href=&quot;http://yimoc.tistory.com/7&quot; target=&quot;_blank&quot;&gt;2018/09/11 - [ASM] - ASM User Guide 번역 3&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4. Metadata&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 장에서는 핵심 API를 사용하여 주석과 같은 컴파일 된 Java 클래스 메타 데이터를 생성하고 변환하는 방법에 대해 설명합니다. 각 섹션은 한 가지 유형의 메타 데이터의 프리젠 테이션으로 시작한 다음 몇 가지 예시적인 예와 함께 해당 메타 데이터를 생성하고 변환하는 해당 ASM 인터페이스, 구성 요소 및 도구를 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.1. Generics&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;List &amp;lt;E&amp;gt;와 같은 일반 클래스와이를 사용하는 클래스에는 선언하거나 사용하는 제네릭 형식에 대한 정보가 들어 있습니다. 이 정보는 런타임시 바이트 코드 명령어에 의해 사용되지 않지만 리플렉션 API를 통해 액세스 할 수 있습니다. 또한 별도의 컴파일을 위해 컴파일러에서 사용됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.1.1. Structure&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이전 버전과의 호환성을 위해 제네릭 형식에 대한 정보는 형식 또는 메서드 설명자 (Java 5에서 제네릭 소개 이전에 정의 된)에 저장되지 않았지만 형식, 메서드 및 클래스 서명이라는 유사한 구문으로 저장되었습니다. 이러한 서명은 제네릭 형식이 관련된 경우 클래스, 필드 및 메서드 선언의 설명자와 함께 저장됩니다 (제네릭 형식은 메서드의 바이트 코드에 영향을주지 않습니다. 컴파일러는 정적 형식 검사를 수행하기 위해이를 사용하지만 메서드를 컴파일 한 것처럼 필요한 경우 유형 캐스트를 다시 도입하여 사용하지 않음).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;형식 및 메서드 설명자와 달리 generic 형식의 재귀 적 특성 때문에 (generic 형식은 generic 형식으로 매개 변수화 될 수 있습니다. 예를 들어 List &amp;lt;List &amp;lt;E&amp;gt;?&amp;gt;를 고려하십시오.) 형식 시그니처의 문법은 상당히 복잡합니다. 이것은 다음 규칙에 따라 제공됩니다 (이 규칙에 대한 자세한 설명은 Java Virtual Machine Specification 참조).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;TypeSignature&lt;/i&gt;: Z | C | B | S | I | F | J | D |&amp;nbsp;&lt;i&gt;FieldTypeSignature&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;FieldTypeSignature&lt;/i&gt;:&amp;nbsp;&lt;i&gt;ClassTypeSignature&amp;nbsp;&lt;/i&gt;| [&amp;nbsp;&lt;i&gt;TypeSignature&amp;nbsp;&lt;/i&gt;|&amp;nbsp;&lt;i&gt;TypeVar&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;ClassTypeSignature&lt;/i&gt;: L&amp;nbsp;&lt;i&gt;Id&lt;/i&gt;&amp;nbsp;( /&amp;nbsp;&lt;i&gt;Id&lt;/i&gt;&amp;nbsp;)*&amp;nbsp;&lt;i&gt;TypeArgs&lt;/i&gt;? ( .&lt;i&gt;&amp;nbsp;Id TypeArgs&lt;/i&gt;? )* ;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;TypeArgs&lt;/i&gt;: &amp;lt;&amp;nbsp;&lt;i&gt;TypeArg&lt;/i&gt;+ &amp;gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;TypeArg&lt;/i&gt;: * | ( + | - )?&amp;nbsp;&lt;i&gt;FieldTypeSignature&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;TypeVar&lt;/i&gt;: T&amp;nbsp;&lt;i&gt;Id&lt;/i&gt;&amp;nbsp;;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;첫 번째 규칙은 형식 서명이 원시 형식 설명자 또는 필드 형식 서명 중 하나임을 나타냅니다. 두 번째 규칙은 클래스 형식 서명, 배열 형식 서명 또는 형식 변수로 필드 형식 서명을 정의합니다. 세 번째 규칙은 클래스 유형 시그니처를 정의합니다. 가능한 유형 인수를 갖는 클래스 유형 설명자, 꺾쇠 괄호 사이, 주 클래스 이름 뒤 또는 내부 클래스 이름 뒤 (점 앞에 접두사가 있음)입니다. 나머지 규칙은 유형 인수 및 유형 변수를 정의합니다. 타입 인자는 타입 인자가있는 완전한 필드 타입 시그니쳐가 될 수있다. 그러므로 타입 시그니쳐는 매우 복잡 할 수있다 (그림 4.1 참조).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table class=&quot;txc-table&quot; width=&quot;784&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; border=&quot;0&quot; 맑은=&quot;&quot; 고딕&quot;,=&quot;&quot; sans-serif;font-size:13px&quot;=&quot;&quot; style=&quot;border: none; border-collapse: collapse;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 24px; border-width: 1px; border-style: solid; border-color: rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Java type and corresponding type signature&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;List&lt;e&gt;&amp;nbsp;&amp;lt;E&amp;gt;&lt;/e&gt;&lt;/p&gt;&lt;p&gt;&lt;e&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Ljava/util/List&lt;te;&gt;;&lt;/te;&gt;&lt;/e&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 35px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;List &amp;lt;?&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Ljava/util/List&amp;lt;*&amp;gt;;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;List&amp;lt;? extends Number&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Ljava/util/List&amp;lt;+Ljava/lang/Number;&amp;gt;;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;List&amp;lt;? super Integer&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Ljava/util/List&amp;lt;-Ljava/lang/Integer;&amp;gt;;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;List&amp;lt;List&amp;lt;String&amp;gt;[]&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Ljava/util/List&amp;lt;[Ljava/util/List&amp;lt;Ljava/lang/String;&amp;gt;;&amp;gt;;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 784px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;HashMap&amp;lt;K, V&amp;gt;.HashIterator&amp;lt;K&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Ljava/util/HashMap&amp;lt;TK;TV;&amp;gt;.HashIterator&amp;lt;TK;&amp;gt;;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 4.1.: Sample type signatures&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;메서드 서명은 형식 시그니처와 같은 메서드 설명자를 확장하여 형식 설명자를 확장합니다. 메소드 서명은 메소드 매개 변수의 유형 서명과 리턴 유형의 서명을 설명합니다. 메소드 서술자와는 달리 메소드 앞에 던져진 예외의 서명을 포함하며 꺽쇠 괄호 사이에 선택적 형식 매개 변수도 포함 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;MethodTypeSignature:&lt;/p&gt;&lt;p&gt;TypeParams? ( TypeSignature* ) ( TypeSignature | V ) Exception*&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Exception: ^ClassTypeSignature | ^TypeVar&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;TypeParams: &amp;lt; TypeParam+ &amp;gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;TypeParam: Id : FieldTypeSignature? ( : FieldTypeSignature )*&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;예를 들어 다음과 같은 제네릭 정적 메서드의 메서드 시그니처는 형식 변수 T로 매개 변수화됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;static &amp;lt;T&amp;gt; Class&amp;lt;? extends T&amp;gt; m (int n)&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;is the following method signature:&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;lt;T:Ljava/lang/Object;&amp;gt;(I)Ljava/lang/Class&amp;lt;+TT;&amp;gt;;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;마지막으로 클래스 서명과 혼동되어서는 안되는 클래스 서명은, 수퍼 클래스의 타입 시그니처, 구현 된 인터페이스의 타입 시그니처 및 선택적 형식 타입 매개 변수로 정의됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassSignature: TypeParams? ClassTypeSignature ClassTypeSignature*&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;For example the class signature of a class declared as C&amp;lt;E&amp;gt; extends List&amp;lt;E&amp;gt;&lt;/p&gt;&lt;p&gt;is &amp;lt;E:Ljava/lang/Object;&amp;gt;Ljava/util/List&amp;lt;TE;&amp;gt;;.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.1.2. Interfaces and components&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Descriptor디스크립터와 동일한 효율성을 이유로 (2.3.1 절 참조), ASM API는 컴파일 된 클래스에 저장된 서명을 노출합니다&lt;/p&gt;&lt;p&gt;(서명의 주된 발생은 선택적 클래스, 유형 또는 메소드 서명 인수로서 각각 ClassVisitor 클래스의 visitField 및 visitMethod 메소드에 있습니다). 바라건대 SignatureVisitor 추상 클래스 (그림 4.2 참조)를 기반으로 org.objectweb.asm.signature 패키지에서 서명을 생성하고 변환하는 도구를 제공하기를 바랍니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public abstract class SignatureVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public final static char EXTENDS = ’+’;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public final static char SUPER = ’-’;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public final static char INSTANCEOF = ’=’;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor(int api);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitFormalTypeParameter(String name);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitClassBound();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitInterfaceBound();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitSuperclass();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitInterface();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitParameterType();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitReturnType();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitExceptionType();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void&amp;nbsp;&lt;b&gt;visitBaseType&lt;/b&gt;(char descriptor);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void&amp;nbsp;&lt;b&gt;visitTypeVariable&lt;/b&gt;(String name);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor&amp;nbsp;&lt;b&gt;visitArrayType()&lt;/b&gt;;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void&amp;nbsp;&lt;b&gt;visitClassType&lt;/b&gt;(String name);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void&amp;nbsp;&lt;b&gt;visitInnerClassType&lt;/b&gt;(String name);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void&amp;nbsp;&lt;b&gt;visitTypeArgument&lt;/b&gt;();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor&amp;nbsp;&lt;b&gt;visitTypeArgument&lt;/b&gt;(char wildcard);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void&amp;nbsp;&lt;b&gt;visitEnd&lt;/b&gt;();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 4.2.: The SignatureVisitor class&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 추상 클래스는 type 시그니처, 메소드 서명과 클래스 서명를 방문하는 데 사용됩니다. 형식 시그니처를 방문하는 데 사용되는 메서드는 굵게 표시되어 이전 문법 규칙을 반영하는 다음 순서로 호출해야합니다 (두 개는 SignatureVisitor를 반환 함 : 이는 형식 시그니처의 재귀 적 정의 때문 임).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;visitBaseType | visitArrayType | visitTypeVariable |&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;( visitClassType visitTypeArgument*&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&amp;nbsp;( visitInnerClassType visitTypeArgument* )* visitEnd ) )&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;The methods used to visit method signatures are the following:&lt;/p&gt;&lt;p&gt;( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )*&lt;/p&gt;&lt;p&gt;visitParameterType* visitReturnType visitExceptionType*&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Finally the methods used to visit class signatures are:&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;( visitFormalTypeParameter visitClassBound? visitInterfaceBound* )*&lt;/p&gt;&lt;p&gt;visitSuperClass visitInterface*&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이러한 메서드의 대부분은 SignatureVisitor를 반환합니다. type 서명을 방문하기위한 것입니다. ClassVisitor에 의해 리턴 된 MethodVisitors와는 달리, SignatureVisitor에 의해 반환 된 SignatureVisitor는 null이 아니어야하며 순차적으로 사용해야합니다. 실제로 중첩 된 서명이 완전히 방문되기 전에 부모 방문자의 메서드를 호출해야합니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;클래스와 마찬가지로 ASM API는이 API를 기반으로 두 가지 구성 요소를 제공합니다. SignatureReader 구성 요소는 서명을 파싱하고 주어진 서명 방문객에 대해 적절한 방문 메소드를 호출하며 SignatureWriter 구성 요소는 수신 한 메소드 호출을 기반으로 서명을 작성합니다. 이 두 클래스는 클래스 및 메서드와 동일한 원칙을 사용하여 시그니처를 생성하고 변환하는 데 사용할 수 있습니다. 예를 들어, 일부 서명에 나타나는 클래스 이름의 이름을 바꾸려고한다고 가정 해 보겠습니다. 이는 visitClassType 및 visitInnerClassType 메소드를 제외하고 변경되지 않은 모든 메소드 호출을 전달하는 다음 서명 어댑터로 수행 할 수 있습니다 (여기서 sv 메소드는 항상 SignatureWriter의 경우를 리턴한다고 가정합니다).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class RenameSignatureAdapter extends SignatureVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private SignatureVisitor sv;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private Map&amp;lt;String, String&amp;gt; renaming;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private String oldName;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public RenameSignatureAdapter(SignatureVisitor sv, Map&amp;lt;String, String&amp;gt; renaming) {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;super(ASM4);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;this.sv = sv;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;this.renaming = renaming;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitFormalTypeParameter(String name) {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitFormalTypeParameter(name);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitClassBound() {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitClassBound();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;return this;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitInterfaceBound() {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitInterfaceBound();&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;return this;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp;&amp;nbsp;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;...&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitClassType(String name) {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;oldName = name;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;String newName = renaming.get(oldName);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitClassType(newName == null ? name : newName);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitInnerClassType(String name) {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;oldName = oldName + &quot;.&quot; + name;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;String newName = renaming.get(oldName);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitInnerClassType(newName == null ? name : newName);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitTypeArgument() {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitTypeArgument();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public SignatureVisitor visitTypeArgument(char wildcard) {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitTypeArgument(wildcard);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;return this;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitEnd() {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;sv.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Then the result of the following code is &quot;LA&lt;tk;tv;&gt;.B&lt;tk;&gt;;&quot;:&lt;/tk;&gt;&lt;/tk;tv;&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;String s = &quot;Ljava/util/HashMap&amp;lt;TK;TV;&amp;gt;.HashIterator&amp;lt;TK;&amp;gt;;&quot;;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;Map&amp;lt;String, String&amp;gt; renaming = new HashMap&amp;lt;String, String&amp;gt;();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;renaming.put(&quot;java/util/HashMap&quot;, &quot;A&quot;);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;renaming.put(&quot;java/util/HashMap.HashIterator&quot;, &quot;B&quot;);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;SignatureWriter sw = new SignatureWriter();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;SignatureVisitor sa = new RenameSignatureAdapter(sw, renaming);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;SignatureReader sr = new SignatureReader(s);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;sr.acceptType(sa);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;sw.toString();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.1.3. Tools&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.3 절에 제시된 TraceClassVisitor와 ASMifier 클래스는 클래스 파일에 포함 된 서명을 내부 형식으로 인쇄합니다. 이 클래스는 특정 제네릭 형식에 해당하는 서명을 찾기 위해 사용할 수 있습니다. 일부 일반 형식으로 Java 클래스를 작성하고 컴파일 한 다음 해당 명령 줄 도구를 사용하여 해당 서명을 찾습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.2. Annotations&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;@Deprecated 또는 @Override와 같은 클래스, 필드, 메서드 및 메서드 매개 변수 주석은 보유 정책이 RetentionPolicy.SOURCE가 아닌 경우 컴파일 된 클래스에 저장됩니다. 이 정보는 런타임시 바이트 코드 명령어에 의해 사용되지 않지만 보존 정책이 RetentionPolicy.RUNTIME 인 경우 리플렉션 API를 통해 액세스 할 수 있습니다. 컴파일러에서 사용할 수도 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.2.1. Structure&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;소스 코드의 annotation은 다양한 형식을 가질 수 있지만,&lt;/p&gt;&lt;p&gt;@ Retention (RetentionPolicy.CLASS) 또는 @Task (desc = &quot;refactor&quot;, id = 1)와 같은 속성을 사용합니다. 그러나 내부적으로 모든 주석은 동일한 형식을 가지며 주석 유형과 이름 값 쌍으로 지정됩니다. 여기서 값은 다음으로 제한됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• primitives, Strng 또는 Vlass&amp;nbsp;값&lt;/p&gt;&lt;p&gt;• enum 값,&lt;/p&gt;&lt;p&gt;• annotation 값,&lt;/p&gt;&lt;p&gt;• 위의 값의 배열.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;주석은 다른 주석 또는 심지어 주석 배열을 포함 할 수 있습니다. 따라서 어노테이션은 매우 복잡 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.2.2. Interfaces and components&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;어노테이션 생성 및 변환을위한 ASM API는 AnnotationVisitor 추상 클래스를 기반으로합니다 (그림 4.3 참조).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public abstract class AnnotationVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AnnotationVisitor(int api);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AnnotationVisitor(int api, AnnotationVisitor av);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visit(String name, Object value);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitEnum(String name, String desc, String value);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AnnotationVisitor visitAnnotation(String name, String desc);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AnnotationVisitor visitArray(String name);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 4.3.: The AnnotationVisitor class&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 클래스의 메소드는 annotation의 이름와 값 쌍을 방문하는 데 사용됩니다. (annnotation 형식은 이 형식을 반환하는 메서드, 즉 visitAnnotation 메서드에서 방문됩니다. 첫 번째 메서드는 primitive, String 및 Class 값 (나중에 Type 개체로 나타냄)에 사용되며 다른 메서드는 열거 형, annotation 및 배열 값에 사용됩니다. visitEnd를 제외한 모든 순서로 호출 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;( visit | visitEnum | visitAnnotation | visitArray )* visitEnd&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;두 가지 메소드가 AnnotationVisitor를 리턴한다는 점에 유의하십시오.&lt;/p&gt;&lt;p&gt;annotation은 다른 annotation을 포함 할 수 있기 때문입니다. 또한 ClassVisitor에서 반환 한 MethodVisitors와 달리 이 두 메서드에 의해 반환 된 AnnotationVisitors는 순차적으로 사용해야합니다. 실제로 중첩 된 주석을 완전히 방문하기 전에 상위 방문자에 대한 메서드를 호출해야합니다. visitArray 메서드는 AnnotationVisitor를 반환하여 배열의 요소를 방문합니다. 그러나 배열의 요소 이름이 지정되지 않았으므로 이름 인수는 visitArray가 반환 한 방문자의 메서드에 의해 무시되며 null로 설정할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;Adding, removing and detecting annotations&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;필드 및 메소드와 마찬가지로 visitAnnotation 메소드에서 null을 반환하여 주석을 제거 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class RemoveAnnotationAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private String annDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public RemoveAnnotationAdapter(ClassVisitor cv, String annDesc) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;super(ASM4, cv);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;this.annDesc = annDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AnnotationVisitor visitAnnotation(String desc, boolean vis) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;&lt;b&gt;if (desc.equals(annDesc)) {&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;return null;&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;}&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;return cv.visitAnnotation(desc, vis);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ClassVisitor 클래스의 메서드를 호출해야하는 제약 때문에 클래스 주석을 추가하는 것이 더 어렵습니다. 실제로 visitAnnotation을 따르는 모든 메소드는 모든 annotation이 방문되었을 때를 감지하기 위해 무시되어야합니다 (메소드 주석은 visitCode 메소드로 인해 더 쉽게 추가 할 수 있습니다).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class AddAnnotationAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private String annotationDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private boolean isAnnotationPresent;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;super(ASM4, cv);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;this.annotationDesc = annotationDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visit(int version, int access, String name,&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;String signature, String superName, String[] interfaces) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;int v = (version &amp;amp; 0xFF) &amp;lt; V1_5 ? V1_5 : version;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cv.visit(v, access, name, signature, superName, interfaces);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public AnnotationVisitor visitAnnotation(String desc,&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;boolean visible) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;if (visible &amp;amp;&amp;amp; desc.equals(annotationDesc)) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;isAnnotationPresent = true;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;return cv.visitAnnotation(desc, visible);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitInnerClass(String name, String outerName,String innerName, int access) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;addAnnotation();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cv.visitInnerClass(name, outerName, innerName, access);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public FieldVisitor visitField(int access, String name, String desc,String signature, Object value) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;addAnnotation();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;return cv.visitField(access, name, desc, signature, value);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public MethodVisitor visitMethod(int access, String name,String desc, String signature, String[] exceptions) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;addAnnotation();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;return cv.visitMethod(access, name, desc, signature, exceptions);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitEnd() {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;addAnnotation();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cv.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;private void addAnnotation() {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;if (!isAnnotationPresent) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;if (av != null) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;av.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;isAnnotationPresent = true;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 어댑터는 클래스 버전이 1.5보다 작 으면 클래스 버전을 1.5로 업그레이드합니다. 이것은 JVM이 버전이 1.5보다 작은 클래스의 주석을 무시하기 때문에 필요합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;클래스 및 메소드 어댑터에서 annotation를 사용한 가장 자주 사용되는 사례는 annotation를 사용하여 변형을 매개 변수화하는 것입니다. 예를 들어 @Persistent 주석이있는 필드에 대해서만 필드 액세스를 변환하고 @Log 주석이있는 메소드에만 로깅 코드를 추가 할 수 있습니다. 주석을 먼저 방문해야하기 때문에 이러한 모든 사례를 쉽게 구현할 수 있습니다. 필드 및 메소드와 메소드 및 매개 변수 주석을 코드보다 먼저 방문해야 클래스 annotation을 먼저 방문해야합니다. 그러므로 isAnnotationPresent 플래그로 위의 예에서 수행 된 것처럼 원하는 annotation이 감지 될 때 플래그를 설정하고 변환에서 나중에 사용하는 것으로 충분합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.2.3. Tools&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;TraceClassVisitor, CheckClassAdapter 및 ASMifier 클래스는 2.3 절에서 주석을 지원합니다 (예 : 메서드의 경우 TraceAnnotationVisitor 또는 CheckAnnotationAdapter를 사용하여 클래스 수준이 아닌 개별 주석 수준에서 작업 할 수도 있음). 특정 주석을 생성하는 방법을 확인하는 데 사용할 수 있습니다. 예를 들면 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;java -classpath asm.jar:asm-util.jar \&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;org.objectweb.asm.util.ASMifier \&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;java.lang.Deprecated&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;prints code that, after minor refactoring, reads:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;package asm.java.lang;&lt;/div&gt;&lt;div&gt;import org.objectweb.asm.*;&lt;/div&gt;&lt;div&gt;public class DeprecatedDump implements Opcodes {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public static byte[] dump() throws Exception {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;ClassWriter cw = new ClassWriter(0);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;AnnotationVisitor av;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cw.visit(V1_5, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT+ ACC_INTERFACE, &quot;java/lang/Deprecated&quot;, null,&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;&quot;java/lang/Object&quot;,&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;new String[] { &quot;java/lang/annotation/Annotation&quot; });&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;{&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;&lt;b&gt;av = cw.visitAnnotation(&quot;Ljava/lang/annotation/Documented;&quot;, true);&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;av.visitEnd();&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;{&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;&lt;b&gt;av = cw.visitAnnotation(&quot;Ljava/lang/annotation/Retention;&quot;, true);&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;av.visitEnum(&quot;value&quot;, &quot;Ljava/lang/annotation/RetentionPolicy;&quot;,&quot;RUNTIME&quot;);&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;			&lt;/span&gt;av.visitEnd();&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;cw.visitEnd();&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;return cw.toByteArray();&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;이 코드는 ACC_ANNOTATION 플래그를 사용하여 두 사람이 주석 클래스를 작성하는 방법과 값이 없는 클래스 주석과 enum 값이있는 클래스 annotation을 작성하는 방법을 보여줍니다. MethodVisitor 클래스에 정의 된 visitAnnotation 및 visitParameterAnnotation 메서드를 사용하여 메서드 및 매개 변수 annotation을 비슷한 방식으로 만들 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;4.3. Debug&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;javac -g로 컴파일 된 클래스는 소스 파일의 이름, 소스 행 번호와 바이트 코드 명령어 간의 매핑, 소스 코드의 로컬 변수 이름과 바이트 코드의 로컬 변수 슬롯 간의 매핑을 포함합니다. 이 선택적 정보는 디버거 및 예외 스택 추적이 사용 가능할 때 사용됩니다.&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.3.1. Structure&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;클래스의 소스 파일 이름은 전용 클래스 파일 구조 섹션에 저장됩니다 (그림 2.1 참조).&lt;/p&gt;&lt;p&gt;소스 행 번호와 바이트 코드 명령 간의 매핑은 메소드의 컴파일 된 코드 섹션에 (행 번호, 레이블) 쌍의 목록으로 저장됩니다. 예를 들어, l1, l2 및 l3이이 순서로 나타나는 세 개의 레이블이면 다음 쌍이됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;(n1, l1)&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;(n2, l2)&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;(n3, l3)&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;l1과 l2 사이의 명령어가 n1 라인에서 나오고, l2와 l3 사이의 명령어는 n2 라인에서 나오고, l3 이후의 명령어는 n3 라인에서 옵니다. 주어진 줄 번호는 여러 쌍으로 나타날 수 있습니다. 단일 소스 행에 나타나는 표현식에 해당하는 명령어가 바이트 코드에서 연속적이지 않을 수 있기 때문입니다. 예를 들어 (init; cond; incr) 문; 일반적으로 다음 순서로 컴파일됩니다 :&amp;nbsp;&lt;i&gt;init statement incr cond.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;소스 코드의 로컬 변수 이름과 바이트 코드의 로컬 변수 슬롯 간의 매핑은 메소드의 컴파일 된 코드 섹션에있는 (&lt;i&gt;이름&lt;/i&gt;,&amp;nbsp;&lt;i&gt;type 설명자&lt;/i&gt;,&amp;nbsp;&lt;i&gt;type 서명, 시작, 끝, 색인&lt;/i&gt;) 튜플의 목록으로 저장됩니다. 이러한 튜플은 두 레이블 시작과 끝 사이에서 슬롯 인덱스의 로컬 변수가 소스 코드의 이름과 유형이 처음 세 튜플 요소에 의해 주어진 로컬 변수에 해당 함을 의미합니다. 컴파일러는 동일한 지역 변수 슬롯을 사용하여 서로 다른 범위의 고유 한 소스 로컬 변수를 저장할 수 있습니다. 반대로 고유 한 소스 로컬 변수는 인접하지 않은 범위가있는 로컬 변수 슬롯으로 컴파일 될 수 있습니다. 예를 들어 다음과 같은 상황이있을 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;l1:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;... // here slot 1 contains local variable i&lt;/p&gt;&lt;p&gt;l2:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;... // here slot 1 contains local variable j&lt;/p&gt;&lt;p&gt;l3:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;... // here slot 1 contains local variable i again&lt;/p&gt;&lt;p&gt;end:&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;해당 튜플은 다음과 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;(&quot;i&quot;, &quot;I&quot;, null, l1, l2, 1)&lt;/p&gt;&lt;p&gt;(&quot;j&quot;, &quot;I&quot;, null, l2, l3, 1)&lt;/p&gt;&lt;p&gt;(&quot;i&quot;, &quot;I&quot;, null, l3, end, 1)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.3.2. Interfaces and components&lt;/p&gt;&lt;p&gt;디버그 정보는 ClassVisitor 및 MethodVisitor 클래스의 세 가지 메소드로 방문합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 소스 파일 이름은 ClassVisitor 클래스의 visitSource 메소드로 방문합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 소스 라인 번호와 바이트 코드 명령어 간의 매핑은 MethodVisitor 클래스의 visitLineNumber 메소드를 사용하여 한 번에 한 쌍씩 방문합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 소스 코드의 로컬 변수 이름과 바이트 코드의 로컬 변수 슬롯 간의 매핑은 MethodVisitor 클래스의 visitLocalVariable 메소드 (한 번에 하나의 튜플)를 통해 수행됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;인수로 전달 된 레이블을 방문한 후에 visitLineNumber 메소드를 호출해야합니다. 실제로이 라벨 바로 다음에 호출되므로 메서드 호출자에서 현재 명령의 소스 행을 쉽게 알 수 있습니다&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class MyAdapter extends MethodVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;int currentLine;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public MyAdapter(MethodVisitor mv) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;super(ASM4, mv);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;public void visitLineNumber(int line, Label start) {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;mv.visitLineNumber(line, start);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;		&lt;/span&gt;currentLine = line;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;...&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;마찬가지로 인수로 전달 된 레이블을 방문한 후에 visitLocalVariable 메소드를 호출해야합니다. 이전 섹션에서 제시된 쌍 및 튜플에 해당하는 예제 메서드 호출은 다음과 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;visitLineNumber(n1, l1);&lt;/p&gt;&lt;p&gt;visitLineNumber(n2, l2);&lt;/p&gt;&lt;p&gt;visitLineNumber(n3, l3);&lt;/p&gt;&lt;p&gt;visitLocalVariable(&quot;i&quot;, &quot;I&quot;, null, l1, l2, 1);&lt;/p&gt;&lt;p&gt;visitLocalVariable(&quot;j&quot;, &quot;I&quot;, null, l2, l3, 1);&lt;/p&gt;&lt;p&gt;visitLocalVariable(&quot;i&quot;, &quot;I&quot;, null, l3, end, 1);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Ignoring debug information&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;줄 번호와 지역 변수 이름을 방문하기 위해 ClassReader 클래스는 점프 지침에서는 필요하지 않지만 디버그 정보 만 나타낼 수 있다는 의미에서 &quot;인공&quot;Label 객체를 도입해야 할 수도 있습니다. 3.2.5 절에서 설명 된 것과 같이 명령 시퀀스 중간의 레이블이 점프 대상으로 간주되어이 시퀀스가 ​​제거되지 않도록하는 상황에서 오탐 (false positive)이 발생할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이러한 잘못된 긍정을 피하기 위해 ClassReader.accept 메서드에서 SKIP_DEBUG 옵션을 사용할 수 있습니다. 이 옵션을 사용하면 클래스 판독기가 디버그 정보를 방문하지 않고 인공 지능 레이블을 생성하지 않습니다. 물론 디버그 정보는 클래스에서 제거되므로이 옵션은 응용 프로그램에 문제가되지 않는 경우에만 사용할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;참고 : ClassReader 클래스는 컴파일 된 코드 (클래스 구조 만 있으면 유용 할 수 있음), 스택 맵 프레임을 건너 뛰는 SKIP_FRAMES 및 이러한 프레임의 압축을 해제하는 EXPAND_FRAMES의 방문을 건너 뛰는 SKIP_CODE와 같은 다른 옵션을 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4.3.3. Tools&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;제네릭 형식 및 annotation과 마찬가지로 TraceClassVisitor, CheckClassAdapter 및 ASMifier 클래스를 사용하여 디버그 정보로 작업하는 방법을 찾을 수 있습니다.&lt;/p&gt;</description>
      <category>ASM</category>
      <category>ASM</category>
      <category>bytecode</category>
      <category>Java</category>
      <category>user guide</category>
      <category>번역</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/9</guid>
      <comments>https://yimoc.tistory.com/9#entry9comment</comments>
      <pubDate>Fri, 14 Sep 2018 16:44:15 +0900</pubDate>
    </item>
    <item>
      <title>ASM User Guide 번역 3</title>
      <link>https://yimoc.tistory.com/7</link>
      <description>&lt;p&gt;3. Methods&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 장에서는 core&amp;nbsp;ASM API를 사용하여 컴파일 된 메소드를 생성하고 변환하는 방법을 설명합니다. 그것은 컴파일 된 메소드의 표현으로 시작하여 많은 예제를 사용하여 해당하는 ASM 인터페이스, 컴포넌트 및 툴을 생성하고 변환하는 도구를 제공합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1. Structure&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;컴파일 된 클래스 내에서 메소드 코드는 바이트 코드 명령어의 시퀀스로 저장됩니다. 클래스를 생성하고 변환하기 위해 이들 지침을 알고 그들이 어떻게 작동하는지 이해하는 것이 필수적입니다. 이 섹션에서는 간단한 클래스 생성기 및 변환기의 코딩을 시작하기에 충분해야하는 지침의 개요를 제공합니다. 전체 정의를 보려면 Java Virtual Machine Specification을 읽어야합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.1. Execution model&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;바이트 코드 명령을 제시하기 전에 Java Virtual Machine 실행 모델을 제시합니다.&amp;nbsp;아시다시피 Java 코드는 스레드 내에서 실행됩니다.&lt;/p&gt;
&lt;p&gt;각 스레드는 프레임으로 구성된 자체 실행 스택을 가지고 있습니다. 각 프레임은 메소드 호출을 나타냅니다. 메소드가 호출 될 때마다 현재 스레드의 실행 스택에 새로운 프레임이 푸시됩니다. 메서드가 정상적으로 또는 예외로 인해 반환되면이 프레임이 실행 스택에서 pop되고 호출 메서드에서 실행이 계속됩니다 (이제 프레임이 스택 맨 위에 있음).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;각 프레임은 로컬 변수 부분과 피연산자(Operand)&amp;nbsp;스택 부분의 두 부분으로 구성됩니다. 지역 변수 부분에는 인덱스로 임의의 순서로 액세스 할 수있는 변수가 들어 있습니다. 피연산자 스택 부분은 이름에서 알 수 있듯이 바이트 코드 명령어에 의해 피연산자로 사용되는 값들의 스택입니다. 즉,이 스택의 값은 선입 선출 (Last In First Out) 순서로만 액세스 할 수 있습니다. 피연산자 스택과 스레드의 실행 스택을 혼동하지 마십시오. 실행 스택의 각 프레임에는 자체 피연산자 스택이 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;로컬 변수와 피연산자 스택 부분의 크기는 메서드의 코드에 따라 다릅니다. 컴파일시에 계산되어 컴파일 된 클래스의 바이트 코드 명령어와 함께 저장됩니다. 결과적으로, 주어진 메소드의 호출에 대응하는 모든 프레임은 같은 크기를 갖지만, 다른 메소드에 대응하는 프레임은 로컬 변수와 피연산자 스택 부분에 대해 다른 크기를 가질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 460px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9916B94F5B9764860D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9916B94F5B9764860D&quot; width=&quot;460&quot; height=&quot;160&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.1 : An execution stack with 3 frames&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;그림 3.1은 3 프레임을 가진 샘플 실행 스택을 보여줍니다. 첫 번째 프레임은 3 개의 로컬 변수를 포함하고, 피연산자 스택의 최대 크기는 4이며 두 개의 값을 포함합니다. 두 번째 프레임은 2 개의 로컬 변수와 피연산자 스택에 두 개의 값을 포함합니다. 마지막으로 실행 스택 맨 위에있는 세 번째 프레임에는 4 개의 로컬 변수와 두 개의 피연산자가 들어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;프레임이 생성되면 프레임은 빈 스택으로 초기화되고 해당 로컬 변수는 대상 객체로 this (비 정적 메서드의 경우) 및 메서드의 인수로 초기화됩니다. 예를 들어, a.equals (b) 메서드를 호출하면 스택이 비어 있고 처음 두 개의 로컬 변수가 a 및 b (다른 로컬 변수는 초기화되지 않음)로 초기화 된 프레임이 만들어집니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;로컬 변수와 피연산자 스택 부분의 각 슬롯은 long 값과 double 값을 제외한 모든 Java 값을 포함 할 수 있습니다. 이 값에는 두 개의 슬롯이 필요합니다. 이것은 로컬 변수의 관리를 복잡하게합니다. 예를 들어 i 번째 메소드 인수가 반드시 로컬 변수 i에 저장되는 것은 아닙니다. 예를 들어, Math.max (1L, 2L)를 호출하면 첫 번째 두 개의 로컬 변수 슬롯에 1L 값을 가진 프레임이 만들어지고 세 번째와 네 번째 슬롯에 2L 값이있는 프레임이 만들어집니다&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.2. Bytecode instructions&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;바이트 코드 명령어는이 명령어를 식별하는 opcode와 고정 된 수의 argument로 구성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• opcode는 부호없는 바이트 값이며 따라서 바이트 코드 이름이며 니모닉(기억하기 쉬운) 기호로 식별됩니다. 예를 들어 opcode 값 0은 니모닉 기호 NOP에 의해 설계되며 아무 것도 수행하지 않는 명령에 해당합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• 인수는 정확한 명령어 동작을 정의하는 정적 값입니다.&lt;/p&gt;
&lt;p&gt;그들은 opcode 바로 다음에 주어집니다. 예를 들어 opcode 값이 167 인 GOTO 레이블 명령어는 실행될 다음 명령어를 지정하는 레이블 인 인수 레이블을 취합니다. 명령어 인수는 명령어 피연산자와 혼동되어서는 안됩니다. 인수 값은 정적으로 알려져 컴파일 된 코드에 저장되는 반면, 피연산자 값은 피연산자 스택에서 가져온 것이며 런타임에만 인식됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;바이트 코드 명령어는 두 가지 카테고리로 나누어 질 수 있습니다 : 로컬 변수에서 피연산자 스택으로 값을 전송하도록 작은 명령어 세트가 설계되었습니다. 다른 명령어는 피연산자 스택에서만 작동합니다. 즉, 스택에서 일부 값을 팝핑하고 이러한 값을 기반으로 결과를 계산 한 다음 다시 스택에 푸시합니다.&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;ILOAD, LLOAD, FLOAD, DLOAD&lt;/b&gt; 및 &lt;b&gt;ALOAD&lt;/b&gt; 명령어는 로컬 변수를 읽고 해당 값을 피연산자 스택에 푸시합니다. 그들은 읽어야 만하는 로컬 변수의 인덱스 i를 인수로 취합니다. &lt;b&gt;ILOAD&lt;/b&gt;는 boolean, byte, char, short 또는 int 로컬 변수를 로드하는 데 사용됩니다. &lt;b&gt;LLOAD&lt;/b&gt;, &lt;b&gt;FLOAD&lt;/b&gt; 및 &lt;b&gt;DLOAD&lt;/b&gt;는 각각 long, float 또는 double 값을 로드하는 데 사용됩니다 (&lt;b&gt;LLOAD&lt;/b&gt; 및 &lt;b&gt;DLOAD&lt;/b&gt;는 실제로 두 개의 슬롯 i 및 i + 1을 로드합니다). 마지막으로 &lt;b&gt;ALOAD&lt;/b&gt;는 non primitive 값, 즉 객체 및 배열 참조를 로드하는 데 사용됩니다. 대칭적으로 &lt;b&gt;ISTORE, LSTORE, FSTORE, DSTORE &lt;/b&gt;및&lt;b&gt; ASTORE&lt;/b&gt; 명령어는 피연산자 스택에서 값을 pop하고 해당 인덱스 i로 지정된 로컬 변수에 저장합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;보시다시피 &lt;b&gt;xLOAD&lt;/b&gt; 및 &lt;b&gt;xSTORE&lt;/b&gt; 명령어가 입력됩니다 (실제로는 아래에서 볼 수 있듯이 거의 모든 명령어가 입력됩니다). 이것은 불법적인 변환이 수행되지 않도록하기 위해 사용됩니다. 실제로 지역 변수에 값을 저장 한 다음 다른 type으로 값을 로드하는 것은 불법입니다. 예를 들어, ISTORE 1 ALOAD 1 시퀀스는 불법입니다 - 이것은 임의의 메모리 주소를 로컬 변수 1에 저장하고이 주소를 객체 참조로 변환하는 것을 허용합니다! 그러나, 이 로컬 변수에 저장된 현재 값의 type과 다른 type의 값을 로컬 변수에 저장하는 것이 가장 합당합니다. 이것은 로컬 변수의 type, 즉 이 로컬 변수에 저장된 값의 type이 메소드의 실행 중에 변경 될 수 있음을 의미합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;위에서 말한 것처럼 다른 모든 바이트 코드 명령어는 피연산자 스택에서만 작동합니다.&lt;/div&gt;&lt;div&gt;다음 범주로 그룹화 할 수 있습니다 (부록 A.1 참조).&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;Stack&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 스택에서 값을 조작하는 데 사용됩니다.&lt;/div&gt;&lt;div&gt;&lt;b&gt;POP&lt;/b&gt;는 스택 맨 위에 pop을 팝하고, &lt;b&gt;DUP&lt;/b&gt;는 맨 위 스택 값의 복사본을 push하고, &lt;b&gt;SWAP&lt;/b&gt;는 두 값을 pop하고 역순으로 push합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Constants&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 피연산자 스택에 상수 값을 푸시합니다.&lt;/div&gt;&lt;div&gt;&lt;b&gt;ACONST_NULL&lt;/b&gt;은 null을 푸시합니다. &lt;b&gt;ICONST_0&lt;/b&gt;은 int 값 0을, &lt;b&gt;FCONST_0&lt;/b&gt;은 0f를, &lt;b&gt;DCONST_0&lt;/b&gt;은 0d를, &lt;b&gt;BIPUSH&lt;/b&gt;는 b를, &lt;b&gt;SIPUSH&lt;/b&gt;는 short 값을, &lt;b&gt;LDC&lt;/b&gt; cst는 임의의 int, float, long, double, String 또는 class constant&amp;nbsp;cst, 등등을 push한다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Arithmetic and logic&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 피연산자 스택에서 숫자 값을 가져 와서 결합하여 결과를 스택에 푸시합니다. 그들에게는 어떤 arguemnt도 없다. &lt;b&gt;xADD, xSUB, xMUL, xDIV&lt;/b&gt; 및 &lt;b&gt;xREM&lt;/b&gt;은 +, -, * 및 / 또는 % 연산에 해당하며 여기서 x는 &lt;b&gt;I, L, F&lt;/b&gt; 또는 &lt;b&gt;D&lt;/b&gt;입니다. 마찬가지로 int 및 long 값에 대해 &amp;lt;&amp;lt;&amp;nbsp;&amp;lt;,&amp;gt;&amp;gt;&amp;gt;,&amp;gt;&amp;gt;&amp;gt;, |, &amp;amp; 및 ^에 해당하는 다른 명령어가 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Casts&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 스택에서 값을 팝하고 다른 유형으로 변환 한 다음 결과를 다시 푸시합니다. 그것들은 Java의 캐스트 표현식에 해당합니다.&lt;b&gt; I2F, F2D, L2D&lt;/b&gt; 등은 하나의 숫자 유형에서 다른 숫자 유형으로 숫자 값을 변환합니다. &lt;b&gt;CHECKCAST&lt;/b&gt; t는 참조 값을 type t로 변환합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Objects&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 객체를 생성하고, 잠그고, type을 테스트하는 등의 작업에 사용됩니다. 예를 들어 &lt;b&gt;NEW&lt;/b&gt; 유형의 명령어는 스택에 새로운 유형의 객체를 푸시합니다 (유형은 내부 이름 임).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Fields&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 필드의 값을 읽거나 씁니다. &lt;b&gt;GETFIELD&lt;/b&gt; &lt;i&gt;owner name desc&lt;/i&gt;는 객체 참조를 팝하고 이름 필드의 값을 푸시합니다. &lt;b&gt;PUTFIELD&lt;/b&gt; &lt;i&gt;owner name desc&lt;/i&gt;는 값과 객체 참조를 팝하고이 값을 이름 필드에 저장합니다. 두 경우 모두 객체는 ​​owner 유형이어야하며 필드의 형식은 desc 여야합니다. &lt;b&gt;GETSTATIC&lt;/b&gt; 및 &lt;b&gt;PUTSTATIC&lt;/b&gt;은 유사한 명령어이지만 정적 필드의 경우에 사용됩니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Methods&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 메소드 또는 생성자를 호출합니다. 메서드 인수만큼의 값들과 대상 객체에 대한 값을 추가하여&amp;nbsp;pop하고, 메서드 호출의 결과를 푸시합니다. &lt;b&gt;INVOKEVIRTUAL&lt;/b&gt; &lt;i&gt;owner name desc&lt;/i&gt;는 owner 클래스에 정의 된 name 메소드를 호출하고 method descriptor가 desc 인 메소드를 호출합니다. &lt;b&gt;INVOKESTATIC&lt;/b&gt;은 static메서드에 사용되며, &lt;b&gt;INVOKESPECIAL&lt;/b&gt;에는 private 메서드와 생성자가 사용되고 &lt;b&gt;INVOKEINTERFACE&lt;/b&gt;에는 인터페이스에 정의 된 메서드가 사용됩니다. 마지막으로 Java 7 클래스의 경우 &lt;b&gt;INVOKEDYNAMIC&lt;/b&gt;이 새로운 동적 메서드 호출 메커니즘에 사용됩니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Arrays&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 배열에서 값을 읽고 쓰는 데 사용됩니다. &lt;b&gt;xALOAD&lt;/b&gt; 명령은 인덱스 및 배열을 팝하고 이 인덱스에서 배열 요소의 값을 푸시합니다. &lt;b&gt;xASTORE&lt;/b&gt; 명령어는 값, 인덱스 및 배열을 pop하고이 값을 배열의 해당 인덱스에 저장합니다. 여기서 x는 I, L, F, D 또는 A뿐만 아니라 B, C 또는 S가 될 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Jumps&lt;/b&gt;&lt;/div&gt;&lt;div&gt;이 명령어는 어떤 조건이 참이거나 무조건 조건이면 임의의 명령어로 점프합니다. 그것들은 if, for, do, while, break , continue명렴어를 컴파일하기 위해 사용된다. 예를 들어 &lt;b&gt;IFEQ &lt;/b&gt;&lt;i&gt;label&lt;/i&gt;&amp;nbsp;은 스택의 int 값을 팝하며 이 값이 0이면 label에 의해 설계된 명령으로 점프합니다 (그렇지 않으면 실행이 다음 명령으로 정상적으로 계속됩니다). &lt;b&gt;IFNE&lt;/b&gt; 나 &lt;b&gt;IFGE&lt;/b&gt;와 같은 다른 많은 점프 명령어가 존재합니다. 마지막으로 &lt;b&gt;TABLESWITCH&lt;/b&gt; 및 &lt;b&gt;LOOKUPSWITCH&lt;/b&gt;는 switch&amp;nbsp;Java 명령어에 해당합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Return&lt;/b&gt;&lt;/div&gt;&lt;div&gt;마지막으로 &lt;b&gt;xRETURN&lt;/b&gt; 및 &lt;b&gt;RETURN&lt;/b&gt; 명령어는 메소드의 실행을 종료하고 그 결과를 호출자에게 리턴하는 데 사용됩니다. &lt;b&gt;RETURN&lt;/b&gt;은 void를 반환하는 메서드에 사용되고 &lt;b&gt;xRETURN&lt;/b&gt;은 다른 메서드에 사용됩니다.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.3. Examples
Lets look at some basic examples to get a more concrete sense of how bytecode
instructions work. Consider the following bean class:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;package pkg;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class Bean {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private int f;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public int getF() {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;return this.f;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void setF(int f) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;this.f = f;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;The bytecode of the getter method is:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;ALOAD 0&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;GETFIELD pkg/Bean f I&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;IRETURN&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 명령은 로컬변수 0를 읽습니다. 이 변수는 메서드 호출에 대한 프레임을 만드는 동안이 변수에 초기화되고 이 값을 피연산자 스택에 푸시합니다. 두 번째 명령은 스택에서 이 값을 팝합니다. 즉,이 객체의 f 필드 즉 this.f를 푸시합니다. 마지막 명령은 스택에서이 값을 팝하고 호출자에게 반환합니다. 이 방법에 대한 실행 프레임의 연속 상태가 그림 3.2에 나와 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 408px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996CD03B5B976D9A14&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996CD03B5B976D9A14&quot; width=&quot;408&quot; height=&quot;143&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Figure 3.2.: Successive frame states for the getF method: a) initial state, b)
after ALOAD 0 and c) after GETFIELD&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;The bytecode of the setter method is:&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;ALOAD 0&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;ILOAD 1&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;PUTFIELD pkg/Bean f I&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;RETURN&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 명령어는 이전과 마찬가지로 피연산자 스택에서 이를 푸시합니다.&lt;/p&gt;
&lt;p&gt;두 번째 명령은이 메서드 호출을위한 프레임을 만드는 동안 f 인수 값으로 초기화 된 로컬 변수 1을 푸시합니다.&lt;/p&gt;
&lt;p&gt;세 번째 명령은이 두 값을 팝하고 참조 된 객체의 f 필드에 int 값을 this.f로 저장합니다. 소스 코드에 내포되어 있지만 컴파일 된 코드에서 필수적인 마지막 명령은 현재 실행 프레임을 파괴하고 호출자에게 반환됩니다. 이 방법에 대한 실행 프레임의 연속 상태가 그림 3.3에 나와 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 476px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999CB5495B976E8606&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999CB5495B976E8606&quot; width=&quot;476&quot; height=&quot;147&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Figure 3.3.: Successive frame states for the setF method: a) initial state, b)
after ALOAD 0, c) after ILOAD 1 and d) after PUTFIELD
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Bean 클래스에는 프로그래머가 명시 적 생성자를 정의하지 않았기 때문에 컴파일러에 의해 생성되는 기본 public 생성자가 있습니다. 이 기본 공용 생성자는 Bean () {super (); }. 이 생성자의 바이트 코드는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;INVOKESPECIAL java/lang/Object &amp;lt;init&amp;gt; ()V&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;&lt;init&gt;RETURN&lt;/init&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;&lt;init&gt;&lt;br /&gt;&lt;/init&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 명령어는 이것을 피연산자 스택에 푸시합니다. 두 번째 명령은 스택에서 이 값을 팝하고 Object 클래스에 정의 된 &amp;lt;init&amp;gt; 메서드를 호출합니다. super () 호출, 즉 수퍼 클래스 인 Object의 생성자에 대한 호출에 해당합니다. 생성자는 컴파일 된 클래스와 소스 클래스에서 이름이 다르게 표시됩니다. 컴파일 된 클래스에서는 항상 &amp;lt;init&amp;gt;으로 명명되지만 소스 클래스에서는 정의 된 클래스의 이름을 갖습니다. 마지막으로 마지막 명령이 호출자에게 반환됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;init&gt;&lt;/init&gt;&lt;/p&gt;
&lt;p&gt;이제 좀 더 복잡한 setter 메서드를 살펴 보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;&lt;init&gt;&lt;br /&gt;&lt;/init&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void checkAndSetF(int f) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;if (f &amp;gt;= 0) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;this.f = f;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;} else {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;throw new IllegalArgumentException();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;init&gt;&lt;/init&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;The bytecode for this new setter method is the following:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ILOAD 1&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;IFLT label&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ALOAD 0&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ILOAD 1&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;PUTFIELD pkg/Bean f I&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;GOTO end&lt;/p&gt;
&lt;p&gt;label:&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;NEW java/lang/IllegalArgumentException&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;DUP&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;INVOKESPECIAL java/lang/IllegalArgumentException &amp;lt;init&amp;gt; ()V&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ATHROW&lt;/p&gt;
&lt;p&gt;end:&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;RETURN&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 명령은 피연산자 스택에서 f로 초기화 된 로컬 변수 1을 푸시합니다. IFLT 명령어는 스택에서 이 값을 가져와 0과 비교합니다. LT (Less Than) 0이면 레이블 레이블에 지정된 명령어로 점프합니다. 그렇지 않으면 아무 것도 수행하지 않고 실행이 다음 명령어로 계속 진행됩니다. 다음 세 명령어는 setF 메소드에서와 같은 명령어이다. GOTO 명령어는 RETURN 명령어 인 끝 레이블로 지정된 명령어로 무조건 점프합니다. 레이블과 끝 레이블 사이의 명령어는 예외를 만들고 생성합니다. NEW 명령어는 예외 객체를 만들고이를 피연산자 스택에 푸시합니다. DUP 명령은이 값을 스택에 복제합니다. INVOKESPECIAL 명령어는이 두 복사본 중 하나를 팝하고 예외 생성자를 호출합니다. 마지막으로 ATHROW 명령은 나머지 복사본을 팝하고 예외로 throw합니다 (실행이 다음 명령으로 이어지지 않도록).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.4. Exception handlers&lt;/p&gt;
&lt;p&gt;예외를 잡는 바이트 코드 명령어가 없습니다. 대신 메소드의 바이트 코드는 메소드의 주어진 부분에서 예외가 발생할 때 실행되어야 하는 코드를 지정하는 예외 핸들러 목록과 연관됩니다. 예외 핸들러는 try catch 블록과 유사합니다. 범위에는 try 블록의 내용에 해당하는 명령어 시퀀스와 catch 블록의 내용에 해당하는 핸들러가 있습니다. 범위는 시작 및 끝 레이블로 지정되고 처리기는 시작 레이블로 지정됩니다. 아래 소스 코드 예를 들면 :&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public static void sleep(long d) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;try {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;Thread.sleep(d);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;} catch (InterruptedException e) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;e.printStackTrace();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;can be compiled into:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;TRYCATCHBLOCK try catch catch java/lang/InterruptedException&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;try:&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;LLOAD 0&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;INVOKESTATIC java/lang/Thread sleep (J)V&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;RETURN&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;i&gt;catch:&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;INVOKEVIRTUAL java/lang/InterruptedException printStackTrace ()V&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;RETURN&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;try&lt;/i&gt; 및 &lt;i&gt;catch&lt;/i&gt; 레이블 사이의 코드는 try 블록에 해당하고&amp;nbsp;&lt;i&gt;catch&lt;/i&gt; 레이블 뒤의 코드는 catch 블록에 해당합니다. TRYCATCHBLOCK 행은 &lt;i&gt;try &lt;/i&gt;와&amp;nbsp;&lt;i&gt;catch&lt;/i&gt; 레이블 사이의 범위를 처리하는 예외 핸들러를 지정하며, &lt;i&gt;catch&lt;/i&gt; 레이블에서 시작하는 핸들러는 InterruptedException의 서브 클래스 인 예외에 대해 지정합니다. 즉, try와 catch 사이에 이러한 예외가 발생하면 스택이 지워지고 예외가이 빈 스택에 푸시되고 catch에서 실행이 계속됨을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.5. Frames&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Java 6 이상으로 컴파일 된 클래스에는 바이트 코드 명령어 외에도 Java Virtual Machine 내부의 클래스 검증 프로세스를 가속화하는 데 사용되는 stack map frames의 set가 포함되어 있습니다. stack map frame은 실행 중 어느 시점에서 메소드의 실행 프레임 상태를 제공합니다. 좀 더 정확히 말하면, 특정 바이트 코드 명령어가 실행되기 직전에 특정 로컬 변수 슬롯과 각 피연산자 스택 슬롯에 포함 된 값의 type을&amp;nbsp; 제공합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;예를 들어, 이전 섹션의 getF 메소드를 살펴보면 실행 프레임의 상태를 ALOAD 직전, GETFIELD 직전, IRETURN 직전에 제공하는 세 개의 stack map frame을 정의 할 수 있습니다. 이 세 개의 stack map frames은 Figure 3.2의 세 가지 경우에 해당하며 첫 번째 대괄호 사이의 유형이 로컬 변수에 해당하고 다른 유형은 피연산자 스택에 해당하는 경우 다음과 같이 설명 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table class=&quot;txc-table&quot; width=&quot;784&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; border=&quot;0&quot; style=&quot;border:none;border-collapse:collapse;;font-family:&quot; 맑은=&quot;&quot; 고딕&quot;,=&quot;&quot; sans-serif;font-size:13px&quot;=&quot;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;border-top:0px solid #ccc;border-left:0px solid #ccc;;&quot;&gt;&lt;p&gt; State of the execution frame&amp;nbsp;&lt;i&gt;before&amp;nbsp;&lt;/i&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;border-top:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Instruction&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;border-left:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean] []&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;ALOAD 0&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;border-left:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean] [pkg/Bean]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;GETFIELD&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;border-left:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean] [I]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:0px solid #ccc;border-right:0px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;IRETURN&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;checkAndSetF 메소드에 대해서도 동일한 작업을 수행 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table class=&quot;txc-table&quot; width=&quot;784&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; border=&quot;0&quot; style=&quot;border:none;border-collapse:collapse;;font-family:&quot; 맑은=&quot;&quot; 고딕&quot;,=&quot;&quot; sans-serif;font-size:13px&quot;=&quot;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt; State of the execution frame&amp;nbsp;&lt;i&gt;before&lt;/i&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Instruction&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] []&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;ILOAD 1&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] [I]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;IFLT label&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] []&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;ALOAD 0&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] [pkg/Bean]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;ILOAD 1&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] [pkg/Bean I]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;PUTFIELD&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] []&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;GOTO end&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] []&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;label :&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] []&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;NEW&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[pkg/Bean I] [Uninitialized(label)]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;DUP&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;[pkg/Bean I] [Uninitialized(label) Uninitialized(label)]&lt;/td&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;INVOKESPECIAL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;[pkg/Bean I] [java/lang/IllegalArgumentException]&lt;/td&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;ATHROW&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;[pkg/Bean I] []&lt;/td&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;end :&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;[pkg/Bean I] []&lt;/td&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot; rowspan=&quot;1&quot;&gt;&amp;nbsp;RETURN&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이는 초기화되지 않은 (레이블) 유형을 제외하고 이전 메서드과 유사합니다. 이것은 stack map frame에서만 사용되며 메모리가 할당되었지만 아직 생성자가 호출되지 않은 객체를 지정하는 특수 유형입니다. 인수는 이 객체를 생성 한 명령어를 지정합니다. 이 유형의 값에서 호출 할 수있는 유일한 방법은 생성자입니다. 호출 될 때 프레임의이 유형이 모두 실제 유형 여기서는 IllegalArgumentException)으로 바뀝니다. 스택 맵 프레임은 다음과 같은 세 가지 특수 유형을 사용할 수 있습니다. UNINITIALIZED_THIS는 생성자의 로컬 변수 0의 초기 유형이고 TOP은 정의되지 않은 값에 해당하며 NULL은 null에 해당합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;위에서 말했듯이, Java 6부터 컴파일 된 클래스에는 bytecode, stack map frames의 set를 포함하고 있다. 저장공간을 절약하기 위해 컴파일 된 메소드에는 명령어 당 하나의 프레임이 포함되지는 않습니다. 실제로는 jump 대상이나 예외 핸들러에 해당하거나 무조건 점프 명령어를 따르는 명령어 프레임 만 포함합니다. 실제로 다른 프레임은 이 프레임에서 쉽고 빠르게 추론 할 수 있습니다. checkAndSetF 메소드의 경우 이는 두 개의 프레임 만 저장된다는 것을 의미합니다. 하나는 NEW 명령, IFLT 명령의 대상이며 또한 무조건 점프 GOTO 명령을 따르기 때문이며, 하나는 RETURN 명령인데 이는 GOTO 명령의 대상며 또한 &quot;무조건 점프&quot;ATHROW 명령을 따르기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;더 많은 공간을 절약하기 위해, 각 프레임은 이전 프레임과 비교 된 그 차이만을 저장함으로써 압축되고,초기 프레임은 메소드 매개 변수 유형에서 쉽게 추론 할 수 있기 때문에 전혀 저장되지 않습니다. checkAndSetF 메소드의 경우, 저장되어야하는 두 개의 프레임은 동일하고 초기 프레임과 동일하므로 F_SAME 니모닉에 의해 지정된 단일 바이트 값으로 저장됩니다. 이러한 프레임은 관련 바이트 코드 명령어 바로 직전에 나타낼 수 있습니다. checkAndSetF 메소드에 대한 최종 바이트 코드를 제공합니다.:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ILOAD 1&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;IFLT label&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ALOAD 0&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ILOAD 1&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;PUTFIELD pkg/Bean f I&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;GOTO end&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;label:&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;F_SAME&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;NEW java/lang/IllegalArgumentException&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;DUP&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;INVOKESPECIAL java/lang/IllegalArgumentException &amp;lt;init&amp;gt; ()V&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;ATHROW&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;end:&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;F_SAME&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;RETURN&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;color: rgb(9, 0, 255);&quot;&gt;※&lt;/span&gt;&lt;span style=&quot;color: rgb(9, 0, 255);&quot;&gt;참고할 내용&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;color: rgb(9, 0, 255);&quot;&gt;https://stackoverflow.com/questions/25109942/is-there-a-better-explanation-of-stack-map-frames&lt;/span&gt;&lt;/p&gt;&lt;span style=&quot;color: rgb(9, 0, 255);&quot;&gt;
&lt;/span&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.2. Interfaces and components&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.2.1. Presentation&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;컴파일 된 메소드를 생성하고 변환하는 ASM API는 MethodVisitor 추상 클래스 (그림 3.4 참조)에 기반한다. 이것은 ClassVisitor의 visitMethod 메소드에 의해 리턴됩니다. 어노테이션 및 디버그 정보와 관련된 일부 메소드 외에도,&lt;/p&gt;
&lt;p&gt;다음 장에서 설명 할 이 클래스는 바이트 코드 명령어 카테고리 당 하나의 메소드를 정의하며, 그 명령어들의 인자의 수와 유형를 기준으로 합니다. (이러한 카테고리는 섹션 3.1.2에 제시된 클래스와 일치하지 않습니다) .이러한 메서드는 다음 순서로 호출해야합니다.&lt;/p&gt;
&lt;p&gt;(MethodVisitor 인터페이스의 Javadoc에 지정된 몇 가지 추가 제약 조건이 있음).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;visitAnnotationDefault?&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;( visitAnnotation | visitParameterAnnotation | visitAttribute )*&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;( visitCode&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |
visitLocalVariable | visitLineNumber )*&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;visitMaxs )?&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;visitEnd&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;즉, annotaion과 attribute가 있는 경우&amp;nbsp;먼저 visit해야 하며,&amp;nbsp; 이어서 non abstract method메소드의 바이트 코드가 와야합니다.&lt;/p&gt;
&lt;p&gt;이들&amp;nbsp;메소드의 코드는 정확하게 한번만&amp;nbsp;visitCode 호출하고 visitMaxs를 한번만 호출하는 순차적인 순서로 visit해야한다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;abstract class MethodVisitor { // public accessors ommited&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;MethodVisitor(int api);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;MethodVisitor(int api, MethodVisitor mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;AnnotationVisitor visitAnnotationDefault();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;AnnotationVisitor visitAnnotation(String desc, boolean visible);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;AnnotationVisitor visitParameterAnnotation(int parameter,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;String desc, boolean visible);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitAttribute(Attribute attr);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitFrame(int type, int nLocal, Object[] local, int nStack,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;Object[] stack);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitInsn(int opcode);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitIntInsn(int opcode, int operand);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitVarInsn(int opcode, int var);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitTypeInsn(int opcode, String desc);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitFieldInsn(int opc, String owner, String name, String desc);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitMethodInsn(int opc, String owner, String name, String desc);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitInvokeDynamicInsn(String name, String desc, Handle bsm,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;Object... bsmArgs);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitJumpInsn(int opcode, Label label);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitLabel(Label label);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitLdcInsn(Object cst);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitIincInsn(int var, int increment);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitMultiANewArrayInsn(String desc, int dims);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitTryCatchBlock(Label start, Label end, Label handler,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;String type);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitLocalVariable(String name, String desc, String signature,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;Label start, Label end, int index);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitLineNumber(int line, Label start);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitMaxs(int maxStack, int maxLocals);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;void visitEnd();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Figure 3.4.: The MethodVisitor class&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;따라서 visitCode 및 visitMaxs 메서드를 사용하여 이벤트 시퀀스에서 메서드의 바이트 코드 시작 및 끝을 검색 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;클래스와 마찬가지로 visitEnd 메서드는 마지막에 호출되어야하며 이벤트 시퀀스에서 메서드의 끝을 감지하는 데 사용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;완전한 클래스를 생성하기 위해서, ClassVisitor 클래스와 MethodVisitor 클래스를 조합 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassVisitor cv = ...;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visit(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;MethodVisitor mv1 = cv.visitMethod(..., &quot;m1&quot;, ...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitInsn(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitMaxs(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitEnd();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;MethodVisitor mv2 = cv.visitMethod(..., &quot;m2&quot;, ...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitInsn(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitMaxs(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitEnd();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visitEnd();&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;또 다른 visit하기 시작하기 위해 꼭&amp;nbsp;메소드를 마칠 필요는 없습니다. 사실 MethodVisitor 인스턴스는 완전히 독립적이며 어떤 순서로든 사용할 수 있습니다 (cv.visitEnd ()가 호출되지 않은 한).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassVisitor cv = ...;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visit(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;MethodVisitor mv1 = cv.visitMethod(..., &quot;m1&quot;, ...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitInsn(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;MethodVisitor mv2 = cv.visitMethod(..., &quot;m2&quot;, ...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitInsn(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitMaxs(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv1.visitEnd();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitMaxs(...);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv2.visitEnd();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visitEnd();&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;ASM은 메소드를 생성하고 변환하는 MethodVisitor API를 기반으로 세 가지 핵심 컴포넌트를 제공합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• ClassReader 클래스는 컴파일 된 메서드의 내용을 구문 분석하고 인수로 전달 된 ClassVisitor에 의해 반환 된 MethodVisitor 객체의 해당 메서드를 accept 메서드에 호출합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• ClassWriter의 visitMethod 메소드는 컴파일 된 메소드를 직접 바이너리 형식으로 빌드하는 MethodVisitor 인터페이스의 구현을 반환합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• MethodVisitor 클래스는 수신 한 모든 메소드 호출을 다른 MethodVisitor 인스턴스에 위임합니다. 이벤트 필터로 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;ClassWriter options&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.5 절에서 보았 듯이, 메서드에 대한 스택 맵 프레임을 계산하는 것은 그리 쉽지 않습니다.&lt;/p&gt;
&lt;p&gt;당신은 모든 프레임을 계산해야합니다 . 점프 대상에 해당하거나 무조건 점프를 따르는 프레임을 찾은 다음 마지막으로 나머지 프레임을 압축합니다.&amp;nbsp; 마찬가지로 메소드의 로컬 변수와 피연산자 스택 부분의 크기를 계산하는 것이 더 쉽지만, 그 역시 그리 쉽지는 않습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다행히도 ASM이이를 계산할 수 있습니다. ClassWriter를 만들 때 자동으로 계산해야하는 것을 지정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• &lt;b&gt;new&lt;/b&gt;&amp;nbsp;&lt;b&gt;ClassWriter (0)&lt;/b&gt;에서는 아무 것도 계산되지 않습니다. 프레임과 로컬 변수 및 피연산자 스택 크기를 직접 계산해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• &lt;b&gt;new&lt;/b&gt;&amp;nbsp;&lt;b&gt;ClassWriter (ClassWriter.COMPUTE_MAXS)&lt;/b&gt;를 사용하면 로컬 변수와 피연산자 스택 부분의 크기가 계산됩니다. 여전히 visitMaxs를 호출해야하지만 argument를 사용할 수 있습니다.이 인수는 무시되고 다시 계산됩니다. 이 옵션을 사용하면 여전히 프레임을 스스로 계산해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;• &lt;b&gt;new ClassWriter (ClassWriter.COMPUTE_FRAMES)&lt;/b&gt;를 사용하면 모든 것이 자동으로 계산됩니다. visitFrame을 호출 할 필요는 없지만 visitMaxs를 호출해야합니다 (argument는 무시되고 다시 계산됩니다).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이러한 옵션을 사용하는 것이 편리하지만 비용이 많이 듭니다. &lt;b&gt;COMPUTE_MAXS&lt;/b&gt; 옵션을 사용하면 ClassWriter가 10 % 더 늦어지며, &lt;b&gt;COMPUTE_FRAMES&lt;/b&gt; 옵션을 사용하면 속도가 두 배 느려집니다. 이것은 자신이 계산하는데 걸리는 시간과 비교되어야합니다. 모든 경우를 처리해야하는 ASM에서 사용되는 알고리즘에 비해 특정 상황에서 종종 더 쉽고 빠르게 알고리즘으로 계산할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;프레임을 직접 계산하도록 선택한 경우, ClassWriter 클래스가 압축 단계를 수행하도록 할 수 있습니다. 이를 위해서는 visitFrame (&lt;b&gt;F_NEW&lt;/b&gt;, nLocals, locals, nStack, stack)을 사용하여 압축되지 않은 프레임을 visit하기 만하면 됩니다. 여기서 nLocals 및 nStack은 local 과 피연산자 스택 크기이며, local 및 stack은 해당 type이 들어있는 배열입니다 (자세한 내용은 Javadoc을 참조하십시오).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;또한 프레임을 자동으로 계산하기 위해, 때때로 주어진 두 클래스의 공통 수퍼 클래스를 계산하는 데 필요합니다. 기본적으로 ClassWriter 클래스는 getCommonSuperClass 메소드에서 이를 계산하고, 두 클래스를 JVM에 로드하고 리플렉션 API를 사용합니다. 서로를 참조하는 여러 클래스를 참조 된 클래스가 아직 없기 때문에&amp;nbsp;생성하는 경우 문제가 될 수 있습니다. 이 경우 getCommonSuperClass 메소드를 override하여이 문제점을 해결할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.2.2. Generating methods&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.1.3 절에 정의 된 getF 메소드의 바이트 코드는 mv가 MethodVisitor 인 경우 다음 메소드 호출을 사용하여 생성 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv.visitFieldInsn(GETFIELD, &quot;pkg/Bean&quot;, &quot;f&quot;, &quot;I&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv.visitInsn(IRETURN);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv.visitMaxs(1, 1);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv.visitEnd();&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;첫 번째 호출은 bytecode&amp;nbsp;생성을 시작합니다. 그 다음에는 이 메소드의 세 가지 명령어를 생성하는 세 번의 호출이 이어집니다 (bytecode와 ASM API 간의 매핑은 매우 간단합니다). visitMaxs는 모든 visit가 완료된&amp;nbsp;명령어가 수행되고 나서 호출해야 합니다. 이것은 메소드의 실행 프레임에 대한 로컬 변수 및 피연산자 스택 부분의 크기를 정의하는 데 사용됩니다. 3.1.3 절에서 보았 듯이 이 크기는 각 부분에 대해 1 슬롯입니다. 마지막으로 메소드의 생성을 끝내기 위해 마지막 호출이 사용됩니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;setF 메소드와 생성자의 바이트 코드는 비슷한 방법으로 생성 될 수있다. 보다 흥미로운 예제는 checkAndSetF 메소드입니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitCode();&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitVarInsn(ILOAD, 1);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;Label label = new Label();&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitJumpInsn(IFLT, label);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitVarInsn(ALOAD, 0);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitVarInsn(ILOAD, 1);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitFieldInsn(PUTFIELD, &quot;pkg/Bean&quot;, &quot;f&quot;, &quot;I&quot;);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;Label end = new Label();&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitJumpInsn(GOTO, end);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitLabel(label);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitFrame(F_SAME, 0, null, 0, null);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitTypeInsn(NEW, &quot;java/lang/IllegalArgumentException&quot;);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitInsn(DUP);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitMethodInsn(INVOKESPECIAL,&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;&quot;java/lang/IllegalArgumentException&quot;, &quot;&amp;lt;init&amp;gt;&quot;, &quot;()V&quot;);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitInsn(ATHROW);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitLabel(end);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitFrame(F_SAME, 0, null, 0, null);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitInsn(RETURN);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitMaxs(2, 2);&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;mv.visitEnd();&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;visitCode 호출과 visitEnd 호출 사이에서 섹션 3.1.5의 끝에 표시된 바이트 코드와 정확하게 매핑되는 메서드 호출을 볼 수 있습니다.&lt;/div&gt;&lt;div&gt;명령, 레이블 또는 프레임 당 하나의 호출 (유일한 예외는 레이블 및 종료 레이블 객체의 선언 및 구성입니다).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;참고 : Label 객체는이 레이블에 대한 visitLabel 호출 뒤에 오는 명령어를 지정합니다. 예를 들어, end는 지시가 아니기 때문에 RETURN 명령어를 지정하고 직후에 방문한 프레임은 지정하지 않습니다. 동일한 명령어를 지정하는 레이블이 여러 개있는 것은 당연한 일이지만 레이블은 정확히 하나의 명령어 만 지정해야합니다. 즉, 서로 다른 레이블로 visitLabel을 연속적으로 호출 할 수는 있지만 명령에 사용 된 레이블은 visitLabel을 사용하여 정확히 한 번만 방문해야합니다. 마지막 제약 조건은 라벨을 공유 할 수 없다는 것입니다. 각 메소드는 자체 라벨을 가져야합니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3.2.3. Transforming methods&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;이제 메서드도 클래스처럼 변형 될 수 있다고 추측 했어야합니다. 즉, 메서드 호출을 전달하는 메서드 어댑터를 사용하여 일부 수정을 통해 메서드 호출을 전달할 수 있습니다. 변경 인수는 개별 명령어를 변경하는 데 사용할 수 있으며 수신 된 호출을 전달하지 않고 명령어를 제거하고 수신 된 호출간에 호출을 삽입하면 새로운 명령어가 추가됩니다. MethodVisitor 클래스는 그러한 메소드 어댑터의 기본 구현을 제공합니다.이 어댑터는 수신 한 모든 메소드 호출을 전달하는 것 외에는 아무것도 수행하지 않습니다.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;메소드 어댑터가 사용되는 방법을 이해하기 위해 메소드 내부에서 NOP 명령어를 제거하는 매우 간단한 어댑터를 고려해 봅시다. 아무 것도하지 않기 때문에 문제없이 제거 할 수 있습니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;public class RemoveNopAdapter extends MethodVisitor {&lt;/div&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public RemoveNopAdapter(MethodVisitor mv) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4, mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;if (opcode != NOP) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;mv.visitInsn(opcode);&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;This adapter can be used inside a class adapter as follows:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class RemoveNopClassAdapter extends ClassVisitor {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;public RemoveNopClassAdapter(ClassVisitor cv) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4, cv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;MethodVisitor mv;&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;mv = cv.visitMethod(access, name, desc, signature, exceptions);&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;if (mv != null) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 8em;&quot;&gt;mv = new RemoveNopAdapter(mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;return mv;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;즉, 클래스 어댑터는 체인의 다음 class visitor가 반환 한 메서드 어댑터(method visitor를 캡슐화한)를 빌드하고 이 어댑터를 반환합니다. 그 효과는 클래스 어댑터 체인과 유사한 메소드 어댑터 체인의 생성입니다 (그림 3.5 참조).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 518px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E6483D5B9B1BCA0A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E6483D5B9B1BCA0A&quot; width=&quot;518&quot; height=&quot;292&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 3.5 : Sequence diagram for the RemoveNoAdapter&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;그러나 필수 사항은 아닙니다: 클래스 어댑터 체인과 유사하지 않은 메소드 어댑터 체인을 빌드하는 것이 완벽하게 가능합니다. 각 메소드는 다른 메소드 어댑터 체인을 가질 수 있습니다. 예를 들어, 클래스 어댑터는 생성자가 아닌 메소드에서만 NOP를 제거하도록 선택할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 작업은 다음과 같이 수행 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;mv = cv.visitMethod(access, name, desc, signature, exceptions);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;if (mv != null &amp;amp;&amp;amp; !name.equals(&quot;&amp;lt;init&amp;gt;&quot;)) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;mv = new RemoveNopAdapter(mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;&lt;div&gt;&lt;div&gt;이 경우 어댑터 체인은 생성자에 대해 더 짧습니다. 반대로, 생성자 용 어댑터 체인은 visitMethod 내부에 함께 연결된 여러 메소드 어댑터와 함께 더 길 수있었습니다. 메소드 어댑터 체인은 클래스 어댑터 체인과 다른 토폴로지를 가질 수도 있습니다. 예를 들어, 클래스 어댑터 체인은 선형 일 수 있지만, 메소드 어댑터 체인에 branch를 가집니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;MethodVisitor mv1, mv2;&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;mv1 = cv.visitMethod(access, name, desc, signature, exceptions);&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;mv2 = cv.visitMethod(access, &quot;_&quot; + name, desc, signature, exceptions);&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;return new MultiMethodAdapter(mv1, mv2);&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;}&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;이제 메소드 어댑터의 사용과 클래스 어댑터내부에서 결합하는 방법을 살펴 보았습니다. 이제 RemoveNopAdapter보다 더 흥미로운 어댑터를 구현하는 방법을 살펴 보겠습니다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3.2.4. Stateless transformations&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;프로그램의 각 클래스에서 소비 한 시간을 측정한다고 가정 해 봅시다.&lt;/div&gt;&lt;div&gt;각 클래스에 정적 타이머 필드를 추가해야하며 이 클래스의 각 메서드 실행 시간을이 타이머 필드에 추가해야합니다.&lt;/div&gt;&lt;div&gt;다시 말하면 우리는 C를 변환하려합니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;public class C {&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;public void m() throws Exception {&lt;/div&gt;&lt;div style=&quot;margin-left: 6em;&quot;&gt;Thread.sleep(100);&lt;/div&gt;&lt;div style=&quot;margin-left: 4em;&quot;&gt;}&lt;/div&gt;&lt;div style=&quot;margin-left: 2em;&quot;&gt;}&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이걸로&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class C {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public static long timer;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void m() throws Exception {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;timer -= System.currentTimeMillis();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;Thread.sleep(100);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;timer += System.currentTimeMillis();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이것이 ASM에서 어떻게 구현 될 수 있는지에 대한 아이디어를 얻기 위해, 이 두 클래스를 컴파일하고이 두 버전 (기본 Textifier 백엔드 또는 ASMifier 백엔드)에서 TraceClassVisitor의 출력을 비교할 수 있습니다. 기본 백엔드에서는 다음과 같은 차이점이 있습니다 (굵게 표시).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;GETSTATIC C.timer : J&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;INVOKESTATIC java/lang/System.currentTimeMillis()J&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;LSUB&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;PUTSTATIC C.timer : J&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;LDC 100&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;INVOKESTATIC java/lang/Thread.sleep(J)V&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;GETSTATIC C.timer : J&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;INVOKESTATIC java/lang/System.currentTimeMillis()J&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;LADD&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;PUTSTATIC C.timer : J&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;RETURN&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;MAXSTACK = 4&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;MAXLOCALS = 1&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;우리는 메서드의 시작 부분에 4 개의 명령와 리턴되는 명령어 앞에 4 개의 다른 명령어를 추가해야한다는 것을 알 수 있습니다. 또한 최대 operan stack 크기를 업데이트해야 합니다. 메소드 코드의 시작 부분은 visitCode 메소드를 사용하여 visit합니다. 따라서 메소드 어댑터에서 이 메소드를 대체하여 처음 네 명령어를 추가 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void visitCode() {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;mv.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;b&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;b&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;b&gt;mv.visitInsn(LSUB);&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;b&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;여기서 소유자는 변형 될 클래스의 이름으로 설정되어야합니다.&lt;/p&gt;
&lt;p&gt;이제 RETURN 이전에 네 가지 다른 명령어를 추가해야합니다. xRETURN 이전 또는 ATHROW 앞에는 메소드 실행을 종료하는 모든 명령어가 추가되어야합니다. 이 명령어에는 인수가 없으므로 visitInsn 메서드에서 방문합니다. 그런 다음 명령어을 추가하기 위해이 메소드를 override&amp;nbsp;할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;if ((opcode &amp;gt;= IRETURN &amp;amp;&amp;amp; opcode &amp;lt;= RETURN) || opcode == ATHROW) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(LADD);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;mv.visitInsn(opcode);&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;마지막으로 최대 피연산자 스택 크기를 업데이트해야합니다. 추가하는 명령어는 두 개의 long 값을 푸시하므로 피연산자 스택에 네 개의 슬롯이 필요합니다. 메소드의 시작 부분에서 피연산자 스택은 처음에 비어 있으므로 처음에 추가 된 4 개의 명령어에는 크기 4의 스택이 필요합니다. 또한 삽입 된 코드는 스택 상태를 변경되지 않은 상태로 유지한다는 것을 알고 있습니다 (밀어 넣을만큼 많은 값을 표시하기 때문에).&lt;/p&gt;
&lt;p&gt;결과적으로, 원래 코드가 크기 s의 스택을 필요로한다면, 변환 된 메소드에 필요한 최대 스택 크기는 max (4, s)입니다.&lt;/p&gt;
&lt;p&gt;불행하게도 우리는 return 명령어 앞에 네 개의 명령어를 추가합니다. 여기서는 이러한 명령어 바로 앞에있는 피연산자 스택의 크기를 알지 못합니다. 우리는 그것이 s보다 작거나 같다는 것을 압니다. 결과적으로 리턴 명령어 전에 추가 된 코드에는 s + 4까지의 크기의 피연산자 스택이 필요할 수 있다고 말할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 최악의 시나리오는 실제로는 거의 발생하지 않습니다. 일반적인 컴파일러에서는 RETURN 앞에 피연산자 스택이 반환 값, 즉 최대 0, 1 또는 2의 크기를 포함합니다.그러나 가능한 모든 경우를 처리하기를 원한다면 최악의 시나리오 2를 사용해야합니다.visitMaxs 메서드를 다음과 같이 재정의해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void visitMaxs(int maxStack, int maxLocals) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;mv.visitMaxs(maxStack + 4, maxLocals);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;물론 최대 스택 크기에 신경 쓰지 않고 COMPUTE_MAXS 옵션을 사용하여 최적의 값을 계산하고 최악의 값을 계산하지 않을 수도 있습니다.&lt;/p&gt;
&lt;p&gt;그러나 이러한 단순한 변환의 경우 maxStack을 수동으로 업데이트하는 데 많은 비용이 들지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이제 흥미로운 질문은 :&lt;/p&gt;
&lt;p&gt;스택 맵 프레임은? 원래의 코드는 프레임을 포함하지 않았으며, 변형 된 것도 포함하지 않았습니다. 그러나 이것은 우리가 예제로 사용한 특정 코드로 인한 것입니까? 프레임을 업데이트해야하는 경우가 있습니까?&lt;/p&gt;
&lt;p&gt;대답은 '아니오'입니다.&lt;/p&gt;
&lt;p&gt;1) 삽입 된 코드는 피연산자 스택을 변경하지 않고 남겨 둡니다.&lt;/p&gt;
&lt;p&gt;2) 삽입 된 코드는 점프 명령을 포함하지 않으며&lt;/p&gt;
&lt;p&gt;3) 원래 코드의 점프 명령 또는 더 형식적으로는 제어 흐름 그래프가 수정되지 않습니다.&lt;/p&gt;
&lt;p&gt;이는 원래 프레임이 변경되지 않으며 삽입 된 코드에 대해 새 프레임을 저장해야하므로 압축 된 원래 프레임도 변경되지 않음을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이제 모든 요소를 연관된 ClassVisitor 및 MethodVisitor 하위 클래스에 함께 넣을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;public class AddTimerAdapter extends ClassVisitor {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private String owner;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private boolean isInterface;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AddTimerAdapter(ClassVisitor cv) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, cv);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override public void visit(int version, int access, String name,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;String signature, String superName, String[] interfaces) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;cv.visit(version, access, name, signature, superName, interfaces);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;owner = name;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;isInterface = (access &amp;amp; ACC_INTERFACE) != 0;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override public MethodVisitor visitMethod(int access, String name,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;String desc, String signature, String[] exceptions) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;MethodVisitor mv = cv.visitMethod(access, name, desc, signature,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;exceptions);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;if (!isInterface &amp;amp;&amp;amp; mv != null &amp;amp;&amp;amp; !name.equals(&quot;&amp;lt;init&amp;gt;&quot;)) {&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv = new AddTimerMethodAdapter(mv);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;return mv;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override public void visitEnd() {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;if (!isInterface) {&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;FieldVisitor fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, &quot;timer&quot;,&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;J&quot;, null, null);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if (fv != null) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;fv.visitEnd();&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;cv.visitEnd();&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;class AddTimerMethodAdapter extends MethodVisitor {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;public AddTimerMethodAdapter(MethodVisitor mv) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;super(ASM4, mv);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;@Override public void visitCode() {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitCode();&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitInsn(LSUB);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;@Override public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;if ((opcode &amp;gt;= IRETURN &amp;amp;&amp;amp; opcode &amp;lt;= RETURN) || opcode == ATHROW) {&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;				&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;				&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;				&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;				&lt;/span&gt;mv.visitInsn(LADD);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;				&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;mv.visitInsn(opcode);&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;@Override public void visitMaxs(int maxStack, int maxLocals) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitMaxs(maxStack &lt;b&gt;+ 4,&lt;/b&gt; maxLocals);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;클래스 어댑터는 메소드 어댑터 (생성자 제외)를 인스턴스화하는 데 사용되고, 타이머 필드를 추가하고 .메소드 어댑터에서 액세스 할 수있는 필드를 변환하여 이름을 저장합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.2.5. Statefull transformations&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이전 섹션에서 보았던 변환은 지역적이며 현재 명령어 이전에 방문한 명령어에 의존하지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;처음에 추가 된 코드는 항상 동일하며 항상 추가되며, 각 RETURN 명령 앞에 삽입 된 코드에 대해서도 마찬가지입니다.&lt;/p&gt;
&lt;p&gt;이러한 변환을 상태 비 저장 변환이라고합니다. 구현하기는 간단하지만 가장 간단한 변환 만이 속성을 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;보다 복잡한 변환을 위해서는 현재 상태 이전에 visit한 명령어에 대한 일부 상태를 기억해야합니다. 예를 들어 ICONST_0 IADD 시퀀스의 모든 발생을 제거하는 변환은 그의 빈 효과는 0을 더하는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;IADD 명령을 방문하면 마지막으로 방문한 명령이 ICONST_0 인 경우에만 명령을 제거해야합니다.&lt;/p&gt;
&lt;p&gt;이를 위해서는 메소드 어댑터 내부에 상태를 저장해야합니다. 이러한 이유로 이러한 변환을 상태 기반 변환이라고합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 예제에서 더 자세히 살펴 보겠습니다. ICONST_0을 방문하면 다음 명령어가 IADD 인 경우에만 ICONST_0을 제거해야합니다. 문제는 다음 명령어가 아직 알려지지 않았다는 것입니다. 해결 방법은이 결정을 다음 명령으로 연기하는 것입니다. IADD 인 경우 두 명령을 모두 제거하고 그렇지 않으면 ICONST_0 및 현재 명령을 내 보냅니다. 일부 명령 시퀀스를 제거하거나 대체하는 변환을 구현하려면, visitXxx Insn 메소드가 일반적인 visitInsn () 메소드를 호출하는 MethodVisitor 서브 클래스를 도입하는 것이 편리합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public abstract class PatternMethodAdapter extends MethodVisitor {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;protected final static int SEEN_NOTHING = 0;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;protected int &lt;b&gt;state&lt;/b&gt;;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public PatternMethodAdapter(int api, MethodVisitor mv) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(api, mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Overrid public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;visitInsn&lt;/b&gt;();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(opcode);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override public void visitIntInsn(int opcode, int operand) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;visitInsn&lt;/b&gt;();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitIntInsn(opcode, operand);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;...&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;protected abstract void &lt;b&gt;visitInsn&lt;/b&gt;();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Then the above transformation can be implemented like this:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class RemoveAddZeroAdapter extends PatternMethodAdapter {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private static int SEEN_ICONST_0 = 1;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public RemoveAddZeroAdapter(MethodVisitor mv) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if (state == SEEN_ICONST_0) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if (opcode == IADD) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;state = SEEN_NOTHING;&lt;/p&gt;
&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;return;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;visitInsn();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if (opcode == ICONST_0) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;state = SEEN_ICONST_0;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;return;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(opcode);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override protected void visitInsn() {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if (state == SEEN_ICONST_0) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(ICONST_0);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;state = SEEN_NOTHING;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;visitInsn (int) 메서드는 먼저 시퀀스가 검색되었는지 테스트합니다.&lt;/p&gt;
&lt;p&gt;이 경우 상태를 다시 초기화하고 즉시 반환하므로 시퀀스를 제거하는 효과가 있습니다.&lt;/p&gt;
&lt;p&gt;다른 경우에는 일반적인 visitInsn 메소드를 호출합니다.이 메소드는 마지막으로 방문한 명령어 인 경우 ICONST_0을 내 보냅니다.&lt;/p&gt;
&lt;p&gt;그리고, 현재의 명령이 ICONST_0이면, 이 명령에 대한 결정을 연기하기 위해 이 사실을 기억하고 반환합니다. 다른 모든 경우에는 현재 명령어가 다음 방문객에게 전달됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Labels and frames&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이전 섹션에서 보았 듯이 레이블과 프레임은 연관된 지시문 바로 전에 방문합니다.&lt;/p&gt;
&lt;p&gt;다른 말로하면 지시 사항과 동일한 시간에 방문하지만 지침 자체는 아닙니다. 이것은 명령 시퀀스를 감지하는 변환에 영향을 미치지 만 실제로이 영향은 장점입니다. 실제로 우리가 제거한 지침 중 하나가 점프 지침의 대상이라면 어떻게됩니까? 어떤 명령어가 ICONST_0으로 점프 할 수 있다면, 이는이 명령어를 지정하는 라벨이 있음을 의미합니다.&lt;/p&gt;
&lt;p&gt;두 명령어를 제거한 후에이 레이블은 제거 된 IADD 다음에 오는 명령어를 지정합니다.&lt;/p&gt;
&lt;p&gt;그것은 우리가 원하는 것입니다. 그러나 어떤 지시가 IADD로 점프 할 수 있다면,&lt;/p&gt;
&lt;p&gt;우리는 명령 순서를 제거 할 수 없습니다 (이 점프 전에 0이 스택에 푸시되었음을 확신 할 수 없습니다). ICONST_0과 IADD 사이에는 쉽게 감지 할 수있는 레이블이 있어야합니다. 추론은 스택 맵 프레임에서도 동일합니다. 스택 맵 프레임이 두 명령어 사이에서 방문되면이를 제거 할 수 없습니다. 두 경우 모두 레이블 및 프레임을 패턴 일치 알고리즘의 지침으로 간주하여 처리 할 수 ​​있습니다. PatternMethodAdapter에서이 작업을 수행 할 수 있습니다 (visitMaxs는 일반적인 visitInsn 메서드도 호출하므로 메서드의 끝 부분을 검색해야하는 시퀀스의 접두사로 처리하는 데 사용됩니다).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public abstract class PatternMethodAdapter extends MethodVisitor {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;...&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;@Override public void visitFrame(int type, int nLocal, Object[] local,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;int nStack, Object[] stack) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;visitInsn();&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFrame(type, nLocal, local, nStack, stack);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;@Override public void visitLabel(Label label) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;visitInsn()&lt;/b&gt;;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitLabel(label);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;@Override public void visitMaxs(int maxStack, int maxLocals) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;visitInsn();&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitMaxs(maxStack, maxLocals);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;다음 장에서 보게 될 것처럼, 컴파일 된 메소드는 예를 들어 예외 스택 추적에 사용되는 소스 파일 행 번호에 대한 정보를 포함 할 수 있습니다. 이 정보는 visitLineNumber 메소드로 방문하며 지침과 동시에 호출됩니다. 그러나 명령 시퀀스의 중간에있는 행 번호의 존재는 이를 변환하거나 제거 할 가능성에 아무런 영향을 미치지 않습니다. 해결책은 패턴 일치 알고리즘에서 완전히 무시하는 것입니다&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;A more complex example&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이전 예제는보다 복잡한 명령 시퀀스로 쉽게 일반화 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;예를 들어, 일반적으로 f = f와 같은 오타로 인해 자체 필드 할당을 제거하는 변환을 고려하십시오. 또는 바이트 코드에서 ALOAD 0 ALOAD 0 GETFIELD f PUTFIELD f.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 변환을 구현하기 전에 상태 시스템이이 시퀀스를 인식하도록 설계하는 것이 바람직합니다 (그림 3.6 참조).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;각 변환에는 조건 (현재 명령어의 값)과 작업 (굵게 표시된 명령어 시퀀스)이 표시됩니다. 예를 들어 현재 명령이 ALOAD 0이 아닌 경우 S1에서 S0 로의 전환이 발생합니다. 이 경우, 이 상태에 도달하기 위해 방문한 ALOAD 0이 방출됩니다. S2에서 자체로의 전이에 주의하십시오. 이것은 3 개 이상의 연속적인 ALOAD 0이 발견 될 때 발생합니다. 이 경우 두 개의 ALOAD 0을 방문한 상태에서 머물며 세 번째를 내 보냅니다. state machine이 발견되면 해당 메소드 어댑터 작성은 간단합니다 ((8 스위치 케이스는 다이어그램의 8 전환에 해당합니다).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 692px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991FEC365B98C7920F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991FEC365B98C7920F&quot; width=&quot;692&quot; height=&quot;399&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Figure 3.6.: State machine for ALOAD 0 ALOAD 0 GETFIELD f PUTFIELD f
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;class RemoveGetFieldPutFieldAdapter extends PatternMethodAdapter {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private final static int SEEN_ALOAD_0 = 1;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private final static int SEEN_ALOAD_0ALOAD_0 = 2;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private final static int SEEN_ALOAD_0ALOAD_0GETFIELD = 3;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private String fieldOwner;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private String fieldName;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private String fieldDesc;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public RemoveGetFieldPutFieldAdapter(MethodVisitor mv) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(mv);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitVarInsn(int opcode, int var) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;switch (state) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_NOTHING: // S0 -&amp;gt; S1&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;if (opcode == ALOAD &amp;amp;&amp;amp; var == 0) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;state = SEEN_ALOAD_0;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;return;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_ALOAD_0: // S1 -&amp;gt; S2&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;if (opcode == ALOAD &amp;amp;&amp;amp; var == 0) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;state = SEEN_ALOAD_0ALOAD_0;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;return;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_ALOAD_0ALOAD_0: // S2 -&amp;gt; S2&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if (opcode == ALOAD &amp;amp;&amp;amp; var == 0) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;return;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;visitInsn();&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitVarInsn(opcode, var);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitFieldInsn(int opcode, String owner, String name,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;String desc) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;switch (state) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;case SEEN_ALOAD_0ALOAD_0: // S2 -&amp;gt; S3&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;if (opcode == GETFIELD) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;state = SEEN_ALOAD_0ALOAD_0GETFIELD;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;fieldOwner = owner;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;fieldName = name;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;fieldDesc = desc;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;return;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -&amp;gt; S0&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;if (opcode == PUTFIELD &amp;amp;&amp;amp; name.equals(fieldName)) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;state = SEEN_NOTHING;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;return;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;visitInsn();&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(opcode, owner, name, desc);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override protected void visitInsn() {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;switch (state) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_ALOAD_0: // S1 -&amp;gt; S0&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_ALOAD_0ALOAD_0: // S2 -&amp;gt; S0&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;case SEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -&amp;gt; S0&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(ALOAD, 0);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(GETFIELD, fieldOwner, fieldName, fieldDesc);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;break;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;state = SEEN_NOTHING;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;}&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;섹션 3.2.4의 AddTimerAdapter 케이스와 같은 이유로, 이 섹션에 제시된 상태 기반 변환은 스택 맵 프레임을 변환 할 필요가 없습니다. 원래 프레임은 변환 후에도 유효합니다. 로컬 변수와 피연산자 스택 크기를 변환 할 필요조차 없습니다. 마지막으로 statefull 변환은 명령 시퀀스를 탐지하고 변환하는 변환에 국한되지 않음에 유의해야합니다. 다른 유형의 변형도 statefull입니다.&lt;/p&gt;
&lt;p&gt;예를 들어, 다음 섹션에 제시된 메소드 어댑터의 경우입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.3. Tools&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;org.objectweb.asm.commons 패키지에는 사용자 정의 어댑터를 정의하는 데 유용한 미리 정의 된 메소드 어댑터가 포함되어 있습니다. 이 섹션에서는 세 가지를 제시하고 섹션 3.2.4의 AddTimerAdapter 예제와 함께 어떻게 사용할 수 있는지 보여줍니다. 또한 이전 장에서 본 도구를 사용하여 메서드 생성 또는 변형을 쉽게 수행 할 수있는 방법을 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.3.1. Basic tools&lt;/p&gt;
&lt;p&gt;섹션 2.3에 제시된 도구는 메소드에도 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Type&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;xLOAD, xADD 또는 xRETURN과 같은 많은 바이트 코드 명령어는 적용되는 type에 따라 다릅니다. Type 클래스는 getOpcode 메소드를 제공합니다.이 메소드는이 명령어에 대해 주어진 유형에 해당하는 opcode를 가져 오는 데 사용할 수 있습니다. 이 메서드는 int 형식의 opcode를 매개 변수로 사용하고 해당 형식의 opcode를 반환합니다. 예를 들어, t.getOpcode (IMUL)는 t가 Type.FLOAT_TYPE과 같으면 FMUL을 반환합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;TraceClassVisitor&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이전 장에서 이미 제시한&amp;nbsp;이 클래스는 visit하는 클래스의 텍스트 표현을 인쇄하고, 그들의 방법의 텍스트 표현을 포함하여,이 장에서 사용 된 것과 매우 유사한 형태로 나타납니다. 따라서 변환 체인의 어느 지점에서 생성되거나 변환 된 메서드의 내용을 추적하는 데 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;예 :&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;java -classpath asm.jar:asm-util.jar \&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;org.objectweb.asm.util.TraceClassVisitor \&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;java.lang.Void&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;prints:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;// class version 49.0 (49)&lt;/p&gt;
&lt;p&gt;// access flags 49&lt;/p&gt;
&lt;p&gt;public final class java/lang/Void {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;// access flags 25&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;// signature Ljava/lang/Class&amp;lt;Ljava/lang/Void;&amp;gt;;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;// declaration: java.lang.Class&amp;lt;java.lang.Void&amp;gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public final static Ljava/lang/Class; TYPE&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;// access flags 2&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;private &amp;lt;init&amp;gt;()V&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;ALOAD 0&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;INVOKESPECIAL java/lang/Object.&amp;lt;init&amp;gt; ()V&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;RETURN&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;MAXSTACK = 1&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;MAXLOCALS = 1&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;// access flags 8&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;static &amp;lt;clinit&amp;gt;()V&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;LDC &quot;void&quot;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;INVOKESTATIC java/lang/Class.getPrimitiveClass (...)...&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;PUTSTATIC java/lang/Void.TYPE : Ljava/lang/Class;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;RETURN&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;MAXSTACK = 1&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;MAXLOCALS = 0&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;이것은 정적 블록 static {...}을 생성하는 방법, 즉 &amp;lt;clinit&amp;gt; 메소드 (CLass INITializer의 경우)를 생성하는 방법을 보여줍니다.&lt;/div&gt;&lt;div&gt;체인의 모든 부분을 추적하는 대신 체인의 특정 지점에서 단일 메서드의 내용을 추적하려는 경우 TraceClassVisitor 대신 TraceMethodVisitor를 사용할 수 있습니다. (이 경우에는 백엔드를 명시 적으로 지정해야하며 여기서 Textifier를 사용합니다.)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;public MethodVisitor visitMethod(int access, String name,String desc, String signature, String[] exceptions) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;if (debug &amp;amp;&amp;amp; mv != null &amp;amp;&amp;amp; ...) { // if this method must be traced&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;Printer p = new Textifier(ASM4) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;@Override public void visitMethodEnd() {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;				&lt;/span&gt;print(aPrintWriter); // print it after it has been visited&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;};&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;mv = new TraceMethodVisitor(mv, p);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;return new MyMethodAdapter(mv);&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;이 코드는 MyMethodAdapter로 변환 한 후 메서드를 인쇄합니다.&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;CheckClassAdapter&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 장은 이전 장에서 이미 제시되었지만,&lt;/p&gt;
&lt;p&gt;ClassVisitor 메소드가 적절한 순서로 호출되고 유효한 인수로 호출되는지 확인하며 MethodVisitor 메소드에 대해서도 동일하게 수행합니다.&lt;/p&gt;
&lt;p&gt;따라서 변환 체인의 어느 지점에서나 MethodVisitor API가 올바르게 사용되었는지 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;TraceMethodVisitor와 마찬가지로 CheckMethodAdapter 클래스를 사용하여 모든 클래스를 검사하는 대신 단일 메서드를 검사 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;public MethodVisitor visitMethod(int access, String name,String desc, String signature, String[] exceptions) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;MethodVisitor mv = cv.visitMethod(access, name, desc, signature,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;exceptions);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;if (debug &amp;amp;&amp;amp; mv != null &amp;amp;&amp;amp; ...) { // if this method must be checked&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv = new CheckMethodAdapter(mv);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;return new MyMethodAdapter(mv);&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 코드는 MyMethodAdapter가 MethodVisitor API를 올바르게 사용하는지 확인합니다. 그러나, 이 어댑터는 바이트 코드가 올바른지 확인하지 않습니다. 예를 들어 ISTORE 1 ALOAD 1이 유효하지 않음을 감지하지 못합니다. 실제로 CheckMethodAdapter (Javadoc 참조)의 다른 생성자를 사용하고 visitMaxs에 유효한 maxStack 및 maxLocals 인수를 제공하면 이러한 종류의 오류가 감지 될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ASMifier&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이전 장에서 이미 설명한이 클래스는 메서드 내용과 함께 작동합니다. 이것은 ASM을 사용하여 컴파일 된 코드를 생성하는 방법을 알기 위해 사용할 수 있습니다. Java에서 해당 소스 코드를 작성하고 javac로 컴파일 한 다음 ASMifier를 사용하여이 클래스를 방문하십시오. 소스 코드에 해당하는 바이트 코드를 생성하는 ASM 코드가 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.3.2. AnalyzerAdapter&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 메소드 어댑터는 visitFrame에서 방문한 프레임을 기반으로 각 명령어 앞에 스택 맵 프레임을 계산합니다. 사실, 3.1.5 절에서 설명한 것처럼 visitFrame은 공간을 절약하기 위해 메소드의 일부 특정 지침보다 먼저 호출되기 때문에 &quot;다른 프레임은이 프레임에서 쉽고 빠르게 추정 할 수 있습니다&quot;. 이것은 이 어댑터가하는 것입니다. 물론 사전 계산 된 스택 맵 프레임을 포함하는 클래스, 즉 Java 6 이상으로 컴파일 된 (또는 이전에 COMPUTE_FRAMES 옵션을 사용하여 ASM 어댑터가있는 Java 6으로 업그레이드 된) 경우에만 작동합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;AddTimerAdapter 예제의 경우이 어댑터를 사용하여 RETURN 명령어 바로 앞에있는 피연산자 스택의 크기를 가져올 수 있으므로 visitMax에서 maxStack에 대한 최적의 변환 값을 계산할 수 있습니다 (실제로이 방법은 실제로 권장되지 않습니다. 왜냐하면 COMPUTE_MAXS를 사용하는 것보다 훨씬 효율적이지 않기 때문입니다.)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;class AddTimerMethodAdapter2 extends &lt;b&gt;AnalyzerAdapter &lt;/b&gt;{&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;b&gt;private int maxStack;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AddTimerMethodAdapter2(String owner, int access,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;String name, String desc, MethodVisitor mv) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, owner, access, name, desc, mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitCode() {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(LSUB);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;maxStack &lt;/b&gt;= 4;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if ((opcode &amp;gt;= IRETURN &amp;amp;&amp;amp; opcode &amp;lt;= RETURN) || opcode == ATHROW) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitInsn(LADD);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;maxStack = Math.max(maxStack, stack.size() + 4);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitInsn(opcode);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitMaxs(int maxStack, int maxLocals) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitMaxs(Math.max(this.maxStack, maxStack), maxLocals);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;스택 필드는 AnalyzerAdapter 클래스에 정의되며,&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;피연산자 스택에 유형을 포함합니다. 보다 정확하게는 visitXxx Insn에서 오버라이드 된 메소드가 호출되기 전에,&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;이 목록에는이 명령어 바로 앞에있는 피연산자 스택의 상태가 포함됩니다. 스택 필드가 올바르게 업데이트되도록 오버라이드 된 메서드를 호출해야합니다 (따라서 원본 코드에서 mv 대신 super를 사용).&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;슈퍼 클래스의 메소드를 호출하여 새 명령어를 삽입 할 수도 있습니다.이 명령어의 프레임은 AnalyzerAdapter에 의해 계산됩니다. 또한이 어댑터는 계산 된 프레임을 기반으로 visitMaxs의 인수를 업데이트하므로 직접 업데이트하지 않아도됩니다.&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;class AddTimerMethodAdapter3 extends AnalyzerAdapter {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AddTimerMethodAdapter3(String owner, int access,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;String name, String desc, MethodVisitor mv) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, owner, access, name, desc, mv);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitCode() {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitCode();&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitInsn(LSUB);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitInsn(int opcode) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if ((opcode &amp;gt;= IRETURN &amp;amp;&amp;amp; opcode &amp;lt;= RETURN) || opcode == ATHROW) {&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitInsn(LADD);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&lt;b&gt;super&lt;/b&gt;.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitInsn(opcode);&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.3.3. LocalVariablesSorter&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 메소드 어댑터는 메소드에서 사용 된 로컬 변수를이 메소드에 나타나는 순서대로 번호를 재 지정합니다.&lt;/p&gt;
&lt;p&gt;예를 들어 두 개의 매개 변수가있는 메소드에서 인덱스가 3보다 크거나 같은 첫 번째 로컬 변수는 처음 세 개의 로컬 변수가 this와 두 개의 메소드 매개 변수에 해당하므로 변경할 수 없습니다 - 할당됩니다. 인덱스 3, 두 번째 인덱스 4 등이 할당됩니다.&lt;/p&gt;
&lt;p&gt;이 어댑터는 메소드에 새 로컬 변수를 삽입 할 때 유용합니다. 이 어댑터가 없으면 기존의 모든 지역 변수 뒤에 새로운 지역 변수를 추가해야하지만, 아쉽게도 숫자가 visitMaxs의 메소드 끝까지 알려지지 않습니다.&lt;/p&gt;
&lt;p&gt;이 어댑터를 사용하는 방법을 보여주기 위해 로컬 변수를 사용하여 AddTimerAdapter를 구현한다고 가정 해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class C {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public static long timer;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void m() throws Exception {&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;long t = System.currentTimeMillis();&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;Thread.sleep(100);&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;timer += System.currentTimeMillis() - t;&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;LocalVariablesSorter를 확장하고이 클래스에 정의 된 newLocal 메서드를 사용하면 쉽게 수행 할 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;class AddTimerMethodAdapter4 extends LocalVariablesSorter {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private int time;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AddTimerMethodAdapter4(int access, String desc,&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;MethodVisitor mv) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, access, desc, mv);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitCode() {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitCode();&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;time = newLocal(Type.LONG_TYPE);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitVarInsn(LSTORE, time);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitInsn(int opcode) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if ((opcode &amp;gt;= IRETURN &amp;amp;&amp;amp; opcode &amp;lt;= RETURN) || opcode == ATHROW) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(LLOAD, time);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitInsn(LSUB);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitInsn(LADD);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitInsn(opcode);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitMaxs(int maxStack, int maxLocals) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitMaxs(maxStack + 4, maxLocals);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;로컬 변수가 번호가 매겨지면 메소드에 연결된 원본 프레임이 유효하지 않게되고 새로운 로컬 변수가 삽입되면 fortiori가됩니다. 다행히도이 프레임을 처음부터 다시 계산하는 것을 피할 수 있습니다. 실제로 프레임을 추가하거나 제거해야하며 변형 된 메서드의 프레임을 가져 오려면 원래 프레임의 로컬 변수 내용을 재정렬하는 것으로 충분합니다. LocalVariablesSorter는 자동으로 처리합니다. 메소드 어댑터 중 하나에 대해 점진적 스택 맵 프레임 업데이트를 수행해야하는 경우이 클래스의 소스에서 영감을 얻을 수 있습니다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;위에서 볼 수 있듯이 로컬 변수를 사용한다고해서 maxStack에 대한 최악의 경우에 대한이 클래스의 원래 버전에서의 문제는 해결되지 않습니다. AnalyzerAdapter를 사용하여이를 해결하려면 LocalVariablesSorter 외에도 상속을 통해 위임하지 않고 위임을 통해 이러한 어댑터를 사용해야합니다. (다중 상속이 불가능하기 때문에) :&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;class AddTimerMethodAdapter5 extends MethodVisitor {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public LocalVariablesSorter lvs;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AnalyzerAdapter aa;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private int time;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;private int maxStack;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AddTimerMethodAdapter5(MethodVisitor mv) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, mv);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitCode() {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitCode();&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;time = lvs.newLocal(Type.LONG_TYPE);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitVarInsn(LSTORE, time);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;maxStack = 4;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitInsn(int opcode) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;if ((opcode &amp;gt;= IRETURN &amp;amp;&amp;amp; opcode &amp;lt;= RETURN) || opcode == ATHROW) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitVarInsn(LLOAD, time);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitInsn(LSUB);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitInsn(LADD);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;			&lt;/span&gt;maxStack = Math.max(aa.stack.size() + 4, maxStack);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(opcode);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitMaxs(int maxStack, int maxLocals) {&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitMaxs(Math.max(this.maxStack, maxStack), maxLocals);&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;이 어댑터를 사용하려면 LocalVariablesSorter를 어댑터에 연결해야하는 AnalyzerAdapter에 연결해야합니다. 첫 번째 어댑터는 로컬 변수를 정렬하고 이에 따라 프레임을 업데이트하며, 분석기 어댑터는 이전 어댑터에서 수행 된 번호 매김을 고려하여 중간 프레임을 계산합니다 어댑터는 이러한 번호가 다시 매겨진 중간 프레임에 액세스 할 수 있습니다. 이 체인은 visitMethod에서 다음과 같이 구성 할 수 있습니다.&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;mv = cv.visitMethod(access, name, desc, signature, exceptions);&lt;/p&gt;
&lt;p&gt;if (!isInterface &amp;amp;&amp;amp; mv != null &amp;amp;&amp;amp; !name.equals(&quot;&amp;lt;init&amp;gt;&quot;)) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;AddTimerMethodAdapter5 at = new AddTimerMethodAdapter5(mv);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;at.aa = new AnalyzerAdapter(owner, access, name, desc, at);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;at.lvs = new LocalVariablesSorter(access, desc, at.aa);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;return at.lvs;&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.3.4. AdviceAdapter&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;이 메소드 어댑터는 메소드 시작과 모든 RETURN 또는 ATHROW 명령어 바로 앞에 코드를 삽입하는 데 사용할 수있는 추상 클래스입니다.&lt;/p&gt;
&lt;p&gt;주된 장점은 생성자의 처음에 코드를 삽입하지 말고 상위 생성자를 호출 한 후 생성자에 대해서도 작동한다는 것입니다. 사실이 어댑터의 대부분의 코드는이 수퍼 생성자 호출을 탐지하는 데 사용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;3.2.4 절의 AddTimerAdapter 클래스를 자세히 살펴보면,&lt;/p&gt;
&lt;p&gt;이 문제 때문에 AddTimerMethodAdapter가 생성자에 사용되지 않는다는 것을 알 수 있습니다. AdviceAdapter를 상속하면이 메소드 어댑터가 생성자에서도 작동하도록 향상 될 수 있습니다 (AdviceAdapter는 LocalVariablesSorter에서 상속되므로 로컬 변수도 쉽게 사용할 수 있습니다).&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;class AddTimerMethodAdapter6 extends AdviceAdapter {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public AddTimerMethodAdapter6(int access, String name, String desc,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;MethodVisitor mv) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super(ASM4, mv, access, name, desc);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;protected void onMethodEnter() {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(LSUB);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;protected void onMethodExit(int opcode) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(GETSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitMethodInsn(INVOKESTATIC, &quot;java/lang/System&quot;,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;&quot;currentTimeMillis&quot;, &quot;()J&quot;);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitInsn(LADD);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;mv.visitFieldInsn(PUTSTATIC, owner, &quot;timer&quot;, &quot;J&quot;);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;@Override&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;public void visitMaxs(int maxStack, int maxLocals) {&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;		&lt;/span&gt;super.visitMaxs(maxStack + 4, maxLocals);&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;white-space:pre&quot;&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>ASM</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/7</guid>
      <comments>https://yimoc.tistory.com/7#entry7comment</comments>
      <pubDate>Tue, 11 Sep 2018 16:04:48 +0900</pubDate>
    </item>
    <item>
      <title>ASM User Guide 번역 2</title>
      <link>https://yimoc.tistory.com/6</link>
      <description>&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://yimoc.tistory.com/5&quot; target=&quot;_blank&quot;&gt;2018/09/10 - [ASM] - ASM User Guide 번역 1&lt;/a&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Part I.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Core API&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;2. Classes&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 장에서는 코어 ASM API를 사용하여 컴파일 된 Java 클래스를 생성하고 변환하는 방법을 설명합니다. 그것은 컴파일 된 클래스의 표현부터 시작하여 많은 예제를 사용하여 해당하는 ASM 인터페이스, 컴포넌트 및 툴을 생성 및 변환하는 도구를 제공합니다. 메소드, 주석 및 제네릭의 내용은 다음 장에서 설명합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.1. Structure&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.1.1. Overview&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;컴파일 된 클래스의 전체 구조는 아주 간단합니다. 실제로 컴파일 된 클래스는 기본적으로 컴파일 된 응용 프로그램과 달리 구조 정보와 소스 코드의 거의 모든 심볼을 유지합니다. 실제로 컴파일 된 클래스에는 다음이 포함됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• modifier (예 : public 또는 private), name, super class, interface 및 클래스의 annotation을 설명하는 section&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 클래스에서 선언 된 field 당 하나의 section. 각 섹션은 필드의 modifier , name , type&amp;nbsp;및 annotation을 설명합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 클래스에서 선언 된 메서드 및 생성자 당 하나의 section. 각 섹션은 modifier, name, return 및 parameter type 및 메소드의 annotation을 설명합니다. 또한 컴파일 된 코드를 포함하고 있으며 Java 바이트 코드 명령어의 시퀀스 형태로 구현됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;그러나 소스 클래스와 컴파일 된 클래스에는 몇 가지 차이점이 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 컴파일 된 클래스는 하나의 클래스만 설명하지만 소스 파일에는 여러 클래스가 포함될 수 있습니다. 예를 들어, 내부 클래스가 하나 인 클래스를 설명하는 소스 파일은 메인 클래스 용과 내부 클래스 용의 두 클래스 파일로 컴파일됩니다. 그러나 메인 클래스 파일에는 내부 클래스에 대한 참조가 포함되어 있습니다. 클래스 및 메서드 내부에 정의 된 내부 클래스에는 메서드를 둘러싼 참조가 포함되어 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 컴파일 된 클래스에는 물론 주석이 포함되지 않습니다. 하지만 , 클래스, 필드, 메서드 및 코드 속성(attribute)를 포함합니다. 이것은 이러한 요소와 연관시키는 데 사용할 수있습니다. Java 5에서 주석을 도입 한 이후로 동일한 목적으로 사용될 수있는&amp;nbsp;속성은 거의 쓸모 없게 되었습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 컴파일 된 클래스에는 패키지 및 가져 오기 섹션이 없으므로 모든 Type의 이름은&amp;nbsp;fully Qualified합니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;매우 중요한 또 다른 구조적 차이점은 컴파일 된 클래스가 &lt;i&gt;상수 풀(Constant pool)&amp;nbsp;&lt;/i&gt;섹션을 포함한다는 것입니다. 이 풀은 클래스에 나타나는 모든 숫자, 문자열 및 type&amp;nbsp;상수를 포함하는 배열입니다. 이 상수는 상수 풀 섹션에서 한 번만 정의되며 클래스 파일의 다른 모든 섹션에서 해당 인덱스로 참조됩니다. ASM은 상수 풀과 관련된 모든 세부 사항을 숨기므로 바라기를 귀찮게 생각하지 않아도됩니다. 그림 2.1은 컴파일 된 클래스의 전체 구조를 요약 한 것입니다. 정확한 구조는 Java Virtual Machine Specification, 섹션 4에 설명되어 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table class=&quot;txc-table&quot; width=&quot;784&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; border=&quot;0&quot; style=&quot;border:none;border-collapse:collapse;;font-family:&quot; 맑은=&quot;&quot; 고딕&quot;,=&quot;&quot; sans-serif;font-size:13px&quot;=&quot;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 783px; height: 24px; border-width: 1px; border-style: solid; border-color: rgb(204, 204, 204);&quot; colspan=&quot;2&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;Modifiers, name, super class, interfaces&amp;nbsp;&lt;/p&gt;&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 783px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;2&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;Constant pool: numeric, string and type constants&lt;/p&gt;&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 783px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;2&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;Source file name (optional)&lt;/p&gt;&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 783px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;2&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;Enclosing class reference
&lt;/p&gt;&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 783px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;2&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;Annotation*
&lt;/p&gt;&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 783px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;2&quot; rowspan=&quot;1&quot;&gt;&lt;p&gt;&amp;nbsp;Attribute*&lt;/p&gt;&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 213px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Inner class*&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Name
&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 213px; height: 72px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;1&quot; rowspan=&quot;3&quot;&gt;&lt;p&gt;&amp;nbsp;Field*&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Modifiers, name, type&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Annotation*
&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Attribute*&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 213px; height: 96px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot; colspan=&quot;1&quot; rowspan=&quot;4&quot;&gt;&lt;p&gt;&amp;nbsp;Method*&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Modifiers, name, return and parameter types&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Annotation*&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Attribute*&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 570px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Compiled code&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.1.: Overall structure of a compiled class (* means zero or more)&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;또 다른 중요한 차이점은 Java 유형은 컴파일 된 클래스와 소스 클래스에서 다르게 표현된다는 것입니다. 다음 섹션에서는 컴파일 된 클래스의 표현에 대해 설명합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.1.2. Internal names&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;많은 경우, Type은&amp;nbsp;클래스 또는 인터페이스 Type으로 제한됩니다. 예를 들어, 클래스의 수퍼 클래스, 클래스에 의해 구현 된 인터페이스 또는 메소드에 의해 던져지는 예외는 기본 유형이나 배열 유형 일 수 없으며 반드시 클래스 또는 인터페이스 Type입니다. 이러한 Type은 내부 이름이있는 컴파일 된 클래스로 표시됩니다. 클래스의 내부 이름은이 클래스의 fully qualified된 이름이며 점(.)이 슬래시(/)로 대체됩니다. 예를 들어 String의 내부 이름은 java / lang / String입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.1.3. Type descriptors&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;내부 이름은 클래스 또는 인터페이스 Type으로 제한되는 Type에만 사용됩니다. 필드 Type과 같은 다른 모든 상황에서는&amp;nbsp;Java Type은 type descriptor(see Figure 2.2)가있는 컴파일 된 클래스로 표시됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table class=&quot;txc-table&quot; width=&quot;784&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; border=&quot;0&quot; style=&quot;border:none;border-collapse:collapse;;font-family:&quot; 맑은=&quot;&quot; 고딕&quot;,=&quot;&quot; sans-serif;font-size:13px&quot;=&quot;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-width: 1px; border-style: solid; border-color: rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp; Java type&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Type descriptor&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;boolean&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Z&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;char&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;C&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;byte&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;B&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;short&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;S&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;int&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;I&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;float&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;F&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;long&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;J&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;double&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;D&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Object&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Ljava/lang/Object;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;int[]&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;[I&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width: 391px; height: 24px; border-bottom: 1px solid rgb(204, 204, 204); border-right: 1px solid rgb(204, 204, 204); border-left: 1px solid rgb(204, 204, 204);&quot;&gt;&lt;p&gt;&amp;nbsp;Object[][]&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&amp;nbsp;[[Ljava/lang/Object;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.2.: Type descriptors of some Java types&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;primitive type의 descriptor는 Single character로 표기됩니다:&amp;nbsp;boolean 형의 경우는 Z, char의 경우는 C, byte 형의 경우는 B, int의 경우는 I, float의 경우는 F, long의 경우는 J, double의 경우는 D입니다. 클래스 타입의 기술자는이 클래스의 내부 이름이며 L이 앞에오고 세미콜론이 뒤 따른다. 예를 들어 String의 유형 설명자는 Ljava / lang / String입니다. 마지막으로 배열 타입의 기술자는 대괄호 뒤에 배열 원소 타입의 기술자가옵니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.1.4. Method descriptors&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Method descriptors는 Type descriptor의 List입니다. 하나의 Type Descriptor는 메소드의 parameter type과 return type을 단일 문자로 설명되어있습니다. Method descriptor는 왼쪽 괄호로 시작하여 각 형식 매개 변수의 type descriptor, 이어서 오른쪽 괄호, 그 다음에 반환 유형의 유형 설명자, 혹은 void를 반환하면 V로 시작합니다 (메소드 설명자에는 메소드의 이름 또는 인수 이름을 포함하지 않는다)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;table class=&quot;txc-table&quot; width=&quot;784&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; border=&quot;0&quot; style=&quot;border:none;border-collapse:collapse;;font-family:&quot; 맑은=&quot;&quot; 고딕&quot;,=&quot;&quot; sans-serif;font-size:13px&quot;=&quot;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Method declaration in source file&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-top:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Method descriptor&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;void m(int i, float f)&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;(IF)V&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;int m(Object o)&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;(Ljava/lang/Object;)I&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;int[] m(int i, String s)&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;(ILjava/lang/String;)[I&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;border-left:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;Object m(int[] i)&lt;/p&gt;&lt;/td&gt;
&lt;td style=&quot;width:392;height:24;border-bottom:1px solid #ccc;border-right:1px solid #ccc;;&quot;&gt;&lt;p&gt;&amp;nbsp;([I)Ljava/lang/Object;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.3.: Sample method descriptors&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;타입 디스크립터가 어떻게 작동하는지 알게되면, 디스크립터를 이해하는 것이 쉽습니다. 예를 들어 (I)는 int 형의 하나의 인수를 취하고 int를 리턴하는 메소드를 설명합니다.&amp;nbsp;&lt;span style=&quot;text-align: center;&quot;&gt;Figure&amp;nbsp;&lt;/span&gt;&amp;nbsp;2.3은 몇 가지 메소드 설명자 예제를 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2. Interfaces and components&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2.1. Presentation&lt;/p&gt;&lt;p&gt;.&lt;/p&gt;&lt;p&gt;컴파일 된 클래스를 생성하고 변환하는 ASM API는 ClassVisitor 추상 클래스를 기반으로합니다 (Figure&amp;nbsp;2.4 참조). 이 클래스의 각 메소드는 같은 이름의 클래스 파일 구조 섹션에 해당합니다.(그림 2.1 참조). &lt;i&gt;&amp;gt; visitSource() 가 Source 구조에 해당한다.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;단순 섹션은 인수가 내용을 설명하고 void를 반환하는 단일 메소드 호출로 찾습니다.&lt;i&gt;&amp;gt;visit으로 '방문하다'보단 '찾다'란 의미가 강하다. 다만 함수 이름이 visit임으로 이렇게 사용하는듯하다.&lt;/i&gt;&amp;nbsp;콘텐츠가 임의의 길이와 복잡성을 가질 수있는 섹션은 보조 visitor&amp;nbsp;클래스를 반환하는 초기 메서드 호출을 통해 찾습니다.&amp;nbsp; visitAnnotation, visitField 및 visitMethod 메서드의 경우는 각각 AnnotationVisitor, FieldVisitor 및 MethodVisitor를 반환한다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 동일한 원칙이 보조 클래스에 대해 재귀적으로 사용됩니다. 예를 들어 FieldVisitor 추상 클래스 (그림 2.5 참조)의 각 메소드는 동일한 이름의 클래스 파일 하위 구조에 해당하며 visitAnnotation은 ClassVisitor 에서처럼 보조 AnnotationVisitor를 반환합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;public abstract class &lt;b&gt;ClassVisitor&lt;/b&gt; {&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public ClassVisitor(int api);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public ClassVisitor(int api, ClassVisitor cv);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void &lt;b&gt;visit&lt;/b&gt;(int version, int access, String name,
String signature, String superName, String[] interfaces);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void &lt;b&gt;visitSource&lt;/b&gt;(String source, String debug);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void &lt;b&gt;visitOuterClass&lt;/b&gt;(String owner, String name, String desc);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;AnnotationVisitor &lt;b&gt;visitAnnotation&lt;/b&gt;(String desc, boolean visible);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void &lt;b&gt;visitAttribute&lt;/b&gt;(Attribute attr);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void &lt;b&gt;visitInnerClass&lt;/b&gt;(String name, String outerName,
String innerName, int access);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public FieldVisitor &lt;b&gt;visitField&lt;/b&gt;(int access, String name, String desc,
String signature, Object value);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public MethodVisitor &lt;b&gt;visitMethod&lt;/b&gt;(int access, String name, String desc,
String signature, String[] exceptions);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;void &lt;b&gt;visitEnd&lt;/b&gt;();&amp;nbsp;&lt;/p&gt;&lt;p&gt;}&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.4.: The ClassVisitor class&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;public abstract class FieldVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public FieldVisitor(int api);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public FieldVisitor(int api, FieldVisitor fv);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public AnnotationVisitor &lt;b&gt;visitAnnotation&lt;/b&gt;(String desc, boolean visible);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void &lt;b&gt;visitAttribute&lt;/b&gt;(Attribute attr);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public void visitEnd();&lt;/p&gt;&lt;p&gt;}&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.5.: The FieldVisitor class&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이러한 보조 방문자의 생성 및 사용법은 다음 장에서 설명합니다. 실제로 이 장은 ClassVisitor 클래스만으로 해결할 수있는 간단한 문제로 제한됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ClassVisitor 클래스의 메서드는이 클래스의 JavaDoc에 지정된, 다음과 같은 순서로 호출해야합니다 :&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;즉, visit를 먼저 호출 한 다음,&amp;nbsp; visitSource를 한 번만 호출 한 다음,&amp;nbsp; visitAuterClass를 한 번만 호출하고 , 그 다음에 임의의 순서로&amp;nbsp;&lt;/font&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;visitAnnotation 및 visitAttribute를&amp;nbsp;&lt;/span&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;호출하고,&amp;nbsp;다음 visitInnerClass, visitField 및 visitMethod를&amp;nbsp;&lt;/font&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;임의의 순서로 호출 할 수 있고&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;visitEnd를 한 번 호출하여 종료되었습니다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;ASM은&amp;nbsp;&lt;/font&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;ClassVisitor API를 기반으로 &lt;/span&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;클래스를 생성하고 변환하는 세 가지 핵심 컴포넌트를 제공합니다.&amp;nbsp;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;•&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;ClassReader 클래스는 바이트 배열된 컴파일 된 클래스를 파싱하고 인수로 전달 된 ClassVisitor 인스턴스의 해당 visitXxx 메서드를 accept 메서드로 호출합니다. 이벤트 프로듀서로 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;• ClassWriter 클래스는 컴파일 된 클래스를 직접 바이너리 형식으로 빌드하는 ClassVisitor 추상 클래스의 하위 클래스입니다.&amp;nbsp;toByteArray 메소드로 검색 할 수있는 컴파일 된 클래스를 포함하는 바이트 배열을 출력으로 생성합니다.&amp;nbsp;&lt;/font&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;이벤트 소비자로 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;• ClassVisitor 클래스는 수신 한 모든 메서드 호출을 다른 ClassVisitor 인스턴스에 위임합니다. 이벤트 필터로 볼 수 있습니다.&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;다음 섹션에서는 이러한 구성 요소로&amp;nbsp;&lt;/font&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;어떻게 클래스를 생성하고 변환하는지&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;구체적인 예를 제시합니다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;2.2.2. Parsing classes&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;기존 클래스를 구문 분석하는 데 필요한 유일한 구성 요소는 ClassReader 구성 요소입니다. 이것을 설명하기위한 예를 들어 봅시다. javap 도구와 비슷한 방법으로 클래스의 내용을 인쇄하고 싶다고 가정 해 보겠습니다. 첫 번째 단계는 방문하는 클래스에 대한 정보를 인쇄하는 ClassVisitor 클래스의 하위 클래스를 작성하는 것입니다.&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font face=&quot;Batang, 바탕&quot;&gt;다음은 지나치게 단순화 된 구현입니다.&lt;/font&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;다.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class ClassPrinter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public ClassPrinter() {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;System.out.println(name + &quot; extends &quot; + superName + &quot; {&quot;);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitSource(String source, String debug) {
}&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitOuterClass(String owner, String name, String desc) {
}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public AnnotationVisitor visitAnnotation(String desc,
boolean visible) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;return null;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitAttribute(Attribute attr) {
}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;System.out.println(&quot; &quot; + desc + &quot; &quot; + name);&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;return null;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;System.out.println(&quot; &quot; + name + desc);
return null;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitEnd() {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;System.out.println(&quot;}&quot;);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;두 번째 단계는 이 ClassPrinter를 ClassReader 구성 요소와 결합하여 ClassReader가 생성 한 이벤트가 ClassPrinter에서 사용되도록합니다.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Batang, 바탕;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassPrinter cp = new ClassPrinter();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassReader cr = new ClassReader(&quot;java.lang.Runnable&quot;);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cr.accept(cp, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;두 번째 행은 ClassReader를 만들어 Runnable 클래스를 구문 분석합니다. 마지막 줄에서 호출 된 accept 메소드는 Runnable 클래스 바이트 코드를 파싱하고 cp에서 해당 ClassVisitor 메소드를 호출합니다. 결과는 다음과 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;java/lang/Runnable extends java/lang/Object {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;run()V&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ClassReader 인스턴스를 구성하는 방법은 여러 가지가 있습니다. 읽어야하는 클래스는 이름, 위와 같이 또는 값, 바이트 배열 또는 InputStream으로 지정할 수 있습니다. 클래스의 내용을 읽어 들이기위한 입력 스트림은, 다음과 같이 ClassLoader의 getResourceAsStream 메소드를 사용해 취득 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cl.getResourceAsStream(classname.replace(’.’, ’/’) + &quot;.class&quot;);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2.3. Generating classes&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;클래스를 생성하는 데 필요한 유일한 구성 요소는 ClassWriter 구성 요소입니다. 이것을 설명하기위한 예를 들어 봅시다. 다음 인터페이스를 고려하십시오.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;package pkg;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public interface Comparable extends Mesurable {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;int LESS = -1;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;int EQUAL = 0;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;int GREATER = 1;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;int compareTo(Object o);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ClassVisitor에 대한 6 가지 메소드 호출로 생성 될 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = &lt;b&gt;new ClassWriter(0)&lt;/b&gt;;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cw.&lt;b&gt;visit&lt;/b&gt;(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&quot;pkg/Comparable&quot;, null, &quot;java/lang/Object&quot;,
new String[] { &quot;pkg/Mesurable&quot; });&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cw.&lt;b&gt;visitField&lt;/b&gt;(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, &quot;LESS&quot;, &quot;I&quot;,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;null, new Integer(-1)).visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cw.&lt;b&gt;visitField&lt;/b&gt;(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, &quot;EQUAL&quot;, &quot;I&quot;,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;null, new Integer(0)).visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cw.&lt;b&gt;visitField&lt;/b&gt;(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, &quot;GREATER&quot;, &quot;I&quot;,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;null, new Integer(1)).visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cw.&lt;b&gt;visitMethod&lt;/b&gt;(ACC_PUBLIC + ACC_ABSTRACT, &quot;compareTo&quot;,
&quot;(Ljava/lang/Object;)I&quot;,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;null, null).visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cw.&lt;b&gt;visitEnd&lt;/b&gt;();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b = cw.&lt;b&gt;toByteArray&lt;/b&gt;();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;첫 번째 줄은 클래스의 바이트 배열 표현을 실제로 작성하는 ClassWriter 인스턴스를 만듭니다 (생성자 인수는 다음 장에서 설명합니다).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;visit&lt;/b&gt; 메소드를 호출하여&amp;nbsp;클래스 헤더를 정의합니다. V1_5 인수는 다른 모든 ASM 상수와 마찬가지로 ASM Opcode 인터페이스에 정의 된 상수입니다. 클래스 버전 인 Java 1.5를 지정합니다. ACC_XXX 상수는 Java 제어자(modifier)에 해당하는 플래그입니다. 여기에서는 클래스가 인터페이스이고 public과 abstract(인스턴스화 될 수 없기 때문에)임을 지정합니다. 다음 인수는 클래스 이름을 내부 형식으로 지정합니다 (2.1.2 절 참조). 컴파일 된 클래스에는 패키지 또는 import 섹션이 없으므로 모든 클래스 이름을 fully qualified해야합니다. 다음 인수는 제네릭에 해당합니다 (4.1 절 참조). 이 경우에는 인터페이스가 type변수에 의해 매개 변수화되지 않았기 때문에 null입니다. 다섯 번째 인수는 내부 형식 (객체 클래스에서 암시 적으로 상속 된 인터페이스 클래스)의 수퍼 클래스입니다. 마지막 인수는 내부 이름으로 기술한 extends된 인터페이스들의 배열입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;다음 세 번의 &lt;b&gt;visitField &lt;/b&gt;호출은 비슷하며 세 인터페이스 필드를 정의하는 데 사용됩니다. 첫 번째 인수는 Java 제어자에 해당하는 플래그 집합입니다. 여기서 필드는 public, final 및 static으로 지정합니다. 두 번째 인수는 소스 코드에 나타나는대로 필드의 이름입니다. 세 번째 인수는 type descriptor&amp;nbsp;양식의 필드 유형입니다. 여기서 필드는 int 필드이며, 그 기술자는 I입니다. 네 번째 인수는 제네릭에 해당합니다. 이 경우 필드 유형이 제네릭을 사용하지 않기 때문에 null입니다. 마지막 인수는 필드의 상수 값입니다.이 인수는 진정한 상수 필드, 즉 final static 필드에만 사용해야합니다. 다른 필드의 경우, 널 (NULL)이어야합니다. 여기에 annotation이 없으므로 반환 된 FieldVisitor 후에&amp;nbsp;visitEnd 메서드를 호출합니다. 즉, 해당 visitAnnotation 또는 visitAttribute 메서드를 호출하지 않습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;마지막으로 visitEnd에 대한 마지막 호출은 클래스가 완료되었음을 cw에 알리고 toByteArray에 대한 호출을 사용하여이를 바이트 배열로 검색하는 데 사용됩니다&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Using generated classes&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이전 바이트 배열은 나중에 사용할 수 있도록 Comparable.class 파일에 저장할 수 있습니다. 다른 방법으로는 ClassLoader를 사용하여 동적으로 로드 할 수 있습니다. 한 가지 방법은 public인 defineClass 메소드를 가진 ClassLoader 서브 클래스를 정의하는 것입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;class MyClassLoader extends ClassLoader {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public Class defineClass(String name, byte[] b) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;return defineClass(name, b, 0, b.length);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;그런 다음 생성 된 클래스는 다음을 사용하여 직접로드 할 수 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left; margin-left: 2em;&quot;&gt;Class c = myClassLoader.defineClass(&quot;pkg.Comparable&quot;, b);&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;또 다른 방법은 요청 된 클래스를 생성하기 위해 findClass 메서드가 재정의 된 ClassLoader 하위 클래스를 정의하는 것입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;class StubClassLoader extends ClassLoader {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;protected Class findClass(String name)
throws ClassNotFoundException {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;if (name.endsWith(&quot;_Stub&quot;)) {&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;...&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;byte[] b = cw.toByteArray();&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;return defineClass(name, b, 0, b.length);&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;return super.findClass(name);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;실제로 생성 된 클래스를 사용하는 방법은 컨텍스트에 따라 다르며 ASM API의 범위를 벗어납니다. 컴파일러를 작성하는 경우 클래스 생성 프로세스는 컴파일 할 프로그램을 나타내는 추상 구문 트리로 구동되며 생성 된 클래스는 디스크에 저장됩니다. 동적 프록시 클래스 생성기 또는 aspect weaver를 작성하는 경우, 어떤 방법 으로든 ClassLoader를 사용할 것입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2.4. Transforming classes&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;지금까지 ClassReader 및 ClassWriter 구성 요소는 단독으로 사용되었습니다. 이벤트는 &quot;손으로&quot;제작되어 ClassWriter에 의해 직접 소비되거나 대칭 적으로 ClassReader에 의해 생성되어 &quot;손으로&quot;즉 사용자 정의 ClassVisitor 구현에 의해 소비되었습니다. 이러한 구성 요소를 함께 사용하면 모든 것이 정말 흥미로워지기 시작합니다. 첫 번째 단계는 ClassReader가 생성 한 이벤트를 ClassWriter로 전달하는 것입니다. 결과적으로, 클래스 리더에 의해 구문 분석 된 클래스는 클래스 작성자에 의해 재구성됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b1 = ...;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassReader cr = new ClassReader(b1);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cr.accept(cw, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b2 = cw.toByteArray(); // b2 represents the same class as b1&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이것은 그 자체로는별로 흥미롭지 않습니다 (바이트 배열을 복사하는 더 쉬운 방법이 있습니다!). 그러나 기다려주십시오. 다음 단계는 클래스 판독기와 클래스 작성기 사이에 ClassVisitor를 도입하는 것입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b1 = ...;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;// cv forwards all events to cw\&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;ClassVisitor cv = new ClassVisitor(ASM4, cw) { };&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassReader cr = new ClassReader(b1);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cr.accept(cv, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b2 = cw.toByteArray(); // b2 represents the same class as b1&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;위의 코드에 해당하는 아키텍처는 Figure 2.6에 나와 있으며 구성 요소는 사각형으로 표시되고, 이벤트는 화살표로 표시된다.((시퀀스 다이어그램 에서처럼 수직 타임 라인이 있음).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 617px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99A9554B5B97456937&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99A9554B5B97456937&quot; width=&quot;617&quot; height=&quot;161&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;Figure 2.6 : A transformation chain&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;그러나 ClassVisitor 이벤트 필터가 아무 것도 필터링하지 않기 때문에 결과는 변경되지 않습니다. 그러나 이제는 클래스를 변형 할 수 있도록 일부 메서드를 재정 의하여 이벤트를 필터링하는 것으로 충분합니다. 예를 들어 다음 ClassVisitor 하위 클래스를 생각해 볼 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class ChangeVersionAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public ChangeVersionAdapter(ClassVisitor cv) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4, cv);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;cv.visit(V1_5, access, name, signature, superName, interfaces);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 클래스는 ClassVisitor 클래스의 한 메서드 만 재정의합니다. 결과적으로 모든 호출은 변경된 클래스 버전 번호로 전달되어진다.&lt;/p&gt;&lt;p&gt;visit&amp;nbsp;메소드에 대한 호출을 제외하고 생성자에 class visitor&amp;nbsp;CV로 변경되지 않고 전달됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;해당 시퀀스 다이어그램이 Figure 2.7에 나와 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 661px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990E7A4C5B97467C11&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990E7A4C5B97467C11&quot; width=&quot;661&quot; height=&quot;358&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Figure 2.7 : Sequence&amp;nbsp;diagram for the ChangeVersionAdaptor&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;visit 메소드의 다른 인수를 수정하면 클래스 버전을 변경하는 것 이외의 다른 변환을 구현할 수 있습니다. 예를 들어, 구현 된 인터페이스 목록에 인터페이스를 추가 할 수 있습니다. 클래스의 이름을 변경하는 것도 가능하지만, 이는 visit 메소드에서 name 인수를 변경하는 것 이상을 필요로합니다. 실제로 클래스의 이름은 컴파일 된 클래스의 여러 다른 위치에 나타날 수 있으며 이러한 모든 변경은 클래스의 실제 이름을 바꾸기 위해 변경되어야 합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Optimization&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이전 변환은 원래 클래스에서 4 바이트 만 변경합니다. 그러나 위의 코드에서 b1은 완전히 파싱되고 해당 이벤트는 처음부터 b2를 구성하는 데 사용되며 이는 그리 효율적이지 않습니다. 이러한 부분을 파싱하지 않고 해당 이벤트를 생성하지 않고 직접 b2로 변환되지 않은 b1 부분을 복사하는 것이 훨씬 더 효율적입니다. ASM은 메소드 최적화를 자동으로 수행합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;•&amp;nbsp;ClassReader Component에서 MethodVisitor를 감지하면 ,이 메서드의 내용은 변형되지 않으며 응용 프로그램에서도 실제로 볼 수 없습니다.&amp;nbsp; #MethodVisitor는 ClassVisitor의&amp;nbsp;accept 메서드에 대한 인수로 전달 된 ClassWriter에 &amp;gt;해석 애모모함&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;•&amp;nbsp;이 경우 ClassReader Component는 이 메서드의 내용을 parse하지 않고, 해당 이벤트를 생성하지 않고, 그냥 바이트 배열 표현을 ClassWriter에 복사합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 최적화는 ClassReader 및 ClassWriter 구성 요소에 의해 수행됩니다.&lt;/p&gt;&lt;p&gt;그들이 서로에 대한 참조를 가지고 있다면, 다음과 같이 설정할 수 있습니다 :&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b1 = ...&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassReader cr = new ClassReader(b1);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = new ClassWriter(&lt;b&gt;cr&lt;/b&gt;, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ChangeVersionAdapter ca = new ChangeVersionAdapter(cw);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cr.accept(ca, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte[] b2 = cw.toByteArray();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 최적화 덕분에 위의 코드는 이전 버전보다 2 배 빠릅니다. 왜냐하면 ChangeVersionAdapter가 어떤 방법도 변환하지 않기 때문입니다.&lt;/p&gt;&lt;p&gt;일부 또는 모든 메소드를 변환하는 공통 클래스 변환의 경우 속도향상은 작지만 여전히 눈에 띕니다. 그것은 실제로 10-20 %의 순서입니다. 불행하게도이 최적화는 원래 클래스에 정의 된 모든 상수를 변환 된 상수로 복사해야합니다. 이는 필드, 메소드 또는 지침을 추가하는 변환에 대해서는 문제가되지 않지만 많은 클래스 요소를 제거하거나 이름을 바꾼 변환에 대해서는 최적화되지 않은 경우와 비교하여 더 큰 클래스 파일로 이어집니다. 따라서 &quot;additive&quot;변환에만이 최적화를 사용하는 것이 좋습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Using transformed classes&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;변환 된 클래스 b2는 이전 섹션에서 설명한대로 디스크에 저장하거나 ClassLoader로로드 할 수 있습니다. 다만, ClassLoader 내부에서 행해지는 클래스 변환은,이 클래스 로더에 의해로드 된 클래스만을 변환 할 수 있습니다. 모든 클래스를 변환하려면 java.lang.instrument 패키지에 정의 된대로 ClassFileTransformer 내에 변환을 넣어야합니다.&lt;/p&gt;&lt;p&gt;(자세한 내용은이 패키지의 문서를 참조하십시오) :&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public static void premain(String agentArgs, Instrumentation inst) {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;inst.addTransformer(new ClassFileTransformer() {&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;public byte[] transform(ClassLoader l, String name, Class c,&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;ProtectionDomain d, byte[] b)&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;throws IllegalClassFormatException {&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;ClassReader cr = new ClassReader(b);&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;ClassWriter cw = new ClassWriter(cr, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;ClassVisitor cv = new ChangeVersionAdapter(cw);&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;cr.accept(cv, 0);&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;return cw.toByteArray();&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;});&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2.5. Removing class members&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이전 섹션에서 클래스 버전을 변환하는 데 사용 된 메서드는 ClassVisitor의 메서드입니다.&amp;nbsp;예를 들어, visitField 및 visitMethod 메소드에서 access 또는 name 인수를 변경하기위해 ,필드나 메소드의 modifier나 이름을 변경할 수 있습니다. 또한 , 수정 된 인수를 사용하여 메서드 호출을 전달하는 대신, 이 호출에&amp;nbsp;전혀 전달하지 않도록 선택할 수 있습니다. 결과는 해당 클래스 요소가 제거 된 것입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;예를 들어, 다음 클래스 어댑터가 클래스의 컴파일 된 소스 파일의 이름뿐만 아니라 외부 및 내부 클래스에 대한 정보를 제거합니다 (결과 클래스는 디버깅 목적으로 만 사용되기 때문에 완전한 기능을 유지함). 이는 적절한 visit&amp;nbsp;메서드에서 아무 것도 전달되지 않게 수행됩니다.&lt;/p&gt;&lt;p&gt;&lt;u&gt;즉, 아무것도 설정하지 class생성에 값이 전달되지않는다. 유지하기 위해서는 cv.visitXXX을 호출 해 줘야한다.&lt;/u&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class RemoveDebugAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public RemoveDebugAdapter(ClassVisitor cv) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4, cv);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitSource(String source, String debug) {
}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override
public void visitOuterClass(String owner, String name, String desc) {
}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 전략은 visitField 및 visitMethod 메서드가 결과를 반환해야하기 때문에 필드와 메서드에 대해서는 작동하지 않습니다. 필드 또는 메서드를 제거하려면 메서드 호출을 전달하지 말고 호출자에게 null을 반환해야합니다. 예를 들어, 다음 클래스 어댑터는 이름과 설명자로 지정된 단일 메소드를 제거합니다 (클래스는 동일한 이름이지만 매개 변수가 다른 여러 메소드를 포함 할 수 있기 때문에 이름은 메소드를 식별하기에 충분하지 않습니다).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class RemoveMethodAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;private String mName;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;private String mDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public RemoveMethodAdapter(
ClassVisitor cv, String mName, String mDesc) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4, cv);
this.mName = mName;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;this.mDesc = mDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;b&gt;if (name.equals(mName) &amp;amp;&amp;amp; desc.equals(mDesc)) {&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;b&gt;// do not delegate to next visitor -&amp;gt; this removes the method&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;b&gt;return null;&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;return cv.visitMethod(access, name, desc, signature, exceptions);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2.6. Adding class members&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;당신은&amp;nbsp; 클래스 element를&amp;nbsp;추가하여 더 많은 것을 전달할 수 있습니다. 원래의 메소드 호출 사이의 여러 곳에 새로운 호출을 삽입할 수 있으며, 다양한 visitXxx 메소드를 이용하면 된다. (섹션 2.2.1 참조).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;예를 들어, 클래스에 필드를 추가하려면 원래 메서드 호출 사이에 새로운 visitField의 호출을 삽입해야 하며, 클래스 어댑터의 visit 메서드 중 하나로 이 새로운 호출을 추가해야 합니다. visit메소드에서 이 작업을 수행 할 수 없습니다. 예를 들어&amp;nbsp; visitField 호출 다음에의 visitSource, visitOuterClass, visitAnnotation 또는 visitAttribute&amp;nbsp;는 유효하지 않습니다. 같은 이유로 새로운 호출을 visitSource, visitOuterClass, visitAnnotation 또는 visitAttribute 메소드에 넣을 수 없습니다. 가능한 옵션은 visitInnerClass, visitField, visitMethod 또는 visitEnd 메소드입니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;gt;&amp;gt;visit는 순서가 있다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;visitEnd 메소드에 새 호출을 넣으면 이 메소드가 항상 호출되기 때문에 필드가 항상 추가됩니다 (명시 적 조건을 추가하지 않는 한).&lt;/p&gt;&lt;p&gt;visitField 또는 visitMethod에 입력하면 여러 필드가 추가됩니다. 원래 클래스의 필드 또는 메소드 당 하나씩 필드가 추가됩니다. 두 솔루션 모두 의미가 있습니다. 그것은 당신이 필요한 것에 달려 있습니다. 예를 들어 객체의 호출 수를 계산하는 단일 카운터 필드 또는 메소드 당 하나의 카운터를 추가하여 각 메소드의 호출을 개별적으로 계산할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;참고 : 사실 유일한 해결책은 visitEnd 메서드에서 추가 호출을 만들어 새 멤버를 추가하는 것입니다. 실제로 클래스에는 중복 멤버가 없어야하며 새 멤버가 고유한지 확인하는 유일한 방법은 모든 기존 멤버와 비교하는 것입니다.이 멤버는 모두 방문한 후에 만 수행 할 수 있습니다 (예 : visitEnd 메서드). 이것은 다소 제한적입니다. _counter $ 또는 _4B7F_와 같이 프로그래머가 사용하지 않는 생성 된 이름을 사용하면 중복 된 멤버가 visitEnd에 추가하지 않고도 피할 수 있습니다. 첫 번째 장에서 설명한 것처럼 트리 API에는이 제한이 없습니다.이 API를 사용하여 변환 내에서 언제든지 새 멤버를 추가 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;위의 설명을 설명하기 위해이 필드가 이미 존재하지 않는 한 클래스에 필드를 추가하는 클래스 어댑터가 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class AddFieldAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;private int fAcc;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;private String fName;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;private String fDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;private boolean isFieldPresent;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public AddFieldAdapter(ClassVisitor cv, int fAcc, String fName, String fDesc) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4, cv);&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;this.fAcc = fAcc;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;this.fName = fName;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;this.fDesc = fDesc;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public FieldVisitor visitField(int access, String name, String desc,String signature, Object value) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;b&gt;if (name.equals(fName)) {&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;b&gt;isFieldPresent = true;&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;return cv.visitField(access, name, desc, signature, value);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public void visitEnd() {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;b&gt;if (!isFieldPresent) {&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;b&gt;FieldVisitor fv = cv.visitField(fAcc, fName, fDesc, null, null);&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;b&gt;if (fv != null) {&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 10em;&quot;&gt;&lt;b&gt;fv.visitEnd();&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;cv.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;필드는 visitEnd 메소드에 추가됩니다. 기존 필드를 수정하거나 필드를 제거하기 위해 visitField 메소드를 재정의하지 않고 추가하려는 필드가 이미 존재하는지 여부를 감지합니다. fv.visitEnd ()를 호출하기 전에 visitEnd 메소드에서 fv! = null 테스트를 참고하십시오. 이전 섹션에서 보았듯이 class visitor는 visitField에서 null을 리턴 할 수 있기 때문입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.2.7. Transformation chains&lt;/p&gt;&lt;p&gt;지금까지 우리는 ClassReader, class adapter&amp;nbsp;및 ClassWriter로 이루어진 간단한 변환 체인을 보았습니다. 물론 더 복잡한 체인을 사용할 수도 있습니다. 여러 개의 클래스 어댑터가 함께 연결됩니다. 여러 어댑터를 연결하면 복잡한 변형을 수행하기 위해 여러 개의 독립적 인 클래스 변환을 작성할 수 있습니다. 변환 체인은 반드시 선형 적이지는 않습니다. 받은 모든 메소드 호출을 여러 ClassVisitor에 동시에 전달하는 ClassVisitor를 작성할 수있다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public class MultiClassAdapter extends ClassVisitor {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;protected ClassVisitor[] cvs;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public MultiClassAdapter(ClassVisitor[] cvs) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;super(ASM4);&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;this.cvs = cvs;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;@Override public void visit(int version, int access, String name,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;String signature, String superName, String[] interfaces) {&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;for (ClassVisitor cv : cvs) {&lt;/p&gt;&lt;p style=&quot;margin-left: 8em;&quot;&gt;cv.visit(version, access, name, signature, superName, interfaces);&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;...&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;대칭 적으로 여러 클래스 어댑터가 동일한 ClassVisitor에 위임 할 수 있습니다 (예 : visit 및 visitEnd 메소드가이 ClassVisitor에서 한 번만 호출되도록하는 몇 가지 예방 조치가 필요함). 따라서 그림 2.8에 제시된 것과 같은 변형 체인이 완벽하게 가능합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 613px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99289D3C5B97569D10&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99289D3C5B97569D10&quot; width=&quot;613&quot; height=&quot;242&quot; filename=&quot;캡처.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Figure 2.8 :&amp;nbsp; A complex transformation chain&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;2.3. Tools&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ClassVisitor 클래스 및 관련 ClassReader 및 ClassWriter 구성 요소 외에도 ASM은 org.objectweb.asm.util 패키지에 클래스 생성기 또는 어댑터의 개발 중에 유용 할 수 있는&amp;nbsp;(runtime에서필요하지 않은) 여러 가지 도구를 제공합니다.&amp;nbsp; ASM은 런타임에 내부 이름, 유형 설명자 및 메소드 설명자를 조작하기위한 유틸리티 클래스도 제공합니다. 이 모든 도구는 다음과 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;2.3.1. Type&lt;/span&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이전 섹션에서 보았 듯이, ASM API는 컴파일 된 클래스, 즉 내부 이름 또는 유형 설명자로 저장되는 Java 유형을 노출합니다. 소스 코드에 나타나는대로 코드를 노출하여 코드를 보다 쉽게 ​​읽을 수 있습니다. 그러나 이것은 ClassReader와 ClassWriter의 두 표현 사이의 체계적인 변환을 필요로 하며 이는 성능을 저하시킵니다. 그렇기 때문에 ASM은 내부 이름과 유형 설명자를 동등한 소스 코드 형식으로 투명하게 변환하지 않습니다. 그러나 필요한 경우 수동으로 수행하기위한 Type 클래스를 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Type object는 Java 타입을 나타내며, type descriptor 또는 Class object부터 구축 할 수 있습니다. Type class는 primitive type을 나타내는 정적 변수도 포함합니다. 예를 들어, Type.INT_TYPE는 int 유형을 나타내는 Type 객체입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;getInternalName 메소드는 Type의 내부 이름을 리턴합니다. 예를 들어, Type.getType (String.class) .getInternalName ()은 String 클래스의 내부 이름을 제공합니다 (예 : &quot;java / lang / String&quot;). 이 메소드는 클래스 또는 인터페이스 유형에만 사용해야합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;getDescriptor 메소드는 Type의 디스크립터를 리턴한다. 예를 들어, 대신 &quot;Ljava / lang / String;&quot;을 사용하십시오. 코드에서 Type.getType (String.class) .getDescriptor ()를 사용할 수 있습니다. 또는 I를 사용하는 대신 Type.INT_TYPE.getDescriptor ()를 사용할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Type object는 메서드 type을 나타낼 수도 있습니다. 이러한 객체는 메소드 설명자 또는 Method 객체에서 구성 할 수 있습니다.. 그런 다음 getDescriptor 메소드는이 유형에 해당하는 메소드 설명자를 리턴합니다. 또한 getArgumentTypes 및 getReturnType 메서드를 사용하여 메서드의 인수 형식 및 반환 형식에 해당하는 Type 개체를 가져올 수 있습니다. 예를 들어 Type.getArgumentTypes ( &quot;(I) V&quot;)는 단일 요소 Type을 포함하는 배열을 반환합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;INT_TYPE. 같이, Type.getReturnType ( &quot;(I) V&quot;)를 호출하면 (자), Type.VOID_TYPE object가 반환됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 11pt;&quot;&gt;2.3.2. TraceClassVisitor&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;생성되거나 변환 된 클래스가 예상 한 것과 일치하는지 확인하려면, ClassWriter에 의해 반환 된 바이트 배열은 사람이 읽을 수 없으므로 별로 도움이 되지 않습니다. 텍스트 표현은 사용하기가 훨씬 쉬울 것입니다. 이것은 TraceClassVisitor 클래스가 제공하는 것입니다. 이 클래스는 이름에서 알 수 있듯이 ClassVisitor 클래스를 확장하고 visited class의 텍스트 표현을 작성합니다. 따라서, ClassWriter를 사용하여 클래스를 생성하는 대신 TraceClassVisitor를 사용하여 실제로 생성 된 내용을 읽을 수있는 추적을 얻을 수 있습니다. 또는 두 가지를 동시에 사용할 수 있습니다. 실제로 TraceClassVisitor는 기본 비헤이비어 외에 메소드에 대한 모든 호출을 다른 방문자 (예 : ClassWriter)에게 위임 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visit(...);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte b[] = cw.toByteArray();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 코드는 수신 한 모든 호출을 cw에 위임 한 TraceClassVisitor를 만들고이 호출의 텍스트 표현을 printWriter에 인쇄합니다.&lt;/p&gt;&lt;p&gt;예를 들어, 2.2.3 절의 예제에서 TraceClassVisitor를 사용하면 다음을 얻을 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;// class version 49.0 (49)&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;// access flags 1537&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public abstract interface pkg/Comparable implements pkg/Mesurable {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;// access flags 25&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public final static I LESS = -1&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;// access flags 25&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public final static I EQUAL = 0&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;// access flags 25&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public final static I GREATER = 1&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;// access flags 1025&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;public abstract compareTo(Ljava/lang/Object;)I&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;TraceClassVisitor는 생성 또는 변형 체인의 어느 지점에서나 ClassWriter 바로 전에뿐만 아니라 체인의이 시점에서 어떤 일이 발생하는지 확인할 수 있습니다. 이 어댑터에 의해 생성 된 클래스의 텍스트 표현은 String.equals ()를 사용하여 클래스를 쉽게 비교하는 데 사용될 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.3.3. CheckClassAdapter&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ClassWriter 클래스는 메서드가 올바른 순서와 유효한 인수로 호출되는지 확인하지 않습니다. 따라서 Java 가상 머신 검증 자에 의해 거부 될 유효하지 않은 클래스를 생성 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;가능한 빨리 이러한 오류 중 일부를 감지하기 위해 CheckClassAdapter 클래스를 사용할 수 있습니다. TraceClassVisitor와 마찬가지로이 클래스는 ClassVisitor 클래스를 확장하고 메서드에 대한 모든 호출을 다른 ClassVisitor (예 : TraceClassVisitor 또는 ClassWriter)에 위임합니다. 그러나 vistied class의 텍스트 표현을 인쇄하는 대신이 클래스는 다음 방문자에게 위임하기 전에 해당 메소드가 올바른 순서 및 유효한 인수로 호출되는지 확인합니다. 에러가 발생했을 경우, IllegalStateException 또는 IllegalArgumentException가 발생됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;클래스를 검사하고이 클래스의 텍스트 표현을 출력하고 마지막으로 바이트 배열 표현을 만들려면 다음과 같이 사용해야합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;TraceClassVisitor tcv = new TraceClassVisitor(cw, printWriter);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;CheckClassAdapter cv = new CheckClassAdapter(tcv);&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visit(...);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;...&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;cv.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;byte b[] = cw.toByteArray();&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이러한 클래스 visitor를 다른 순서로 연결하면 수행하는 작업도 다른 순서로 수행됩니다. 예를 들어 다음 코드를 사용하면 trace 후에 검사가 수행됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;b&gt;CheckClassAdapter cca = new CheckClassAdapter(cw);&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;TraceClassVisitor cv = new TraceClassVisitor(cca, printWriter);&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;TraceClassVisitor와 마찬가지로, 체인의 클래스를 확인하기 위해 ClassWriter 바로 전에뿐만 아니라 생성 또는 변형 체인의 어느 지점에서나 CheckClassAdapter를 사용할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2.3.4. ASMifier&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 클래스는 TraceClassVisitor 도구에 대한 대체 백엔드를 제공합니다 (기본적으로 Textifier 백엔드를 사용하여 위에 표시된 출력 유형을 생성합니다). 이 백엔드는 TraceClassVisitor 클래스의 각 메소드를 호출하는 데 사용 된 Java 코드를 인쇄합니다. 예를 들어 visitEnd () 메서드를 호출하면 cv.visitEnd ();가 인쇄됩니다. 결과적으로 ASMifier 백엔드가있는 TraceClassVisitor 방문자가 클래스를 방문하면 ASM을 사용하여이 클래스를 생성하는 소스 코드를 인쇄합니다. 이 방문자를 사용하여 이미 존재하는 클래스를 방문하는 경우에 유용합니다. 예를 들어 ASM을 사용하여 컴파일 된 클래스를 생성하는 방법을 모르는 경우 해당 소스 코드를 작성하고 javac을 사용하여 컴파일 한 다음 ASMifier를 사용하여 컴파일 된 클래스를 방문하십시오.&lt;/p&gt;&lt;p&gt;이 컴파일 된 클래스를 생성하는 ASM 코드를 얻게됩니다!&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The ASMifier class can be used from the command line. For example using:&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;java -classpath asm.jar:asm-util.jar \
org.objectweb.asm.util.ASMifier \
java.lang.Runnable&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;produces code that, after indentation, reads:&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;package asm.java.lang;&lt;/p&gt;&lt;p&gt;import org.objectweb.asm.*;&lt;/p&gt;&lt;p&gt;public class RunnableDump implements Opcodes {&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;public static byte[] dump() throws Exception {&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;ClassWriter cw = new ClassWriter(0);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;FieldVisitor fv;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;MethodVisitor mv;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;AnnotationVisitor av0;&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;&quot;java/lang/Runnable&quot;, null, &quot;java/lang/Object&quot;, null);&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;{&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, &quot;run&quot;, &quot;()V&quot;,null, null);&lt;/p&gt;&lt;p style=&quot;margin-left: 6em;&quot;&gt;mv.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;}&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;cw.visitEnd();&lt;/p&gt;&lt;p style=&quot;margin-left: 4em;&quot;&gt;return cw.toByteArray();&lt;/p&gt;&lt;p style=&quot;margin-left: 2em;&quot;&gt;}&lt;/p&gt;&lt;p&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://yimoc.tistory.com/7&quot; target=&quot;_blank&quot;&gt;2018/09/11 - [분류 전체보기] - ASM User Guide 번역 3&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>ASM</category>
      <category>#ASM</category>
      <category>#BCI</category>
      <category>#JAVA</category>
      <category>#User Guide</category>
      <category>#번역</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/6</guid>
      <comments>https://yimoc.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 10 Sep 2018 17:30:56 +0900</pubDate>
    </item>
    <item>
      <title>ASM User Guide 번역 1</title>
      <link>https://yimoc.tistory.com/5</link>
      <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;https://asm.ow2.io/asm4-guide.pdf&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;ASM 4.0&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;A Java bytecode engineering library&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1.2. Overview&lt;/p&gt;&lt;p&gt;1.2.1. Scope&lt;/p&gt;&lt;p&gt;ASM 라이브러리의 목표는 컴파일 자바클래스&amp;nbsp;파일을 생성, 변환 및 분석하는 것입니다.Java 클래스는 바이트 배열로 표현됩니다 (디스크에 저장되고 Java 가상 머신에 로드 됨). 이를 위해 ASM은 숫자 상수, 문자열, Java 식별자, Java 유형, Java 클래스 구조 요소 등과 같이 바이트보다 상위 개념을 사용하여 바이트 배열을 읽고 쓰고 변환하는 도구를 제공합니다. ASM의 범위는 클래스의 읽기, 쓰기, 변형 및 분석에만 엄격하게 제한됩니다. 특히 클래스 로딩 프로세스는 범위를 벗어납니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1.2.2. Model&lt;/p&gt;&lt;p&gt;ASM 라이브러리는 컴파일된 클래스의 생성과 변환을위한 두 개의 API를 제공합니다.&lt;/p&gt;&lt;p&gt;코어 API는 클래스의 이벤트 기반 표현을 제공하지만 트리 API는 객체 기반 표현을 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이벤트 기반 모델에서 클래스는 일련의 이벤트로 표현되며, 각 이벤트는 클래스의 요소 (예 : 헤더, 필드,메소드 선언, 명령어 등)를 표현한다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이벤트 기반 API는 가능한 이벤트 세트 및 발생 순서를 정의하며&lt;/p&gt;&lt;p&gt;분석되는 요소 당 하나의 이벤트를 발생하는 클래스 구문 분석기와&amp;nbsp;&lt;/p&gt;&lt;p&gt;이러한 이벤트의 시퀀스에서 컴파일 된 클래스를 생성하는 클래스 작성기를 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;객체 기반 모델을 사용하면 클래스는 객체 트리로 표현됩니다.&lt;/p&gt;&lt;p&gt;각 객체는 클래스 자체, 필드, 메소드 등의 클래스의 일부를 나타내는 객체.지시 등를 나타냅니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;각 객체는 해당 구성을 나타내는 객체에 대한 참조를가집니다.&lt;/p&gt;&lt;p&gt;객체 기반 API는 클래스를 나타내는 일련의 이벤트를 그클래스를 나타내는 객체 트리로 변환하거나 ,&lt;/p&gt;&lt;p&gt;반대로 객체 트리를 동등한 이벤트 시퀀스로 변환하는 방법을 제공합니다.&lt;/p&gt;&lt;p&gt;즉, 객체 기반 API는 이벤트 기반 API 위에 구축됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 두 API는 SAX (Simple API for XML) 및 DOM (Document Object Model) API와 비교할수 있습니다&amp;nbsp;: 이벤트 기반&lt;/p&gt;&lt;p&gt;API는 SAX와 유사하지만 객체 기반 API는 DOM과 유사합니다. 객체 기반 API는 DOM이 SAX기반에 제공되듯이 이벤트 기반 객체 위에 구축됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ASM은 최상의 API가 없기 때문에 두 API를 모두 제공합니다. 실제로 각 API에는 장점과 단점이 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 이벤트 기반 API가 빠르고 객체기반API보다 적은 메모리가 필요합니다.&lt;/p&gt;&lt;p&gt;왜냐하면 , 클래스를 표현하기 위해 메모리에 트리를 생성하고 저장할 필요가 없기 때문이다.(SAX 및 DOM도 동일한 차이가 있다).&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 그러나, 이벤트 기반 API를 사용하여 클래스 변환을 구현하는 것이 더 어려울 수 있습니다.&lt;/p&gt;&lt;p&gt;객체 기반 API를 사용하여 전체 클래스를 메모리에서 사용하여 하는것보다, 어느 한 주어진 시점(현재 이벤트에 해당하는 요소)에&amp;nbsp;클래스의 한 요소만 사용하기 때문이다.&lt;/p&gt;&lt;p&gt;&amp;nbsp; &lt;i&gt;&amp;gt; SAX처럼 한 이벤트가 발생하는 시점에 이루어지기 때문에 전체를 보고 작업하는것보다는 어렵다는 얘기&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;두 API는 한 번에 하나의 클래스 만 관리하고 독립적으로 관리합니다.&lt;/p&gt;&lt;p&gt;클래스 계층 구조에 대한 정보는 유지되지 않으며 클래스 변환이 다른 클래스에 영향을주는 경우 다른 클래스를 수정하는 것은 사용자의 책임입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1.2.3. Architecture&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ASM 애플리케이션은 강력한 아키텍처 측면을 가지고 있습니다. 사실 이벤트 기반 API는 이벤트 생성자 (클래스 파서), 이벤트 소비자 (클래스 작성자) 및 미리 정의 된 다양한 이벤트 필터를 구성됩니다.&amp;nbsp; 또한, 사용자 정의한 생성자, 소비자 및 필터도 추가할수 있습니다. 따라서, 이 API를 사용하는 것은 두 단계 프로세스입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 이벤트 생성자, 필터 및 소비자 구성 요소를 가능한 복잡한 구조로 모으고,&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• 생성 또는 변환 프로세스를 실행하기 위해 이벤트 생성자를 시작합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;오브젝트 기반 API는 또한 아키텍처 측면을 가지고 있습니다. 실제로 객체트리에서 작동하는 클래스 생성기 또는 변환기 구성 요소가 구성 될 수 있으며, 이들 사이의 링크는 변환 순서를 나타냅니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;전형적인 ASM 애플리케이션의 대부분의 컴포넌트 아키텍처는 매우 간단하지만 다음과 같은 복잡한 아키텍처를 상상할 수 있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;화살표는 클래스 파서, 생성자 또는 변환기 사이의 이벤트 기반 또는 오브젝트 기반 통신을 나타냅니다.&lt;/p&gt;&lt;p&gt;체인 내의 이벤트 기반 표현과 객체 기반 표현 간의 가능한 변환이다.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 643px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99B96B3E5B9616972B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99B96B3E5B9616972B&quot; width=&quot;643&quot; height=&quot;245&quot; filename=&quot;capture.PNG&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;u&gt;CR은 ClassReader, CW는 ClassWriter이다. 중간의 박스는 Adaptor 및 기타 CR, CW 사용가능하다.&lt;/u&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;1.3. Organization&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ASM 라이브러리는 여러 패키지로 구성되며 각패키지는 jar 파일로 배포됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• org.objectweb.asm 및 org.objectweb.asm.signature 패키지&lt;/p&gt;&lt;p&gt;이벤트 기반 API를 정의하고 클래스 parser와 writer&amp;nbsp;구성 요소를 제공합니다.&lt;/p&gt;&lt;p&gt;이 파일은 asm.jar 아카이브에 들어 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• org.objectweb.asm.util 패키지는 asm-util.jar 에 있습니다.&lt;/p&gt;&lt;p&gt;ASM 애플리케이션의 개발 및 디버깅에 사용되는 다양한 도구를 제공합니다.&lt;/p&gt;&lt;p&gt;해당 tool은 core API를 기반으로 합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• org.objectweb.asm.commons 패키지는 core API를 기반으로 하는 몇 가지 유용한 미리 정의 된 클래스 변환기를 제공합니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;asm-commons.jar 아카이브에 포함되어 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• org.objectweb.asm.tree 패키지는 asm-tree.jar 아카이브에 있습니다.&lt;/p&gt;&lt;p&gt;객체 기반 API를 정의하고 이벤트 기반 및 객체 기반 표현간의 변환을&amp;nbsp;위한 툴을 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;• org.objectweb.asm.tree.analysis 패키지는 클래스 분석 프레임워크을 제공합니다.&lt;/p&gt;&lt;p&gt;또한, 몇 가지 미리 정의 된 클래스 분석기를 제공합니다. 해당 기능은 트리API기반입니다.&lt;/p&gt;&lt;p&gt;asm-analysis.jar 아카이브에 포함되어 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 문서는 두 부분으로 구성되어 있습니다. 첫 번째 부분은 핵심 API, 즉 asm, asm-util 및 asm-commons 아카이브를 다룹니다. 두 번째 부분은 트리 API, 즉 asm-tree 및 asm-analysis 아카이브를 다룹니다. 각 Part에는 클래스와 관련된 API 장, 메소드와 관련된 API에 대한 장 및 annotation, generic Type&amp;nbsp;등과 관련된 API에 대한 장이 포함됩니다. 각 장에는 프로그래밍 인터페이스와 관련 도구가 포함되어 있습니다 사전 정의 된 구성 요소. 모든 예제의 소스 코드는 ASM 웹 사이트에서 제공됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 구조을 통해 클래스 파일 기능을 점진적으로 도입하는 것이 더 쉬워 지지만 때로는 여러 섹션에서 단일 ASM 클래스의 프레젠테이션을 분산해야하는 경우가 있습니다. 따라서이 문서를 순차적으로 읽는 것이 좋습니다. ASM API에 대한 참조 가이드는 Javadoc을 사용하십시오.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;javadoc :&amp;nbsp;https://javadoc.io/doc/org.ow2.asm/asm/6.2.1&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://yimoc.tistory.com/6&quot; target=&quot;_blank&quot;&gt;2018/09/10 - [ASM] - ASM User Guide 번역 2&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>ASM</category>
      <category>ASM</category>
      <category>BCI</category>
      <category>bytecode</category>
      <category>Java</category>
      <category>javaaent</category>
      <category>javaagent</category>
      <category>번역</category>
      <author>yimoc</author>
      <guid isPermaLink="true">https://yimoc.tistory.com/5</guid>
      <comments>https://yimoc.tistory.com/5#entry5comment</comments>
      <pubDate>Mon, 10 Sep 2018 16:24:52 +0900</pubDate>
    </item>
  </channel>
</rss>