Spring控制反转(IOC) 和依赖注入(DI)

SSH:

Hibernate:
  用来做持久层: 它将JDBC做了一个良好的封装,程序员在与数据库进行交互时可以不用书写大量的SQL语句.
  
Struts2:
  用来做应用层: 负责控制service(业务逻辑处理类),从而控制了service的生命周期.
  
SSH框架流程:
  JSP页面—-Struts2—-Service(业务逻辑处理类)—-Hibernate
这样层与层之间耦合性强,这时,使用spring的 IOC机制(控制反转和依赖注入)就起到了控制Action对象(Struts2中的) 和 Service类的作用,降低它们之间的耦合度.
  Spring实际的使用场景是整合其它框架: 非侵入式框架,对现有存在的项目结构不会产生影响.

Spring(控制反转和依赖注入):
(1)控制反转(IOC):inversion of control
   以前我们需要对象,需要我们主动的创建;
   IOC: 使用者只需要知道需要什么对象,然后直接从容器中直接获取即可,不用再关注对象的创建过程,只需要关注拿到该对象之后需要做的业务.
   
 IOC容器:
    创建对象和处理对象之间的依赖关系, 工厂模式 + 反射机制
 
 IOC加载对象的方式:
     1. 通过bean 的 class来加载
    2. 通过静态工厂加载
     3. 通过工厂方法加载 
 通过IOC获取要使用的对象
  所有bean对象的构造方法都是在配置文件加载之前完成,并且bean对象是一个单例,所以每次getBean获取的都是同一个对象;
  如果bean是一个单例对象,一般来说,会有效的减少服务器的内存空间,Spring通过监听器来监听bean对象,如果该bean对象长期不使用,会被销毁掉.当我们再次使用bean对象时,会再次创建.
 
(2)依赖注入(DI):
  IOC的一种特殊体现形式,配合接口,达到不同层之间的解耦
我们在获取A对象时,在A的内容必须依赖B对象,我们在获取A对象时,将B对象注入到A对象中
DI依赖注入的方式: :     
    1.构造器注入
    2.setter注入
    3.静态工厂注入
    4.实例化工厂注入
项目中具体的创建注入都是Spring容器帮我们实现的,即通过控制反转和依赖注入,实现都是基于反射机制来实现的

  Spring容器控制所有的Action对象和业务逻辑类的生命周期,这样Action只是充当了Service的控制工具,它只要知道这些业务实现类所提供的方法接口就行了.层与层之间完全脱耦,是程序运行起来效率更高,维护起来更方便.
  以往单独使用Struts2框架时,所有业务方法类的生命周期,甚至是一些业务流程都是由Action来控制的,层与层之间耦合性太紧密了,既降低了数据访问的效率,又使业务逻辑看起来很复杂,代码量也很多.
  
Spring
 
<1>创建对象(IOC):
  (1)通过静态工厂模式创建对象:
  1. 根据 id 获取到工厂的类名
  2. 通过类调用factory-method来指定创建bean实例的静态工厂方法

<bean id="car" class="com.xalo.action.CarFactory" factory-method="getCar">
</bean>

(2)工厂方法模式创建对象:
工厂中获取具体对象的方法不是静态方法,而是成员方法
1.定义一个工厂类(加载工厂对象)
2.通过factory-bean获取工厂对象,通过factory-method配置工厂对象要调用的方法.

<bean id="carFactory" class="com.xalo.action.CarFactory"></bean>     
<bean id="car" factory-bean="carFactory" factory-method="getCar"> 
</bean>

(3)通过bean的class来加载:

<bean id="girl" class="com.xalo.entity.Girl"></bean>
<bean id="person" name="father,monther" class="com.xalo.entity.Person" init-method="init" destroy-method="destory"></bean>

<2>依赖注入(DI)

(1)setter注入:
1.根据property标签的name属性的值去找对应的setter方法.
例如: name= “aa” 对应的就是setAa方法.
2.由于属性注入具有可选性和灵活性高的优点,是实际上最常用的注入方式.
3.属性注入要求bean提供一个默认的构造函数,并为需要注入的属性提供对应的setter方法.spring先调用bean默认的构造函数实例化bean对象,然后通过反射机制的方法调用setter方法注入属性值.

public class SetterAction {
      private String name;
   public String getName() {
        return name;
    }   
   public void setName(String name) {
        this.name = name;
    } 
 }

 //ApplicationContext.xml
<bean id="setter" class="com.xalo.action.SetterAction">
       <property name="name" value="碧瑶"/>
</bean>

//DITest
 public class DITest {
    @Test
    public void test() {
       ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
           SetterAction setterAction = (SetterAction) context.getBean("setter");
             System.out.println(setterAction.getName());
      }
    }   
  /*
   *向SetterAction里面注入bean  setter注入
   */
  public interface ServiceInterf {
            }

  public class LoginService implements ServiceInterf {
      public void sayHello(){
             }
    }  

 public class LoginAction {
    private ServiceInterf loginService;
     //setter注入需要提供setter方法.
    public void setLoginService(ServiceInterf loginService) {
           this.loginService = loginService;
           System.out.println(loginService);
      }
  }

 // ApplicationContext.xml
  <bean id="service" class="com.xalo.service.LoginService">        
  </bean> 

  <bean id="setter" class="com.xalo.action.SetterAction">
       <property name="loginService" ref="service" />
  </bean>

//DITest
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
SetterAction setterAction = (SetterAction) context.getBean("setter");

//打印出来的结果是:com.xalo.service.LoginService@3d921e20

(2)构造器注入
构造器注入:保证了一些必要的属性在Bean实例化时就设置,并且确保了bean实例在实例化后就可以使用.
1.在类中,不用为属性设置setter方法,只需提供构造方法即可
2.在构造文件中配置该类bean,并配置构造器,在配置构造器中用

public class LoginAction {
    private  String name;
    //提供构造方法
   public LoginAction(String name) {
         this.name = name;
      System.out.println("name:" + name);
}

//ApplicationContext.xml
<bean id="action" class="com.xalo.action.LoginAction">
    <constructor-arg index ="0" name="name" value="碧瑶"></constructor-arg>
</bean>

//DITest
public class DITest {
   @Test
    public void test() {
    //加载配置文件
     ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        LoginAction action = (LoginAction) context.getBean("action");
  }
 /*
  *向LoginAction里面注入bean 构造器注入
  */
 public interface ServiceInterf {

     }

 public class LoginService implements ServiceInterf {
    public void sayHello(){ 
     }
   }

 public class LoginAction {
  /*
   *我们将要使用的类型声明为接口类型,当前类对象和要使用的对象就没有直接的联系.
   *主要适合接口进行对接,所以两个类之间的耦合度就降低了
   *(不同层之间的耦合度越低越好,同层之间的耦合度越高越好)
   */
     private ServiceInterf loginService;
    //提供有参的构造方法 
     public LoginAction(ServiceInterf loginService) {
        this.loginService = loginService;
        System.out.println(loginService);
  }
}

 //ApplicationContext.xml
 <bean id="service" class="com.xalo.service.LoginService">        
 </bean> 
 <bean id="action" class="com.xalo.action.LoginAction">
      <constructor-arg name="loginService" ref="service" ></constructor-arg>
 </bean> 

//DITest
public class DITest {
   @Test
  public void test() {
    //加载配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        LoginAction action = (LoginAction) context.getBean("action");
   }
 }

Bean常用的属性:
  将要使用的bean,配置在IOC容器中.
IOC: 底层就是工厂模式(map), bean是通过反射机制创建的对象

id:  bean对象的唯一标识符,通过id获取对象,同一文件中id不能有重复的.

class:  bean的类型.

name: 作用和id类似,但是可以有多个,用逗号隔开(基本不使用)

default- lazy-init:
 true: 加载配置文件时,不加载 getBean时,才会加载. 此时该bean还是单例 懒加载
  false: 加载配置文件时加载

init-method: bean初始化方法.它会在bean组装之后调用.必须无参,在对象创建好之后(构造方法之后)执行.

destroy-method: bean销毁方法 扫尾工作.在beanFactory关闭时调用,必须无参,只能用于singletonBean.

abstract:
  true: 当前bean为抽象模板,不能获取对象.一般用于父类bean,因为父类bean主要是供子类bean继承使用.
 false: 非抽象

factory-method:  静态工厂时,从工厂获取对象的静态方法(不常用)

factory-bean: 实例工厂时,要引入的工厂对象, 配合factory-method使用(不常用)

parent: 实际上是对依赖注入的继承关系的确定. 子类bean要使用父类bean的依赖注入的属性值和构造器参数,必须指明父类.
(1)不指定parent时:
1.可以为父类中的属性进行setter注入:
      如果父类进行了setter注入,子类对象得不到该值
      父类进行了构造器注入,子类对象无法使用该值
4.子类要使用父类的构造器进行注入,子类必须有一个对应的构造器,在此构造器中调用父类的构造器.
(2)指定parent时:
    1.子类可以使用父类中进行setter注入的值

//2.子类要使用父类构造器中注入的值,必须在子类中定义一个对应的构造器,在此构造器中调用父类的构造器
public class ParentAction {
     private Integer age; 
    public ParentAction() {
        System.out.println("父类的构造方法");
    }
    public ParentAction(Integer age) {
        this.age = age;
    }
    public Integer getAge() {
        return age;
     }  
    }

public class TestAction extends ParentAction {
    public TestAction() { }
    public TestAction(Integer age) {
        super(age);         //子类定义对应的构造器        
       }
       
 // ApplicationContext.xml:

  <bean id="parent" class="com.axlo.action.ParentAction">
     <constructor-arg name="age" value="18"></constructor-arg>
  </bean>
 <bean id="action" class="com.axlo.action.TestAction" parent="parent">
 </bean>

public class Test {
    @org.junit.Test
  public void test() {
     ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");    
     TestAction testAction = context.getBean("action",TestAction.class);
     System.out.println(testAction.getAge());
      }
    } 

2.父类进行了构造器注入,如果子类没有对应的构造器方法,获取子类对象就会失败
如果子类有对应的构造器方法,但是并没有调用父类的构造器,子类对象可以正常获取,但是无法使用父类构造方法的值
如果子类既有对应的构造器方法,又调用父类的构造器,子类和父类对同一属性都进行依赖注入,父类和子类都用自己的值.

depends-on: 
 当前的bean依赖于 depends-on 所指定的bean,这个对象会在bean初始化之前创建.

scope: 指定当前bean的模式
 singleton: 单例模式 默认: 在beanFactory作用范围内,只维护此bean的一个实例.在服务器启动时(容器初始化之前)创建.主要用与service, Dao层还有一些utils工具类等,只需要在服务器启动时初始化一次即可.
  prototype:  原型模式 每次getBean获取对象时,都重新实例化一个对象.在SSH项目中主要用于action对象,这种方式一般在服务器启动时不会创建对象,在每次使用时才创建.
  request:  将该对象放入request域中     
  session:  将对象放入 session域中
  global session: 全局session
    
autowire: 自动装配(自动依赖注入)
先对类中的setter或者构造方法进行扫描,在配置文件中,扫描所有的bean,找到对应的id进行注入.如果没有匹配的id,就不进行注入.
优点: 配置简单,容错率低
缺点:影响项目的性能
      byName: 根据id注入(setter)
      byType: 根据类型注入(setter)
     constructor: 既可以根据id 也可以根据类型(构造器注入)
      先根据id匹配,再根据类型匹配.
       no: 不使用自动注入
      
bean的常用的子标签: (几乎都是注入用的)

constructor-arg:  构造器注入使用
   index:  要注入的构造方法的参数位置 从0开始.如果不设置index,自上而下为对应位置的参数赋值
   name:  指定参数名称,构造方法的形参名称
   type:  指定注入数据的类型,当使用构造器注入时,如果参数除了类型不一样,
        其它都一样需要指定type来区分执行哪一个构造方法
   value:  要注入的值,必须和对应的参数的类型匹配
           [简单值 (四类八种 字符串)]
     ref:  要注入的值是一个 JAVA bean, 写bean的 id.
   如果没有index也没有name,必须按照参数的顺序进行注入

property:  setter注入使用
根据property标签的name属性的值去找对应的setter方法.
    name:  setter方法对应的属性名
   ref:   要注入的bean的id
   value:  要注入的值
   
构造器注入,对象中需要提供有参的构造方法

集合类型的注入

 <!-- 构造器注入:
     1.不够灵活
     2.会影响对象的创建效率--> 
public class LoginAction {
      private List<Integer> list;
      private Map<String, String> map;
  <!--构造器依赖注入,对象中需要提供有参的构造方法-->
     public LoginAction(List<Integer> list){
            this.list = list;
         System.out.println(list);
         }
     public LoginAction(Map<String, String> map) {
          this.map = map;
          System.out.println(map);
        }
     }

<!--ApplicationContext.xml-->
<!--spring-framework-5.0.7.RELEASE  docs  spring-framework-reference  pdf   core.pdf   9.1.4-->
<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"
  default-lazy-init="true">

   <bean id="action" class="com.xalo.action.LoginAction">
        <!-- 集合类型的注入 -->
       <constructor-arg name="list">
       <!-- 创建了一个list对象 -->
           <list>
             <!-- 给list中添加元素 -->
               <value>1</value>
               <value>2</value>
               <value>3</value>
           </list>
        </constructor-arg>
<!--给map中添加值时,如果属性名一样,后面的值会覆盖前面的值 -->
     <constructor-arg name="map">
         <map>
            <entry key="name" value="张小凡" />
            <entry key="age" value="12" /> 
            <entry key="name" value="道玄真人" />               
        </map>
     </constructor-arg> 
   </bean>
</beans>

   <!--DITest-->
public class DITest {
  @Test
    public void test(){
    <!--加载配置文件-->
  ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    LoginAction action = (LoginAction) context.getBean("action");
    }
  }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值