你真的了解Ioc与AOP吗?(3)

本文介绍了一种基于配置文件和反射技术的工厂模式实现方法,通过自定义配置文件和.NET Reflection技术,实现了组件间的松耦合及动态对象创建与组装。

三、基于配置文件和Reflection的工厂模式

为了消除MainApp对其它组件的依赖性,我们引入工厂模式,并且根据配置文件指定的装配规程,利用.net提供的反射技术完成对象的组装工作。本部分代码仅仅提供一种功能演示,如果实际应用仍需进一步完善(建议使用一些成型的Ioc框架,例如Spring.net或Castle等)。经过改造后的系统,组件间依赖关系如下图:

可以看出这次实现了真正的“针对接口编程”。所有的组件只依赖于接口。MainApp所需的对象是由工厂根据配置文件动态创建并组装起来的。当系统需求发生变化时,只需要修改一下配置文件就可以了。而且MainApp、SayHello和HelloGenerator之间不存在任何的依赖关系,实现了松耦合。

这是如何实现的呢?我们首先要能够解析配置文件中的信息,然后建立包含相关信息的对象。最后根据这些信息利用反射机制完成对象的创建。首先我们看一下配置文件所包含的内容:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <configSections>
      <sectionGroup name="IocInCSharp">
         <section name="objects" type="IocInCSharp.ConfigHandler, MainApp" />
      </sectionGroup>
   </configSections>
   <IocInCSharp>
      <objects>
         <object name="SayHello" assembly="SayHello.dll" typeName="IocInCSharp.SayHello">
            <property name="HelloGenerator" assembly="HelloGenerator.dll" 
                      typeName="IocInCSharp.CnHelloGenerator"></property>
         </object>
      </objects>
   </IocInCSharp>
</configuration>

从中我们可以看出,我们实现了一个IocInCSharp.ConfigHandler类,用来处理配置文件中IocInCSharp/objects结点中的内容。ConfigHandler类将根据该结点下的内容处理并创建一ConfigInfo对象(关于ConfigInfo、ObjectInfo以及PropertyInfo的代码可自行查看源代码,这里就不再赘述)。ConfigHandler类的代码实现如下:

using System;
using System.Configuration;
using System.Xml;
namespace IocInCSharp
{
   public class ConfigHandler:IConfigurationSectionHandler
   {
      public object Create(object parent, object configContext, System.Xml.XmlNode section)
      {
         ObjectInfo info;
         PropertyInfo propInfo;
         ConfigInfo cfgInfo = new ConfigInfo();
         foreach(XmlNode node in section.ChildNodes)
         {
            info = new ObjectInfo();
            info.name = node.Attributes["name"].Value;
            info.assemblyName = node.Attributes["assembly"].Value;
            info.typeName = node.Attributes["typeName"].Value;
            foreach(XmlNode prop in node)
            {
               propInfo = new PropertyInfo();
               propInfo.propertyName = prop.Attributes["name"].Value;
               propInfo.assemblyName = prop.Attributes["assembly"].Value;
               propInfo.typeName = prop.Attributes["typeName"].Value;
               info.properties.Add(propInfo);
            }
            cfgInfo.Objects.Add(info);
         }
         return cfgInfo;
      }
   }
}

通过ConfigHandler的解析,我们最终得到一个ConfigInfo实例,Factory就是根据这个实例中所包含的配置信息,利用反射技术对所需对象生成并组装的。SayHelloFactory的代码如下:

using System;
using System.IO;
using System.Configuration;
using System.Reflection;
namespace IocInCSharp
{
   public class SayHelloFactory
   {
      public static object Create(string name)
      {
         Assembly assembly;
         object o = null;
         object p;
         string rootPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + 
                           Path.DirectorySeparatorChar;
         ConfigInfo cfgInfo = (ConfigInfo)ConfigurationSettings.GetConfig("IocInCSharp/objects"); 
         ObjectInfo info = cfgInfo.FindByName(name);
         if(info != null)
         {
            assembly = Assembly.LoadFile(rootPath + info.assemblyName);
            o = assembly.CreateInstance(info.typeName);
            Type t = o.GetType();
            for(int i=0; i<info.properties.Count; i++)
            {               
               PropertyInfo prop = (PropertyInfo)info.properties[i];
               
               assembly = Assembly.LoadFile(rootPath + prop.assemblyName);
               p = assembly.CreateInstance(prop.typeName);
               t.InvokeMember(prop.propertyName, 
                  BindingFlags.DeclaredOnly | 
                  BindingFlags.Public | BindingFlags.NonPublic | 
                  BindingFlags.Instance | BindingFlags.SetProperty, null, o, new Object[] {p});
            }
         }
         return o;
      }
   }
}

在上面这段代码中,重点注意三条命令的使用方法:

assembly = Assembly.LoadFile(rootPath + prop.assemblyName);
p = assembly.CreateInstance(prop.typeName);
t.InvokeMember(prop.propertyName, 
   BindingFlags.DeclaredOnly | 
   BindingFlags.Public | BindingFlags.NonPublic | 
   BindingFlags.Instance | BindingFlags.SetProperty, null, o, new Object[] {p});

Assembly.LoadFile()用于将外部文件装载进来;assembly.CreateInstance()根据装载进来的程序集创建一指定类型的对象;t.InvokeMember(prop.propertyName, ........BindingFlags.SetProperty, null, o, new Object[] {p})利用反射机制对创建出来的对象设置属性值。

我们的Factory就是利用这种方式根据配置文件动态加载程序集,动态创建对象并设置属性的。有了这个Factory,MainApp中的内容就很简单了:

using System;
namespace IocInCSharp
{
   public class MainApp
   {
      public static void Main()
      {
         ISayHello sayHello = (ISayHello)SayHelloFactory.Create("SayHello");
         if(sayHello != null)
            sayHello.SayHelloTo("zhenyulu");
         else
            Console.WriteLine("Got an Error!");
      }
   }
}

现在,MainApp只依赖于接口,不再依赖于其它组件,实现了松耦合。在本例子中,大家可以尝试将配置文件中的IocInCSharp.CnHelloGenerator更改为IocInCSharp.EnHelloGenerator,看看是否输出内容由中文变为了英文。这便是“注入”的效果。

从上面这个例子我们可以看出,通过自定义配置文件和.net中的Reflection技术,我们自己就可以开发Ioc应用,根据配置文件的信息自行组装相应的对象。但是Reflection编程的技术门槛还是比较高的,并且在实际应用中配置文件的格式、Handler的设计都不是象上面代码那样的简单。不过幸好我们现在有很多的Ioc容器可供选择,它们都提供了完整的依赖注入方式,并且比自己写代码更加成熟、更加稳定。使用这些框架可以让程序员在三两行代码里完成“注入”工作。在我们下一个案例中,我们将使用Spring.net实现依赖注入。我们会发现仅仅添加几行代码并更改一下配置文件就可轻松实现依赖注入。(待续)

<think>好的,我需要回答用户关于IOCAOP的概念、原理、区别和联系的问题。首先,我得回忆一下相关的知识,并结合提供的引用内容来组织答案。 首先,IOC,也就是控制反转,是Spring的核心之一。根据引用[1]和[4],IOC的主要思想是将对象的创建和依赖关系的管理从程序内部转移到第三方容器。这样做的目的是降低耦合度,提高灵活性。用户提到控制反转的具体实现是依赖注入(DI),或者有人认为DI是IoC的一种实现方式。这点需要明确,可能需要指出不同的观点。 接下来是AOP,面向切面编程。引用[2]和[3]详细说明了AOP的基本概念,比如连接点、切入点、通知等。AOP用于处理横切关注点,如日志、事务等,这些功能分散在多个地方,通过AOP可以模块化处理,避免代码冗余。需要解释AOP的原理,比如动态代理,以及它在Spring中的实现方式,比如JDK动态代理和CGLIB,引用[3]提到了这些内容。 然后,关于两者的区别和联系。IOC主要负责对象的管理和依赖注入,而AOP处理横切关注点。它们的应用场景不同,但都属于Spring的核心机制,共同提升代码的可维护性和扩展性。需要指出它们如何协同工作,比如在IOC管理的对象上应用AOP的切面。 在结构上,用户希望答案分点清晰,可能需要分为概念、原理、区别、联系几个部分。每个部分引用相应的引用内容,比如在解释IOC时引用[1]和[4],AOP引用[2]和[3]。要注意引用标识的正确添加,比如在提到控制反转时引用[^1][^4],AOP的基本概念引用[^2][^3]。 可能还需要检查是否有遗漏的知识点,比如IOC的其他实现方式,或者AOP的织入过程。确保覆盖用户的问题,包括原理、区别和联系,而不仅仅是概念。 最后,生成相关问题,可能需要围绕IOCAOP的应用、具体实现方式、优缺点等方面提问。例如,如何实现依赖注入,或者AOP在实际项目中的使用案例。 现在需要把这些思考整理成一个结构清晰的中文回答,正确使用引用,并确保LaTeX格式符合要求,比如行内公式用$...$,独立公式用$$...$$。不过当前问题可能不需要数学公式,但如果有相关例子可能需要添加。例如,如果讨论AOP的切入点表达式,可能需要使用LaTeX,但根据用户的问题,可能不涉及数学表达式,所以可能不需要。不过要确保其他格式正确,比如代码块使用反引号,引用标识正确放置。 总结一下,回答的结构应该是: 1. 概念 - IOC - AOP 2. 原理 - IOC的实现(DI) - AOP的动态代理 3. 区别 - 关注点不同 - 实现方式不同 4. 联系 - 共同目标 - 协同工作 5. 相关问题 每个部分引用相应的内容,例如在IOC概念部分引用[^1],在AOP原理部分引用[^3]等。确保每个引用标识自然出现在句末,不重复使用同一引用多次,除非必要。 现在开始撰写正式的回答,确保符合所有格式要求,使用中文,分点清晰,引用正确。</think>### 一、IOC(控制反转)AOP(面向切面编程)的概念及原理 #### 1. **IOC(控制反转)** - **概念** **控制反转(IoC)** 是一种设计思想,其核心是将对象的创建和依赖关系的控制权从程序代码中转移到外部容器(如Spring框架)。在传统编程中,对象直接通过`new`关键字创建并管理依赖,而IoC通过第三方(容器)统一管理对象生命周期和依赖注入(DI)。例如,类A依赖类B时,不再由A主动创建B,而是由容器将B注入到A中[^4]。 - **原理** IoC通过**依赖注入**实现,常见方式包括构造器注入、Setter方法注入和接口注入。Spring容器负责读取配置(XML或注解),创建对象并建立依赖关系。这种机制降低了代码耦合度,提升了可维护性和扩展性。 #### 2. **AOP(面向切面编程)** - **概念** **面向切面编程(AOP)** 是对面向对象编程(OOP)的补充,用于处理**横切关注点**(如日志、事务、权限等)。这些功能分散在多个模块中,AOP通过动态织入代码的方式将其模块化,避免代码冗余[^2][^3]。 - **原理** AOP基于**动态代理**技术实现,主要步骤如下: 1. **定义切面(Aspect)**:包含通知(Advice)和切入点(Pointcut)。 2. **织入(Weaving)**:在目标对象的方法执行前后插入增强逻辑。 3. **代理生成**:Spring通过JDK动态代理(接口)或CGLIB(类)创建代理对象,实现对目标对象的增强。 ### 二、IOCAOP的区别联系 #### 1. **区别** | **维度** | **IOC** | **AOP** | |----------------|----------------------------------|----------------------------------| | **关注点** | 对象创建依赖管理 | 横切关注点的模块化 | | **实现方式** | 依赖注入(DI) | 动态代理、反射 | | **应用场景** | 解耦对象间的直接依赖 | 日志、事务、权限等公共功能 | #### 2. **联系** - **共同目标**:提升代码的可维护性和扩展性。IOC通过解耦对象依赖,AOP通过分离横切逻辑,两者协同降低系统复杂度。 - **协同工作**:在Spring中,IOC容器管理的对象是AOP的**目标对象**。AOP通过代理机制增强这些对象的功能,例如事务管理依赖于IOC注入的数据源。 ### 三、示例说明 - **IOC示例**: ```java // 传统方式:由类A主动创建B class A { private B b = new B(); } // IOC方式:由容器注入B @Component class A { @Autowired private B b; } ``` - **AOP示例**(记录方法执行时间): ```java @Aspect public class LogAspect { @Around("execution(* com.example.service.*.*(..))") public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms"); return result; } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值