• IOC容器和Bean的配置


    IOC容器和Bean的配置

    1. 什么是IOC和DI

    IOC(反转控制)

    反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式

    DI(依赖注入)

    IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。

    总结: IOC 就是一种反转控制的思想, 而DI是对IOC的一种具体实现

    2. IOC容器在Spring中的实现

    前提:

    Spring中有IOC思想, IOC思想必须基于 IOC容器来完成, 而IOC容器在最底层实质上就是一个对象工厂

    1. 在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
    2. Spring提供了IOC容器的两种实现方式
      1. BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的
      2. ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。

    3. ApplicationContext的主要实现类

    1. ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
    2. FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
    3. 在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的

    4. ConfigurableApplicationContext

    1. 是ApplicationContext的子接口,包含一些扩展方法

    2. refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。

    5. WebApplicationContext

    专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作

    (一)通过类型获取bean

    从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。

    代码

    //初始化容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取对象常用的三种方法
    //1. 通过getBean()获取对象,传入参数id,具有唯一id的对象
    Person person = (Person)ac.getBean("personOne");
    
    //2. 使用此方法获取对象时,要求spring所管理的此类型的对象只能有一个
    Person person = ac.getBean(Person.class);
    
    //3. 综合以上两种方法,目标更明确
    Person person = ac.getBean("personTwo", Person.class);
    

    配置文件:applicationContext.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 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    	<!-- 
    		<bean>:定义spring所管理的一个对象
    		id:该对象的唯一标示,注意:不能重复,在通过类型获取bean的过程中可以不设置
    		class:此对象所属类的全限定名
    	 -->
    	<bean id="personOne" class="com.atguigu.spring.mod.Person">
    		<!-- 
    			<property>:为对象的某个属性赋值
    			name:属性名
    			value:属性值
    		 -->
    		<property name="id" value="1111"></property>
    		<property name="name" value="小明"></property>
    	</bean>
    	
    	<bean id="personTwo" class="com.atguigu.spring.mod.Person">
    		<property name="id" value="2222"></property>
    		<property name="name" value="小红"></property>
    	</bean>
    
    </beans>
    

    (二)给bean的属性赋值

    1. 依赖注入的方式

    1)通过bean的setXxx()方法赋值

    	<bean id="s1" class="com.atguigu.spring.di.Student">
        <!-- value子节点 -->
    		<property name="id">
    			<value>10010</value>
    		</property>
        <!-- 这里的property标签里的name="name" 相当于调用实体类的setName()方法 -->
    		<property name="name" value="张三"></property>
    		<property name="age" value="23"></property>
    		<property name="sex" value="男"></property>
    	</bean>
    

    2)通过bean的构造器赋值

    1. Spring自动匹配合适的构造器
    	<bean id="s2" class="com.atguigu.spring.di.Student">
    		<constructor-arg value="10086"></constructor-arg>
    		<constructor-arg value="李四"></constructor-arg>
    		<constructor-arg value="24"></constructor-arg>
    		<constructor-arg value="女"></constructor-arg>
    	</bean>
    
    1. 通过索引值指定参数位置,并通过类型区分重载的构造器
    	<bean id="s3" class="com.atguigu.spring.di.Student">
    		<constructor-arg value="10022"></constructor-arg>
    		<constructor-arg value="王五"></constructor-arg>
        <!-- 
    				index="2"表示构造器的第三个参数,
    				type="java.lang.Double"表示这个参数是Double类型的
     		-->
    		<constructor-arg value="90" index="2" type="java.lang.Double"></constructor-arg>
    		<constructor-arg value="女"></constructor-arg>
    	</bean>
    

    2. p名称空间

    为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。Spring 从2.5版本开始引入了一个新的p命名空间,可以通过元素属性的方式配置Bean 的属性。

    使用p命名空间后,基于XML的配置方式将进一步简化。

    <bean id="s4" class="com.atguigu.spring.di.Student" 
          p:id="10033" p:name="赵六" p:age="26" p:sex="男" 
          p:teacher-ref="teacher">
    </bean>
    

    3. 可以使用的值

    1)字面量

    1. 可以使用字符串表示的值,可以通过value属性或value子节点的方式指定

    2. 基本数据类型及其封装类、String等类型都可以采取字面值注入的方式

    3. 若字面值中包含特殊字符,可以使用把字面值包裹起来

    2)null值

    对于引用数据类型,如果不需要赋值

    		<property name="id">
    			<null/>
    		</property>
    

    3)给bean的级联属性赋值

    <property name="teacher.tname" value="小红"></property>
    

    4)外部已声明的bean、引用其他的bean

    <bean id="s5" class="com.atguigu.spring.di.Student">
      <property name="id" value="10055"></property>
      <property name="name" value="张三三"></property>
      <property name="age" value="23"></property>
      <property name="sex" value="男"></property>
      <property name="teacher" ref="teacher"></property>
      <property name="teacher.tname" value="小红"></property>
    </bean>
    
    <bean id="teacher" class="com.atguigu.spring.di.Teacher">
      <property name="tid" value="10000"></property>
      <property name="tname" value="小明"></property>
    </bean>
    

    5)内部bean

    <bean id="s6" class="com.atguigu.spring.di.Student">
      <property name="id" value="10066"></property>
      <property name="name" value="崔八"></property>
      <property name="age" value="18"></property>
      <property name="sex" value="男"></property>
      <property name="teacher">
        <bean id="tt" class="com.atguigu.spring.di.Teacher">
          <property name="tid" value="2222"></property>
          <property name="tname" value="admin"></property>
        </bean>
      </property>
    </bean>
    
    注意

    定义在某个bean内部的bean,只能在当前bean中使用

    5. 集合属性

    1)数组和List

    配置java.util.List类型的属性,需要指定标签,在标签里包含一些元素。这些标签 可以通过指定简单的常量值,通过指定对其他Bean的引用。通过指定内置bean定义。通过指定空元素。甚至可以内嵌其他集合。

    注意

    ​ 数组的定义和List一样,都使用元素。

    ​ 配置java.util.Set需要使用标签,定义的方法与List一样。

    <!-- 参数值为基本类型时 -->
    <bean id="t1" class="com.atguigu.spring.di.Teacher">
    		<property name="tid" value="10001"></property>
    		<property name="tname" value="佟刚"></property>
    		<property name="cls">
    			<list>
    				<value>A</value>
    				<value>B</value>
    				<value>C</value>
    			</list>
    		</property>
    </bean>
    
    <!-- 参数值为引用类型时 -->
    <bean id="t2" class="com.atguigu.spring.di.Teacher">
    		<property name="tid" value="10002"></property>
    		<property name="tname" value="婷姐"></property>
    		<property name="students">
    			<list>
    				<ref bean="s1"/>
    				<ref bean="s2"/>
    				<ref bean="s3"/>
    			</list>
    		</property>
    </bean>
    

    2)Map

    Java.util.Map通过标签定义,标签里可以使用多个作为子标签。每个条目包含一个键和一个值。

    ​ 必须在标签里定义键。

    ​ 因为键和值的类型没有限制,所以可以自由地为它们指定元素。

    可以将Map的键和值作为的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义

    	<bean id="t3" class="com.atguigu.spring.di.Teacher">
    		<property name="tid" value="10003"></property>
    		<property name="tname" value="admin"></property>
    		<property name="bossMap">
    			<map>
    				<entry>
    					<key>
    						<value>10001</value>
    					</key>
    					<value>佟老师</value>
    				</entry>
    				<entry>
    					<key>
    						<value>10002</value>
    					</key>
    					<ref bean="teacher"/>
    				</entry>
    			</map>
    		</property>
    	</bean>
    

    3)集合类型的bean

    如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean引用

    配置集合类型的bean需要引入util名称空间

    <!-- 在<beans>标签内写入以下语句 -->
    xmlns:util="http://www.springframework.org/schema/util"
    
    	<bean id="t4" class="com.atguigu.spring.di.Teacher">
    		<property name="tid" value="10004"></property>
    		<property name="tname" value="root"></property>
    		<property name="students" ref="students"></property>
    	</bean>
    	
    	<util:list id="students">
    		<ref bean="s4"/>
    		<ref bean="s5"/>
    		<ref bean="s6"/>
    	</util:list>
    	
    	<util:map id="map">
    		<entry>
    			<key>
    				<value>1</value>
    			</key>
    			<value>张三</value>
    		</entry>
    	</util:map>
    

    6. FactoryBean

    ​ Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。

    ​ 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。

    ​ 工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。

    <bean id="factory" class="com.atguigu.spring.factorybean.MyFactory"></bean>
    
    import org.springframework.beans.factory.FactoryBean;
    
    public class MyFactory implements FactoryBean<Car> {
    	@Override
    	public Car getObject() throws Exception {
    		Car car = new Car();
    		car.setBrand("奥迪");
    		car.setPrice(200000.0);
    		return car;
    	}
    	@Override
    	public Class<?> getObjectType() {
    		// TODO Auto-generated method stub
    		return Car.class;
    	}
    	@Override
    	public boolean isSingleton() {
    		// TODO Auto-generated method stub
    		return false;
    	}
    }
    
    /**********************************************/
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    	public static void main(String[] args) {		
    		ApplicationContext ac = new ClassPathXmlApplicationContext("factory-bean.xml");
        //返回值是MyFactory中getObject()方法的返回值
    		Object object = ac.getBean("factory");
    		System.out.println(object);		
    	}
    }
    

    7. bean的作用域

    ​ 在Spring中,可以在元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。

    ​ 默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域

    	<bean id="student" class="com.atguigu.ioc.scope.Student" scope="singleton">
    		<property name="sid" value="1001"></property>
    		<property name="sname" value="张三"></property>
    	</bean>
    

    其他的作用域:

    • singleton:在SpringIOC容器中仅存在一个Bean实例,Bean以单例的方式存在
    • prototype:每次调用getBean()时都会返回一个新的实例
    • request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
    • session:同一个HTTP Session 共享一个Bean ,不同的HTTP Session 使用不同的Bean。该作用仅适用于WebApplicationContext环境

    注意:当bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。

    8. bean的生命周期

    1. SpringIOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。

    2. SpringIOC容器对bean的生命周期进行管理的过程:

      1. 通过构造器或工厂方法创建bean实例
      2. 为bean的属性设置值和对其它bean的引用
      3. 调用bean的初始化方法
      4. bean可以使用了
      5. 当容器关闭时,调用bean的销毁方法
    3. 在配置bean时,通过init-method 和 destroy-method 属性为bean指定初始化和销毁方法

      <bean id="person" class="com.atguigu.ioc.life.Person" init-method="init" destroy-method="destory">
        <property name="id" value="1001"></property>
        <property name="sex" value="男"></property>
      </bean>
      
      <!-- 配置后置处理器 -->
      <bean class="com.atguigu.ioc.life.AfterHandler"></bean>
      
    4. bean的后置处理

      1. bean 后置处理器允许在调用初始化方法前后对bean 进行额外的处理。

      2. bean 后置处理器对IOC容器里的所有bean 实例逐一处理,为非单一实例。

        其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。

      3. bean 后置处理器需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:

        • postProcessBeforeInitialization(Object, String)
        • postProcessAfterInitialization(Object, String)
        public class AfterHandler implements BeanPostProcessor {
        	@Override
        	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        		Person person = (Person) bean;
        		if(person.getSex().equals("男")) {
        			person.setName("张无忌");
        		}else {
        			person.setName("赵敏");
        		}
        		return person;
        	}
        	@Override
        	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        		// 注意此处不做处理时,要返回原 bean
        		return bean;
        	}
        }
        
    5. 添加 bean 后置处理器后 bean 的生命周期

      1. 通过构造器或工厂创建 bean 实例
      2. 为 bean 的属性设置值和对其他 bean 的引用
      3. 将 bean 实例传递给bean 后置处理器的postProcessBeforeInitialization()方法
      4. 调用 bean 的初始化方法
      5. 将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
      6. bean 可以使用了
      7. 当容器关闭时,调用 bean 的销毁方法

    (三)引用外部属性文件

    以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。

    1. 直接配置

    <!-- 直接配置 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    	<property name="user" value="root"/>
    	<property name="password" value="root"/>
    	<property name="jdbcUrl" value="jdbc:mysql:///test"/>
    	<property name="driverClass" value="com.mysql.jdbc.Driver"/>
    </bean>
    

    2. 使用外部的属性文件

    1. 创建properties属性文件

      jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/test
      jdbc.username=root
      jdbc.password=123456
      
    2. 引入 context 名称空间

    3. 指定 properties 属性文件位置

    4. 从 properties 属性文件中引入属性值

    <!-- 加载资源文件(方式1) -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="location" value="db.properties"></property>
    </bean>
    
    <!-- 加载资源文件(方式2) -->
    <context:property-placeholder location="db.properties"/>
    
    <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
      <!-- 从 properties 属性文件中引入属性值 -->
      <property name="driverClassName" value="${jdbc.driver}"></property>
      <property name="url" value="${jdbc.url}"></property>
      <property name="username" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
    </bean> 
    

    (四)自动装配

    1. 概念

    • 手动装配:以value或ref的方式明确指定属性值都是手动装配
    • 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中

    2. 装配模式

    1. 根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配。
    2. 根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同。
    3. 通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
    	<!-- 
    		autowire:根据某种策略自动为非字面量属性赋值
    		autowire="byName|byType"
    		byName:通过属性名和spring容器中bean的id进行比较,若一致则可直接赋值
    		byType:通过spring容器中bean的类型,为兼容性的属性赋值
    			   在使用byType的过程中,要求spring容器中只能有一个能为属性赋值的bean
    		选用建议: 当设置autowire属性,会作用于该bean中所有的非字面量属性,因此谁都不用
    	 -->
    	
    	<bean id="emp" class="com.atguigu.ioc.auto.Emp" autowire="byType">
    		<property name="eid" value="1001"></property>
    		<property name="ename" value="张三"></property>
    	</bean>
    

    3. 选用建议

    以上两种均不建议使用

    相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。

    (五)通过注解配置 bean(重点)

    1. 使用注解标识组件

    普通组件

    @Component :标识一个受 SpringIOC 容器管理的组件

    持久化层组件

    @Repository :标识一个受Spring IOC容器管理的持久化层组件

    业务逻辑层组件

    @Service :标识一个受Spring IOC容器管理的业务逻辑层组件

    表述层控制器组件

    @Conntroller :标识一个受Spring IOC容器管理的表述层控制器组件

    注意:事实上Spring 并没有能力识别一个组件到底是不是它标记的类型,即使将@Respository 注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller 这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。说白了就是,以上四种注释的作用完全一样,只是为了让开发人员便于区分

    2. 扫描组件

    组件只有被上述注解标识后还需要通过Spring 进行扫描才能够侦测到

    1. 指定被扫描的package
    2. base-package 属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其包中的所有类。
    3. 当需要扫描多个包时可以使用逗号分隔
    4. 如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern 属性过滤特定类
    5. 包含和排除
    	<!-- 
      <context:component-scan>:扫描组件,对设置的包下面的类进行扫苗,会将加上注解的类作为spring的组件进行加载
      组件:指spring中管理的bean
    
      作为spring的组件进行加载:会自动在spring的配置文件中生成相对应的bean,这些bean的id会以类的首字母小写为值
    
      <context:include-filter>:在设定的包结构下,再次通过注解或类型具体包含到某个或某几个类
    		注意:在使用包含时,一定要设置use-default-filters="false",将默认的过滤(即扫描包下所有的类)关闭
    
      <context:exclude-filter>:在设定的包结构下,再次通过注解或类型排除某个或某几个类
    		注意:在使用排除时,一定要设置use-default-filters="true",将默认的过滤(即扫描包下所有的类)打开
    	
     		切记:一个<context:component-scan>中可以出现多个include,也可以同时出现多个exclude,但是两个不能同时出现
    	 -->
    	<context:component-scan base-package="com.atguigu.ioc.userMod" use-default-filters="true">
        
        <!--
    				type="annotation" :表示注解类型
    				type="assignable" :表示类的类型
    		-->
        
    		<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> -->
    		<!-- <context:include-filter type="assignable" expression="com.atguigu.ioc.userMod.service.UserServiceImpl"/> -->
    		<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> -->
    		<!-- <context:exclude-filter type="assignable" expression="com.atguigu.ioc.userMod.dao.UserDaoImpl"/> -->
    	</context:component-scan>
    

    完成组件化管理的过程:

    • 在需要被spring管理的类上加上相应注解
    • 在配置文件中通过<context:component-scan>对所设置的包结构进行扫描,就会将加上注解的类,作为spring的组件进行加载

    组件:

    指spring中管理的bean

    作为spring的组件进行加载:会自动在spring的配置文件中生成相对应的bean,这些bean的id会以类的首字母小写为值;也可以通过@Controller("beanId")为自动生成的bean指定id

    //@Controller(value="aaa")
    @Controller("aaa")
    public class UserController {
    
    	@Autowired
    	private UserService userService;
    	
    	public void addUser() {
    		userService.addUser();
    	}
      
    	public UserController() {
    		System.out.println("UserController");
    	}	
    }
    

    3. 基于注解的自动装配(重重点)

    在需要赋值的非字面量属性上,加上@Autowired,就可以在spring容器中,通过不同的方式匹配到相对应的bean。

    @Autowired装配时,会默认使用byType的方式,此时要求spring容器中只有一个bean能够为其赋值。

    当byType实现不了装配时,会自动切换到byName,此时要求spring容器中,有一个bean的id和属性名一致。

    若自动装配时,匹配到多个能够复制的bean,可使用@Qualifier(value="beanId")指定使用的bean

    @Autowired和@Qualifier(value="beanId")可以一起作用域一个带形参的方法上,此时,@Qualifier(value="beanId"),所指定的bean作用于形参。

    @Autowired
    @Qualifier(value="userDaoMybatisImpl")
    public void setUserDao(UserDao userDao) {//自动装配的是形式参数 userDao
      this.userDao = userDao;
    }
    
  • 相关阅读:
    [PHP] PHP1 与 CGI
    [PHP] Phalcon操作示范
    [Shell] swoole_timer_tick 与 crontab 实现定时任务和监控
    [PHP] Phalcon应用升级PHP7记录
    [GNU] 喝一杯咖啡, 写一写 Makefile
    [PHP] Xhprof 非侵入式使用指南
    [PHP]OOP两类写法的性能对比
    [OSI] 网络间通信流程
    [OSI] 网络7层模型的理解
    [Tools] Vim 插件管理
  • 原文地址:https://www.cnblogs.com/chaozhengtx/p/13792388.html
Copyright © 2020-2023  润新知