Рассмотрим такую практическую задачу. Есть некое множество классов. У этих классов есть методы, по которым нам хотелось бы, например, собирать метрики: количество вызовов, время выполнения, число ошибок. Наиболее простой и красивый способ решения этой задачи, не требующий написания тысяч строк кода в разных местах проекта, заключается в использовании динамических прокси-классов.

Ссылку на соответствующий пример исходного кода мне подсказал Alexander Semenov в ScalaChat. В упрощенном виде решение выглядит как-то так:

import scala. concurrent . _
import java. lang . reflect . { Method, InvocationHandler, Proxy }
import scala. concurrent . ExecutionContext . Implicits . global

package object util {
def createProxy [ I ] ( proxee : I,
interfaceClass : Class [ I ] ,
handler : InvocationHandler ) : I = {
Proxy. newProxyInstance (
interfaceClass. getClassLoader ,
Array ( interfaceClass ) ,
handler ) . asInstanceOf [ I ]
}
}

import util. _

abstract class AspectBase ( proxee : AnyRef ) extends InvocationHandler {
protected def invokeProxee ( method : Method,
args : Array [ Object ] ) : AnyRef =
method. invoke ( proxee, args : _* )
}

class ProxyClass
( proxee : AnyRef )
( implicit protected val executionContext : ExecutionContext )
extends AspectBase ( proxee ) {

override def invoke ( proxy : Object,
method : Method,
args : Array [ Object ] ) : AnyRef = {

println ( s «${method.getName} invoked» )

val result = invokeProxee ( method, args )
result match {
case f : Future [ _ ] =>
println ( «Future returned!» )
case _ => ;
}
result
}
}

trait TestTrait {
def someMethod ( x : Long ) : Long
def someOtherMethod ( x : Long ) : Future [ Long ]
}

class TestTraitImpl extends TestTrait {
def someMethod ( x : Long ) = x * 2
def someOtherMethod ( x : Long ) = Future { x * 3 }
}

object Debug extends App {
val t = {
val proxee = new TestTraitImpl ( )
createProxy ( proxee, classOf [ TestTrait ] , new ProxyClass ( proxee ) )
}

t. someMethod ( 1 )
t. someOtherMethod ( 2 )
}

При запуске программы мы получим следующий вывод:

someMethod invoked
someOtherMethod invoked
Future returned!

Как видите, мы перехватываем имя метода, его аргументы, возвращаемое значение. Таким образом, мы можем получить время перед вызовом оригинального метода и при возврате из него, повесить onComplete на возвращаемую футуру и так далее. Можно писать логи, собирать метрики, сериализовать аргументы и отправлять их в REST-сервис, и так далее.

Работает это примерно таким образом. При вызове Proxy.newProxyInstance генерируется обычный (то есть, как бы статический) прокси-класс, как если бы мы писали его вручную . Только делается это во время исполнения, потому прокси и динамический. Хорошо все-таки работать под виртуальной машиной, правда? Однако важно помнить, что при использовании динамических классов-прокси мы можем проксировать только интерфейс (в приведенном ранее примере — TestTrait), а не какой попало класс.

Ссылки по теме:

А используете ли вы в своих проектах динамические прокси-классы?

admin

Share
Published by
admin

Recent Posts

Консоль удаленного рабочего стола(rdp console)

Клиент удаленного рабочего стола (rdp) предоставляет нам возможность войти на сервер терминалов через консоль. Что…

2 месяца ago

Настройка сети в VMware Workstation

В VMware Workstation есть несколько способов настройки сети гостевой машины: 1) Bridged networking 2) Network…

2 месяца ago

Логи брандмауэра Windows

Встроенный брандмауэр Windows может не только остановить нежелательный трафик на вашем пороге, но и может…

2 месяца ago

Правильный способ отключения IPv6

Вопреки распространенному мнению, отключить IPv6 в Windows Vista и Server 2008 это не просто снять…

2 месяца ago

Ключи реестра Windows, отвечающие за параметры экранной заставки

Параметры экранной заставки для текущего пользователя можно править из системного реестра, для чего: Запустите редактор…

2 месяца ago

Как управлять журналами событий из командной строки

В этой статье расскажу про возможность просмотра журналов событий из командной строки. Эти возможности можно…

2 месяца ago