Continue the first part of the article, we will consider the problems proposed in the original text in this text, and explore the relevant technical mechanisms provided in the .NET / CLR, and finally achieve the same results in the form of AOP. . In order to let you enter the status as soon as possible, let's briefly review the contents already discussed in the previous article:
In the first part of the article, we started from a very simple fictional business operation (Calculator), combined with the non-functional needs (operational logs, permission control, performance monitoring, etc.) that often face in business development. The idea of the object, combines the Decorator design mode, separating the core functions of the business operation object and other other service function code to achieve higher cohesion, loosely coupled software components.
However, after the problem of solving the confusion of the target code (that is, improved code cohesive), we have introduced a lot of code, including more objects and more complex structures introduced to solve problems (with JJX) The words are "excessive design" - although I emphasized this is a relatively reasonable design that can solve the problem we have to solve, but I am also hope that readers can experience this article in this feeling. The superiority of the method - It seems that JJX really occasionally this eye-catching, the trend is ignorant :)
The newly introduced problem becomes more prominent when the number of similar business operations continues to grow. In our current enterprise application project, the number of business components is already dozens. And we have several basic non-business needs for each component: based on method calling particle size rights control, method call log, business operation review, informative performance monitoring, configurable buffer strategy ... these requirements are once Another enterprise project was developed again and again. Do you say that there is an Application Building Block? Don't forget, that is just the tool you can use, even if the implementation of each function requires only one statement, think about it, 50 components take 20 methods ... How? Is it enough? Result Technology Director said a "Can I use my own buffer controller to replace the new buffer control application building block for MS?" ......
But so many object-oriented design methods also have design models still can't solve these problems? Why does the buffer control logic applied to each method of this component cannot be reused on another method of another component? Why can't the logic written almost exactly the same method logic logic on one component of a component? I think the answer may be an object-oriented package mechanism - method, this is already a detail of the object package in the interior, you have returned to the structure of structured programming at this level - you can call another method, Parameters you want to pass, but this call is no longer able to save or merge. Since OOP is not suitable for new production relationships, it is bound to produce demand for new production technology - this new product is called AOP. Abstract, AOP is a functional implant mechanism that is cross-cut on the internal hierarchical element (mainly constructor and method); simply, AOP allows you to intercept the method and implant the new code (but now technology The evolution has been developed towards more and more complex directions), and the most critical is that this cross-cutting is the type of object, and even unrelated to the object type. In this article, we will use the technical mechanisms provided in .Net / CLR to intercept and implant our non-business demand code for all 50 components.
Ok, talk less, we cut into the topic. Still from the easiest example (or that sentence: I hope you can imagine it is more complicated, more realistic - or for such a simple thing, any design technology is too designed to escape the design.)
Public Class Calculator {Public Int Add (INT X, INT Y) {RETURN X Y;}} This is a NUNIT-based unit test code:
[TestFixTure] public class unittest {public void testcalculator () {Calculator Calc = new calculator (); assert.isnotnull (CALC); askERT.Add (3, 5));}}
Still as part of the same needs, let's add way to this class to call the log. This time we use a new design pattern proxy to think. In fact, Proxy 's structure and Decorator are basically the same, the main difference between these two modes is that Decorator is mainly used to add responsibilities to objects; and Proxy is mainly used to control / master access to objects. Now, we want to have an proxy responsible for the calling code and real objects to master / control access to objects, while also need to know its existence. In order to apply this mode, we still can't escape the abstract base or interface, introduce the factory and other steps, then we first use the factory method to encapsulate the creation process of the object:
Public class calculator {} public static calculator createInstance () {return new calculator ();} public int address {Return x y;}}
Since the default non-parameter constructor has been modified as internal visibility private, the test code for using the new statement cannot be compiled, and we adjust the test code to the newly provided static factory method call:
Public class unittest {public void testcalculator () {Calculator Calc = Calculator.createInstance (); ...}}
Now let's see how to embed a proxy between calling code and real objects, apparently we should move your hands during the process of object creation, such as this:
Public class calculator {... public static calculator createinstance () {return (Calculator) New loggingProxy (new calculator ());}}
In the imaginary code above, we want to put a new instance of a real object (New Calculator ()) as a constructor that is incompatulative into the agent object, because the ultimate is really working or our true object, must definitely put this guy Pass it to the agent object. Then we want to create a good proxy object to return to the call code with the identity of the real object (ie, Calculator class). However, in terms of knowledge of object-oriented programming for C #, only when loggerproxy is the derived class of Calculator, the above type conversion code may be established at the runtime. The Calculator itself is already a specific class, so that LoggerProxy is probably not reason, all in order to have a proxy class that can be compatible with it, we can only extract public base categories or abstract interfaces (such as Icalculator), then separately Delivery, then I want to use the factory to combine it ... so, it is equal to the old way to solve the problem with Decorator mode, isn't it? :) However, if there is a way to make the LoggerProxy class with "imitation" other classes, or say that it seems to be in the case of calling the code and the proxy, the previous code can be settled! So we need a so-called transparent proxy, also referred to as TP)! Good news: There is also such a transparent agent class (__transparantProxy) in the CLR; unfortunately, we can't let your agent class derive from the transparent agent class to get this ability (just as most people hope), Cary can't make CLR think about a transparent "imitation" another class. To get a transparent agent in the CLR, we actually need a real agent (RP).
A real agent is a class that is derived from System.Runtime.Remoting.Proxies.RealProxy. The primary feature of this REALPROXY class is to help us dynamically generate a transparent agent class instance that can be transparently compatible with a specified class in runtime. How do you tell it what we want "imitate" class? You need to explicitly call a protected realproxy (Type ClasStoproxy) constructor in this class from the constructor derived from the RealProxy class, which is incorporated into the type of transparent agent to imitate, as shown below:
Using system.runtime.remoting.proxies;
Public Class MyRealProxy: RealProxy {Public MyRealProxy (Type ClasStoproxy): Base (ClasStoproxy) {...}}
This way, when constructing a new instance of the MyRealProxy class, RealProxy will help us construct a transparent agent that can be transparent in the interior! And when you get the instance of this new real agent, you can use its getTransParentProxy () method to get the inside of its internal constructive transparent agent. In order to verify that the transparent agent imitation can imitate any type of extraordinary ability, please add and run this test code in unit test:
public void TestTransparentProxy () {Type classToProxy = typeof (Calculator); RealProxy realProxy = new MyRealProxy (classToProxy); object transparentProxy = realProxy.GetTransparentProxy (); Assert.IsNotNull (transparentProxy); Assert.IsTrue (classToProxy.IsInstanceOfType (transparentProxy)) } We first choose a classtoproxy, and then construct our real agent (REALPROY), and then remove the internal dynamically generated transparent agent instance to imitate the type of agent type from the created real agent instance. TRANSPARENTPROXY). Next we verify two things: First, our transparent agent is not empty reference (indicating that it is indeed a transparent agent); then use the type.isinstanceOfType () method to verify the type of object is really wishing to imitate the type ( Of course, you can also write to detect the form of static types, namely Assert.IStrue (TransparentProxy is Calculator), but this method in code is possible to dynamically test type) ......
(Rely ... Meng! Compile not to go!) Hey, friends who want to learn too lazy to do it or active, actually verify the above code, so the impression is deep! :)
In fact, there is a problem, and the new problem is precisely the driving force of us to learn new things. @ # $ #% $ & * &% ... still let us first solve the problem of compilation. Look at the error message know: Our own defined real agent class (MyRealProxy) does not implement a method called invoke (). Turn over the document, discover this method to pick a type of parameters for system.runtime.remoting.Messaging.IMessage, and return a same type of object - what is this stuff? No matter, I have realized it (I will explain this later)! Who let REALPROY are an abstract base class, no matter what you have to remember to achieve this method when you derive it from this class:
Using system.Runtime.Remoting.MESSAGING; Public Class MyRealProxy: RealProxy {... public override iMessage Invoke (iMessage MSG) {Return null;}}
Friends using Visual C # .NET 2003 are blessed. If you can't stand it, you can also check yourself to check the definition of the method of Override, just get "Override" in the editor, then after you press the space - - "Till, don't tell me ..."
This compilation is definitely no problem, running NUnit Execute the TestTransparentProxy test node, you will immediately discover the second question (enough - a next two): An exception is generated when the RealProxy (Type Classtoproxy) constructor is executed Saying ClasStoproxy must be a type derived from the MarshalByrefObject type. What is this stuff? We still have to let go later. In fact, familiar or use .Net Remoting friends know that if you want an object to go out of AppDomain around the world, it or if MBV (Marshal By Reference), or be MBR method That is to let the type derive from this weird MarshalByrefObject (as for the method of MBV object, there are two kinds, please review it!) So we follow the CLR protest, specify the base class of our Calculator class as MarshalByrefObject (or Derived class):
Public Class Calculator: MarshalByrefObject {...}
This time, running the test, you will see green through the symbol, which verifies the basic understanding of TP / RP (Note: If you don't use NUNIT, create or modify the project into the Console type, then use console .Writeline () Output We need to verify the results after Assert - such as console.writeline (TransparentProxy! = Null) then look at it is True is also possible - but you really don't want to install a NUnit?: )
Now let's take a look at what IMESSAGE I have just said. Check the definition of this interface, find that there is a Properties property inside, the type is iDictionary, which means that iMessage is just a message that provides data with a dictionary. I want to know what news this is, we have to study what data is there in this dictionary. Then let's take a look - how to see? We noticed that this iMessage is incorporated when we are called by our real agent class, apparent that we should check the incoming message in this method. But Who will call this method? When is it called this method? Let us first recall how the call is to be done (the picture is gradually falling) ......
In the Feng's computer system, the calling method is performed through the stack. The stack is a data area that calls the code and the transfer parameter data and execution results between the modified code. Even in today's object-oriented world (or even in today's .NET world), ordinary method calls are still done via the stack. However, our advanced code is unless of this underlying mechanism. We just get all the parameters incorporated after entering the method, and returns the return value return to the caller when returned (of course, there is all REF) The value of the / OUT parameter) is good. In other words, because our advanced code cannot be manipulated directly, we can only interpret parameters at the level of the method and return the result, which is difficult to embed extra code for existing methods. Remember how we used Decorator mode to solve this problem? When the invoking code passes the input parameter to a method of Decorator, we can check even these parameters in this method, and then use the mechanism that uses the method call to call a method of forwarding a decorator, until the method Call the core object that arrives inside and returns. This process is actually a series of construction / parsing methods to call the stack process. Using the transparent proxy mechanism in .NET / CLR, the situation has changed fundamental changes (gradually recovering to color screen) ... When the call code acquires a transparent agent instance and treats it as a real object to issue any way to call This transparent agent will use the internal mechanism to intercept the method call and all related data related to the stack (incoming parameter values, parameter addresses, etc.), and store these data into a data structure that can be processed by advanced code. And forward this data structure to the code capable of processing it. As you imagined, the so-called "Senior Code can handle the data structure" is the iMessage we see before (more specific - in which the data dictionary provided); and that "can handle it" naturally Our real agent's code inside! That is, the transparent agent helps us intercept all methods calls from the calling code based on the stack, and packed it into a data dictionary to send us the real agent to our real agent to handle the real agent to process in the high-level language level. It is the core issue that this article is to be described, that is, using CLR's TP / RP mechanism to intercept method calls, implement basic AOP programming tasks - through the initial introduction here, you must have to have this mechanism with traditional-oriented objects (including The difference between the mechanism used by the Decorator design mode described above is preliminary.
In preliminary understanding of these theoretical knowledge, let's take a look at the transparent agent to pack what data on the calling method. First, we change the CREATEINSTANCE () method of the Calculator class to return a transparent agent that can imitate the Calculator class, and the real agent that this transparent agent relies on, it is just now what we wrote. (Is actually I can't do it) MyRealProxy!
public class Calculator: MarshalByRefObject {public static Calculator CreateInstance () {RealProxy realProxy = new MyRealProxy (typeof (Calculator)); object transparentProxy = realProxy.GetTransparentProxy (); return (Calculator) transparentProxy;}}
Now this code should be very easy to understand for you (or I have not written clearly)! After compiling, run the first TestCalculator test, hey ... an error! Looking at the call stack when you are wrong, you will find that it is very simple. We don't do anything in the innovoke () method of the real agent. You can do it! However, the error does not matter, we can first take a check in the end-entered the dictionary of attributes IMessage wisdom of which have their places: public class MyRealProxy: RealProxy {... public override IMessage Invoke (IMessage msg) {IDictionary properties = msg.Properties; foreach (Object Key In Properties.keys) {Console.WriteLine ("{0} = {1}", key, proties [key]);} return null;}}
We know that an IDictionary data dictionary is actually a key / value value to an array. In this newly added code, we enumerate each key value in the dictionary, and print out it and it in the dictionary (you said that I am stupid, every entry in the dictionary is a Dictionaryentry. It should be accessed with entry.key and entry.value to access to entry.key and entry.value ... Unfortunately, this dictionary is not top, so it's more quirky, interested. You can go see it. Source code :). Run the test TestCalculator node again, still an error! Also, we have not solved the problem yet. But even if I have already peek into the output result in the console.out window:
__Uri = __ MethodName = Add__MethodSignature = System.Type [] __ TypeName = AOP.Part.II.Calculator, AOP.Part.II, Version = 1.0 ... __Args = System.Object [] __ CallContext = System.Runtime.Remoting.Messaging.LogicalCallContext
Sure enough, this dictionary contains some information about this method call, guessing it is almost the same: __ methodname is obviously the name of the method being called, __ typeename is a full name of the type of this method, __ args is an Object [] It should be the parameter value passed in when the method is called? What __uri is the stuff? __Methodsignature this type [] What is it? There is also a __callcontext, it looks a bit like the context introduced in the decorator in Decorator, is it? Write two lines of code analysis:
public class MyRealProxy: RealProxy {... public override IMessage Invoke (IMessage msg) {... foreach (object arg in (object []) msg.Properties [ "__ Args"]) {Console.WriteLine ( "Arg: {0}", arg } Foreach (Type Type in (Type ") msg.properties [" __ methodsignature "]) {console.writeline (" Signature: {0} ", type);} return null;}} Run test, really see Expectative results: ... Arg: 3ARG: 5Signature: System.Int32Signature: system.int32
That is, in the data dictionary in the incoming iMessage, all the numerical sequences of all incoming parameters are included in __methodsignature, and the type sequence of the corresponding parameters (Method Signature is translated in many books). Signature method, in fact, its definition is very simple: it is a sequence of parameter types in the parameter list of methods, and its initial use is probably used to combine the method name to identify a specific method to be overloaded).
Now we hope to make the test code to run correctly, which requires us to return the method when calling the method on a real object from the invoke () method. Return to the call code. We write Return 8 ... I am afraid I can't do it. Because the return type of the invoke () method is also an iMessage, that is, the transparent agent wants us to put the return result is also wrapped in a message object - but how do I know how to pack such a data dictionary? Fortunately, I found a class named ReturnMessage, seeing it is dry. We can construct an instance of ReturnMessage, let it bring our return value to call the code back through the transparent agent! This class has two distinct constructors (you turn over the document), one is used to handle normal return (this is the RET parameter, it should be the actual return value), and the other The exception is handled (that is, that E). Outargs / outargscount is not used, it should be used to return output parameters. LogicalCallContext No matter in it, give a null first! What is IMETHODCALLMESSAGE? As the name, a message represented by the method - It turns out that it is an interface from iMessage (more exactful - imethodMessage). If you see the definition, you will understand that it uses many of the properties dictionary in iMessage. The properties and methods are released, so that we can more intuitively access the incoming news that represents the method called. So now we let Invoke () return to the "correct result" to test the test code:
public class MyRealProxy: RealProxy {public override IMessage Invoke (IMessage msg) {... IMethodCallMessage callMsg = msg as IMethodCallMessage; int x = (int) callMsg.InArgs [0]; int y = (int) callMsg.InArgs [1]; int Result = x y; Return New ReturnMessage (Result, NULL, 0, NULL, CALLMSG);}} Compiled and runs test, cool - green.咦,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, Fortunately, it's just a plus, if you count a circumference rate, you will have a good look! You said, this is still not good, I created a Calculator to work, don't work:
Public class myrealproxy: realproxy {public override iMessage Invoke (iMESSAGE MSG) {... Calculator Calc = Calculator.createInstance (); int result = cagc.add (x, y); ...}}}
Ok? It seems that it is not possible to die, because we are handling this add () method call, and the instance returned by CREATEINSTANCE () will also be a transparent agent (although not the same instance). Here if you don't have to transparent agents, we need the core realization of the truly work, this good office, when constructing the real agent, you will not be able to work in a real object.
public class Calculator: MarshalByRefObject {public static Calculator CreateInstance () {Calculator realCalculator = new Calculator (); RealProxy realProxy = new MyRealProxy (realCalculator); object transparentProxy = realProxy.GetTransparentProxy (); return (Calculator) transparentProxy;}}
In this case, we have to add a corresponding constructor for MyRealProxy:
Public class myrealproxy: realproxy {... private marshalbyrefObject target; public myRealProxy (MarshalByrefObject target): base (target.gettype ()) {this.target = target;}}
After such transformation, when we encounter Invoke () call, you should access the true work of the true and managed Calculator object that started to start and use it. But ..., etc., how should we forward the way to this object from the method you get there from the transparent agent? Can we do not operate the stack! I can't write int result = (Calculator) Target .Add (x, y)! We plan to let this real agent to serve the 1000 ways of 50 components ... Our answer is the remotingServices.executeMessage () method. RemotingServices is a tool class in the System.Runtime.Remoting Namespace, which provides a lot of practical auxiliary methods to help us implement a lot of underlying classes including real agents. Its ExecuteMessage () method is used to be super simple, and the effect is also a flash. With its help, we do not have their own touch those InArgs what it: public class MyRealProxy: RealProxy {public override IMessage Invoke (IMessage msg) {... IMethodCallMessage callMsg = msg as IMethodCallMessage; IMessage returnMsg = RemotingServices.ExecuteMessage (target , Callmsg; Return ReturnMsg;}}
Compile and run test, you can find everything is normal. However, things happening are the most important: we already have the basic means of intercept any method of any MarshalByrefObject object, then there are more things that are left! Here, it is like this, it is like this, the transparent agent is responsible for converting the stack of the method into a message and forwarding the real agent of the real agent () method that consulates it, and who returns the message Convert the stack and send it to the real object? Who is the result of the real object method to package the INVOKE () method from the result of the real object method from the stack to the message back to the invoke () method? This guy is actually StackBuildersink, and we will mention it later, and now make a look.
Go back the head to carefully observe the above code, you can find that the implementation of the truly real object method is to occur when the transutementage () method of calling RemotingServices. Before it, we can obtain (or even modify) all information recall by callmsg (the pre-processing of the AOP basic operation); after it, we can get (or even modifications) all about the method to return. Information (that is, AOP basic operation of post-processing! Remember two different situations - Normal return and throw an exception) - Tips you prompt you to see the iMethodReturnMessage interface corresponding to iMethodCallMessage, we are from ExecuteMessage The iMessage obtained by the method is to be converted to this interface.
Ok, please write a real agent class in accordance with the current information, such as call LoggingProxy. You can make this real agent to print a record in each method of the agent's object, such as:
[2004-02-16 12:34:56] Calculator.Add (3, 5) - (37ns) -> 8 should not be difficult? Write well, continue to look down ...
Now we have two real agent classes (what? Still not write? Don't be lazy :), one is myRealProxy, it is simple to output the contents of Msg.Properties when the method is called; one is you just written LoggingProxy, it should be able to record log information called each method. What should we do if we want to combine the functions provided by these two real agents like combined Decorator? With the knowledge and ideas of the previous text, we can first try again to connect the transparent agents constructed by several real agents to connect to each other with Decorator mode (okay? - how can you notice transparent agent? Magic - it is unlimited to the instance of calling the code. like this:
public class Calculator: MarshalByRefObject {public static Calculator CreateInstance () {Calculator realCalculator = new Calculator (); RealProxy realProxy = new MyRealProxy (realCalculator); Calculator fakeCalculator = (Calculator) realProxy.GetTransparentProxy (); RealProxy loggingProxy = new LoggingProxy (fakeCalculator) Calculator result = (calculator) LoggingProxy.getTransparentProxy (); returnrate;} ...}
The transparent agent returned by CreateInstance () is actually constructed by the LogGingProxy instance, and the real-working object inside the instance is a transparent agent constructed by the previous MyRealProxy and returned, and the final work is the first constructed Realcalculator. . When the call code is called to this transparent agent, the LogGingProxy's transparent agent first packs the information on the stack to the INVOKE () method of the LoggingProxy; then call RemotingServices.executeMedEMEMEMESSAGE () in the loggingProxy INVOKE () (Convert back to stack by StackBuildersink - Remember the friend mentioned earlier? :) Forward to the target object - the transparent agent constructed by MyRealProxy - however, this forwarding will be once by the message to the stack The process of the message (else don't forget, no matter how often returned from the invoke () method, the transparent agent converts back to the stack-based method call results - calling the code to this string of this road It's really unknowing)!
A strike? This can't help but think of another design pattern, that is, Chain of Responsibility (duty chain). We can package a series of processing code interested in the method to call the message to a separate, highly constructed message processing object, and form a duty chain by forming its string into a chain list, let the method call the message along this The bellows go all the way until the real object is arrived until the real object - and the post method returns the message and then returns each participant on the sequential channel. Through this mechanism, we avoid the additional overhead that repeatedly conversions between stacks and messages, thereby intercepting all method intercepts processing activities in a unified message-based world - that is, we need place a chain reaction - apparently, it should be a real agent, we might as well call it MessageChainProxy, namely news agency chain it: public class MessageChainProxy: RealProxy {private MarshalByRefObject target; public MessageChainProxy (MarshalByRefObject target): base ( Target.gettype ()) {this.target = target;} public override iMessage Invoke (IMESSAGE MSG) {Return Null;}}
How, this six lines realize a non-working REALPROXY skeleton code now you can also write it out? Now let's start writing substantive code. As mentioned earlier, if there are multiple pieces of code that need to be connected to the method call message, we want to encaps them, becoming a message processing and forwarder - just in the .NET Remoting has defined this. A semantics, we may wish to use it directly, this is the iMessageSink interface (same in system.Runtime.Remoting.Messaging namespace):
Public interface iMESSAGESINK {iMessage SyncProcessMessage (iMessage MSG); iMessageCtrl AsyncProcessMessage (iMessage MSG, IMESSAGESIN);}
Take a closer look. In fact, this interface is very simple: NextSink as the name suggests, it must be the next acceptor in the message processing chain; SyncProcessMessage is definitely a method of truly handling method messages (this is used for synchronization occasion, and the other A begins The asynchronous occasion-asynchronous method is also supported in .NET is also supported in .NET). Considering that we may need to write more and more messaging processors, you may wish to provide a basic implementation for this interface:
public abstract class MessageSinkBase: IMessageSink {private readonly IMessageSink nextSink; public MessageSinkBase (IMessageSink nextSink) {this.nextSink = nextSink;} public IMessageSink NextSink {get {return nextSink;}} public abstract IMessage SyncProcessMessage (IMessage msg); public virtual IMessageCtrl AsyncProcessMessage (IMessage MSG, IMESSAGESINK) {Return nextsink.asyncProcessMessage (msg, replysink);}} In this abstract base class, we implemented two methods in the iMessageSink interface: NextSink's getter because it is unlikely to be flexible Therefore, it is achieved as the default non-virtual method (um? How to make a way, don't you attribute? Don't forget the property is actually a one or a method! Logic (this is not a template method), so declares that it is still very natural; asyncProcessMessage, considering that all message receivers supports interception processing of asynchronous methods, it is better to provide a default implementation Forward to NextSink (or implementing NotSupportedException, this is also a common practice), that is, derived classes can be able to achieve or override this logic as needed. Now we can extract the Invoke () method in the original REALPROY as the syncprocessMessage () in iMessagesink, because there is nothing to say, this is no longer wasting network bandwidth 喽 (% $ ^ %%) $ #). Let's still see how MessageChainProxy implements the duties, because this block is not a topic of this article, so I will no longer be derived, I believe you can understand this part of this code:
public class MessageChainProxy: RealProxy {private MarshalByRefObject target; private IMessageSink headSink; public MessageChainProxy (MarshalByRefObject target): base (target.GetType ()) {this.target = target; this.headSink = new TerminatorSink (target);} public override IMessage Invoke (IMessage msg) {return headSink.SyncProcessMessage (msg);} public void AppendSinkType (Type sinkType) {object [] ctorArgs = new object [] {headSink}; IMessageSink newSink = (MessageSinkBase) Activator.CreateInstance (sinkType, ctorArgs) Headsink = newsink;}} code, Headsink is the head junction of the entire message processing chain, and the entire call chain is explicitly called by each Sink in its own xxxProcessMessage () method (MSG) To form it (this is called Decoratee.Method () in each decorator in Decorator mode). It is worth noting that when MessageChaInProxy is completed after the construct is completed, there is no call to any appendsinktype (), we hope that it will work properly (that is, nothing to do, as long as you do not throw out empty reference exception Ok, so we have to introduce a concept of TerminatorSink - this is the same as the Terminator concept in the SCSI device chain, is a terminator. We have achieved it as follows (note that the part of the bold is fine, I will no longer analyze - if you still can't understand, then my article is white, I :)
private class TerminatorSink: IMessageSink {private MarshalByRefObject target; public TerminatorSink (MarshalByRefObject target) {this.target = target;} IMessageSink IMessageSink.NextSink {get {return null;}} IMessage IMessageSink.SyncProcessMessage (IMessage msg) {return RemotingServices.ExecuteMessage ( Target, MSG as ImethodCallMessage);} iMessage IMESSAGESIN.ASYNCPROCESSMESSAGE (IMESSAGE MSG, IMESSAGSINK) {throw new notsupportedException ();}}
Think about it, the truth is actually very simple. This is also a very common object-oriented design pattern. Some places are called null object, in short, it is also an alternative to the occasion that may have an empty reference, thereby simplifying Complex conditions judgment logic - Write here, I am afraid there is a master to protest. In fact, I will give you more ideas to solve problems. How do you choose or put it in a specific environment? More choices are better than choosing to choose better (let alone this concept is also putting mats for some of the contents in the next item :) Well to write down ... Because this TerminatorSink is only used in our own MessageChainProxy , So it is appropriate to define it as a private embedded class.
Public Class MessageChainProxy: RealProxy {Private Class Terminatorsink: iMessagesink {...} ...}
Now we can use this message processing chain proxy class with scalable capabilities in the Factory Method of Calculator:
public class Calculator: MarshalByRefObject {public static Calculator CreateInstance () {Calculator realCalculator = new Calculator (); MessageChainProxy chainedProxy = new MessageChainProxy (realCalculator); chainedProxy.AppendSinkType (typeof (MyMessageSink1)); chainedProxy.AppendSinkType (typeof (MyMessageSink2)); ... Return (Calculator) ChainedProxy.getTransparentProxy ();} ...}
Here you need to pay attention to it, because it is an ordered call chain, the order of adding message processor nodes has a great impact on the final execution logic. For example, you have two message processors: one is to do method logs, the first addition to the processing chain (its nextsink will point to TerminatorSink); the second is to do access control (is based on the security identity of the code caller) And permission decisions whether this method is allowed to be executed, and then it is added to the processing chain (NextSink pointing to the front method log processor). At this time, when the calling code issues a call to the method's method, the stack-based call first is intercepted by the CLR's transparent agent, and converted into method call messages, and then sent to the corresponding real agent is our MessageChainProxy The invoke () method is handled, and the method immediately passes the message to Headsink, which is the SyncProcessMessage () method of the access control processor that is last added to the processing chain. At this time, if the access controller allows the method to continue, the same method call message is forwarded to the method log processor processing pointed to the nextsink, the final method message reaches TerminatorSink and is forwarded to transuteMessage () Method for RemotingServices - Method - I still remember the execution logic of this method: a transparent agent's "anti-transparent agent" is StackBuildersink (you see, its name has hints it is actually an IMESSAGESINK!) Ultimately use incoming method to call messages Reconstruction call stack and send our Calculator class to real work ... but if there is no pass in the access controller, it is thrown out of safety unusual (to remember to construct a representative method to call exceptions with ReturnMessage Return to the message 噢! Otherwise, the transparent agent is no longer transparent ...), obviously the approach is not to be executed at this part of the method (more 甭 甭 甭 甭 随 t t t 了Calculator will never see this time-free approach.). So, if you want to record all the method call requests, you should put the method log processor in front of the permission control, that is, the log processor will be added after adding the access controller. This truth is also relatively simple, and the implementation of the Decorator model said in front - I will look back in this article again here.
Broken to this (good), we have had a preliminary understanding of the method interception mechanism based on TP / RP and IMESSAGE, and uses these knowledge should have solved a lot of problems. Then there is any use of Decorator-based mechanism described above. actually not. The truse of these two mechanisms itself also implies that they will play different advantages in different occasions and in situations. First, it is undeniable that Decorator's stack-based method forwarding mechanism is definitely more efficient than iMessage-based conversion transfer mechanisms, of course, for many enterprise applications involving remote methods or desktop applications for local operations Performance may not be mainly contradictory, but you still have to remember this potential trap - especially in the starting phase of the selection technology. However, through a transparent agent-based mechanism we can focus some universal logic that is not related to specific object types or even methods in a real agent, and use the object build mode to dynamically (ie, runtime) "sew" to the target Every way of objects, this mechanism is very tempting for adding N more universal behavior for a large number of objects - even easy to make you compromise therefore a little performance! However, since it is very versatility, then for the need for targeted occasions, for example, according to the value of the parameter introduced in the method, it is more flexible to implement the Decorator interface-based approach to the Decorator interface. The mechanism designed for versions to deal with targeted occasions itself will definitely be lost. Therefore, the best ending may still need to integrate two mechanisms, which means that some of the functionally stacked features that need to be treated, applied to the core object, and uses some universal functions to utilize transparency Agent's magic effect on the object - this mechanism is in fact that it has been designed and adopted in our current project development, and it has brought a lot of benefits to our project development (reducing thousands of lines of code). Software system that is easy to maintain and flexible - this is not enough? :) Finally, put some small skills related to this part of the content, maybe it is useful to you:
in
IMethodMessage
Introduced in this interface
Methodbase
This property, through it, you can enter
REFLECTION
The type information on the worldview method includes custom properties. Use it you can make real agents
INVOKE ()
The method provides parameters, thereby increasing the flexibility of the system - remember: this is just
increase
A flexible mechanism does not replace other mechanisms - you can still read the relevant parameterized information from the configuration file, right
PROGAME
? :)
Real agents are not only for
MarshalByrefObject
Constructing a transparent agent, which can also construct a transparent agent for an interface. So you can use a real agent to provide methods to call messages based on many different interfaces - just like we are
Elegantdal
Like the method used, as long as the interface is defined, obtain the transparent agent, it is not necessary to provide the implementation class of the interface, you can complete the cumbersome features that you need to implement one by one. For details, please refer to us directly at the beginning.
ReturnMessage
Returns the implementation of the execution result, the principle is the same.
However, when a transparent agent agent is an interface, you will not be able to use it.
RemotingServices.executeMessage ()
Forward method call messages to it. Here you need these two helpers:
RemotingServices.istransparentProxy ()
with
RemotingServices.getRealProxy ()
. Once you have a real agent in your hand, how to forward the news?
More tips still leave you yourself to explore it in practice, I have said that I am not intended! :) Here, the content of this article has been basically completed, but the picky readers must have a lot of uncomfortable feelings, such as:
Want to intercept the constructor call, but the real object has been created in the real agent, what should I do?
To use
TP / RP
Packaging object, you must write
Factory Method
Package the entire construction process, can not use more intuitive
New
Command to create a proxy object directly?
If you are
Calculator
Some methods of classes will represent object instances
THIS
When passing to the outside of the object, this reference is a reference to the real object directly! So you carefully set the key than the access controller, etc. will be bypassed! How can I guarantee that the external call always has a heavy level I set up?
More unhappy, please send me by feedback :)
In fact, these contents are also scheduled to be completed in this article, but the space is limited (excuse!) I think it is still to continue to analyze and solve it in the next text! It is still as always, please tell me through the message or email, please tell me through the message or email, your support is the biggest movement I wrote! Thank you again! :)