}
}
}
[Synchronization()]
[MyCallTrace("LogFile")]
public class News: ContextBoundObject {
public News(){
Console.WriteLine("News context = " +
Thread.CurrentContext.ContextID + "\n" +
"News constructor thread = " +
Thread.CurrentThread.GetHasheode() +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
}
public void Notify(String msg) {
Console.WriteLine("News notification: " + msg);
Console.WriteLine("News Notify thread = " +
Thread.CurrentThread.GetHasheode() +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
}
}
public class AccountApp {
public static void Main(){
HttpChannel myChannel = new HttpChannel(8080);
ChannelServices.RegisterChannel(myChannel);
RemotingConfiguration.RegisterWellKnownServiceType (
typeof(Account), "Account",
WellKnownObjectMode.Singleton);
Console.WriteLine("Server is listening");
Console.ReadLine();
Console.WriteLine("Bye");
}
}
}
Некоторые комментарии:
1. Определяемые в этом коде классы включаются в новое пространство имен — SPBU. AOP_NET. В этом же пространстве имен будет определен далее и атрибут трассировки вызовов MyCallTraceAttribute. При выборе имени пространства имен использовалась следующая рекомендация — префикс имени должен определять организацию, в которой работает разработчик. Попутно стоит заметить, что атрибут SynchronizationAttribute принадлежит пространству имен System.Runtime.Remoting.Contexts.
2. Классу Account наряду с атрибутом синхронизации (можно опустить часть "Attribute" при задании имени атрибута) приписан атрибут трассировки вызовов — [MyCallTrace ("LogFile")]. Здесь аргумент задает имя файла в рабочем каталоге, в конец которого будут записываться данные о вызовах методов этого класса. Однако трассировка вызовов будет обеспечиваться не всегда. Это касается только вызовов, сделанных извне контекста, в котором живет объект — экземпляр данного класса. Трассировка вызовов внутри данного контекста не производится. Понятие контекста и семантика данного атрибута будут рассмотрены далее.
3. Код класса Account претерпел некоторые изменения по сравнению с предыдущей главой:
♦ Появилось поле _tax — ссылка на экземпляр класса Tах. Новый экземпляр этого класса активируется в конструкторе класса Account с помощью оператора new. В результате при построении на стороне сервера экземпляра класса Account в этом же домене приложения формируется новый экземпляр класса Tах.
♦ В конструкторе класса Account выполняется вывод на консоль сервера идентификатора текущего контекста, т. е. контекста, в котором создается экземпляр класса Account. Кроме того на консоль выводится хеш потока, в котором выполняется конструктор, и логическое значение, равное true если этот поток выбран системой из пула потоков,
♦ При вызове метода Add происходит не только увеличение счета на величину нового вклада, но и вызывается метод Notify на объекте Tax, которому В качестве аргумента передается строка, сигнализирующая о поступлении нового вклада на счет.
Кроме того в этом же методе выполняется прямое уведомление компонента News. Для этого используется ссылка _tax.news на Экземпляр класса News, активированного к этому моменту экземпляром класса Tах. И здесь же на консоль выводится хеш потока, выполняющего метод Add, и информация о том, является ли этот поток потоком из пула потоков. Заметим, что хеш потока является уникальным в системе и может использоваться для идентификации потоков.
4. Прежде чем продолжить обсуждение кода сервера, необходимо остановиться на понятии контекста. Это важнейшее понятие данной главы. Именно механизм контекстов обеспечивает некоторый уровень реализации парадигмы аспектно-ориентированного программирования в рамках .NET. Смысл понятия контекста будет разъясняться последовательно в процессе разбора кода рассматриваемого здесь примера и обсуждения результатов экспериментов с этим кодом. Здесь обсудим связь между контекстом и доменом приложения.
5. Все объекты, живущие в некотором домене приложения, разбиваются на непересекающиеся группы. Грубо говоря, в одну группу попадают объекты, имеющие сходные запросы на использование сервисов. Все объекты одной группы живут в одном контексте, а объекты из разных групп живут в разных контекстах.
6. В каждом домене приложения имеется контекст по умолчанию, в который попадают все объекты, классы которых не являются производными от класса ContextBoundObject. Таким классам нельзя приписать какой-либо пользовательский атрибут. Точнее, экземпляры таких классов не могут воспользоваться связанными с этими атрибутами сервисами. Напротив, экземпляры классов, производных от класса ContextBoundObject, привязаны к конкретным контекстам (кроме контекста по умолчанию) и могут пользоваться связанными с такими контекстами сервисами.
7. Однако у объектов, живущих в контексте по умолчанию, имеется одно большое преимущество. Это преимущество состоит в том, что в любом контексте данного домена приложения можно использовать прямую ссылку на объект, живущий в контексте по умолчанию. Если же вызывается объект из какого-либо другого контекста и этот вызов пересекает границу контекста, то вызывающая сторона использует прокси для вызываемого объекта, сам вызов преобразуется в прокси в сообщение, которое уже в вызываемом контексте вновь преобразуется в вызов, который и исполняется.
8. Очевидно, что описанный здесь механизм приводит к значительным накладным расходам. Однако именно при использовании такого сложного механизма появляется возможность перехвата сообщений, кодирующих вызовы объектов, привязанных к контекстам. После перехвата вызова можно выполнить некоторый сервис. Аналогичную процедуру можно выполнять, перехватывая результаты, возвращаемые вызывающей стороне.
9. Теперь продолжим обсуждение кода сервера.
10. Класс Tах (как и класс Account) привязан к контексту (он наследует классу ContextBoundObject). Это дает возможность экземплярам данного класса использовать сервисы синхронизации и трассировки вызовов (последнее верно только для вызовов, приходящих извне данного контекста). Именно для этого классу Tах приписаны атрибуты синхронизации и трассировки вызовов.
Дополнительные комментарии, касающиеся класса Tах:
♦ В конструкторе класса Tах активируется новый экземпляр класса News, ссылка на который запоминается в поле _news. Для доступа к этой ссылке предусмотрено публичное свойство news (только для чтения),
♦ При выполнении конструктора класса Tах на консоль сервера выводится информация об идентификаторе контекста, в котором будет жить новый объект, хеш текущего потока и его тип (поток из пула потоков или нет). Эти данные будут использованы при проведении экспериментов,
♦ Метод Notify класса Tах выводит на консоль полученное уведомление и в свою очередь отсылает полученное уведомление экземпляру класса News. Здесь же на консоль выводится хеш текущего потока и его тип.