01、IoCContainer-1 容器、Bean

本文深入探讨Spring框架中的IoC(Inversion of Control)容器原理,解释依赖注入(DI)概念,展示如何使用XML配置元数据来定义、实例化和配置Bean。同时,文章还介绍了ApplicationContext接口及其实现类在企业级应用中的作用。

容器、Bean

  1. 介绍

    IoC(IoCInversion of Control)也叫依赖注入DI(dependency injection)

    对象可以通过构造函数参数、工厂方法参数,或者是对象自己被实例化后设置的属性来定义它们所依赖的其它对象。当所依赖的对象被实例化后,容器注入这些bean。bean自己控制依赖的实例化,或者是直接通过类的解析和Service Locator模式这样的机制来自己决定自己的依赖,这个过程就是IoC。

    使用IoC容器,需要用到org.springframework.beansorg.springframework.context两个包

    BeanFactroy接口提供了一个先进的配置机制,使得容器可以管理任何类型的对象。ApplicationContextBeanFactroy的子接口,它加入了一下功能:

    • 与Spring AOP特性的组合更加容易
    • 消息资源处理(在网络化时使用)
    • 事件发布
    • 如在web应用中使用的WebApplicationContext这样的应用层特殊上下文

    总之,BeanFactroy提供了配置框架和基本的功能,ApplicationContext增加了企业级应用特有的功能。

    在Spring中,对象被称之为Bean,一个Bean就是被实例化的被组装的且被Spring IoC容器管理的众多对象中的一个。bean以及bean之间的依赖关系,在容器使用的配置元数据上反应出来。

  2. IoC 容器

    org.springframework.context.ApplicationContext定义了IoC容器,也负责bean的实例化、配置以及装配。

    容器通过读取配置元数据来获取实例化、配置合装配bean的指令。配置元数据可以通过XML,java注解以及java代码进行描述。通过配置元数据来描述构成应用的对象以及这些对象之间丰富的内在依赖。

    高度抽象的描述Spring如何工作:

    ApplicationContext被创建和初始化后,使用应用程序的类文件+配置文件形成完全配置化的可运行的程序

    5893832-2f2d9057d2c15d96.png
    How Spring Work

    2.1 配置元数据

    我们通过配置元数据来告诉Spring容器如何在应用中初始化、配置合组织对象。

    在基于XML的元数据中,我们使用<bean>标签来配置一个对象,这些<bean>标签被包含在一个<beans>父标签中。

    一个简单的基于XML的配置元数据:

    <?xml version="1.0" encoding="UTF-8"?>
     <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
             https://www.springframework.org/schema/beans/spring-beans.xsd">
    
         <bean id="..." class="...">  
             <!-- collaborators and configuration for this bean go here -->
         </bean>
    
         <bean id="..." class="...">
             <!-- collaborators and configuration for this bean go here -->
         </bean>
    
         <!-- more bean definitions go here -->
    
     </beans>
    

    id:对象的唯一标识符;

    class:使用类的限定类名(带包名)定义bean的类型

  3. 实例化容器

    提供给ApplicationContext构造器的路径是一个资源字符串,这些字符串使得容器从不同的外部资源中加载配置元数据。例如从本地文件系统或者CLASSPATH加载配置元数据。

    //加载在CLASSPATH路径下的service.xml和dao.xml配置元数据来初始化上下文。
    ApplicationContext context = ClassPathXmlApplicationContext("service.xml","dao.xml");
    

    service.xml:

    <?xml version="1.0" encoding="UTF-8"?>
     <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
             https://www.springframework.org/schema/beans/spring-beans.xsd">
    
         <!-- services -->
    
         <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
             <property name="accountDao" ref="accountDao"/>
             <property name="itemDao" ref="itemDao"/>
             <!-- additional collaborators and configuration for this bean go here -->
         </bean>
    
         <!-- more bean definitions for services go here -->
    
     </beans>
    

    dao.xml:

    <?xml version="1.0" encoding="UTF-8"?>
     <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
             https://www.springframework.org/schema/beans/spring-beans.xsd">
    
         <bean id="accountDao"
             class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
             <!-- additional collaborators and configuration for this bean go here -->
         </bean>
    
         <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
             <!-- additional collaborators and configuration for this bean go here -->
         </bean>
    
         <!-- more bean definitions for data access objects go here -->
    
     </beans>
    

    service层由PetStoreServiceImpl类和两个数据访问类JpaAccountDaoJpaItemDao组成,元素property name参考PetStoreServiceImpl类域的名字,ref参考对应bean的id。idref元素之间的链接描述了合作对象之间的依赖关系。

  4. 组织基于XML的配置元数据

    按照不同的逻辑层或者按应用模块将bean的定义分布在不同的xml文件中是非常有用的。

    我们可以使用应用上下文构造器从分散的XML配置文件中加载配置信息。如下所示:

    <beans>
      <import resource="service.xml"/>
      <import resource="resources/message.xml"/>
      <import resource="/resources/themeSource.xml"/>
    
      <bean id="bean1" class="..."/>
      <bean id="bean2" class="..."/>
    </beans>
    

    上述配置加载了三个xml配置文件,其路径都是相对路径,即使第三个资源的路径前有一个/,这个/也会被忽略。

    不建议使用../这样的相对路径来引用父路径文件,并且classpath:URLs(如:classpath:../services.xml)也不建议这样的引用。我们可以使用绝对路径来引用配置文件,如:file:c:/config/service.xml或者classpath:/config/service.xml

  5. 使用容器

    ApplicationContext是一个接口,使得实现它的工厂类拥有各种bean及其依赖bean的注册表。通过调用T getBean(String name, Class<T> requeredType),我们得到需要的bean实例。

    // create and configure beans
    ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
    
    // retrieve configured instance
    PetStoreService service = context.getBean("petStore", PetStoreService.class);
    
    // use configured instance
    List<String> userList = service.getUsernameList();
    

    GenericApplicationContext与加载器的组合灵活多变的,如我们可以使用XmlBeanDefinitionReader来加载XML配置文件

    GenericApplicationContext context = new GenericApplicationContext();
    new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
    context.refresh();
    

    ApplicationContext还有其它的方法,但是理论上,我们的代码不应该去调用这些方法,甚至是getBean,因为调用这些方法,就意味着我们的代码和Spring耦合了。

  6. Bean 简介

    在容器中,我们编写的对bean的配置元数据表现为BeanDefinition对象。这个对象中包含以下的元数据:

    • 一个限定类名:特别的,当配置的是一个接口时,将会定义它的实现类。
    • Bean的行为配置元素,这将描述bean在容器总应该如何运行。
    • bean依赖的其他bean的引用,这些引用也被称为合作者或者依赖。
    • 设置新建对象的配置,如链接池的大小。

    ApplicationContext的实现类也允许注册已经存在由用户自己实例化的尚且不在容器中的对象。调用ApplicationContextgetBeanFactroy()方法,得到一个DefaultListableBeanFactroyBean工厂的实现类对象,调用DefaultListableBeanFactroyregisterSingletion()或者registerBeanDifinition()来注册bean

  7. 命名Bean

    每个Bean都已一到多个标识符,大部分情况下只有一个,在存在多个标识符的情况下,初第一个外的标识符被认定为Bean的别名。

    在基于XML的配置中,可以使用id属性、name属性或者两个都是用来命名Bean。id标签只能定义一个名称,name标签可以定义多个名称,名称之间用,;或者是空格来隔开。

    容器规定Bean的id应该是唯一的。

    容器不强制规定需要定义id或者name属性,如果不需要引用这个bean,我们就可以不提供idname,否则必须命名Bean。

    在Bean的定义之外为其指定别名:

    <!-- 为名为bean的Bean取别名为beanA -->
    <alias name="bean" alias="beanA"/>
    <!-- 为名为bean的Bean取别名为beanB -->
    <alias name="bean" alias="abeanB"/>
    
  8. 实例化Bean

    我们在<bean/>标签中通过class属性指定bean的类型,我们又两种使用class属性的方式:

    • 通常情况下,容器直接通过反射调用构造器构造对象。
    • 如果类存在构造对象的静态工厂方法,在这种情况下,容器可以通过调用工厂方法来构造对象。

    内部类的名称:如果我们想要定义一个嵌套类bean,我们必须使用嵌套类的二进制名称。例如:在com.example包中有一个SomeThing类,这个类嵌套了一个嵌套类OtherThing
    那么在定义这个嵌套类时,class属性的值应该是:com.example.SomeThing$OtherThing。这里使用$将嵌套类名与外围基类名隔开。

    通过构造器实例化Bean

    大部分普通的类都可以通过这种方式实例化Bean,类不需要实现任何指定的接口或者需要按照指定的方式编写,简单的一些约定就足够了。比如需要一个默认的(空的)构造器。

    事实上,SpringIoC容器可以管理任何的类,不管是否符合JavaBean规格。

    通过构造器实例化Bean的配置方式:

    <bean id="exampleBean" class="examples.ExampleBean"/>
    <bean id="ontherExampleBean" class="examples.OntherExampleBean"/>
    

    通过工厂方法实例化Bean

    当一个类存在静态工厂方法时,使用class属性指定bean的类型,使用factory-method属性指定这个类的工厂方法的名字。工厂方法应该能够被调用且返回一个存活的对象。

    下面的定义通过调用静态工厂方法来实例化Bean,定义没有指定返回对象的类型,只指定了工厂方法所存在的类。工厂方法createInstance()必须是静态方法

    <bean id="clientService"
     class="examples.ClientService"
     factory-method="createInstance"/>
    

    下面的ClientService类符合上面的定义

    public class ClientService{
      public static ClientService clientService = new ClientService();
      public ClientService(){}
    
      public static ClientService createInstance(){
        return clientService;
      }
    }
    

    通过实例工厂方法实例化Bean

    同使用静态工厂方法相似,该方法通过调用已近存在于容器中的对象的非静态方法来实例化Bean。

    <!-- 工厂bean, 有一个名为createClientServiceInstance的方法-->
    <bean id="serviceLocator" class="examples.DefaultServiceLocator">
       <!-- inject any dependencies required by this locator bean -->
    </bean>
    
    <!-- 通过实例工厂实例化的bean -->
    <bean id="clientService"
       factory-bean="serviceLocator"
       factory-method="createClientServiceInstance"/>
    
内容概要:本文详细记录了对一个Android ARM64静态ELF文件中字符串加密机制的逆向分析过程。该ELF文件的所有字符串均被加密,无法通过常规strings命令或IDA直接识别。作者通过分析发现,加密字符串存储在.rodata段,其解密所需信息(包括密文地址、长度16位密钥)保存在.data.rel.ro段的40字节描述符中。核心解密函数sub_10F408采用自反的双pass流密码算法,结合固定密钥KEY_TERM(由.data段24字节数据计算得出),实现字节级非线性、位置与长度相关的加密。文章还复现了完整的Python解密脚本,并揭示了该保护机制的本质为代码混淆而非强加密,最终成功批量解密全部956条字符串,暴露程序真实行为,如shell命令模板、设备标识篡改、网络重置等操作。此外,文中还提及未启用的自定义壳框架及其反dump设计。; 适合人群:具备逆向工程基础的安全研究人员、二进制分析人员及对ELF保护技术感兴趣的开发者。; 使用场景及目标:①学习ELF二进制中字符串加密的典型实现方式与逆向突破口;②掌握从结构识别、函数追踪到算法还原的完整逆向流程;③理解“绑定二进制”的完整性校验设计及其局限性;④实践编写IDAPython脚本自动化提取与解密敏感数据。; 阅读建议:此资源以实战案例驱动,不仅展示技术细节,更强调逆向思维与验证方法,建议读者结合IDA调试环境,逐步跟随文中步骤进行动态分析与算法验证,深入理解每一步的推理依据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值