Spring详解之一:ICP(控制反转)

#Spring ##Spring中的Bean的生命周期过程

  1. 通过Bean对应的Java类的构造器或者工厂方法创建Bean实例

  2. 为Bean的属性设置值和对其他Bean的引用(如Spring的xml文件中该Bean配置有properties子节点)

  3. 将Bean实例传递给Bean后置处理器(实现BeanPostProcessor接口的类,并在Spring的xml文件中配置如下Bean标签)的postProcessBeforeInitialization()方法操作

     <!-- com.abinge.spring.beans.MyBeanPostProcessor为自定义的实BeanPostProcessor接口的类 -->
     <!-- 该bean不用配置id,因为其对Spring容器中的全部Bean对象都会起作用进行操作处理 -->
     <bean class="com.abinge.spring.beans.MyBeanPostProcessor"/>
    
  4. 调用Bean的初始化方法(init-method)

  5. 将Bean实例传递给Bean后置处理器(实现BeanPostProcessor接口的类)的postProcessAfterInitialization()方法操作

  6. 使用Bean实例

  7. 当Spring容器关闭时,调用Bean的销毁方法(destroy-method) ###定义

  • Spring是一个开源的控制反转(Inversion of Control-IOC)和面向切面(AOP)的容器框架

  • 其主要作用是简化企业开发 ###控制反转IOC

  • 定义:控制反转IOC指应用本身(PersonServiceBean)不负责依赖对象(PersonDao)的创建及维护,依赖的对象的创建及维护由外部容器负责,这样对依赖对象的控制权就由应用本身转移到了外部容器,这种控制权的转移就是所谓的反转。

      public class PersonServiceBean{
    
      	private PersonDao personDao = new PersonDao();
    
      	public void save(Person person){
      		personDao.save(person);
      	}
      }
    
  • 在上面的例子中,PersonDao是在应用内部创建及维护的,故不是控制反转。若使用控制反转的话,则上面的例子需要进行修改。 ###依赖注入(Dependency Injection-DI)

  • 定义:在运行期,由外部容器动态的将依赖对象注入到组件中

  • 对上述的代码进行控制反转下的修改,改为由外部容器负责创建:

    1. 通过setter方法进行依赖注入

       //定义一个PersonServiceBean类
       public class PersonServiceBean{
       	private PersonDao personDao;
      
       	public void setPersonDaoBean(PersonDao personDao){
       		this.personDao = personDao;
       	}
      
       	public void save(Person person){
       		personDao.save(person);
       	}
       }
       //在Spring配置文件中定义bean:
       //1、
       	<bean id="person" class="com.abinge.springtest.Person"/>
       	<bean id="personDao" class="com.abinge.springtest.PersonDao"/>
       	<bean id="personServiceBean" class="com.abinge.springtest.PersonServiceBean">
       		<property name="personDao" ref="personDao"/>
       	</bean>
       //2、使用内部bean,此时PersonDao不能被其他bean使用
       	<bean id="person" class="com.abinge.springtest.Person"/>
       	<bean id="personServiceBean" class="com.abinge.springtest.PersonServiceBean">
       		<property name="personDao"/>
       			<bean class="com.abinge.springtest.PersonDao"/>
       		</property>
       	</bean>	
      
       //在客户端
       ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
       PersonServiceBean personServiceBean = (Test)context.getBean("personServiceBean");
       Person person = (Test)context.getBean("person");
       personServiceBean.save(person);
      
    2. 通过构造器进行依赖注入

       //定义一个PersonServiceBean类
       public class PersonServiceBean{
       	private PersonDao personDao;
      
       	public PersonServiceBean(PersonDao personDao){
       		this.personDao = personDao;
       	}
      
       	public void save(Person person){
       		personDao.save(person);
       	}
       }
       //在Spring配置文件中定义bean:
       <bean id="person" class="com.abinge.springtest.Person"/>
       <bean id="personDao" class="com.abinge.springtest.PersonDao"/>
       <bean id="personServiceBean" class="com.abinge.springtest.PersonServiceBean">
       	<constructor-arg index="0" ref="personDao" type="personDao"/>
       </bean>
      
       //在客户端
       ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
       PersonServiceBean personServiceBean = (Test)context.getBean("personServiceBean");
       Person person = (Test)context.getBean("person");
       personServiceBean.save(person);
      
      • 说明:
      • 1、constructor-arg代表指定的构造器函数中的一个参数
      • 2、可以利用index(参数的位置索引,从0开始),type(参数类型),ref(参数为依赖bean对象时,赋值为对应的bean标签的id),value(参数为基本数据类型时,可直接赋值)来指定唯一的构造器
      • 3、如果一个bean的配置中没有constructor-arg属性,则必须利用默认的构造函数创建对象
      • 4、所以在写一个javabean的时候,应该提供属性的setter方法,默认的构造器,带参数的构造器
    3. 使用Field注入(用于注解方式)

###Spring注入依赖对象的装配方式

手工装配(推荐使用)—— 存在两种编程方式
  • 在xml配置文件中,通过在bean节点下配置,如上面展示的前2中注入方式

  • 在Java代码中使用@Autowired或者@Resource注解的方式进行装配,但是我们需要在Spring的xml配置文件中添加如下配置:

      <beans
      	……>
      	<context:annotation-config/>
      </beans>
    

#####自动装配(容易出现不可预知的错误)

  • 实现方式:在需要注入依赖对象的应用,在Spring的xml文件中对应bean标签中定义autowire属性

      	//对PersonServiceBean中的PersonServiceDao属性会按照类型自动装配注入
      	<bean id="personServiceBea" class="com.abinge.PersonServiceBean" autowire="byType"/>
    
  • autowire属性的值:

    1. byType——按类型装配,在容器中寻找该类型匹配的bean,如果找到多个则抛出异常,如果没有找到即属性值为null
    2. byName——按名称装配,在容器中寻找该名称匹配的bean,如果没有找到即属性值为null
    3. constructor——与byType类似,不同之处在于它应用于构造器参数,如果构造器参数中没有找到与构造器参数类型一致的bean则抛出异常
    4. autodetect——通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自行装配。如果发现默认的构造器,那就使用byType方式进行自行装配 #####手动装配:@Autowired和@Resource注解比较
  • @Autowired默认按照类型装配

    1. 可用于类的属性字段上,也可用于属性字段的setter方法上

       @Autowired(required=false) @Qualifier("personDao");
       private PersonDao personDao;//注解在字段上
       @Autowired
       public void setPersonDao(PersonDao personDao){//注解在属性字段的setter方法上
       	this.personDao = personDao;
       }
      
    2. 默认情况下要求依赖对象必须存在,如果要允许依赖对象可以为null值时,可设置required属性为false

    3. 如果想让@Autowired按名称来装配的话,可以结合@Qualifier注解一起使用,如:

       @Autowired(required=false) @Qualifier("personDao");
       private PersonDao personDao;
      
  • @Resource默认按名称装配,当找不到与名称匹配的bean时,会按类型装配

    1. 也可用于类的属性字段上,也可用于属性字段的setter方法上

    2. 默认按名称装配,同时名称可以通过其本身的属性name指定,如果没有指定的话:

       1、当@Resource注解注在属性字段上时,默认取字段的名称作为匹配bean名称寻找依赖对象
       2、当@Resource注解注在属性的setter方法上时,默认取属性的名称作为匹配bean名称寻找依赖对象
      
    3. @Resource默认按名称装配,当找不到与名称匹配的bean时,会按类型装配,但是如果指定了name属性,就只能按名称装配了。

###Spring的意义

  • 通过控制反转降低组件之间的耦合度,实现软件各层之间的解耦;

      Controller ——> Service ——>Dao
    
  • 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等;

  • 容器提供单利模式支持,开发人员不再需要自己编写实例代码;

  • 容器提供AOP技术,利用它可以很容易实现如权限拦截、运行期监控等功能;

  • 容器提供的众多辅助类,使用这些类能够加快应用的开发,如:JdbcTemplate、HibernateTemple;

  • 对主流的应用框架提供了集成支持,如:集成了Hibernate、JPA、Sturts等,便于应用的开发。 ###Spring实例化对象的方法

  • 通过默认构造器的方法实例化:

      	//Test类的定义:	
      	public class Test{
      		public Test(){
      			System.out.println("create Test");
      		}
      	}
    
      	//在Spring配置文件中定义bean:
      	<bean id="test" class="com.abinge.springtest.Test"/>
    
      	//在客户端
      	ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      	Test test = (Test)context.getBean("test");
    

在控制台将会打印”create Test” 由此可知:spring内部默认是调用了Test个类的默认的构造函数创建对象

  • 通过静态工厂实例化

      	//Test类的定义:	
      	public class Test{
      		public Test(){
      			System.out.println("create Test");
      		}
      	}
      	//Test工厂的定义,需要在该工厂中定义一个静态的创建Test实例的方法:
      	public class TestFactory{
      		public static Test getInstance(){
      			return new Test();
      		}
      	}
    
      	//在Spring配置文件中定义TestFactory的bean,而不是Test类的bean:
      	<!--<bean id="test" class="com.abinge.springtest.Test"/>-->
      	<bean id="testFactory" class="com.abinge.springtest.TestFactory" factory-method="getInstance"/>
      	//在客户端
      	ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      	Test test = (Test)context.getBean("testFactory");
    
  • 通过实例工厂实例化

      //Test类的定义:	
      	public class Test{
      		public Test(){
      			System.out.println("create Test");
      		}
      	}
      	//Test工厂的定义,需要在改工厂中定义非静态的创建Test实例的方法:
      	public class TestFactory{
      		public Test getInstance(){
      			return new Test();
      		}
      	}
    
      	//在Spring配置文件中定义TestFactory的bean,用于让Spring创建该工厂对象,同时定义一个创建Test类的bean:
      	<!--<bean id="test" class="com.abinge.springtest.Test"/>-->
      	<bean id="testFactory" class="com.abinge.springtest.TestFactory" />
      	<bean id="test" factory-bean="testFactory" factory-method="getInstance"/>
      	//在客户端
      	ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      	Test test = (Test)context.getBean("test");
    

###Spring中Bean的实例化模式(或者叫:实例作用域Scope)

  • singleton(Spring默认)
    • 在每个Spring IOC容器中一个bean定义只有一个实例对象,也就是单例模式
  • prototype
    • 每次从IOC容器中获取的bean都是新的对象。 ###Spring中的Bean的实例化时机(实例对象的创建时机)
  • 默认:
    • 在Spring容器启动的时候创建即创建了各个Bean的实例对象,也就是在执行如下代码时创建了各个Bean的实例对象:

        ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      
  • 调用context.getBean()方法时创建:
    • 可以通过设置bean标签中的lazy-init属性设置为true,即表示该bean在context.getBean()方法被调用时才会被创建实例对象

        <bean id="test" class="com.abinge.springtest.Test" lazy-init="true"/>
      
    • 如果想对所有的bean都应用延迟初始化,可以在Spring配置文件的根节点beans设置dafault-lazy-init="true"属性:

        <beans default-lazy-init="true" ……>
      
    • lazy-init属性有3个属性值:

      1. Default——相当于false,在spring容器启动的时候创建对象
      2. True——在context.getBean时创建对象
      3. False——在spring容器启动的时候创建对象 ###Spring中Bean的实例化模式(实例作用域Scope)与Spring中的Bean的实例化时机(实例创建时机)的结合说明:当scope为prototype时,始终在context.getBean()方法调用时时创建各bean实例对象

###Spring中Bean的初始化

  • 可以在Test类中定义一个方法,用于Test实例对象创建后,对该实例对象进行数据的初始化操作

      //Test类的定义:	
      public class Test{
      	public Test(){
      		System.out.println("create Test");
      	}
      	public init(){
      		//……相应的数据初始化代码
      		System.out.println("init data");
      	}
      }
    
      //在Spring配置文件中定义bean的同时,通过init-mathod属性指定数据初始化方法以完成数据初始化:
      <bean id="test" class="com.abinge.springtest.Test" init-mathod="init"/>
    
      //在客户端
      ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      Test test = (Test)context.getBean("test");
    
  • 执行步骤如下:

    1. Spring容器创建Test实例对象;
    2. 由Spring容器内部调用init初始化方法,也就是说先实例化,然后才会初始化;

###Spring中Bean的销毁

  • Spring中Bean的销毁时机

    • 只有在Spring容器被关闭的时候,该容器中的各个Bean对象才会被销毁
    • 也就是在之心context.close()方法时,该容器中的各个Bean对象才会被销毁
  • 可以在Test类中定义一个方法,用于Test实例对象销毁前,对该实例对象中使用的部分资源进行关闭等操作

      //Test类的定义:	
      public class Test{
      	public Test(){
      		System.out.println("create Test");
      	}
      	public void init(){
      		//……相应的数据初始化代码
      		System.out.println("init data");
      	}
      	public void destory(){
      		//……相应的数据资源的关闭等操作代码
      		System.out.println("destory data");
      	}
      }
    
      //在Spring配置文件中定义bean的同时,通过destory-mathod属性指定数据资源关闭方法以完成资源的关闭:
      <bean id="test" class="com.abinge.springtest.Test" init-mathod="init" destory-mathod="destory"/>
    
      //在客户端
      ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      Test test = (Test)context.getBean("test");
    
  • 注意:

    1. destory方法是在Spring容器关闭后,Test实例对象销毁前执行;
    2. 如果一个bean的配置的实例化模式为”prototype”,则Spring容器不负责销毁。

###让Spring自动扫描和管理Bean

  • 在Spring的xml配置文件中配置如下:

      <beans
      	……>
      	<!--<context:annotation-config/>-->
      	<!--1、base-package="com.abinge":这样配置,Spring会自动扫描com.abinge包下及其子包下的java类,将这些Java类按照注解全部加载到其自己的容器中作为Bean对象进行管理-->
      	<context:component-san base-package="com.abinge"/>
    
      	<!-- 2、resource-pattern="service/*.class":具体指定Spring扫描包的路径,表示扫描com.abinge包下的子包service下的所有class文件
      		也就是com.abinge.service包下的class文件 -->
      	<context:component-san base-package="com.abinge" resource-pattern="service/*.class"/>
    
      	<!-- 3.通过exclude-filter和include-filter指定具体扫描路径 -->
      	<context:component-san base-package="com.abinge" use-default-filters="false">
      		<!-- context:exclude-filter:通过expression属性指定排除指定包下com.abinge.service中的不扫描-->
      		<context:exclude-filter type="annotation" expression="com.abinge.service"/>
    
      		<!-- context:include-filter:通过expression属性指定只扫描指定包下com.abinge.service中的class对象
      				但是需要在context:component-san标签中设置属性use-default-filters="false"-->
      		<context:include-filter type="annotation" expression="com.abinge.service"/>
      	</context:component-san>
      </beans>
    
  • 要想Spring对扫描的各java类进行管理,需要在各java类中注解标识

    1. @Service——业务层组件
    2. @Controller——控制层组件
    3. @Repository——数据访问组件,也就是Dao组件
    4. @Component——泛指组件,当组件不好归类时,使用这个注解标识
    5. @Scope("prototype")——设置bean的实例化模式为非单例,一般与上述各注释一并使用
    6. @PostConstruct——注解数据初始化的方法(原本为EJT3的注解,Spring进行了支持)
    7. @PreDestory——注解Bean销毁前的方法

转载于:https://my.oschina.net/abinge/blog/683594

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值