`

spring Lookup方法注入(转)---cglib解决子类过多问题、重构

阅读更多

“Lookup方法”可以使Spring替换一个bean原有的,获取其它对象具体的方法,并自动返回在容器中的查找结果。
我们来看这个例子:
UserDao.java
    在UserDao的构造函数中接受一个name参数,创建UserDao的对象会把自己的名字传递给userDao,这样userDao的create方法中就会把userDao的创建者打印出来。

package research.spring.beanfactory.ch2;
public class UserDao ...{  
    private String name="";  
    public UserDao(String name)...{
              this.name=name;  
    }  
    public void create()...{
              System.out.println("create user from - "+name);
    }
}
UserManager.java
    在这段代码中UserManager依靠getUserDao方法来获取UserDao对象。由于在getUserDao方法里显示的声明了如何去实例一个UserDao,所以上面的代码不符合IoC模式的风格。虽然使用GetUserDao封装了UserDao的创建过程,但是UserManager和UserDao的关系仍然非常紧密。

package research.spring.beanfactory.ch2;
public class UserManager ...{
      public UserDao getUserDao() ...{
              return new UserDao("UserManager.getUserDao()");
       }
       public void createUser() ...{
              UserDao dao = getUserDao(); //通过getUserDao获得userDao
             dao.create();
        }
}LookupMethodTest.java
    通过BeanFactory获得UserManager,并调用createUser方法。 package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest ...{
        public static void main(String[] args) ...{
              XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource("research/spring/beanfactory/ch2/context.xml"));
              UserManager manager=(UserManager) factory.getBean("userManager");
              manager.createUser();    //create a User
        }
}
   context.xml

context.xml
<xml version="1.0" encoding="UTF-8"?>DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean name="userManager"       class="research.spring.beanfactory.ch2.UserManager">
   </bean>
    <bean name="userDao class="research.spring.beanfactory.ch2.UserDao" >
    </bean>
</beans>
运行LookupMethodTest你会看到屏幕输入” create user from - UserManager.getUserDao()”。

    由于是遗留系统,所以我们不能修改UserManager。现在我希望让这个UserManager依赖的Dao对象由spring管理,而不修改原有的代码。
    在这个场景中我们就可以利用Spring提供的“Lookup方法”来替换原有的getUserDao方法,实现自动获取userDao的功能。修改context.xml:

<xml version="1.0" encoding="UTF-8"?>DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"   
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean name="userManager"       class="research.spring.beanfactory.ch2.UserManager">
       <lookup-method name="getUserDao" bean="userDao" />
    </bean>
    <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" >
       <constructor-arg>
           <value>lookup method</value>
       </constructor-arg>
    </bean>
    <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory">
    </bean>
</beans>
    再次运行LookupMethodTest你会看到不同的输出结果“create user from - lookup method”。字符串“lookup method”是通过构造函数注入给userDao的。原来的userManager.java并没有作任何修改,仍然是通过UserDao dao = getUserDao();来获得userDao的。这说明Spring已经替换了原有的getUserDao方法的实现,当执行getUserDao时Spring会在容器中寻找指定的Bean,并返回这个Bean。

Lookup方法的工作机制
    通过这种机制我们可以在不修改原系统代码的情况下,可以轻易的把UserDao换成别的类型相容的对象而不会影响原系统。Spring是使用CGLIB在字节码级别动态实现出userManager的子类,并重写getUserDao方法的方式来实现这个神奇的功能的。
修改LookupMethodTest.java:

package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest ...{
        public static void main(String[] args) ...{
              XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
         "research/spring/beanfactory/ch2/context.xml"));
              UserManager manager=(UserManager) factory.getBean("userManager");
              System.out.println(manager.toString()); //打印userManager的信息
              manager.createUser();    //create a User
        }
}
    我们在获取UserManager的实例后打印出这个实例的信息,再次运行LookupMethodTest你会看到:

注意manager.toString()打印出了:
research.spring.beanfactory.ch2.UserManager$$EnhancerByCGLIB$$886cdee9@1238bd2
    这个是CGLIG动态生成的类,而不是原来的UserManager的实例。所以请记住在任何时候只要定义了一个Bean的Lookup方法,那么这个Bean的实例将是一个CGLIB动态生成的实例而不是原来类的实例。

Spring允许在一个Bean中定义多个Lookup方法。

<bean name="userManager"       class="research.spring.beanfactory.ch2.UserManager">
       <lookup-method name="getUserDao" bean="userDao" />
       <lookup-method name="getOtherDao" bean="otherDao" />
</bean>上面的做法是合法的,并且可以正常工作。

    虽然Spring会用CGLIB动态生成一个带有Lookup方法bean的子类,但是这并不影响Spring完成其它的功能。Sping还是允许Lookup方法和setXXX、构造函数注入以及后面我们将介绍的自动依赖检查和自动装配的功能同时使用。

    Spring还允许Lookup方法中定义的方法带有参数,但是Sping不会处理这些参数。
修改UserManager:
package research.spring.beanfactory.ch2;
public class UserManager ...{
       private UserDao dao;
        public void setDao(UserDao dao) ...{
              this.dao = dao;
       }
        public UserDao getUserDao(String daoName) ...{
              return new UserDao("UserManager.getUserDao()");
       }
        public void createUser() ...{
              UserDao dao = getUserDao(“userDao”); //通过getUserDao获得userDao
              dao.create();
        }
}虽然方法上由参数,但是上面的代码可以正常工作。Spring不会处理这些参数。

Spring对Lookup方法也存在一些限制:
方法不能是private的,但可以是protected的。
方法不能是静态的。
在抽象类和接口上应用Lookup方法有一个比较有趣的用法,就是在抽象类上定义Lookup方法。你一定记得经典的工厂模式吧。定义一个抽象工厂,然后为每一类具体产品实现一个具体产品的工厂。
一个抽象工厂: package research.spring.beanfactory.ch2;
public abstract class Factory ...{
   public abstract UserDao getProduct();
}
具体一类产品的工厂:
package research.spring.beanfactory.ch2;
public class UserDaoFactory extends Factory...{
   public UserDao getProduct()...{
          return new UserDao("UserDaoFactory");
   }
}

用户可以通过:
new UserDaoFactory().getProduce();
来获取具体的UserDao产品。
    但是如果有很多产品就需要做出实现出很多工厂如,DocumentDaoFactory、GroupDaoFactory等等,这样系统中会出现大量的工厂。工厂的泛滥并不能说明系统的设计是合理的。
    既然Spring可以在抽象类上使用Lookup方法,那么我们就可以不同实现真的去实现那么多的子类了。我们可以在抽象类上直接定义Lookup方法和目标对象。用户直接通过抽象类来获得需要的产品对象。看下面这个例子:
Factory.java

package research.spring.beanfactory.ch2;
public abstract class Factory ...{
   public abstract Object getProduct();
}context.xml
    如果指定userDaoFactory的类为一个抽象类,并且再这个bean里定义了Lookup方法,那么Spring会自动生成这个抽象类的子类实现。 <xml version="1.0" encoding="UTF-8"?>DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean name="userManager"       class="research.spring.beanfactory.ch2.UserManager">
       <lookup-method name="getUserDao" bean="userDao" />
   </bean>
    <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" >
       <constructor-arg>
               <value>lookup method</value>
       </constructor-arg>
   </bean>
    <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
      <lookup-method name="getProduct" bean="userDao" />
    </bean>
</beans>Test.java
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest ...{
        public static void main(String[] args) ...{
              XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource("research/spring/beanfactory/ch2/context.xml"));
              //获得抽象工厂
              Factory abstractFactory=(Factory) factory.getBean("userDaoFactory");
              UserDao userDao=(UserDao) abstractFactory.getProduct();
              System.out.println(userDao.toString());
              userDao.create();
       }
}

运行Test你会看到:
research.spring.beanfactory.ch2.UserManager$$EnhancerByCGLIB$$cc2f8f1c@12c7568
create user from - lookup method

    对,这个结果和上面的例子是完全一样的。UserDao并没有改变,我们通过抽象的Factory获得了具体的UserDao的实例。这样即使系统中很多的具体产品我们也不需要实现每类产品的工厂类了。只需要在系统中配置多个抽象工厂,并且配置每个工厂的singlton为false,在用户使用时使用不同抽象工厂的实例就可以了。

<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
       <lookup-method name="getProduct" bean="userDao" />
</bean>
<bean name="documentDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
       <lookup-method name="getProduct" bean="documentDao" />
</bean>
Spring不关心抽象类中的定义的lookup方法是否时抽象的,Spring都会重写这个方法。

    既然Sping可以动态实现抽象类的子类那么,它能不能动态创建出实现一个接口的类呢。答案时肯定的。上面的例子可以直接把Factory变成一个接口,仍然可以正常工作。
    这里需要注意的是,只要在一个Bean上明确的定义了Lookup方法,Spring才会使用CGLIB来做原对象的字节码代理。如果一个没有定义Lookup方法的抽象类或接口是不能直接被Spring实例的。

    本文介绍了Lookup方法的使用和工作原理,希望读者能够对Lookup方法有了比较深入的了解。虽然我的例子可以简化工厂模式,但是我并不鼓励大家在实际系统中这样做。因为我始终认为“工厂模式”只要在遗留系统中才会碰到。使用IoC模式基本上可以替代所有的对象创建模式。本章的例子只是为了说明Lookup方法如何使用,和Lookup方法的一些特殊情况。Lookup方法一般只在处理遗留代码时使用。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qking93415981/archive/2007/08/10/1735617.aspx

分享到:
评论

相关推荐

    lookup窗体 --- 用x++代码创建.docx

    ### lookup窗体 -- 用X++代码创建 #### 概述 在Microsoft Dynamics AX系统中,`lookup`窗体是一...此外,值得注意的是,在实际开发过程中,还应该考虑错误处理、性能优化等方面的问题,确保lookup窗体的高效稳定运行。

    深入理解Spring中的Lookup(方法注入)

    总的来说,Lookup方法注入是Spring提供的一种解决策略,它使得单例bean能够正确处理非单例依赖,避免了在多线程环境中可能出现的问题,同时保持了代码的简洁性。通过理解和熟练运用这种方法,开发者可以更好地利用...

    Lookup Anything 1.29-541-1-29-0-1608577954.zip

    "Lookup Anything 1.29-541-1-29-0-1608577954.zip" 是一个特定版本的模组(mod)压缩包,主要用于游戏环境,尤其是那些支持模组扩展的游戏。"mods"标签暗示了这个压缩包包含的是一个或多个游戏模组,这些模组通常是...

    Spring-Data-MongoDB3.2

    **Spring Data MongoDB 3.2 整合指南** 在当今的软件开发中,Spring框架以其强大的功能和灵活性深受开发者喜爱,而MongoDB作为一款非关系型数据库,因其高性能、高可扩展性和灵活的数据模型,成为了大数据和实时...

    spring2.0技术手册--林信良

    - **依赖查找**:除了原有的依赖注入之外,还支持依赖查找(Dependency Lookup),即在运行时根据类型或名称查询 Bean。 ##### 2.3 数据访问改进 - **JDBC 模块**:Spring 2.0 对 JDBC 模块进行了大幅度的改进,...

    springboot 基础简易实例, maven项目

    --------------------------- DemoApplication 运行 main 方法即可启动 springboot --------------------------- package com.example.demo; import org.springframework.boot.SpringApplication; import org....

    spring-beans-3.0.xsd

    在`&lt;bean&gt;`元素中,`lookup-method`和`replaced-method`属性的引入,使得在运行时动态查找或替换bean的方法成为可能,这是对AOP(面向切面编程)的一种补充,提供了更细粒度的控制。 除此之外,`&lt;util&gt;`命名空间在...

    PyPI 官网下载 | django_lookup_extensions-0.2.0-py2.py3-none-any.whl

    资源来自pypi官网。 资源全名:django_lookup_extensions-0.2.0-py2.py3-none-any.whl

    RevitLookup-2021.0.0.13

    5. **快速定位问题**:在开发过程中,RevitLookup能快速定位并解决代码错误,提高开发效率。 Revit二次开发涉及到的知识点包括: 1. **C#编程语言**:Revit API主要使用C#进行开发,因此需要掌握基本的C#语法和...

    RevitLookup-2020.0.0.4.zip

    RevitLookup-2020.0.0.4.zip文件包含了2020版本的RevitLookup安装包,版本号为2020.0.0.4,提供了一个编译好的MSI安装文件,使得安装过程变得简单快捷。 首先,让我们详细了解一下RevitLookup的功能和用途。Revit...

    Spring Ioc 注解 依赖注入

    Spring框架是由Rod Johnson创建的一个开源项目,最初是为了解决企业级应用开发中的复杂性问题而诞生的。Spring框架的核心特性包括IoC(Inversion of Control,控制反转)、AOP(Aspect Oriented Programming,面向切...

    解决docker报错dial tcp lookup registry-1.docker.io

    解决docker报错dial tcp lookup registry-1.docker.io

    CMDBuild-增加超类和子类以及lookup,reference的配置

    本文将重点介绍如何在CMDBuild中增加超类(superclass)、子类(subclass),以及lookup和reference的配置方法。 #### 二、增加超类与子类 ##### 1. 超类的概念 在CMDBuild中,超类是一种用于组织和管理相似类型...

    spring依赖注入的实现原理

    Spring依赖注入(Dependency Injection,简称DI)是Java应用开发中常用的设计模式,它极大地提高了代码的可测试性和可维护性。在Spring框架中,依赖注入是核心特性之一,通过控制反转(Inversion of Control,IoC)...

    spring-jdbc-4.2.xsd.zip

    《Spring JDBC 4.2.xsd详解》 在Java企业级开发中,Spring框架是不可或缺的一部分,它提供了全面的编程和配置模型,极大地简化了应用的构建和维护。Spring JDBC作为Spring框架的一部分,主要负责数据库操作,使得...

    JAVA spring 系列案例50个和学习资料

    Spring系列第12篇:lazy-init:bean延迟初始化Spring系列第13篇:使用继承简化bean配置(abstract & parent)Spring系列第14篇:lookup-method和replaced-method比较陌生,怎么玩的?Spring系列第15篇:代理详解(Java...

    Spring-Reference_zh_CN(Spring中文参考手册)

    Lookup方法注入 3.3.8.2. 自定义方法的替代方案 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. 其他作用域 3.4.3.1. 初始化web配置 3.4.3.2. Request作用域 3.4.3.3. Session作用域 3.4...

    PyPI 官网下载 | django_lookup_tables-0.12.2-py3-none-any.whl

    标题"PyPI 官网下载 | django_lookup_tables-0.12.2-py3-none-any.whl"表明这是一个在Python的包索引平台PyPI上发布的软件包,名为`django_lookup_tables`,版本号为0.12.2,适用于Python 3解释器。`.whl`文件是...

    Python库 | iso3_lookup-0.3.0-py3-none-any.whl

    资源分类:Python库 所属语言:Python 使用前提:需要解压 资源全名:iso3_lookup-0.3.0-py3-none-any.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

Global site tag (gtag.js) - Google Analytics