In the previous article, we use the real agent / transparent agent technology in the .NET Remoting infrastructure to implement universal methods that are not targeting specific types, specific methods. Due to too many technical content, I wanted to write a new piece of content in the last big content. In this text, we introduce more in-depth technologies that can be implemented in .NET, and combine the advantages and disadvantages of this technology, one has been applied in our design. AOP frame prototype thinking.
In the end of the previous text, we put forward the uncomfortable links in the technical solution. The biggest voice of the masses is that the structure of the object is not enough INTUTIVE: one side needs to introduce Factory Method (such as CreateInstance ()), on the other hand In this method, it is not enough Simple (Proxied Real Object, Real Proxy, GetTransParentProxy ...), and because we are in real objects, Therefore, we naturally cannot intercept the way to call the object constructor - this shortcomings are fatal for many functional implementations (for example, we need to monitor the creation of various application objects in the system, we need to perform in the constructor. The corresponding count logic; additional record and analysis of the object constructor is also commonly used). In this article, we introduce new technologies to solve these problems one by one!
First of all, is there a simpler way to construct the object being a proxy? Is there a way to set a good news interceptor before constructing an object? Let's consider this line of code:
Calc Calc = New Calc ();
This is the easiest way to create objects! If we can override the default behavior of the New Directive makes it easy to use our own factory - Unfortunately, the CLR / C # does not support override New operators. It seems that this road is not ... But - I suddenly thought of it in .NET Remoting, I can use the New operator to directly construct a remote object (and the remote object is not a proxy object)! As long as Calc is a MARSHALBYREFOBJECT type and correctly configured for WellkNown Type in Remoting, when the CLR encounters new instructions in the C # (i.e., it is the newobj command in IL, it will hand over the object activated .NET Remoting To be done. For example, this:
PUBLIC CLASS CALC: MARSHALBYREFOBJECT {...}
RemotingConfiguration.registerwellknownClientType (TypeOf (CALC), TCP: // RemoteServer: 8080 / CalcService ");
Calc Calc = New Calc ();
Assert.istrue (RemotingServices.IstransparentProxy (CALC));
You can try this kind of writing, look like it is really show - as long as we can implement one and object activation address (URL) supporting Channel Channel in the specified location to call the message and handle it! Just ... Is this not a little more?
Think about what clues ... SYSTEM.ETERPRISESERVICES, SERVICEDComponent! Because it is to let the .NET object can use the Component service provided by COM , it should also apply transparent agent technology. Let's verify (remember Add Reference to System.Enterprise "Assembly!): Public Class Calc: ServiceDComponent {...}
Calc Calc = New Calc ();
Assert.istrue (RemotingServices.IstransparentProxy (CALC));
Console.writeLine (RemotingServices.getRealProxy (CALC) .gettype ());
Of course, in order to implement SERVICEDComponent in an Assembly, we must first give Assembly strong name, the principle is not much, as long as it is simple to apply such a custom property for the Assembly:
[assmbly: system.reflection.assemblykeyKeyfile (@ "c: / xxx.snk")]]
This c: /xxx.snk file is generated by the SN tool for .NET SDK in advance (eg SN -K C: /XXX.snk). Compilation execution, found there is so! At this point, the new CALC instance is indeed a transparent agent package, and the real agent corresponding to this transparent agent is a class called ServicedComponentProxy inside System.EnterpriseServices (Note the output result of the console.writeLine () method) . It means is right, but it feels a bit "pass", or let's study what servicedcomponent is there. Why is a class that is set from it from it to NEW?
It turns out that ServicedComponent is derived from System.ContextBoundObject, and this class is derived from MarshalByrefObject. It seems that MarshalByrefObject cannot meet our needs, and ServicedComponent is set for a base class set for a specific development requirement, then this conteboundObject in it should be a base class that provides a common feature! Hurry to confirm, turn the CALC's base class into this contetboundObject:
Public Class Calc: contextBoundObject {...}
Run the same test code in front ... Till! It's it! It seems that as long as a class is derived from contextBoundObject (in the future, we also use CBO to refer to this object), then when it is new, it will be set on a transparent agent - and the actual agent behind this agent Class (Note again to see the type of transparent agent for console.writeLine () output!) Is the system.runtime.Remoting.Proxies namespace RemotingProxy (this is an internal type, so there is no documentation can not be directly Use - in fact, this real agent is a class that implements all the core mechanisms and extension mechanisms of .NET Remoting!).
Regarding the detailed use and method of use of CBO, please also check the MSDN document (趁 有 看 还 还 吧 吧 吧 吧 喽 看 看 看 看 喽 喽 喽 喽 喽 喽 喽 看 喽 喽 喽 喽. ContextBoundObject, as the name, Object That Will Be Bound with a context. The so-called context here refers to a logically executed environment. There is one or more contexts in each appdomain, which is like one or more appdomains per hosted process. The first context in AppDomain is also called Default Context, all objects that are not CBO (also known as context-agile object) are created and run in this Context. CBO objects will be created and run in the context you need or approved. The method calls across the context are forwarded by transparent agents, because the execution environment set up by Context is guaranteed by several object usage rules, only by the proxy mechanism can be ruled by setting the method interceptor. Guaranteed (this and the concept of COM / COM are similar, all services of COM are also achieved by similar ways to intercept the agent - in fact, more accurate, should be .NET CLR to borrow a large number of COM design and implementation mechanism). The Context where the thread is located can be obtained through the Thread.currentContext in the System.Threading Namespace, and its type is the Context class in system.Runtime.Remoting.Contexts namespace, where each context is a unique ID (ContextID) ). The previously said Default Context is also a static property (DefaultContext) on this context class. We can observe the logical context that occurs when the occurrence of each method calls when each method is across multiple objects on a thread. For example, this (remember using system.threaming!):
Public Class Calc: ContextBoundObject {Public Calc () {Console.Writeline ("CALC ():" thread.currentContext);} public int Add (int x, int y) {console.writeline ("Add:" thread. CurrentContext); Return X Y;}}
Public class appli {public static void main () {
Console.writeline ("main:" thread.currentcontext);
AskERT.Arequal (3, New Calc (). Add (1, 2));
}
You can see that the method call to the CALC instance is in another context (as comparison, you can declare the CALC to see again from MarshalByrefObject or Object). (It is best to find a development environment, experiment - I'm noticed on April 1;)
In addition to contextid, another important concept on the Context object is Context Property. There is a property called ContextProperties in the Context class, which is an icontextproperty []. So what is Context Property? This can be any object that implements the IconTextProperty interface. This object can provide some characteristics for its associated context (here I call the property "feature", because if it is called "attribute", it is possible to confuse Attribute - There is also a thing called Context Attribute! This will encounter later). On the icontextproperty interface only define a Name property, there are several ways, let's talk later. In addition to providing read-only arrays, you can see that both the Context object also provides both methods of getProperty () and setProperty (). If you are interested, you will find that there is a stuff called LeaselifetimeServiceProperty one up to each context, and use its name to getProperty () can also get, but a setProperty () will be wrong - just like Like the object's Property, Although the Property of Context is not part of the type, it can only be formed during the Context constructor and has always existed within the survival of Context. How do I specify some features for Context? We need to use Context Attribute (in .NET, Attribute, all) can set the required CONTEXT Property in Context by reflecting attribute, because Context is a suitable execution environment for ContextBoundObject. So these context attributes are naturally attached to the CBO derived type, just like this: [SomeContextAttribute] public class AnyContextBoundObject: contextBoundObject {...}
So how do Context Attributes provide the specified CONTEXT Property to CONTEXT? In fact, a context attribute is an Attribute (what? Property can also implement the interface of the icontextttribute interface? After you will become more and more in this mechanism, it will be in CBO objects. The structured stage is reflected instantiated. Subsequently, the CLR interacts with the Context Attribute interface through this iconTextTRibute interface to know the appropriate execution environment you need (also determined by the Context Properties). In order to do this, IconTextAttribute defines the following two methods:
Public interface icontextttribute {bool iscontextok (Context CTX, iconstructionCallMallMessage CTORMSG); Void getPropertiesFornewContext (iconstructioncallMessage Ctormsg);
}
Each context attribute is in the constructor of Context (usually constructed by CBO object constructive) will be asked first asContextok, that is, the newly created CBO (which configuration method of which object is known from CTORMSG is used to construct Can CBO objects can exist in a given CTX? This purpose is mainly to reduce the number of potential contexts in AppDomain. If some CBO types can share an execution environment with the desired characteristics, you can do any new environment, but as long as you construct and execute in an existing environment. Enough. If all context attributes set on the CBO type agree with a given context (the context in which the call code is located) is OKAY (ie, iSconTextok returns True), then the new CBO will be bound to this Context. Otherwise, there is only one attribute to vote the veto, and there will be a new context that is created immediately. Following the CLR once again asked if the context attribute this new constructed Context is Okay, this time most people will say no. - After all, it starts. So at this stage, the Context constructor will call each of the getPropertiesforneWconText () method for each Context Attributes that is considered a new CTX not okay - In fact, this method is called setPropertiesforneWContext () may be more appropriate, and Context Attribute can be incorporated with this method. Context Properties list in the constructor method call information (CONTEXTPROPERTIES) is increasing for new Context Properties!
Say more, or write some code to see it! If we ask the Context of the CALC object to have a characteristic of CallLogger (first without using this feature to do what we can do), we will specify it on the CALC class:
[Calllogger] public class coc: contextboundObject {...}
And then we have a Context Attribute called CallLoggerattribute:
public class CallLoggerAttribute: Attribute, IContextAttribute {public void IsContextOK (Context ctx, IConstructionCallMessage ctorMsg) {return false;} public void GetPropertiesForNewContext (IConstructionCallMessage ctorMsg) {ctorMsg.ContextProperties.Add (new CallLoggerProperty ());}}
Note that we first returns iScontextok (), make Calc will always be placed in a new environment (but when writing your own Context Attribute, you should carefully consider as much as possible to share existing environments as much as possible Reduce the creation of a new environment and overhead cross-environment calls). Subsequently, we use the incoming ContextProperties property that is incorporated in the getPropertiesfornewContext () method (this property is ILIST, so it can be add / transide) to add our desired environment. properties, such as CallLoggerProperty: public class CallLoggerProperty: IContextProperty {public string Name {return "CALLLOGGER";} public bool IsNewContextOK (Context newCtx) {return true;} public void Freeze (Context newCtx) {}}
When ICONTEXTPROPERTY is mainly, it is mainly to provide a property (this name must be unique in the entire context!), The other two methods are used to confirm that each other has no conflict after all environment features ( IsnewContextok, the other is to notify Context Property: New Context is completed, please enter the freezing state (Freeze - usually does not need to take any action).
With these codes, let's take a look at whether the new constructed Calc instance is executed in an executive environment with the environmental characteristics we need:
[CallLogger] public class Calc: ContextBoundObject {public int Add (int x, int y) {IContextProperty property = Thread.CurrentContext.GetProperty ( "CALLLOGGER"); Assert.IsNotNull (property); Assert.IsTrue (property is CallLoggerProperty); ...}}
As we wish, by applying context Attribute for CBO derived class, we can control the CBO type construct and run the execution environment, which is set to set the Context Properties in the environment, thereby in the internal method of the CBO instance. Get context properties by Context Attribute by context.getProperty (). Very good, all - what can be used? Now let's fool your imaginary wings - CONTEXT Property is not an object? If it has its own data and method, can we use it to do something? For example:
Public class callloggerproperty: icontextproperty {public void log (string message) {console.writeLine (message);} ...}
Then we can use this feature in the environment in CALC:
[CallLogger] public class Calc: ContextBoundObject {public int Add (int x, int y) {IContextProperty property = Thread.CurrentContext.GetProperty ( "CALLLOGGER"); CallLoggerProperty logger = property as CallLoggerProperty; if (! Logger = null) logger. Log (String.Format ("Add ({0}, {1})", x, y); int result = x y; if (logger! = Null) logger.log (String.Format) ("= { 0} ", result));}} Is this not tossing? What can I do if I have written a word now around a circle? ! Oh, but at least we can now freely switch this Log method? Release [CallLogger], the program is shown, that is, there is no log function (because logger == null) - But after all, Calc's specific classes should be understood (or dependent) This specific CallLoggerProperty can use it to use it LOG operation, This dependence is very ignorant (like PROGAME students pointing out - but please note that the interior of the class is not dependent on Context Attribute!), If .net is really designed to be this look. It is not to be salad ... Fortunately, calm down before anger, because I found that most things are more scientific than we imagined (especially if there is no thorough understanding), let us imagine the ideal situation: Calc does not need to understand any Context Attribute or Context Property, which only needs to do what it needs; and since Calc has run in a context, the method of calling the context is automatically made by the transparent agent, then there should be Some mechanisms can work with transparent proxy. Originally these explicit code can be hidden behind - and we have repeatedly emphasized in the previous article, and the transparent agent based interception mechanism is for type and specific The method is not sensitive, which is also an important feature of it, and we should use it reasonably.
What is this mechanism? Remember the iMessages ITSINK in our last? I still remember that we use a chain agent to connect a string of method messaging interceptors to intercept and handle the way to call messages? That's right, this is the chain method interception mechanism used by .NET! Before you continue, let's write a CallLoggersink, follow:
public class CallLoggerSink: IMessageSink {private readonly IMessageSink _nextSink; public CallLoggerSink (IMessageSink nextSink) {_nextSink = nextSink;} public IMessageSink NextSink {get {return _nextSink;}} public IMessageCtrl AsyncProcessMessage (...) {...} public IMessage SyncProcessMessage (IMessage msg) {IMethodCallMessage callMsg = msg as IMethodCallMessage; msg = NextSink.SyncProcessMessage (msg); IMethodReturnMessage returnMsg = msg as IMethodReturnMessage; LogMessageProcessing (callMsg, returnMsg);} protected virtual void LogMessageProcessing (IMethodCallMessage callMsg, IMethodReturnMessage returnMsg) {string methodName = callMsg.MethodBase .Name; String [] methodargs = new string [callmsg.inargcount]; for (int i = 0; i {methodargs [i] = callmsg.inargs [i] .tostring ();} string returnValue = ReturnMsg.RtrinValue; console .Writeline ("{0} ({1}) = {2}", MethodName, String.join (",", MethodArgs), returnvalue;}}
This part is not explained, and if you don't understand, you can refer to the relevant content in the previous article. What we want to do now is to implant this generic method message interceptor into any CONTEXT transparent agent (actually called by the corresponding real agent) to form an execution environment with a specific function - this Work is made by context property!
In fact, a Context Property class is basically no big purpose if the light implementation IconTextProperty is basically, it is only provided as the most basic information as an environmental characteristic. To implant the Message Sinks in the TP / RP of the CONTEXT Property, you also need to implement one or more icontributexxxxsink interfaces - XXX here may have four possibilities: Envoy, ClientContext, ServerContext, Object (there is still one The species is Dynamicsink, but it is still not quite the same followed by it. Complex, watch the document ... more complex, the document says that these are designed to support the .NET framework infrastructure, not allowing the user code to use. Still studying together! :) First, speculation from the name: icontributesomething, should be "contributing" something. Look at the methods defined in the interface, but only one getxxxsink (), the return value is iMessageSink, and the parameters are also very simple, no more Nextsink (obviously for us to construct iMersink) or plus a MarshalbyrefObject ( Is it the Target that I have been told before :). In fact, these are so-called Factory Methods, and Context Property is used to create this series of iMessageSink implementations! But how come these four SINK? Use the existing knowledge to actually study! Let's write a simple SINK and return this new instance of this class in every factory method, then analyze what the SINK constructed in each factory method:
public class ResearchSink: IMessageSink {private readonly IMessageSink _nextSink; private readonly string _sinkKind; public ResearchSink (string sinkKind, MarshalByRefObject target, IMessageSink nextSink) {_nextSink = nextSink; _sinkKind = sinkKind; Console.WriteLine ( "{0} Sink is created on { 1} ... ", sinkkind, thread.currentcontext); console.writeline (" / ttarget is {0} ", target == null?"
": Target);
Console.writeline ("/ TNEXTSINK's TYPE IS {0}", NextSink.gettype ());
} Public IMessageSink NextSink {get {return _nextSink;}} public IMessage SyncProcessMessage (IMessage msg) {IMethodCallMessage callMsg = msg as IMethodCallMessage; Console.WriteLine ( "{0} Sink is being called to process msg for {1} {2}. () ", _sinkKind, callMsg.MethodBase.DeclaringType, callMsg.MethodBase.Name); return _nextSink.SyncProcessMessage (msg);} public IMessageCtrl AsyncProcessMessage (...) {...}} then we use a ResearchProperty contribution to all four sink:
public class ResearchProperty: IContextProperty, IContributeEnvoySink, IContributeClientContextSink, IContributeServerContextSink, IContributeObjectSink {public string Name {get {return "RESEARCH";}} public bool IsNewContextOK (Context newCtx) {return true;} public void Freeze (Context newCtx) {} public IMessageSink GetEnvoySink (MarshalByRefObject obj, IMessageSink nextSink) {return new ResearchSink ( "Envoy", obj, nextSink);} public IMessageSink GetClientContextSink (IMessageSink nextSink) {return new ResearchSink ( "ClientContext", null, nextSink);} public IMessageSink GetServerContextSink (IMessageSink nextSink) {return new ResearchSink ( "ServerContext", null, nextSink);} public IMessageSink GetObjectSink (MarshalByRefObject obj, IMessageSink nextSink) {return new ResearchSink ( "Object", obj, nextSink);}}
Write a corresponding context attribute to apply this feature to any CBO type definition:
public class ResearchAttribute: Attribute, IContextAttribute {public bool IsContextOK (Context ctx, IConstructionCallMessage ctorMsg) {return false;} public void GetPropertiesForNewContext (IConstructionCallMessage ctorMsg) {ctorMsg.ContextProperties.Add (new ResearchProperty ());}} The above type now Calc Apply [Research] attribute, and call its add () method ... See a series of output information? Feeling is not bad! We can see that all other Sinks have been created and called, in addition to ClientContext, and we even received the method of calling the CALC constructor in the SERVOONTEXTSINK method message processor! In fact, the four SINK mentioned above corresponds to four different phases of the method of interdenvironmental calls: When a CBO object instance receives a method called from other environments, we call this object as a Server Object, and The environment is also server context, then .NET Remoting will call the registered ServerContextSink and ObjectSink method message processors (ie, syncprocessmessage () method); Similarly, when a CBO object is to another When the CBO object is called, when we call this CBO object to the CBO object, which is called CLIENT OBJECT, and its environment is also Client Context, then .NET Remoting will call the registered Envoysink and ClientContextSink method message processor - But at this time, EnVoySink is not contributed by Client Context, but is contributed by Server Context, so it is the special envoy of Server Context for the CLIENT CONTEXT that initiates calls (Envoy)! Of course, ClientContextsink is called in Client Context.
Thiefly, these SINK mechanisms provided in. NET Remoting are not the focus of this article. If you are interested, you can refer to some articles in MSDN (if you demand strong, I will also consider detailing several SINK design in future articles. Intent and application situations :). In fact, support for these SINKs are implemented through the previously mentioned RemotingProxy this .NET Remoting central agent, the discussion of these content is more appropriate in the article related to .NET Remoting.
However, using the research tool I give, it is not difficult to personally understand the design mystery here, and imagine how to use these technical means to achieve a number of AOP aspects of AOP. However, in this article, I still want to continue to expose more technical details. Before proceeding, let's take a look at the features that you have implemented.
First, we can now construct a CBO object directly with the new instruction provided by the C # language and return a transparent agent object using RemotingProxy as a real agent - this solves the problem of the troubleshooting in the previous article; secondly, use RemotingProxy to set up All kinds of mechanisms, we can use Context Attribute to implant the Context Property in custom CBO types, and use the Context Property to inject the method interceptor (iMessageSink) to the execution environment - this can actually do the mergechainProxy Finally, because the establishment of the execution environment is completed before the consignment object instance, the method interceptor is injected during the establishment of the execution environment, so we can already intercept the constructor method used to construct the object instance - This also solved the lack of function mentioned above. So what else can we do? After a period of practice, we have found that if you just need to use some basic development tasks to implement AOP, you need to create a lot of execution environments while creating a CBO object that is created and a specific execution environment. At the same time, method calls are also involved in a large number of environmental switches between different environments. These overheads are actually due to the complex logic implemented by RemotingProxy to support .NET Remoting. Is there a way to replace the default REMOTINGPROY with our own real agent to support the minimum basic mechanism for implementing AOP functions?
Recalling that the study of ServicedComponent at the time can find that when you create a servicedcomponent derived object, the real agent of the transparent agent we get is actually a class called ServicedComponentProxy (not RemotingProxy!). This shows that Net will certainly provide a mechanism that can replace the real agent! Using the .NET Reflector and other tools, we found a serviceDComponentProxyAttribute custom property on the type of ServiceDComponent's derived ContextBoundObject. This attribute is also derived in system.Runtime.Remoting.Proxy PROXYATTRIBUTE. In this custom attribute class, we discovered a use of Virtual's createInstance () method, it seems to be a factory method that truly constructs the CBO object instance!
It turns out that when we construct a CBO object instance, the CLR will first check if the custom property derived from the ProxyAttribute is exerted. If any, the CLR will instantiate the custom attribute and call its CreateInstance () method to get the CBO object instance (MarshalByrefObject). In this method, we can use our own real agent to generate a transparent agent, rather than using the system default more powerful RemotingProxy! In order to confirm this logic, we can replace the default transingproxy:
public class BypassProxy: RealProxy {private readonly MarshalByRefObject _target; public BypassProxy (MarshalByRefObject target): base (target.GetType ()) {_target = target;} public override IMessage Invoke (IMessage msg) {return RemotingServices.ExecuteMessage (_target, (IMethodCallMessage ) MSG);}} Remote a custom proxy attribute derived from ProxyAttribute, constructs an instance of a given type by overlying its CREATEINSTANCE () method, and sets the transparent agent generated by our own specified bypassProxy:
[AttributeUsage (AttributeTargets.Class)] // whose hack to force this public class BypassProxyAttribute:!? ProxyAttribute {public override MarshalByRefObject CreateInstance (Type serverType) {MarshalByRefObject rawInstance = base.CreateInstance (serverType); RealProxy realProxy = new BypassProxy (rawInstance) Return (MarshalByrefObject) RealProxy.getTransparentProxy ();}}
In this code, we first use the CREATEINSTANCE () method of the base class to construct the "instance" (why not use activator.createInstance ()? Please think about it, or try it!) , Then construct our own real agent, pass it into the actual agent example, then generate a real agent, and return it. Please wait until you want to work before you continue - Note New BypassProxy (RawinStance)! If you run this code, you will get a RemotInGexception, say "Trying to Call Proxy While Construction Call is in Progress.". Why is this? It turns out that we call the getType () method of the incoming Target object to get its runtime type in the BYPASSPROXY constructor. This has been working very well. However, at this time, we are in the createInstance () method of BYPASSPROXYATTRIBUTE (), although the object instance has been constructed with base.createInstance (), but the entire constructor has not been completed! So any instance of the object is not allowed at this time. In other words, the just obtained RAWINSTANCE object is just a germination state that is completely internal initialization but has not yet implemented constructor calls (so I call it RAWINSTANCE). Solving this problem is simple, just explicitly incoming Servertype when constructing the real agent:
public class BypassProxy: RealProxy {... public BypassProxy (MarshalByRefObject target, Type targetType): base (targetType) {_target = target;}} [AttributeUsage (AttributeTargets.Class)] public class BypassProxyAttribute: ProxyAttribute {public override MarshalByRefObject CreateInstance (Type serverType) {... realproxy realproxy = new bypassproxy (rawinstance, servertype); ...}}
This time, we will encounter a new exception (still remotingException): "ExecuteMessage Can Be Called Only from the native context of the object.". What is this bird? Debugging, it was found to have already arrived to bypassproxy.invoke (), and at this time we received a constructorcallMessage (the implementation of the interface is IconstructionCallMessage - if there is no intelliSense this day is really not good)! This is not the object constructor method call message! This is a key event we have always wish to intercept. However, how should we handle it? Note that there is a method called InitializeServerObject () in the RealProxy base class, which can accept an iconstructioncallMessage and return an iconstructionReturnMessage, we can use it to handle the constructor method call message:
public class BypassProxy: RealProxy {public override IMessage Invoke (IMessage msg) {if (msg is IConstructionCallMessage) return base.InitializeServerObject ((IConstructionCallMessage) msg); ...}}
Ok, there is this secret weapon, we expect the results that can be asserted:
[BYPASSPROXYATTRIBUTE] public class Anycbo: contextBoundObject {}
Object instance = new anncbo (); assert.istrue (transkingservices.istransparentProxy (instance)); assert.Arequal (typeof (bypassproxy), RemotingServices.getRealProxy (instance);
But don't be too happy, far from the customs clearance (learning and research really like playing games)! Although everything is going well, we can also get a proxy object with the most concise approach, but in addition to the construction stage we can intercept the constructor method (cool), the common method of the previously can be intercepted But why can't I stop it normally (decline)! It's really this! baffling! This is also the most inconsistent thing I have encountered in this technology. Here, I really hope that you will also find a solution to find a solution, analyze a medium in the original - it is also worthy of my pay (brother! How to spend a half day! I have written these articles For more than two months of zeroing time!). The so-called poison attack, in order to deal with this monster, we need to use another monster: system.Runtime.Remoting.Services NamesServicesHelper. Say it is a little monster, not too much: three static methods, but forget to hide your own default constructors (such classes should be marked as static in C # 2.0 to meet the coding criteria); three static methods I haven't done anything - I have made some interunal things public; the most annoying is to put myself in a name space of the crown emperor ... but many times, it is the trick to the monster to pass the customs: We It is to construct a CRETECTRURETURNMESSAGE class that implements IConstructionReturnMessage in the IntenAl in its CreateConstructionReturnMessage (). why? The reason is that the previously used REALPROY INITIALIZESERVEROBJECT () method returned is still the original object instance that has not been proxy - that is, though we put a germination-based original object in proxyttribute () The instance passed to RealProxy, and thus intercepted the first-hand constructor method, but we still returned a naked original object instance to the call code for the constructor when it was handled by the constructor method. Just until now I am still why we use the RemotingServices getRealProxy () method or you can get the real agent of this naked original object instance ... nonsense, let's see what code we need to write to ultimately implement our ideals:
public class BypassProxy: RealProxy {public override IMessage Invoke (IMessage msg) {if (msg is IConstructionCallMessage) {IConstructionCallMessage ctorMsg = (IConstructionCallMessage) msg; RemotingServices.GetRealProxy (_target) .InitializeServerObject (ctorMsg); return EnterpriseServicesHelper.CreateConstructionReturnMessage (ctorMsg, ( MarshalByrefObject) this.gettransparentProxy ());} ...}}
How to see how to change: We first need to use the real agent of the germination-based target object to be created before proxyttribute.createInstance () to initialize according to the constructor method; then we use EnterpriseServicesHelper's CreateConstructionReturnMessage () method to construct a constructor Return the message, and the message used to return to the call is the transparent agent generated by our own real agents (this can not write this, here I want to emphasize the difference between the true agent main body in these two steps). In any case, now we have basically walked all technical difficulties: We can use new to directly construct a proxy object (as long as the object is derived from contextBoundObject); we can use the proxyAttribute derived attribute to arbitrarily assign custom customizes for any CBO class Real agent (overlaid CREATEINSTANCE () method in the ProxyAttribute base class); we can then intercept messages that include all the ways to target objects, including the constructor method, to intercept the method to call messages on the target objects, including the constructor method - and most important What we do not need to switch the execution environment, we can add the method level of the method level for the target object (this is the most advantage of using the Context Property mechanism) - now we only need to provide a custom real agent that can also take the iMessageSink mechanism. You can complete a one-base AOP development framework!
This real agent is the MessageChainProxy speaking in the forebelt, just we need to increase the processing branch of IConstructionCallMessage. Combine some simple custom properties, we can write such a call code to take advantage of this universal AOP framework:
[Aopproxy] public abstract class Objectwithaspects {}
[Calllogger, ExceptionPublisher, PerformancemonIiter, PersistManager] public class anybusinessObject: Objectwithaspects {...}
AnybusinessObject Instance = new annibusinessObject (); instance.dobusiness (...;
Here, AopProxyAttribute is a proxyAttribute that provides custom real agents, basic code schematic is as follows:
[AttributeUsage (AttributeTargets.Class)] public class AopProxyAttribute: ProxyAttribute {private Attribute [] GetAspectAttributes (Type classType) {return classType.GetCustomAttributes (typeof (AspectAttribute), true);} public override MarshalByRefObject CreateInstance (Type serverType) {MarshalByRefObject rawInstance = base.CreateInstance (serverType); IMessageSink headSink = new TerminatorSink (rawInstance); foreach (aspectAttribute aspectAttribute in GetAspectAttributes (serverType)) {headSink = aspectAttribute.CreateAspectSink (headSink);} RealProxy aopProxy = new MessageChainProxy (serverType, rawInstance, headSink); Return (MARSHALBYREFOBJECT) AOPPROXY.GETTRANSPARETPROXY ();}} Take CallLoggerattribute as an example, it is actually an interceptor factory that provides interceptor instances from a public base class and implementing a factory method to the CreateInstance () method of AopProxyAttribute:
public abstract class AspectAttribute: Attribute {public abstract IMessageSink CreateAspectSink (IMessageSink nextSink);} public class CallLoggerAttribute: AspectAttribute {public override IMessageSink CreateAspectSink (IMessageSink nextSink) {return new CallLoggerSink (nextSink);}}
Callloggersink is a standard fully reusable iMessageSink implementation - the beauty of this structure is that we are programming for public interface, so the same implementation can be applied to all mechanisms of this public interface (such as these iMessagesink can also Unchanged Directly use the Context Property to bring its Contribute to any Context).
Of course, if you need to flexibly change the aspect object applied to different objects after the code is deployed, for example:
It only needs to be added to the corresponding logic in the AOPPROXYATTRIBUTE. The mechanism mentioned in this article has been used in some form of enterprise-level projects, and its feasibility, usability has been preliminary inspections, but it is certainly a long way to go. . However, as the title of this article, I just want everyone to taste the freshness (A Taste Of), first touch the concept of AOP, and have a preliminary understanding of several basic technologies to implement AOP in .NET. From this level, I think my purpose has reached - a lot of friends have read these articles carefully to send me the relevant technical issues. Some friends take the initiative to use these technologies to solve some previous If you don't have a good solution, more friends have been considering using various techniques to implement an application-oriented AOP frame for .NET ... this is exactly what I want to see: Technology should be used to reflect value. In fact, why should I write this series of articles? In fact, I would like to write some specific uses related to these technologies, but I found that the friends who know these technologies are too small. It is necessary to pick up the pad, so I will lead this topic and lasting AOP taste series articles. Smart, you must have realized that several techniques introduced in this article have a broader application area - such as Mock Object and Elegant Dal Interpreter, which I have mentioned before ... These topics will be involved later, hope At that time, I can share my experience with everyone and expect more feedback.
According to the practice, my article does not match the code (even the code in the article is not copy-and-paste, so you don't want Copy-And-Paste back - I can't guarantee it to be complete, but I still try to write I encourage everyone to practice, and I am looking forward to hearing your feedback (for example: What do you find in terms of performance? Do you have any experience? Coin? Of course, if there is a spelling error in the article, the syntax error, logic error, concept error, you must tell me! Can you make me confused? :)
Finally, thank you again for reading my article (I don't know how many friends will really read this:) ......