IOC 컨테이너 특성 주입 1편: 프로그램 집합 반사 찾기

40363 단어 IOC
ooboo의 프레임워크를 학습하면 주입용기 방법이 비교적 특별하다는 것을 알 수 있다. 마찬가지로 MVC의 주입점을 이용하지만 사이트 아래 bin의 모든 DLL을 찾아서 반사 검색 특성을 이용하여 해당하는 서비스를 찾아 용기에 주입한다.
이런 장점은 매우 간단하다. IOC 용기가 Ninject인지 autofac인지 다른 어떤 용기인지 전혀 신경 쓰지 않아도 된다.특성 표시를 하고 대응하는 DLL을 BIN 아래로 복사하면 사이트가 시작되면 자동으로 용기에 주입된다.해결을 철저히 진행하다.
IOC 주입 단계 선착순:
1. 프로그램 집합 반사 찾기
2. IOC 컨테이너 엔진 초기화
3. 특성을 이용하여 IOC 용기에 주입
4. MVC 주입점 주입
본인은 둔하고 이해가 잘 안 되기 때문에 7편의 문장으로 각 모듈을 소개하여 자신의 이해를 강화할 것입니다.
IOC 컨테이너 특성 주입 1편: 프로그램 집합 반사 찾기
IOC 컨테이너 특성 주입 2편: 초기 엔진에서 대응하는 IOC 컨테이너 찾기
IOC 컨테이너 특성 주입 섹션 3: Attribute 캡슐화
IOC 컨테이너 특성 주입 4편: 컨테이너 초기화
IOC 컨테이너 특성 주입 5편: 찾기(Attribute) 특성 주입
IOC 용기 특성 주입 6편: MVC 주입점을 이용하여 용기를 가동
IOC 컨테이너 특성 주입 7편: 컨텍스트 역할 영역 요청
원본 코드는 제가 마지막 편에 놓을 테니 여러분이 다운로드하셔도 됩니다.
먼저 웹 사이트 BIN 아래의 DLL을 반사 검색하는 방법에 대해 살펴보겠습니다.
kooboo의 프로그램 집합 반사 검색 클래스는 Nop의 세 가지 봉인 클래스입니다. 각각:
1. 인터페이스(ITypeFinder):
    public interface ITypeFinder {
        IList<Assembly> GetAssemblies();

        IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true);

        IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true);

        IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true);

        IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true);

        IEnumerable<Type> FindClassesOfType<T, TAssemblyAttribute>(bool onlyConcreteClasses = true) where TAssemblyAttribute : Attribute;

        IEnumerable<Assembly> FindAssembliesWithAttribute<T>();

        IEnumerable<Assembly> FindAssembliesWithAttribute<T>(IEnumerable<Assembly> assemblies);

        IEnumerable<Assembly> FindAssembliesWithAttribute<T>(DirectoryInfo assemblyPath);
    }

2. 어플리케이션 구현 클래스(AppDomainTypeFinder):
public class AppDomainTypeFinder : ITypeFinder {
        #region Private Fields

        private bool loadAppDomainAssemblies = true;

        private string assemblySkipLoadingPattern = "^System|^mscorlib|^Microsoft|^CppCodeProvider|^VJSharpCodeProvider|^WebDev|^Castle|^Iesi|^log4net|^NHibernate|^nunit|^TestDriven|^MbUnit|^Rhino|^QuickGraph|^TestFu|^Telerik|^ComponentArt|^MvcContrib|^AjaxControlToolkit|^Antlr3|^Remotion|^Recaptcha|^DotNetOpenAuth,";

        private string assemblyRestrictToLoadingPattern = ".*";
        private IList<string> assemblyNames = new List<string>();

        #endregion

        #region Constructors

        /// <summary>Creates a new instance of the AppDomainTypeFinder.</summary>
        public AppDomainTypeFinder() {
        }

        #endregion

        #region Properties

        /// <summary>The app domain to look for types in.</summary>
        public virtual AppDomain App {
            get { return AppDomain.CurrentDomain; }
        }

        /// <summary>Gets or sets wether app should iterate assemblies in the app domain when loading types. Loading patterns are applied when loading these assemblies.</summary>
        public bool LoadAppDomainAssemblies {
            get { return loadAppDomainAssemblies; }
            set { loadAppDomainAssemblies = value; }
        }

        /// <summary>Gets or sets assemblies loaded a startup in addition to those loaded in the AppDomain.</summary>
        public IList<string> AssemblyNames {
            get { return assemblyNames; }
            set { assemblyNames = value; }
        }

        /// <summary>Gets the pattern for dlls that we know don't need to be investigated.</summary>
        public string AssemblySkipLoadingPattern {
            get { return assemblySkipLoadingPattern; }
            set { assemblySkipLoadingPattern = value; }
        }

        /// <summary>Gets or sets the pattern for dll that will be investigated. For ease of use this defaults to match all but to increase performance you might want to configure a pattern that includes assemblies and your own.</summary>
        /// <remarks>If you change this so that assemblies arn't investigated (e.g. by not including something like "^MTA|..." you may break core functionality.</remarks>
        public string AssemblyRestrictToLoadingPattern {
            get { return assemblyRestrictToLoadingPattern; }
            set { assemblyRestrictToLoadingPattern = value; }
        }

        #endregion

        #region Internal Attributed Assembly class

        private class AttributedAssembly {
            internal Assembly Assembly { get; set; }
            internal Type PluginAttributeType { get; set; }
        }

        #endregion

        #region ITypeFinder

        public IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true) {
            return FindClassesOfType(typeof(T), onlyConcreteClasses);
        }

        public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true) {
            return FindClassesOfType(assignTypeFrom, GetAssemblies(), onlyConcreteClasses);
        }

        public IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true) {
            return FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses);
        }

        public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true) {
            var result = new List<Type>();
            try {
                foreach (var a in assemblies) {
                    foreach (var t in a.GetTypes()) {
                        if (assignTypeFrom.IsAssignableFrom(t) || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom))) {
                            if (!t.IsInterface) {
                                if (onlyConcreteClasses) {
                                    if (t.IsClass && !t.IsAbstract) {
                                        result.Add(t);
                                    }
                                } else {
                                    result.Add(t);
                                }
                            }
                        }
                    }
                }
            } catch (ReflectionTypeLoadException ex) {
                var msg = string.Empty;
                foreach (var e in ex.LoaderExceptions)
                    msg += e.Message + Environment.NewLine;

                var fail = new Exception(msg, ex);
                Debug.WriteLine(fail.Message, fail);

                throw fail;
            }
            return result;
        }

        public IEnumerable<Type> FindClassesOfType<T, TAssemblyAttribute>(bool onlyConcreteClasses = true) where TAssemblyAttribute : Attribute {
            var found = FindAssembliesWithAttribute<TAssemblyAttribute>();
            return FindClassesOfType<T>(found, onlyConcreteClasses);
        }

        public IEnumerable<Assembly> FindAssembliesWithAttribute<T>() {
            return FindAssembliesWithAttribute<T>(GetAssemblies());
        }

        /// <summary>
        /// Caches attributed assembly information so they don't have to be re-read
        /// </summary>
        private readonly List<AttributedAssembly> _attributedAssemblies = new List<AttributedAssembly>();

        /// <summary>
        /// Caches the assembly attributes that have been searched for
        /// </summary>
        private readonly List<Type> _assemblyAttributesSearched = new List<Type>();

        public IEnumerable<Assembly> FindAssembliesWithAttribute<T>(IEnumerable<Assembly> assemblies) {
            //check if we've already searched this assembly);)
            if (!_assemblyAttributesSearched.Contains(typeof(T))) {
                var foundAssemblies = (from assembly in assemblies
                                       let customAttributes = assembly.GetCustomAttributes(typeof(T), false)
                                       where customAttributes.Any()
                                       select assembly).ToList();
                //now update the cache
                _assemblyAttributesSearched.Add(typeof(T));
                foreach (var a in foundAssemblies) {
                    _attributedAssemblies.Add(new AttributedAssembly { Assembly = a, PluginAttributeType = typeof(T) });
                }
            }

            //We must do a ToList() here because it is required to be serializable when using other app domains.
            return _attributedAssemblies
                .Where(x => x.PluginAttributeType.Equals(typeof(T)))
                .Select(x => x.Assembly)
                .ToList();
        }

        public IEnumerable<Assembly> FindAssembliesWithAttribute<T>(DirectoryInfo assemblyPath) {
            var assemblies = (from f in Directory.GetFiles(assemblyPath.FullName, "*.dll")
                              select Assembly.LoadFrom(f)
                                  into assembly
                                  let customAttributes = assembly.GetCustomAttributes(typeof(T), false)
                                  where customAttributes.Any()
                                  select assembly).ToList();
            return FindAssembliesWithAttribute<T>(assemblies);
        }

        /// <summary>Gets tne assemblies related to the current implementation.</summary>
        /// <returns>A list of assemblies that should be loaded by the factory.</returns>
        public virtual IList<Assembly> GetAssemblies() {
            var addedAssemblyNames = new List<string>();
            var assemblies = new List<Assembly>();

            if (LoadAppDomainAssemblies)
                AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
            AddConfiguredAssemblies(addedAssemblyNames, assemblies);

            return assemblies;
        }

        #endregion

        /// <summary>Iterates all assemblies in the AppDomain and if it's name matches the configured patterns add it to our list.</summary>
        /// <param name="addedAssemblyNames"></param>
        /// <param name="assemblies"></param>
        private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies) {
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
                if (Matches(assembly.FullName)) {
                    if (!addedAssemblyNames.Contains(assembly.FullName)) {
                        assemblies.Add(assembly);
                        addedAssemblyNames.Add(assembly.FullName);
                    }
                }
            }
        }

        /// <summary>Adds specificly configured assemblies.</summary>
        protected virtual void AddConfiguredAssemblies(List<string> addedAssemblyNames, List<Assembly> assemblies) {
            foreach (string assemblyName in AssemblyNames) {
                Assembly assembly = Assembly.Load(assemblyName);
                if (!addedAssemblyNames.Contains(assembly.FullName)) {
                    assemblies.Add(assembly);
                    addedAssemblyNames.Add(assembly.FullName);
                }
            }
        }

        /// <summary>Check if a dll is one of the shipped dlls that we know don't need to be investigated.</summary>
        /// <param name="assemblyFullName">The name of the assembly to check.</param>
        /// <returns>True if the assembly should be loaded into app.</returns>
        public virtual bool Matches(string assemblyFullName) {
            return !Matches(assemblyFullName, AssemblySkipLoadingPattern)
                   && Matches(assemblyFullName, AssemblyRestrictToLoadingPattern);
        }

        /// <summary>Check if a dll is one of the shipped dlls that we know don't need to be investigated.</summary>
        /// <param name="assemblyFullName">The assembly name to match.</param>
        /// <param name="pattern">The regular expression pattern to match against the assembly name.</param>
        /// <returns>True if the pattern matches the assembly name.</returns>
        protected virtual bool Matches(string assemblyFullName, string pattern) {
            return Regex.IsMatch(assemblyFullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
        }

        /// <summary>Makes sure matching assemblies in the supplied folder are loaded in the app domain.</summary>
        /// <param name="directoryPath">The physical path to a directory containing dlls to load in the app domain.</param>
        protected virtual void LoadMatchingAssemblies(string directoryPath) {
            var loadedAssemblyNames = new List<string>();
            foreach (Assembly a in GetAssemblies()) {
                loadedAssemblyNames.Add(a.FullName);
            }

            if (!Directory.Exists(directoryPath)) {
                return;
            }

            foreach (string dllPath in Directory.GetFiles(directoryPath, "*.dll")) {
                try {
                    var an = AssemblyName.GetAssemblyName(dllPath);
                    if (Matches(an.FullName) && !loadedAssemblyNames.Contains(an.FullName)) {
                        App.Load(an);
                    }

                    //old loading stuff
                    //Assembly a = Assembly.ReflectionOnlyLoadFrom(dllPath);
                    //if (Matches(a.FullName) && !loadedAssemblyNames.Contains(a.FullName))
                    //{
                    //    App.Load(a.FullName);
                    //}
                } catch (BadImageFormatException ex) {
                    Trace.TraceError(ex.ToString());
                }
            }
        }

        protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric) {
            try {
                var genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
                foreach (var implementedInterface in type.FindInterfaces((objType, objCriteria) => true, null)) {
                    if (!implementedInterface.IsGenericType)
                        continue;

                    var isMatch = genericTypeDefinition.IsAssignableFrom(implementedInterface.GetGenericTypeDefinition());
                    return isMatch;
                }
                return false;
            } catch {
                return false;
            }
        }

    }

3. 웹 애플리케이션은 App 역할 도메인 클래스(WebAppTypeFinder)를 상속합니다.
public class WebAppTypeFinder : AppDomainTypeFinder {
        #region Fields

        private bool _ensureBinFolderAssembliesLoaded = true;
        private bool _binFolderAssembliesLoaded = false;

        #endregion

        #region Ctor

        public WebAppTypeFinder() {
            this._ensureBinFolderAssembliesLoaded = true;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets wether assemblies in the bin folder of the web application should be specificly checked for beeing loaded on application load. This is need in situations where plugins need to be loaded in the AppDomain after the application been reloaded.
        /// </summary>
        public bool EnsureBinFolderAssembliesLoaded {
            get { return _ensureBinFolderAssembliesLoaded; }
            set { _ensureBinFolderAssembliesLoaded = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets a physical disk path of \Bin directory
        /// </summary>
        /// <returns>The physical path. E.g. "c:\inetpub\wwwroot\bin"</returns>
        public virtual string GetBinDirectory() {
            if (HostingEnvironment.IsHosted) {
                //hosted
                return HttpRuntime.BinDirectory;
            } else {
                //not hosted. For example, run either in unit tests
                return AppDomain.CurrentDomain.BaseDirectory;
            }
        }


        public override IList<Assembly> GetAssemblies() {
            if (this.EnsureBinFolderAssembliesLoaded && !_binFolderAssembliesLoaded) {
                _binFolderAssembliesLoaded = true;
                string binPath = GetBinDirectory();
                //binPath = _webHelper.MapPath("~/bin");
                LoadMatchingAssemblies(binPath);
            }

            return base.GetAssemblies();
        }
        #endregion
    }
WebAppTypeFinder            BIN      DLL           ,               ,      ,             。

다음:
IOC 컨테이너 특성 주입 2편: 초기 엔진에서 대응하는 IOC 컨테이너 찾기

좋은 웹페이지 즐겨찾기