Spring:IOC

来自Wikioe
跳到导航 跳到搜索


关于IOC

IOC:Inversion of Control 控制反转,指的是对象的创建权反转(交给)给Spring,作用是实现了程序的解耦合。

IOC 的底层实现原理.jpg


下载:


Spring 目录结构:

  • docs:API 和开发规范
  • libs:jar 包和源码
  • schema:约束


相关jar:

IOC相关jar包.jpg
即“core container”的四个部分;
SpringFramework Runtime.jpg

使用示例

  1. 创建web 项目
  2. 引入Spring 的开发包
  3. 相关配置文件:
    1. log4j.properties:日志配置
    2. applicationContext.xml:Spring配置文件:
      • (配置说明,位于包的doc中:“spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html”)
      <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">
      </beans>
      
  4. 编写相关的类:
    public interface UserDao {
       public void sayHello();
    }
    public class UserDaoImpl implements UserDao {
       @Override
       public void sayHello() {
          System.out.println("Hello Spring...");
       }
    }
    
  5. 完成配置:
    <!-- Spring 的入门案例 -->
    <bean id="userDao" class="cn.itcast.spring.demo1.UserDaoImpl"></bean>
    
  6. 编写测试程序:
    @Test
    // Spring 的方式:
    public void demo2(){
       // 创建Spring 的工厂类:
       ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
       // 通过工厂解析XML 获取Bean 的实例.
       UserDao userDao = (UserDao) applicationContext.getBean("userDao");
       userDao.sayHello();
    }
    


Spring 中的工厂

  • BeanFactory :是在getBean 的时候才会生成类的实例.
  • ApplicationContext :在加载applicationContext.xml 时候就会创建.

ApplicationContext

ApplicatioContext 接口有两个实现类:

  1. “ClassPathXmlApplicationContext”:加载类路径下Spring 的配置文件;
  2. “FileSystemXmlApplicationContext”:加载本地磁盘下Spring 的配置文件;

ApplicatioContext 接口的两个实现类.jpg

BeanFactory

Spring的工厂.jpg

Spring 的相关配置

  • 分配置文件,实现的两种方法:
    1. 加载多个配置文件:
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
      
    2. 在主配置文件(“applicationContext.xml”)中包含其他配置文件,如:
      <import resource="applicationContext2.xml"></import>
      

Bean的管理:XML

相关配置


  • id:唯一确定一个Bean(唯一性约束),必须以字母开始,可以使用字母、数字、连字符、下划线、句点、冒号,不能出现特殊字符;
  • name:Bean的名称(没有唯一性约束),name可以使用特殊字符;如果<bean>没有id 的话, name 可以当做id 使用;
  • class:类的全路径名称;
    • 整合struts1 的时候:“<bean name=”/loginAction” >”;
  • scope:Bean 的作用范围;
    • singleton”:(默认)单例;
    • “prototype”:多例;
    • “request”:WEB 项目中,Spring 创建一个Bean 的对象,将对象存入到request 域中;
    • “session”:WEB 项目中,Spring 创建一个Bean 的对象,将对象存入到session 域中;
    • “globalSession”:WEB 项目中,应用在Porlet 环境.如果没有Porlet 环境那么globalSession 相当于session;
  • Bean 的生命周期的配置:通过配置<bean>标签上的“init-method”作为Bean 的初始化的时候执行的方法,配置“destroy-method”作为Bean 的销毁的时候执行的方法。
    • 销毁方法想要执行,需要是单例创建的Bean;而且在工厂关闭的时候,Bean 才会被销毁.

Bean实例化的三种方式


  1. 使用类的无参数构造创建(重点)
    <!-- 方式一:无参数的构造方法的实例化-->
    <bean id="bean1" class="cn.itcast.spring.demo3.Bean1"></bean>
    
  2. 使用静态工厂创建:
    // 提供一个工厂类
    public class Bean2Factory {
       public static Bean2 getBean2(){
          return new Bean2();
       }
    }
    
    <!-- 方式二:静态工厂实例化Bean -->
    <bean id="bean2" class="cn.itcast.spring.demo3.Bean2Factory" factory-method="getBean2"/>
    
  3. 使用实例工厂创建
    // 提供Bean3 的实例工厂:
    public class Bean3Factory {
       public Bean3 getBean3(){
          return new Bean3();
       }
    }
    
    <!-- 方式三:实例工厂实例化Bean -->
    <bean id="bean3Factory" class="cn.itcast.spring.demo3.Bean3Factory"></bean>
    <bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
    

依赖注入(DI)


DI(Dependency Injection):依赖注入,使用IOC创建类的过程中,将类所依赖的属性设置进去。

依赖注入:方法


依赖注入方法.png
  1. 构造函数注入:
       <!-- 第一种:构造方法的方式-->
       <bean id="car" class="cn.itcast.spring.demo4.Car">
          <constructor-arg name="name" value="保时捷"/>
          <constructor-arg name="price" value="1000000"/>
       </bean>
    
  2. Setter注入
       <!-- 第二种:set 方法的方式-->
       <bean id="car2" class="cn.itcast.spring.demo4.Car2">
          <property name="name" value="奇瑞QQ"/>
          <property name="price" value="40000"/>
       </bean>
    
  3. 接口注入:(要求组件必须与特定的接口相关联,不常使用)

依赖注入:应用


常见的几个应用形式:

  • 【同样依靠与基本的“setter注入”(即必须有对应的set方法),只是写法或注入的内容不一样;】
  1. 对象类型的注入
       <!-- 注入对象类型的属性-->
       <bean id="person" class="cn.itcast.spring.demo4.Person">
          <property name="name" value="会希"/>
          <!-- ref 属性:引用另一个bean 的id 或name -->
          <property name="car2" ref="car2"/>
       </bean>
    
  2. 名称空间p 的属性注入;(Spring2.x 以上)
    <!-- 第一步:引入p 名称空间 -->
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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">
       
       <!-- 第二步:使用p 名称空间.
       * 普通属性: p:属性名称=””
       * 对象类型属性: p:属性名称-ref=”” -->
       
       <!-- p 名称空间的属性注入的方式-->
       <bean id="car2" class="cn.itcast.spring.demo4.Car2" p:name=" 宝马7" p:price="1200000"/>
       <bean id="person" class="cn.itcast.spring.demo4.Person" p:name=" 思聪" p:car2-ref="car2"/>
       ...
    
  3. SpEL 方式的属性注入:(Spring3.x 以上)
    • SpEL:Spring Expression Language,语法;“#{ SpEL }”;
       <!-- SpEL 的注入的方式-->
       <bean id="car2" class="cn.itcast.spring.demo4.Car2">
          <property name="name" value="#{'奔驰'}"/>
          <property name="price" value="#{800000}"/>
       </bean>
    
       <bean id="person" class="cn.itcast.spring.demo4.Person">
          <property name="name" value="#{'冠希'}"/>
          <property name="car2" value="#{car2}"/>
       </bean>
    
       <!-- SpEL 引用另一个类的属性-->
    
       <bean id="carInfo" class="cn.itcast.spring.demo4.CarInfo"></bean>
       
       <bean id="car2" class="cn.itcast.spring.demo4.Car2">
          <!-- <property name="name" value="#{'奔驰'}"/> -->
          <property name="name" value="#{carInfo.carName}"/>
          <property name="price" value="#{carInfo.calculatePrice()}"/>
       </bean>
    
  4. 注入复杂类型:
       <!-- Spring 的复杂类型的注入 -->
       <bean id="collectionBean" class="cn.itcast.spring.demo5.CollectionBean">
          <!-- 数组类型的属性-->
          <property name="arrs">
             <list>
                <value>会希</value>
                <value>冠希</value>
                <value>天一</value>
             </list>
          </property>
          <!-- 注入List 集合的数据-->
          <property name="list">
             <list>
                <value>芙蓉</value>
                <value>如花</value>
                <value>凤姐</value>
             </list>
          </property>
          <!-- 注入Map 集合-->
          <property name="map">
             <map>
                <entry key="aaa" value="111"/>
                <entry key="bbb" value="222"/>
                <entry key="ccc" value="333"/>
             </map>
          </property>
          <!-- Properties 的注入-->
          <property name="properties">
             <props>
                <prop key="username">root</prop>
                <prop key="password">123</prop>
             </props>
          </property>
    </bean>
    

Bean的管理:注解

注解的相关配置

“applicationContext.xml”中添加注解扫描:

  1. 扫描“类”、“方法”、“属性”上面的注解:
    • (包含了“<context:annotation-config>”的功能)
       <!-- Spring 的注解开发:组件扫描(类上注解: 可以直接使用属性注入的注解) -->
       <context:component-scan base-package="com.itheima.spring.demo1"/>
    
  2. 扫描“属性”上面的注解:
       <!-- 扫描属性上面的注解 -->
       <context:annotation-config></context:annotation-config>
    

Bean相关的注解

注解 说明
对象注解

(Component及其衍生注解,功能一致)

@Component 组件;
@Controller WEB 层;
@Service 业务层;
@Repository 持久层;
注入注解

(使用注解注入的方式,可以不用提供“set”方法)

@Value 用于注入普通类型;
@Autowired 自动装配;
  • 默认按类型进行装配;强制使用名称装配:“@Qualifier”;
  • “@Autowired(required=true)”:(默认)必须有一个Bean候选者注入;
    如果允许出现0个Bean候选者需要设置属性:“required=false”;
    (required属性与“@Required”一样,只是@Required只适用于基于XML配置的setter注入方式)
@Resource 相当于“@Autowired”和“@Qualifier” 一起使用;
范围注解
@Scope 作用范围;
  1. “singleton”:单例;
  2. “prototype”:多例;
生命周期
@PostConstruct 相当于“init-method”;
@PreDestroy 相当于“destroy-method”

示例:

@Service(value="userService")
public class UserService {

   // @Autowired
   // private UserDao userDao;

	// name属性值 写 注解创建dao对象 value值
	@Resource(name="userDao")
	private UserDao userDao;
    
   // 使用注解方式时候不需要set方法
	
	public void add() {
		System.out.println("service...........");
		userDao.add();
	}
}

XML 与 注解

XML与注解.jpg

  • XML:结构清晰;
  • 注解:开发方便;(属性注入)

混合使用

  1. 创建对象操作使用配置文件方式实现:
    	<!-- 开启注解扫描 -->
    	<context:component-scan base-package="cn.itcast"></context:component-scan>
    
    	<!-- 配置对象 -->
    	<bean id="bookService" class="cn.itcast.xmlanno.BookService"></bean>
    	<bean id="bookDao" class="cn.itcast.xmlanno.BookDao"></bean>
    	<bean id="ordersDao" class="cn.itcast.xmlanno.OrdersDao"></bean>
    
  2. 注入属性的操作使用注解方式实现:
    public class BookService {
    	
    	//得到bookdao和ordersdao对象
    	@Resource(name="bookDao")
    	private BookDao bookDao;
    	
    	@Resource(name="ordersDao")
    	private OrdersDao ordersDao;
    	
    	public void add() {
    		System.out.println("service.........");
    		bookDao.book();
    		ordersDao.buy();
    	}
    }
    

常见问题

依赖对象冲突(@Qualifier、@Primary)

依赖对象冲突:即,具有多个符合条件的依赖对象;

为了解决这个问题,必须明确地指示 Spring 使用冲突的 bean 中的哪一个:

“@Qualifier”

@Qualifier 注解与“@Autowired”配合使用,来指定注入对象

  1. 具有多个符合条件的依赖对象:
    @Component("combustionEngine")
    public class CombustionEngine implements Engine {
       ...
    }
    
    @Component("electricEngine")
    public class ElectricEngine implements Engine {
       ...
    }
    
  2. 指定对象:
    @Component
    public class Car {
    
       @Autowired
       public Car(@Qualifier("combustionEngine") Engine engine) {
          this.engine = engine;
       }
       ...
    }
    

“@Primary”

将“@Primary”注释添加到类中,设置默认注入对象

  1. 具有多个符合条件的依赖对象,但设置了“@Primary”类型:
    @Component
    @Primary
    public class CombustionEngine implements Engine {
       ...
    }
    
    @Component
    public class ElectricEngine implements Engine {
       ...
    }
    
  2. 自动装配默认对象:
    @Component
    public class Car {
    
       @Autowired
       public Car(Engine engine) {
          this.engine = engine;
       }
       ...
    }
    

循环依赖(@Lazy)

如果对象之间出现了循环依赖,不能成功地创建其中任何一个Bean:

@Component
public class Foo {

   private Bar bar;

   @Autowired
   public Foo(Bar bar) {
      this.bar = bar;
   }
}
@Component
public class Bar {

   private Foo foo;

   @Autowired
   public Bar(Foo foo) {
      this.foo = foo;
   }
}

Foo构造需要Bar对象,Bar构造需要Foo对象;


为了解决打破循环,可以使用“@Lazy”注释,允许其中一个Bean先初始化:

  • “@Lazy”使 Spring 推迟“带注解的bean”和“带注释的@Autowired位置”的初始化。
@Component
public class Foo {

   private Bar bar;

   @Autowired
   public Foo(@Lazy Bar bar) {
      this.bar = bar;
   }
}
@Component
@Lazy
public class Bar {

   private Foo foo;

   @Autowired
   public Bar(Foo foo) {
      this.foo = foo;
   }
}