How Spring.Net Implements Injection in MVC (Principle)

Keywords: ASP.NET Spring Session Attribute

This article will introduce the implementation principle of Spring.Net (not only Spring.Net, but all IoC containers inject into the controller in the same way). There is no information about how Spring is configured, used or implemented in MVC.

Put the quotation first, just to avoid wasting your time.

I hope you can sit still for a moment and read carefully.

Prevent crawling by adding a link: https://www.cnblogs.com/MedlarCanFly/p/11488689.html

scene

1     public class HomeController : Controller
2     {
3         //It's an amazing injection
4         private IBLL.IUserInfoService UserInfoService { get; set; }
5         public ActionResult Index()
6         {
7             return Content(UserInfoService.GetName());
8         }
9     }

 

Every time I look at the code, I have a different understanding. Today, when I look at a UserInfoService attribute in the MVC controller injected through Spring.Net dependency, I suddenly have some questions. The precondition for injection is control reversal. So my Controller came from the IoC container?But I don't remember where I have allocations, and I've done an in-depth study of that.

Start with MVC itself

First, we need to understand how MVC itself acquires controller objects. If we don't understand the nature, how can we extend it?

In MVC mode, the current requested controller object is obtained by implementing the object of the IControllerFactory interface, and the object of the IControllerFactory interface is also the controller creation factory.

A brief look at the IControllerFactory

 1     //
 2     // abstract:
 3     //     Define the method required by the controller factory.
 4     public interface IControllerFactory
 5     {
 6         //
 7         // abstract:
 8         //     Creates the specified controller using the specified request context.
 9         //
10         // parameter:
11         //   requestContext:
12         //     Request context.
13         //
14         //   controllerName:
15         //     The name of the controller.
16         //
17         // Return results:
18         //     Controller.
19         IController CreateController(RequestContext requestContext, string controllerName);
20         //
21         // abstract:
22         //     Gets the session behavior of the controller.
23         //
24         // parameter:
25         //   requestContext:
26         //     Request context.
27         //
28         //   controllerName:
29         //     The name of the controller whose session behavior you want to get.
30         //
31         // Return results:
32         //     Session behavior of the controller.
33         SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
34         //
35         // abstract:
36         //     Release the specified controller.
37         //
38         // parameter:
39         //   controller:
40         //     Controller.
41         void ReleaseController(IController controller);
42     }

 

An Http request comes in and chooses which controller is handled through MvcHandler

Controller factories are provided to MvcHandler using the Controller Builder's Urrent property

The code below is decompiled, so just look at it (because I want to mark the Yellow highlighted part, it doesn't collapse)

 1 internal ControllerBuilder ControllerBuilder
 2 {
 3     get
 4     {
 5         if (this._controllerBuilder == null)
 6         {
 7             this._controllerBuilder = ControllerBuilder.Current;
 8         }
 9         return this._controllerBuilder;
10     }
11     set
12     {
13         this._controllerBuilder = value;
14     }
15 }
 1 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
 2 {
 3     // Fields
 4     private ControllerBuilder _controllerBuilder;
 5     private static readonly object _processRequestTag;
 6     internal static readonly string MvcVersion;
 7     public static readonly string MvcVersionHeaderName;
 8 
 9     // Methods
10     static MvcHandler();
11     public MvcHandler(RequestContext requestContext);
12     protected internal virtual void AddVersionHeader(HttpContextBase httpContext);
13     protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state);
14     protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state);
15     protected internal virtual void EndProcessRequest(IAsyncResult asyncResult);
16     private static string GetMvcVersionString();
17     protected virtual void ProcessRequest(HttpContext httpContext);
18     protected internal virtual void ProcessRequest(HttpContextBase httpContext);
19     private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory);
20     private void RemoveOptionalRoutingParameters();
21     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
22     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result);
23     void IHttpHandler.ProcessRequest(HttpContext httpContext);
24 
25     // Properties
26     internal ControllerBuilder ControllerBuilder { get; set; }
27     public static bool DisableMvcResponseHeader { get; [CompilerGenerated] set; }
28     protected virtual bool IsReusable { get; }
29     public RequestContext RequestContext { get; [CompilerGenerated] private set; }
30     bool IHttpHandler.IsReusable { get; }
31 
32     // Nested Types
33     [Serializable, CompilerGenerated]
34     private sealed class <>c
35     {
36         // Fields
37         public static readonly MvcHandler.<>c <>9;
38         public static BeginInvokeDelegate<MvcHandler.ProcessRequestState> <>9__20_0;
39         public static EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> <>9__20_1;
40         public static Func<KeyValuePair<string, object>, bool> <>9__26_0;
41 
42         // Methods
43         static <>c();
44         public <>c();
45         internal IAsyncResult <BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState);
46         internal void <BeginProcessRequest>b__20_1(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState);
47         internal bool <RemoveOptionalRoutingParameters>b__26_0(KeyValuePair<string, object> entry);
48     }
49 
50     [StructLayout(LayoutKind.Sequential)]
51     private struct ProcessRequestState
52     {
53         internal IAsyncController AsyncController;
54         internal IControllerFactory Factory;
55         internal RequestContext RequestContext;
56         internal void ReleaseController();
57     }
58 }

 

Default factory

By default, an object of type DefaultControllerFactory is created inside ControllerBuilder to provide processing requests.

DefaultControllerFactory is an implementation of the IControllerFactory interface.

  1     //
  2     // abstract:
  3     //     Represents a controller factory that is registered by default.
  4     public class DefaultControllerFactory : IControllerFactory
  5     {
  6         //
  7         // abstract:
  8         //     Initialization System.Web.Mvc.DefaultControllerFactory A new instance of the class.
  9         public DefaultControllerFactory();
 10         //
 11         // abstract:
 12         //     Initialize using controller activator System.Web.Mvc.DefaultControllerFactory A new instance of the class.
 13         //
 14         // parameter:
 15         //   controllerActivator:
 16         //     Object that implements the controller activator interface.
 17         public DefaultControllerFactory(IControllerActivator controllerActivator);
 18 
 19         //
 20         // abstract:
 21         //     Creates the specified controller using the specified request context.
 22         //
 23         // parameter:
 24         //   requestContext:
 25         //     HTTP Context of the request, including HTTP Context and routing data.
 26         //
 27         //   controllerName:
 28         //     The name of the controller.
 29         //
 30         // Return results:
 31         //     Controller.
 32         //
 33         // abnormal:
 34         //   T:System.ArgumentNullException:
 35         //     requestContext The parameter is null. 
 36         //
 37         //   T:System.ArgumentException:
 38         //     controllerName The parameter is null Or empty.
 39         public virtual IController CreateController(RequestContext requestContext, string controllerName);
 40         //
 41         // abstract:
 42         //     Release the specified controller.
 43         //
 44         // parameter:
 45         //   controller:
 46         //     Controller to release.
 47         public virtual void ReleaseController(IController controller);
 48         //
 49         // abstract:
 50         //     Retrieves a controller instance of the specified request context and controller type.
 51         //
 52         // parameter:
 53         //   requestContext:
 54         //     HTTP Context of the request, including HTTP Context and routing data.
 55         //
 56         //   controllerType:
 57         //     Type of controller.
 58         //
 59         // Return results:
 60         //     Controller instance.
 61         //
 62         // abnormal:
 63         //   T:System.Web.HttpException:
 64         //     controllerType by null. 
 65         //
 66         //   T:System.ArgumentException:
 67         //     Unable to allocate controllerType. 
 68         //
 69         //   T:System.InvalidOperationException:
 70         //     could not be built controllerType Instances.
 71         protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType);
 72         //
 73         // abstract:
 74         //     Returns the session behavior of the controller.
 75         //
 76         // parameter:
 77         //   requestContext:
 78         //     Request context.
 79         //
 80         //   controllerType:
 81         //     Type of controller.
 82         //
 83         // Return results:
 84         //     Session behavior of the controller.
 85         protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType);
 86         //
 87         // abstract:
 88         //     Retrieves the controller type for the specified name and request context.
 89         //
 90         // parameter:
 91         //   requestContext:
 92         //     HTTP Context of the request, including HTTP Context and routing data.
 93         //
 94         //   controllerName:
 95         //     The name of the controller.
 96         //
 97         // Return results:
 98         //     Controller type.
 99         protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName);
100     }

 

By default, the Controller class needs to provide a default constructor, because DefaultController Factory creates instances of Controller objects through reflection.

If the Controller we define needs to be created by a constructor or managed by an IoC container, it can be achieved by customizing the Controller Factory.

Custom Controller Factory

Why are there so many things about controller factories that Spring.Net creates by inheriting DefaultControllerFactory?

So much is said to make it easier to understand the controller factory source for Spring.Net later.

Return to the topic and create your own controller factory.

1. The Home Controller is as follows

 1     public class HomeController : Controller
 2     {
 3         private IUserInfoService UserInfoService { get; set; }
 4         public HomeController(IUserInfoService userInfoService)
 5         {
 6             UserInfoService = userInfoService;
 7         }
 8         public ActionResult Index()
 9         {
10             return Content(UserInfoService.GetName());
11         }
12     }

 

Here UserInfoService is just a very rudimentary test class, and there is only one GetName() method to return Xiaoming.

Next, constructs will be injected into UserInfoService via a custom controller factory implementation

2. Create Controller Factory

To make it easier for me to inherit the DefaultController Factory directly, I can also create it by implementing IControllerFactory

 1     public class MyControllerFactory : DefaultControllerFactory
 2     {
 3         private static readonly IBLL.IUserInfoService userInfoService = new BLL.UserInfoService();
 4 
 5         //Rewrite CreateController
 6         public override IController CreateController(RequestContext requestContext, string controllerName)
 7         {
 8             IController controller = null;
 9             if (controllerName == "Home")
10             {
11                 //If it was made by us Home The controller instantiates it and injects it through the construction parameters userInfoService
12                 controller = new HomeController(userInfoService);
13             }
14             else
15             {
16                 //Create Controller from Default Controller Factory
17                 controller = base.CreateController(requestContext, controllerName);
18             }
19             return controller;
20         }
21     }

 

3. Register in Global.asax

1         protected void Application_Start()
2         {
3             MyControllerFactory myControllerFactory = new MyControllerFactory();
4             //adopt ControllerBuilder Set up established controller factories
5             ControllerBuilder.Current.SetControllerFactory(myControllerFactory);
6             AreaRegistration.RegisterAllAreas();
7             RouteConfig.RegisterRoutes(RouteTable.Routes);
8         }

 

4. Run tests (magic no longer magical)

 

Unexpectedly, reasonably, we didn't instantiate it in the controller, but the result came out

(instantiation completed in the factory)

 

Spring.Net Injection Principle

So much said, look back at the title "How Spring.Net is injected into MVC". You said, thanks for all the flowers. You can't even see the hair of Spring.Net...

In fact, if you read it carefully, the answer should already be in your mind.

Open fold, that's the answer

  1 namespace Spring.Web.Mvc
  2 {
  3     /// <summary>
  4     /// Controller Factory for ASP.NET MVC
  5     /// </summary>
  6     public class SpringControllerFactory : DefaultControllerFactory
  7     {
  8         private static IApplicationContext _context;
  9 
 10         /// <summary>
 11         /// Gets the application context.
 12         /// </summary>
 13         /// <value>The application context.</value>
 14         public static IApplicationContext ApplicationContext
 15         {
 16             get
 17             {
 18                 if (_context == null || _context.Name != ApplicationContextName)
 19                 {
 20                     if (string.IsNullOrEmpty(ApplicationContextName))
 21                     {
 22                         _context = ContextRegistry.GetContext();
 23                     }
 24                     else
 25                     {
 26                         _context = ContextRegistry.GetContext(ApplicationContextName);
 27                     }
 28                 }
 29 
 30                 return _context;
 31             }
 32         }
 33 
 34         /// <summary>
 35         /// Gets or sets the name of the application context.
 36         /// </summary>
 37         /// <remarks>
 38         /// Defaults to using the root (default) Application Context.
 39         /// </remarks>
 40         /// <value>The name of the application context.</value>
 41         public static string ApplicationContextName { get; set; }
 42 
 43         /// <summary>
 44         /// Creates the specified controller by using the specified request context.
 45         /// </summary>
 46         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
 47         /// <param name="controllerName">The name of the controller.</param>
 48         /// <returns>A reference to the controller.</returns>
 49         /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext"/> parameter is null.</exception>
 50         /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName"/> parameter is null or empty.</exception>
 51         public override IController CreateController(RequestContext requestContext, string controllerName)
 52         {
 53             IController controller;
 54 
 55             if (ApplicationContext.ContainsObjectDefinition(controllerName))
 56             {
 57                 controller = ApplicationContext.GetObject(controllerName) as IController;
 58             }
 59             else
 60             {
 61                 controller = base.CreateController(requestContext, controllerName);
 62             }
 63 
 64             AddActionInvokerTo(controller);
 65 
 66             return controller;
 67         }
 68 
 69         /// <summary>
 70         /// Retrieves the controller instance for the specified request context and controller type.
 71         /// </summary>
 72         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
 73         /// <param name="controllerType">The type of the controller.</param>
 74         /// <returns>The controller instance.</returns>
 75         /// <exception cref="T:System.Web.HttpException">
 76         ///     <paramref name="controllerType"/> is null.</exception>
 77         /// <exception cref="T:System.ArgumentException">
 78         ///     <paramref name="controllerType"/> cannot be assigned.</exception>
 79         /// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType"/> cannot be created.</exception>
 80         protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
 81         {
 82             IController controller = null;
 83 
 84             if (controllerType != null)
 85             {
 86                 var controllers = ApplicationContext.GetObjectsOfType(controllerType);
 87                 if (controllers.Count > 0)
 88                 {
 89                     controller = (IController)controllers.First().Value;
 90                 }
 91             }
 92 
 93             if (controller == null)
 94             {
 95                 //pass to base class for remainder of handling if can't find it in the context
 96                 controller = base.GetControllerInstance(requestContext, controllerType);
 97             }
 98             
 99             AddActionInvokerTo(controller);
100 
101             return controller;
102         }
103 
104         /// <summary>
105         /// Adds the action invoker to the controller instance.
106         /// </summary>
107         /// <param name="controller">The controller.</param>
108         protected virtual void AddActionInvokerTo(IController controller)
109         {
110             if (controller == null)
111                 return;
112 
113             if (typeof(Controller).IsAssignableFrom(controller.GetType()))
114             {
115                 ((Controller)controller).ActionInvoker = new SpringActionInvoker(ApplicationContext);
116             }
117         }
118 
119     }
120 }

 

I don't think I need to explain much about the code. With the above knowledge base, this is the kind that you can understand at a glance.

Well, let's talk about the CreateController method to prevent small partners who are not familiar with Spring.Net.

 

ApplicationContext: This is what corresponds to the IoC container

ApplicationContext.ContainsObjectDefinition(controllerName): Returns whether an object named controllerName exists in the container

 

summary

Taste through each line of code and you'll find that nothing is as simple as it appears, and that behind each implementation is worth investigating.

After such a long time, I hope it will help you while you are reading.

 

Reference book: Essentials of ASP.NET

 

If something is unclear or incorrect, we hope to correct it.

Posted by fitchn on Mon, 09 Sep 2019 17:48:22 -0700