<del id="d4fwx"><form id="d4fwx"></form></del>
      <del id="d4fwx"><form id="d4fwx"></form></del><del id="d4fwx"><form id="d4fwx"></form></del>

            <code id="d4fwx"><abbr id="d4fwx"></abbr></code>
          • ByteBuddy工具的使用場(chǎng)景有哪些?-創(chuàng)新互聯(lián)

            ByteBuddy工具的使用場(chǎng)景有哪些?針對(duì)這個(gè)問(wèn)題,今天小編總結(jié)這篇有關(guān)ByteBuddy的文章,可供感興趣的小伙伴們參考借鑒,希望對(duì)大家有所幫助。

            十年的昆明網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都全網(wǎng)營(yíng)銷(xiāo)推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整昆明建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“昆明網(wǎng)站設(shè)計(jì)”,“昆明網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

            使用場(chǎng)景1:動(dòng)態(tài)生成JAVA CLASS
            這是ByteBuddy最基本但是最有用的USECASE, 比如說(shuō),我定義一個(gè)SCHEMA,希望根據(jù)SCHEMA生成對(duì)應(yīng)的JAVA類(lèi)并加載到JVM中. 當(dāng)然你可以用一些模板工具比如VELOCITY之類(lèi)生成JAVA源代碼再進(jìn)行編譯, 但是這樣一般需要對(duì)項(xiàng)目的BUILD腳本(maven/gradle)進(jìn)行一些定制. 用ByteBuddy生成類(lèi)的強(qiáng)大之處在于, 這個(gè)類(lèi)可以通過(guò)JAVA代碼生成并且可以自動(dòng)加載到當(dāng)前的JVM中,這樣我可以在application初始化的時(shí)候從SCHEMA REGISTRY中讀取到SCHEMA定義,通過(guò)ByteBuddy生成對(duì)應(yīng)的JAVA類(lèi)加載到JVM中, 并且一旦SCHEMA發(fā)生變化如增加新屬性, 不需要做任何改動(dòng), 在JVM重啟后類(lèi)定義會(huì)自動(dòng)同步更新. 當(dāng)然一個(gè)限制是你只能用反射的方式訪問(wèn)這個(gè)新定義的類(lèi). 代碼樣例如下 (Kotlin語(yǔ)言):

            private fun buildSchemaClass(schema: Schema, classLoader: ClassLoader): Class<*> {
                 logger.info("Start building schema class for schema={}, version={}", schema.name, schema.version)
                 val typeBuilder = ByteBuddy().subclass(Any::class.java).name(className(schema))
                 var tempBuilder = typeBuilder
                 schema.columns.forEach { column ->
                   tempBuilder = tempBuilder
                     .defineField(column.name, columnTypeToJavaClassMapping[column.type], Visibility.PRIVATE)
                 }
                 schema.columns.forEach { column ->
                   tempBuilder = tempBuilder.defineMethod(toGet(column.name), columnTypeToJavaClassMapping[column.type], Modifier.PUBLIC)
                     .intercept(FieldAccessor.ofBeanProperty())
                     .defineMethod(toSet(column.name), Void.TYPE, Modifier.PUBLIC).withParameters(columnTypeToJavaClassMapping[column.type])
                     .intercept(FieldAccessor.ofBeanProperty())
                 }
                 return tempBuilder.make().load(classLoader, ClassLoadingStrategy.Default.WRAPPER).loaded.also {
                   logger.info("Success building schema class: {}", it.name)
                 }
               }

            大家可以看到,定義GET/SET方法完全不需要實(shí)現(xiàn)方法體,通過(guò)intercept(FieldAccessor.ofBeanProperty) 可以自動(dòng)將GET/SET方法和對(duì)應(yīng)的屬性綁定自動(dòng)生成方法體.

            使用場(chǎng)景2:JAVA AGENT代理.
            這是另一個(gè)異常強(qiáng)大的功能,可以動(dòng)態(tài)修改類(lèi)的定義,用于強(qiáng)行修改一些第三方LIB中一些不容易擴(kuò)展的類(lèi),而不需要修改類(lèi)的源代碼和JAR包. 不知道大家有沒(méi)有用過(guò)JAVA 本身的AGENT, 在一些性能監(jiān)控的工具常常會(huì)用到, 需要在JVM啟動(dòng)的時(shí)候加agentlib參數(shù),在AGENT中可以對(duì)原始JAVA的二進(jìn)制碼做增強(qiáng)埋點(diǎn). ByteBuddy強(qiáng)大之處是它連agentlib參數(shù)都不需要了,侵入性更小.  一個(gè)很好的例子是FLYWAY,我們需要在FLYWAY執(zhí)行數(shù)據(jù)庫(kù)腳本的時(shí)候?qū)⒛_本執(zhí)行到一個(gè)額外的數(shù)據(jù)庫(kù)(SPANNER). 需要擴(kuò)展org.flywaydb.core.internal.resolver.sql.SqlMigrationExecutor的execute方法執(zhí)行額外的寫(xiě)操作, 不得不吐槽一下FLYWAY,一大堆的繼承接口但是代碼基本沒(méi)法擴(kuò)展. 這時(shí)可以通過(guò)ByteBuddy Agent 攔截SqlMigrationExecutor的execute方法,在原始方法執(zhí)行之后實(shí)現(xiàn)額外的數(shù)據(jù)庫(kù)寫(xiě)操作. 唯一需要提的一點(diǎn)是ByteBuddy Agent只支持JVM不支持JRE.  代碼示例如下:

            object SqlMigrationResolverEnhancer {
              fun enhance(inst: Instrumentation) {
                 if (!SpannerConfigUtils.enableSpanner) {
                   logger.info("Spanner is not enabled!!!!!!!!!!!!!!!!, no intercept will occure")
                   return
                 }
                 val temp = Files.createTempDirectory("tmp").toFile()
                 ClassInjector.UsingInstrumentation.of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, inst).inject(
                     Collections.singletonMap(
                         TypeDescription.ForLoadedType(MigrationResolverInterceptor::class.java),
                         ClassFileLocator.ForClassLoader.read(MigrationResolverInterceptor::class.java!!)
                     )
                 )
                 AgentBuilder.Default()
                     .ignore(ElementMatchers.nameStartsWith("net."))
                     .ignore(ElementMatchers.nameStartsWith("com."))
                     .enableBootstrapInjection(inst, temp)
                     .type(ElementMatchers.nameEndsWith("SqlMigrationResolver"))
                     .transform { builder, _, _, _ ->
                       builder.method(ElementMatchers.hasMethodName("resolveMigrations"))
                           .intercept(MethodDelegation.to(MigrationResolverInterceptor::class.java))
                     }.with(object : AgentBuilder.Listener {
                       override fun onComplete(
                         typeName: String?,
                         classLoader: ClassLoader?,
                         module: JavaModule?,
                         loaded: Boolean
                       ) {
                         // just ignore onComplete
                       }
            
                       override fun onDiscovery(
                         typeName: String?,
                         classLoader: ClassLoader?,
                         module: JavaModule?,
                         loaded: Boolean
                       ) {
                         // just ignore onDiscovery
                       }
            
                       override fun onIgnored(
                         typeDescription: TypeDescription?,
                         classLoader: ClassLoader?,
                         module: JavaModule?,
                         loaded: Boolean
                       ) {
                         // just ignore onIgnored
                       }
            
                       override fun onTransformation(
                         typeDescription: TypeDescription?,
                         classLoader: ClassLoader?,
                         module: JavaModule?,
                         loaded: Boolean,
                         dynamicType: DynamicType?
                       ) {
                         logger.debug("Tranforming class: $typeDescription")
                       }
            
                       override fun onError(
                         typeName: String?,
                         classLoader: ClassLoader?,
                         module: JavaModule?,
                         loaded: Boolean,
                         throwable: Throwable?
                       ) {
                         logger.error("Error intercepting type: $typeName", throwable)
                       }
                     })
                     .installOn(inst)
               }
            }
            
            class MigrationResolverInterceptor {
               companion object {
                 private val logger: Logger = LoggerFactory.getLogger(MigrationResolverInterceptor::class.java)
            
                 @JvmStatic
                 @RuntimeType
                 fun intercept(@SuperCall delegate: Callable<Collection<ResolvedMigration>>): Collection<ResolvedMigration> {
                   val spannerProperties = SpannerProperties(SpannerConfigUtils.projectId, SpannerConfigUtils.instanceId, SpannerConfigUtils.databaseName)
                   val originalCol = delegate.call() as MutableList<ResolvedMigration>
                   logger.info("Intercepting migration resolver method ---------------------------------------- $originalCol")
                   return ResolvedMigrationExecutorReplacer.replaceSqlMigrationExecutor(originalCol, spannerProperties)
                 }
               }
            }

            代碼并不復(fù)雜,通過(guò)ElementMatchers先縮小CLASSPATH中掃描包的范圍,在通過(guò)ElementMatcher類(lèi)名和方法名指定需要攔截的方法,再指定攔截器的類(lèi)名. 這里注意的是Agent的enhance方法必須在被攔截的類(lèi)被JVM加載之前執(zhí)行,因?yàn)橐粋€(gè)類(lèi)在一個(gè)CLASSLOADER中只能被加載一次,加載完無(wú)法修改了. 注冊(cè)AgentBuilder.Listener并非必須,但是對(duì)排查期望的類(lèi)方法沒(méi)有被正確攔截的問(wèn)題非常有用. 另外注意我們只指定了Interceptor的類(lèi)名而沒(méi)有指定方法, 而且Interceptor類(lèi)中的方法必須是一個(gè)Static方法,通過(guò)@RuntimeType指定是攔截器需要執(zhí)行的方法. @SuperCall用于注入原始方法調(diào)用的代理. 可以在SpringBoot 主方法的開(kāi)始調(diào)用

            SqlMigrationResolverEnhancer.enhance(ByteBuddyAgent.install())

            至于ResolveMigrationExecutorReplacer的實(shí)現(xiàn)和ByteBuddy無(wú)關(guān), 代碼僅供參考不再贅述.

            object ResolvedMigrationExecutorReplacer {
               private val databaseField = SqlMigrationExecutor::class.java.getDeclaredField("database")
               private val sqlScriptField = SqlMigrationExecutor::class.java.getDeclaredField("sqlScript")
            
               init {
                 databaseField.isAccessible = true
                 sqlScriptField.isAccessible = true
               }
            
               fun replaceSqlMigrationExecutor(migrationList: MutableList<ResolvedMigration>, spannerProperties: SpannerProperties): List<ResolvedMigration> {
                 migrationList.forEach { resolvedMigration ->
                   val migrationExecutor = resolvedMigration.executor
                   if (migrationExecutor is SqlMigrationExecutor &&
                       resolvedMigration is ResolvedMigrationImpl) {
                     val database: Database<*> = databaseField.get(migrationExecutor) as Database<*>
                     val sqlScript: SqlScript = sqlScriptField.get(migrationExecutor) as SqlScript
                     resolvedMigration.executor = SpannerSqlMigrationExecutor(database, sqlScript, spannerProperties)
                   }
                 }
                 return migrationList
               }
            }

            使用場(chǎng)景3:AOP切面
            和場(chǎng)景2類(lèi)似,但有時(shí)我們不需要改變?cè)碱?lèi)的實(shí)現(xiàn),而是希望產(chǎn)生一個(gè)新的類(lèi)對(duì)原始類(lèi)的某些行為做增強(qiáng). AOP本質(zhì)是生成一個(gè)新的PROXY代理類(lèi)替換原有的實(shí)現(xiàn). JAVA本身提供了基于InvocationHandler的DynamicProxy, 但是有幾個(gè)比較大的限制. 1. 被攔截的類(lèi)必須實(shí)現(xiàn)一個(gè)接口. 2. InvocationHandler 只提供了一個(gè)方法: public Object invoke(Object proxy, Method method, Object[] args) throws Throwable. 假設(shè)被攔截的接口有很多個(gè)方法, 如java.sql.PreparedStatement, 需要對(duì)某些方法進(jìn)行特殊處理,那需要基于方法名寫(xiě)一大堆的If/else邏輯,代碼不夠優(yōu)雅. Spring提供了基于AspectJ的AOP, 但是這個(gè)強(qiáng)依賴于Spring體系必須是在Spring容器中受管理的Bean. 而B(niǎo)yteBuddy則可通過(guò)靈活的匹配模式指定需要代理的方法,其他方法則可默認(rèn)為原始類(lèi)的實(shí)現(xiàn)不改變行為. 并且類(lèi)似于ASPECTJ, 切面的實(shí)現(xiàn)可以獨(dú)立出來(lái). 一個(gè)使用場(chǎng)景是代理java.sql.DataSource/Connection/PreparedStatement/ResultSet. 指標(biāo)統(tǒng)計(jì),分庫(kù)分表等實(shí)現(xiàn)都需要. 這里實(shí)現(xiàn)了一個(gè)簡(jiǎn)單通用的代理織入器,可以對(duì)某個(gè)類(lèi)的某一組方法應(yīng)用一個(gè)Advisor攔截器,返回一個(gè)被增強(qiáng)的原始類(lèi)的子類(lèi).

            object DelegateAgent {
               fun <T> buildDelegateClass(sourceClass: Class<T>, methodNames: List<String>,
                          advisorClass: Class<*>): Class<out T> {
                 val builder = ByteBuddy().subclass(sourceClass, ConstructorStrategy.Default.IMITATE_SUPER_CLASS)
                 val methodMatchers = getMethodMachers(methodNames)
                 return builder.method(methodMatchers)
                   .intercept(MethodDelegation.to(advisorClass))
                   .make().load(
                     DelegateAgent::class.java.classLoader
                   ).loaded
               }
            
               private fun getMethodMachers(methodNames: List<String>): ElementMatcher<MethodDescription> {
                 var methodMatcher =
                   ElementMatchers.none<MethodDescription>()
                 if (methodNames.isEmpty()) {
                   return ElementMatchers.any()
                 }
                 methodNames.forEach {methodName ->
                  methodMatcher = methodMatcher.or(ElementMatchers.named<MethodDescription>(methodName))
                 }
                 return methodMatcher
               }
            }

            注意ByteBuddy().subclass(sourceClass, ConstructorStrategy.Default.IMITATE_SUPER_CLASS), 這樣生成的子類(lèi)自動(dòng)擁有父類(lèi)所有的Constructor. 無(wú)需重新定義. 使用的例子如下:

            object DataSourceAdvisor {
               @JvmStatic
               @RuntimeType
               fun onMethodExecution(
                 @This sourceObj: Any,
                 @Origin method: Method
                 @AllArguments arguments: Array<Any?>): Any {
                 //just for demo purpose
                     println("Current method is: " + method.name)
                 return method.invoke(sourceObj, * arguments)
               }
            }
            
            fun testAgent() {
                 val config = HikariConfig().apply {
                   this.jdbcUrl = "jdbc:mysql://xxxx"
                   this.driverClassName = "org.postgresql.Driver"
                   this.username = "postgres"
                   this.password = "postgres"
                   this.maximumPoolSize = 1
                 }
                 val resultDsClass = DelegateAgent.buildDelegateClass(HikariDataSource::class.java, listOf("getConnection"),
                   DataSourceAdvisor::class.java)
                 val newDs = resultDsClass.getConstructor(HikariConfig::class.java).newInstance(config)
                 println(newDs.connection)
            
               }

            這里的攔截器僅僅打印了方法名. 和場(chǎng)景二的非常相似,攔截器的實(shí)現(xiàn)也是一個(gè)類(lèi)的靜態(tài)方法,唯一的區(qū)別是原始對(duì)象,參數(shù)列表等使用的Annotation不同,場(chǎng)景2中應(yīng)該使用net.bytebuddy.asm.Advice 中的annotation. 場(chǎng)景3應(yīng)該使用的是net.bytebuddy.implementation.bind.annotation包中的annotation

            使用ByteBuddy中的碰到一些問(wèn)題:

            • 場(chǎng)景2 Agent實(shí)現(xiàn)中如果方法的參數(shù)簽名和攔截器的參數(shù)不完全匹配,則需要使用@RuntimeType annotation. 否則可能遇到以下錯(cuò)誤:
              java.lang.IllegalArgumentException: None of [interceptor methods]  allows for delegation from [target method]
              at net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor.bind(MethodDelegationBinder.java:1096)

              關(guān)于ByteBuddy工具的使用場(chǎng)景就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

            另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

            當(dāng)前文章:ByteBuddy工具的使用場(chǎng)景有哪些?-創(chuàng)新互聯(lián)
            分享鏈接:http://www.jbt999.com/article38/dspssp.html

            成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、定制開(kāi)發(fā)、網(wǎng)站導(dǎo)航、網(wǎng)站設(shè)計(jì)公司移動(dòng)網(wǎng)站建設(shè)、商城網(wǎng)站

            廣告

            聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

            小程序開(kāi)發(fā)

              <del id="d4fwx"><form id="d4fwx"></form></del>
              <del id="d4fwx"><form id="d4fwx"></form></del><del id="d4fwx"><form id="d4fwx"></form></del>

                    <code id="d4fwx"><abbr id="d4fwx"></abbr></code>
                  • 精品国产精品三级精品AV网址 | 日韩AⅤ| 欧美天天干天天女人 | 台湾无码中文字幕 | 小电影毛片 |