Spring IOC

Spring是一个轻量级框架(相较EJB,文件容量较小,需要的资源负担小)
Spring两个目标:

  1. 让现有技术更易于使用;
  2. 促进良好的编程习惯(称为最佳实践)。

Spring的两大核心:

  1. Spring IoC(控制反转)/依赖注入(Dependency Injection);
  2. AOP(Aspect Oriented Pogramming),即面向切面编程;

Spring贯穿表现层,业务层和持久层。提供“一站式”选择,然而Spring并不想取代已有的框架,而是以高度开放性与它们无缝整合。

Spring框架大约由20个功能模块组成。
20个功能模块分为6个部分:


Spring体系结构图
Core Conainer Data Access/Integration Web AOP Intrumentation Test
核心容器 数据访问/集成 web AOP(面向切面编程) 工具 消息和测试

控制反转(Inversion of Control ,IoC)

控制反转也称作依赖注入,是面向对象编程中的一种设计理念,用来降低代码之间的耦合度,同时也有利于代码的扩展。


这里有个工厂模式的例子:

/**
*创建一个Animal接口
*/
public interface Animal {
    public void eat();//定义接口的方法吃食物
    public void sport();;//定义接口的方法运动
}
/**
*创建一个Cat类实现Animal接口
*/
public class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("猫喜欢吃鱼!");
    }
    @Override
    public void sport() {
        System.out.println("猫很懒,不喜欢运动。");
    }
}
/**
*创建一个Dog类实现Animal接口
*/
public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("狗喜欢吃肉!");
    }
    @Override
    public void sport() {
        System.out.println("狗在追飞盘。");
    }
}
/**
*创建Factory类
*/
public class Factory {
    private static final String CAT = "cat";
    private static final String DOG = "dog";
    public Animal getAnimal(String str) {
        if (CAT.equals(str)) {
            return new Cat();    
        }
        if (DOG.equals(str)) {
            return new Dog();
        }
            return null;
    }
}
/**
*创建测试类
*/
import org.junit.Test;

public class TestAnimal {
    @Test
    public void testFactory() {
        Factory factory = new Factory();
        Animal animal = factory.getAnimal("cat");
        animal.eat();
        animal.sport();
    }
}
运行结果:
猫喜欢吃鱼!
猫很懒,不喜欢运动。

从以上代码可以看出,实现类Cat和实现类Dog不再依靠自身的代码去获得所依赖的具体的Animal对象,而是把这一个工作交给第三方——Factory类,从而避免了和具体的Animal实现类之间的耦合。在如何获取所依赖的对象上,控制权发生了反转,从实现类Cat和实现类Dog转移到了Factory,这就是控制反转

Spring框架提供了完整的IoC实现,让程序员更专注于业务的编写。


依赖注入:
依赖注入(Dependency Injection)是用于实现控制反转(Inversion of Control)的最常见的方式之一。
依赖注入的方式:
例子:

/**
*创建Person类
*/
package com.bean;
public class Person {

    private String name;
    private Integer age;

    public Person() { 
     }
    
    public Person(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }
  
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age =age;
    }

    public void showInfo() {
       System.out.print("一个叫"+name+"的人,今年"+age+"岁了。")
    }

}
  • 设值注入(必须要有setter方法)
<?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 id="person" class="com.bean.Person">
      <property name="name" value="楠楠"/>
      <property name="age" value="5"/>
  </bean>
</beans>
  • 构造注入(必须设置构造器)
<?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 class="com.bean.Person" id="person">
        <constructor-arg name="name" value="楠楠"></constructor-arg>
        <constructor-arg name="age" value="5"></constructor-arg>
   </bean>
</beans>
  • P命名空间注入(本质上也属于设值注入,也需setter方法)
<!-- 注意需在头文件中加入xmlns:p="http://www.springframework.org/schema/p" -->

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="person" class="com.bean.Person"
          p:name="楠楠" p:age="5"/>
</beans>

构造注入与设值注入的区别:

  • 设值注入方法支持大部分的依赖注入,如果我们仅需要注入int、String和long类型的变量,我们则不需要使用设值的方法注入,因为对于基本类型,我们可以为基本类型设置默认值。构造方法注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则会报错。
  • 设值注入不会重写构造方法的值,如果对于同一个变量同时使用构造方法注入又使用了设值方法注入的话,那么构造方法将不能覆盖由设值方法注入的值。很明显,因为构造方法在对象被创建时调用;
  • 在使用设值注入时有可能还不能保证某种依赖是否已经被注入,这时对象的依赖关系有可能不完整,而在另一种情况下,构造注入则不允许生成依赖关系不完整的对象;
  • 在设值注入时如果对象A和对象B互相依赖,在创建对象A时Spring会抛出objectcurrentlyincreationexception异常,因为在B对象被创建之前A对象是不能被创建的,反之亦然,所以Spring用设值注入的方法解决了循环依赖的问题,因为对象的设值方法是在对象被创建之前被调用的。

如何使用注解实现依赖注入
Spring在2.5版本后开始支持用注解的方式来配置依赖注入,可以使用注解的方式来代替XML方式的bean描述,这样将bean的描述转移到组件类的内部,只需在相关类上、方法上或者字段上使用注解即可,注解将会被容器在XML注入之前被处理之前被处理,所以后者会覆盖掉前者对于同一个属性的处理结果。
注解装配在Spring中是默认关闭的,所以需要在Spring文件中配置才能使用基于注解的装配模式

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

配置完<context:annotation-config/>就可以使用注解的方式在Spring中向属性,方法和构造方法中装配变量了。

几种重要的注解类型:

  1. @Required:该注解应用于设值方法;
  2. @Autowired:该注解应用于有值设值、非设值方法、构造方法和变量;
  3. @Qualifier:该注解和@Autowired注解搭配使用,用于消除特定bean自动装配的歧义;
  4. @JSR-250 Annotations:Spring支持基于JSR-250注解的以下注解:
  • @Resource
  • @PostConstruct
  • @PreDestroy

Spring的自动装配:
在Spring框架中,Spring容器还可以自动装配合作关系bean之间的关联关系。这意味着Spring可以通过向BeanFactory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。

  1. 在XML配置文件中表明如何根据名称将一个bean设置为自动装配
<bean bean id="person" class="com.bean.Person" autowired="byName">
  1. 使用@Autowired注解来完成自动装配指定的bean,使用之前需配置
<beans>
    <context:annotation-config/>
<beans>

或者在配置文件中配置

<bean class="org.springframework.beans.facyory.annotation.AutowriedAnnotationBeanPostProcessor"/>

配置完后即可使用@Autowired实现自动装配了。

自动装配模式:
Spring框架中5种自动装配:

  1. no:Spring框架的默认设置,在该设置下自动装配是关闭的,需自行在bean定义中用标签明确的设置依赖关系;
  2. byname:可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在配置文件中查询一个匹配的bean,如果找到的话,就装配曾属性,没找到就报错;
  3. byType:可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在配置文件中查询一个匹配的bean,如果找到的话,就装配曾属性,没找到就报错;
  4. constructor:构造器的自动装配和byType模式类似,但仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么会抛出异常;
  5. autodetect:该模式自动探测使用构造器自动装配或者byType自动装配,首先会尝试找合适的带参数的构造器,如果找到的话就是构造器自动装配,如果在bean内部没有找到相应的构造器或是无参构造器,容器就会自动选择byType的自动装配模式。
就到这里了啊!

推荐阅读更多精彩内容