欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

ByteBuddy工具的使用場景有哪些?-創(chuàng)新互聯(lián)

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

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

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

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方法完全不需要實現(xiàn)方法體,通過intercept(FieldAccessor.ofBeanProperty) 可以自動將GET/SET方法和對應的屬性綁定自動生成方法體.

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

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

SqlMigrationResolverEnhancer.enhance(ByteBuddyAgent.install())

至于ResolveMigrationExecutorReplacer的實現(xiàn)和ByteBuddy無關(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
   }
}

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

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), 這樣生成的子類自動擁有父類所有的Constructor. 無需重新定義. 使用的例子如下:

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)

   }

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

使用ByteBuddy中的碰到一些問題:

  • 場景2 Agent實現(xiàn)中如果方法的參數(shù)簽名和攔截器的參數(shù)不完全匹配,則需要使用@RuntimeType annotation. 否則可能遇到以下錯誤:
    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工具的使用場景就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

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

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

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

廣告

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

小程序開發(fā)
欧美日韩丝袜一区二区| 久久精品熟女亚洲av韩国| 国产乱码免费一区二区三区| 久久人妻久久人妻久久| 国产真实老熟女无套内| 日本在线不卡二区三区| 99久久久精品国产免费| 亚洲毛片一区二区在线| 日韩人妖视频在线观看| 深夜av一区二区三区| 国产不卡的视频在线观看| 人人妻人人澡人人爱| 丁香婷婷麻豆激情综合网| 亚洲精品一区二区成人影院| 日韩av亚洲在线观看| 福利一区福利二区视频| 97在线观看视频在线观看| 日韩在线国产精品一区| 亚洲乱码一区二区av| 欧美日韩一区二区三区四区在线观看| 国产 亚洲 一区 二区| 快播av手机在线播放| 日韩精品一区二区毛片| 色婷婷国产精品久久包臀| 亚洲成人黄色片在线观看| 成人性生交大片免费男同| 欧美中文字幕在线精品| 偷拍大神女厕偷拍作品| 日韩精品国产专区一区| 亚洲一区二区视频精品| 人妻人人澡人人添人人爽桃色| 久久99精品综合国产女同| 色哟哟网站之中文字幕| 九九视频免费观看5| 成人中文字幕av电影| 欧美在线观看日韩精品| 欧美成人日本在线播放| 日本中文字幕乱码一区| 白白色发布青青在线视频观看| 国产一级av在线播放| 精品人妻人伦一区二区三区 |