【第十二章】零配置 之 12.2 注解实现Bean依赖注入 ——跟我学spring3

12.2 注解实现Bean依赖注入

12.2.1 概述

注解实现Bean配置主要用来进行如依赖注入、生命周期回调方法定义等,不能消除XML文件中的Bean元数据定义,且基于XML配置中的依赖注入的数据将覆盖基于注解配置中的依赖注入的数据

Spring3的基于注解实现Bean依赖注入支持如下三种注解:

  • Spring自带依赖注入注解:Spring自带的一套依赖注入注解;
  • JSR-250注解:Java平台的公共注解,是Java EE 5规范之一,在JDK6中默认包含这些注解,从Spring2.5开始支持。
  • JSR-330注解:Java 依赖注入标准,Java EE 6规范之一,可能在加入到未来JDK版本,从Spring3开始支持;
  • JPA注解:用于注入持久化上下文和实体管理器。

这三种类型的注解在Spring3中都支持,类似于注解事务支持,想要使用这些注解需要在Spring容器中开启注解驱动支持,即使用如下配置方式开启:

java代码:
Java代码
  1. <beansxmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  6. //www.springframework.org/schema/context
  7. //www.springframework.org/schema/context/spring-context-3.0.xsd">
  8. <context:annotation-config/>
  9. </beans>

这样就能使用注解驱动依赖注入了,该配置文件位于“resources/ chapter12/dependecyInjectWithAnnotation.xml”。

12.2.2 Spring自带依赖注入注解

一、@Required:依赖检查;

对应于基于XML配置中的依赖检查,但XML配置的依赖检查将检查所有setter方法,详见【3.3.4 依赖检查】;

基于@Required的依赖检查表示注解的setter方法必须,即必须通过在XML配置中配置setter注入,如果没有配置在容器启动时会抛出异常从而保证在运行时不会遇到空指针异常,@Required只能放置在setter方法上,且通过XML配置的setter注入,可以使用如下方式来指定:

@Requried

  • setter方法
  • 1、准备测试Bean

    packagecn.javass.spring.chapter12;

  • publicclassTestBean{
  • privateStringmessage;
  • @Required
  • voidsetMessage(Stringmessage){
  • this.message=message;
  • }
  • publicStringgetMessage(){
  • returnmessage;
  • }
  • 2、在Spring配置文件(chapter12/dependecyInjectWithAnnotation.xml)添加如下Bean配置:

    <beanid="testBean"class="cn.javass.spring.chapter12.TestBean">

  • <propertyname="message"ref="message"/>
  • </bean>
  • <beanid="message"class="java.lang.String">
  • <constructor-argindex="0"value="hello"/>
  • </bean>
  • 3、测试类和测试方法如下:

    //省略import

  • classDependencyInjectWithAnnotationTest{
  • privatestaticStringconfigLocation="classpath:chapter12/dependecyInjectWithAnnotation.xml";
  • staticApplicationContextctx=newClassPathXmlApplicationContext(configLocation);
  • //1、Spring自带依赖注入注解
  • @Test
  • voidtestRequiredForXmlSetterInject(){
  • TestBeantestBean=ctx.getBean("testBean",TestBean.class);
  • Assert.assertEquals("hello",testBean.getMessage());
  • 在XML配置文件中必须指定setter注入,否则在Spring容器启动时将抛出如下异常:

    org.springframework.beans.factory.BeanCreationException:

  • Errorcreatingbeanwithname'testBean'definedinclasspathresource[chapter12/dependecyInjectWithAnnotation.xml]:Initializationofbeanfailed;
  • nestedexceptionisorg.springframework.beans.factory.BeanInitializationException:Property'message'isrequiredforbean'testBean'
  • 二、@Autowired:自动装配

    自动装配,用于替代基于XML配置的自动装配,详见【3.3.3 自动装配】。

    基于@Autowired的自动装配,默认是根据类型注入,可以用于构造器、字段、方法注入,使用方式如下:

    @Autowired(required=true)

  • 构造器、字段、方法
  • @Autowired默认是根据参数类型进行自动装配,且必须有一个Bean候选者注入,如果允许出现0个Bean候选者需要设置属性“required=false”,“required”属性含义和@Required一样,只是@Required只适用于基于XML配置的setter注入方式。

    (1)、构造器注入:通过将@Autowired注解放在构造器上来完成构造器注入,默认构造器参数通过类型自动装配,如下所示:

    1、准备测试Bean,在构造器上添加@AutoWired注解:

    importorg.springframework.beans.factory.annotation.Autowired;

  • classTestBean11{
  • @Autowired//构造器注入
  • privateTestBean11(Stringmessage){
  • //省略message的getter和setter
  • <beanid="testBean11"class="cn.javass.spring.chapter12.TestBean11"/>
  • 3、测试类如下:

    voidtestAutowiredForConstructor(){

  • TestBean11testBean11=ctx.getBean("testBean11",TestBean11. 在Spring配置文件中没有对“testBean11”进行构造器注入和setter注入配置,而是通过在构造器上添加@ Autowired来完成根据参数类型完成构造器注入。

    (2)、字段注入:通过将@Autowired注解放在构造器上来完成字段注入。

    1、准备测试Bean,在字段上添加@AutoWired注解:

    classTestBean12{

  • //字段注入
  • //省略getter和setter
  • <beanid="testBean12"class="cn.javass.spring.chapter12.TestBean12"/>
  • 3、测试方法如下:

    voidtestAutowiredForField(){

  • TestBean12testBean12=ctx.getBean("testBean12",TestBean12. 字段注入在基于XML配置中无相应概念,字段注入不支持静态类型字段的注入。

    (3)、方法参数注入:通过将@Autowired注解放在方法上来完成方法参数注入。

    1、准备测试Bean,在方法上添加@AutoWired注解:

    classTestBean13{

  • //setter方法注入
  • classTestBean14{
  • privateList<String>list;
  • true)//任意一个或多个参数方法注入
  • voidinitMessage(Stringmessage,ArrayList<String>list){
  • this.list=list;
  • <beanid="testBean13"class="cn.javass.spring.chapter12.TestBean13"/>
  • <beanid="testBean14"class="cn.javass.spring.chapter12.TestBean14"/>
  • <beanid="list"class="java.util.ArrayList">
  • <constructor-argindex="0">
  • <list>
  • <refbean="message"/>
  • </list>
  • </constructor-arg>
  • voidtestAutowiredForMethod(){
  • TestBean13testBean13=ctx.getBean("testBean13",TestBean13. TestBean14testBean14=ctx.getBean("testBean14",TestBean14. Assert.assertEquals(ctx.getBean("list",List.class),testBean14.getList());
  • 方法参数注入除了支持setter方法注入,还支持1个或多个参数的普通方法注入,在基于XML配置中不支持1个或多个参数的普通方法注入,方法注入不支持静态类型方法的注入。

    注意“initMessage(String message,ArrayList<String> list)”方法签名中为什么使用ArrayList而不是List呢?具体参考【3.3.3 自动装配】一节中的集合类型注入区别。

    三、@Value:注入SpEL表达式;

    用于注入SpEL表达式,可以放置在字段方法或参数上,使用方式如下:

    @Value(value="SpEL表达式")

  • 字段、方法、参数
  • 1、可以在类字段上使用该注解:

    @Value(value="#{message}")

  • privateStringmessage;
  • 2、可以放置在带@Autowired注解的方法的参数上:

    @Autowired

  • voidinitMessage(@Value(value="#{message}#{message}")Stringmessage){
  • 3、还可以放置在带@Autowired注解的构造器的参数上:

    privateTestBean43( 具体测试详见DependencyInjectWithAnnotationTest 类的testValueInject测试方法。

    四、@Qualifier:限定描述符,用于细粒度选择候选者;

    @Autowired默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常,详见【3.3.3 自动装配】中的根据类型进行注入;

    @Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:

    @Qualifier(value="限定标识符")

  • (1)、根据基于XML配置中的<qualifier>标签指定的名字进行注入,使用如下方式指定名称:

    <qualifiertype="org.springframework.beans.factory.annotation.Qualifier"value="限定标识符"/>

  • 其中type属性可选,指定类型,默认就是Qualifier注解类,name就是给Bean候选者指定限定标识符,一个Bean定义中只允许指定类型不同的<qualifier>,如果有多个相同type后面指定的将覆盖前面的。

    1、准备测试Bean:

    importjavax.sql.DataSource;

  • importorg.springframework.beans.factory.annotation.Qualifier;
  • classTestBean31{
  • privateDataSourcedataSource;
  • //根据<qualifier>标签指定Bean限定标识符
  • voidinitDataSource(@Qualifier("mysqlDataSource")DataSourcedataSource){
  • this.dataSource=dataSource;
  • publicDataSourcegetDataSource(){
  • returndataSource;
  • <beanid="testBean31"class="cn.javass.spring.chapter12.TestBean31"/>
  • 我们使用@Qualifier("mysqlDataSource")来指定候选Bean的限定标识符,我们需要在配置文件中使用<qualifier>标签来指定候选Bean的限定标识符“mysqlDataSource”:

    <beanid="mysqlDataSourceBean"class="org.springframework.jdbc.datasource.DriverManagerDataSource">

  • <qualifiervalue="mysqlDataSource"/>
  • voidtestQualifierInject1(){
  • TestBean31testBean31=ctx.getBean("testBean31",TestBean31.try{
  • //使用<qualifier>指定的标识符只能被@Qualifier使用
  • ctx.getBean("mysqlDataSource");
  • Assert.fail();
  • }catch(Exceptione){
  • //找不到该Bean
  • Assert.assertTrue(einstanceofNoSuchBeanDefinitionException);
  • Assert.assertEquals(ctx.getBean("mysqlDataSourceBean"),testBean31.getDataSource());
  • 从测试可以看出使用<qualifier>标签指定的限定标识符只能被@Qualifier使用,不能作为Bean的标识符,如“ctx.getBean("mysqlDataSource")”是获取不到Bean的。

    (2)、缺省的根据Bean名字注入:最基本方式,是在Bean上没有指定<qualifier>标签时一种容错机制,即缺省情况下使用Bean标识符注入,但如果你指定了<qualifier>标签将不会发生容错。

    classTestBean32{

  • @Qualifier(value="mysqlDataSource2")//指定Bean限定标识符
  • //@Qualifier(value="mysqlDataSourceBean")
  • //是错误的注入,不会发生回退容错,因为你指定了<qualifier>
  • voidinitDataSource(DataSourcedataSource){
  • <beanid="testBean32"class="cn.javass.spring.chapter12.TestBean32"/>
  • <beanid="oracleDataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
  • voidtestQualifierInject2(){

  • TestBean32testBean32=ctx.getBean("testBean32",TestBean32. Assert.assertEquals(ctx.getBean("oracleDataSource"),testBean32.getDataSource());
  • 默认情况下(没指定<qualifier>标签)@Qualifier的value属性将匹配Bean 标识符。

    (3)、扩展@Qualifier限定描述符注解:对@Qualifier的扩展来提供细粒度选择候选者;

    具体使用方式就是自定义一个注解并使用@Qualifier注解其即可使用。

    首先让我们考虑这样一个问题,如果我们有两个数据源,分别为Mysql和Oracle,因此注入两者相关资源时就牵扯到数据库相关,如在DAO层注入SessionFactory时,当然可以采用前边介绍的方式,但为了简单和直观我们希望采用自定义注解方式。

    1、扩展@Qualifier限定描述符注解来分别表示Mysql和Oracle数据源

    packagecn.javass.spring.chapter12.qualifier;

  • /**表示注入Mysql相关*/
  • @Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})
  • @Retention(RetentionPolicy.RUNTIME)
  • @Qualifier
  • @interfaceMysql{
  • /**表示注入Oracle相关*/
  • @interfaceOracle{
  • 2、准备测试Bean:

    classTestBean33{

  • privateDataSourcemysqlDataSource;
  • privateDataSourceoracleDataSource;
  • @MysqlDataSourcemysqlDataSource,@OracleDataSourceoracleDataSource){
  • this.mysqlDataSource=mysqlDataSource;
  • this.oracleDataSource=oracleDataSource;
  • publicDataSourcegetMysqlDataSource(){
  • returnmysqlDataSource;
  • publicDataSourcegetOracleDataSource(){
  • returnoracleDataSource;
  • 3、在Spring配置文件(chapter12/dependecyInjectWithAnnotation.xml)添加如下Bean配置:

    <beanid="testBean33"class="cn.javass.spring.chapter12.TestBean33"/>

  • 4、在Spring修改定义的两个数据源:

    <qualifiertype="cn.javass.spring.chapter12.qualifier.Mysql"/>

  • <qualifiertype="cn.javass.spring.chapter12.qualifier.Oracle"/>
  • 5、测试方法如下:

    voidtestQualifierInject3(){

  • TestBean33testBean33=ctx.getBean("testBean33",TestBean33.
  • 测试也通过了,说明我们扩展的@Qualifier限定描述符注解也能很好工作。

    前边演示了不带属性的注解,接下来演示一下带参数的注解:

    1、首先定义数据库类型:

    enumDataBase{

  • ORACLE,MYSQL;
  • 2、其次扩展@Qualifier限定描述符注解

    @interfaceDataSourceType{

  • Stringip();//指定ip,用于多数据源情况
  • DataBasedatabase();//指定数据库类型
  • 3、准备测试Bean:

    importcn.javass.spring.chapter12.qualifier.DataBase;

  • importcn.javass.spring.chapter12.qualifier.DataSourceType;
  • classTestBean34{
  • voidinitDataSource(
  • @DataSourceType(ip="localhost",database=DataBase.MYSQL)
  • DataSourcemysqlDataSource,
  • DataSourceoracleDataSource){
  • //省略getter方法
  • 4、在Spring配置文件(chapter12/dependecyInjectWithAnnotation.xml)添加如下Bean配置:

    <beanid="testBean34"class="cn.javass.spring.chapter12.TestBean34"/>

  • 5、在Spring修改定义的两个数据源:

    <qualifiertype="cn.javass.spring.chapter12.qualifier.DataSourceType">

  • <attributekey="ip"value="localhost"/>
  • <attributekey="database"value="MYSQL"/>
  • </qualifier>
  • <attributekey="database"value="ORACLE"/>
  • 6、测试方法如下:

    TestBean34testBean34=ctx.getBean("testBean34",TestBean34.

  • 测试也通过了,说明我们扩展的@Qualifier限定描述符注解也能很好工作。

    四、自定义注解限定描述符:完全不使用@Qualifier,而是自己定义一个独立的限定注解;

    1、首先使用如下方式定义一个自定义注解限定描述符:

    @interfaceCustomQualifier{

  • Stringvalue();
  • classTestBean35{
  • privateDataSourcedataSoruce;
  • publicTestBean35(@CustomQualifier("oracleDataSource")DataSourcedataSource){
  • this.dataSoruce=dataSource;
  • publicDataSourcegetDataSoruce(){
  • returndataSoruce;
  • <beanid="testBean35"class="cn.javass.spring.chapter12.TestBean35"/>
  • 4、然后在Spring配置文件中注册CustomQualifier自定义注解限定描述符,只有注册了Spring才能识别:

    <beanid="customAutowireConfigurer"class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">

  • <propertyname="customQualifierTypes">
  • <set>
  • <value>cn.javass.spring.chapter12.qualifier.CustomQualifier</value>
  • </set>
  • </property>
  • voidtestQualifierInject5(){
  • TestBean35testBean35=ctx.getBean("testBean35",TestBean35. 从测试中可看出,自定义的和Spring自带的没什么区别,因此如果没有足够的理由请使用Spring自带的Qualifier注解。

    到此限定描述符介绍完毕,在此一定要注意以下几点:

    • 限定标识符和Bean的描述符是不一样的;
    • 多个Bean定义中可以使用相同的限定标识符;
    • 对于集合、数组、字典类型的限定描述符注入,将注入多个具有相同限定标识符的Bean。

    12.2.3 JSR-250注解

    一、@Resource:自动装配,默认是先按名称找找不到再byType,如果指定name属性将根据名字装配,可以使用如下方式来指定:

    @Resource(name="标识符")

  • 字段或setter方法
  • importjavax.annotation.Resource;

  • classTestBean41{
  • @Resource(name="message")
  • <beanid="testBean41"class="cn.javass.spring.chapter12.TestBean41"/>
  • voidtestResourceInject1(){

  • TestBean41testBean41=ctx.getBean("testBean41",TestBean41. 使用非常简单,和@Autowired不同的是可以指定name来根据名字注入。

    使用@Resource需要注意以下几点:

    • @Resource注解应该只用于setter方法注入,不能提供如@Autowired多参数方法注入;
    • @Resource在没有指定name属性的情况下首先将根据setter方法对于的字段名查找资源,如果找不到再根据类型查找;
    • @Resource首先将从JNDI环境中查找资源,如果没找到默认再到Spring容器中查找,因此如果JNDI环境中有和Spring容器同名的资源时需要注意。

    二、@PostConstruct和PreDestroy:通过注解指定初始化和销毁方法定义;

    1、在测试类TestBean41中添加如下代码:

    @PostConstruct

  • voidinit(){
  • System.out.println("==========init");
  • @PreDestroy
  • voiddestroy(){
  • System.out.println("==========destroy");
  • 2、修改测试方法如下:

    voidresourceInjectTest1(){

  • ((ClassPathXmlApplicationContext)ctx).registerShutdownHook();
  • 类似于通过<bean>标签的init-method和destroy-method属性指定的初始化和销毁方法,但具有更高优先级,即注解方式的初始化和销毁方法将先执行。

    12.2.4 JSR-330注解

    在测试之前需要准备JSR-330注解所需要的jar包,到spring-framework-3.0.5.RELEASE-dependencies.zip中拷贝如下jar包到类路径:

    com.springsource.javax.inject-1.0.0.jar

    一、@Inject:等价于默认的@Autowired,只是没有required属性;

    二、@Named:指定Bean名字,对应于Spring自带@Qualifier中的缺省的根据Bean名字注入情况;

    三、@Qualifier:只对应于Spring自带@Qualifier中的扩展@Qualifier限定描述符注解,即只能扩展使用,没有value属性。

    1、首先扩展@Qualifier限定描述符注解来表示Mysql数据源

    //省略部分import

  • importjavax.inject.Qualifier;
  • @Target({ElementType.FIELD,85); font-weight:bold">@interfaceJSR330Mysql{
  • importjavax.inject.Inject;
  • importjavax.inject.Named;
  • importcn.javass.spring.chapter12.qualifier.JSR330Mysql;
  • classTestBean51{
  • @Inject
  • voidinitDataSoruce(
  • @JSR330MysqlDataSourcemysqlDataSource,100)">@Named("oracleDataSource")DataSourceoracleDataSource){
  • //省略getter
  • <beanid="testBean51"class="cn.javass.spring.chapter12.TestBean51"/>
  • 4、在Spring修改定义的mysqlDataSourceBean数据源:

    <qualifiertype="cn.javass.spring.chapter12.qualifier.JSR330Mysql"/>

  • voidtestInject(){
  • TestBean51testBean51=ctx.getBean("testBean51",TestBean51.
  • 测试也通过了,说明JSR-330注解也能很好工作。

    从测试中可以看出JSR-330注解和Spring自带注解依赖注入时主要有以下特点:

    12.2.5 JPA注解

    用于注入EntityManagerFactory和EntityManager。

    classTestBean61{

  • @PersistenceContext(unitName="entityManagerFactory")
  • privateEntityManagerentityManager;
  • @PersistenceUnit(unitName="entityManagerFactory")
  • privateEntityManagerFactoryentityManagerFactory;
  • publicEntityManagergetEntityManager(){
  • returnentityManager;
  • publicEntityManagerFactorygetEntityManagerFactory(){
  • returnentityManagerFactory;
  • <importresource="classpath:chapter7/applicationContext-resources.xml"/>
  • importresource="classpath:chapter8/applicationContext-jpa.xml"/>
  • <beanid="testBean61"class="cn.javass.spring.chapter12.TestBean61"/>
  • 此处需要引用第七章和八章的配置文件,细节内容请参考七八两章。

    voidtestJpaInject(){

  • TestBean61testBean61=ctx.getBean("testBean61",TestBean61. Assert.assertNotNull(testBean61.getEntityManager());
  • Assert.assertNotNull(testBean61.getEntityManagerFactory());
  • 测试也通过了,说明JPA注解也能很好工作。

    JPA注解类似于@Resource注解同样是先根据unitName属性去JNDI环境中查找,如果没找到在到Spring容器中查找。

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

    相关推荐


    什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
    单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
    动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
    适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
    策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
    设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
    模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
    迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
    外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
    单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
    组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
    装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
    观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
    代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
    工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
    状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
    命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
    备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
    顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
    享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结