【spring】---IOC和DI

一、IOC和DI

说明:IOC和DI是同一个概念的不同角度描述。

IOC是控制反转,由spring来负责控制对象Bean的生命周期和对象间的关系。
“控制”:Ioc容器控制对象及外部资源获取。
“反转”:由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象。

DI是依赖注入。由容器动态的将某个依赖关系注入到组件中。被注入的对象依赖IOC容器配置依赖对象。在系统运行中,动态的向某个对象提供它所需要的其他对象。
DI是依赖注入,动态的向某个对象提供它所需要的其他对象。另一个同一概念,不同描述的IOC控制反转。让Ioc容器控制对象及外部资源获取,并且由IOC容器帮忙查找和注入依赖对象。

1.1、spring的Bean

bean是一个由Spring IoC容器实例化、组装和管理的对象。

  • bean是对象,一个或者多个不限定
  • bean由Spring中一个叫IoC的东西管理
  • 我们的应用程序由一个个bean构成

二、DI的依赖注入方式

在这里插入图片描述

DI注入实现方式:XML【构造器注入、setter方法注入、FactoryBean工厂类注入、factory-method属性注入(静态和动态)】和注解。

2.1、xml–setter方法注入

2.1.1 注入案例1

Dao层-------一般类

public class SetterDao {
    public String operator1(){
        return "调用了operator1";
    }
}

Dao层-----接口+实现类

public interface SetterDaoInterface {
    public String operator2();
}
public class SetterDaoInterfaceImpl implements SetterDaoInterface {
    @Override
    public String operator2() {
        return "调用了operator2";
    }
}

service层-----接口+实现类

public interface SetterService1 {
    public void way();
}
public class SetterService1Impl implements SetterService1 {
    /**
     *一般类声明
     */
    private SetterDao setterDao;
    /**
     * 接口声明
     */
    private SetterDaoInterface setterDaoInterface1;

    public void setSetterDao(SetterDao setterDao) {
        this.setterDao = setterDao;
    }
    public void setSetterDaoInterface(SetterDaoInterface setterDaoInterface) {
        this.setterDaoInterface1 = setterDaoInterface;
    }
    @Override
    public void way() {
        System.out.println( setterDao.operator1());
        System.out.println( setterDaoInterface1.operator2());
    }
}

service层—测试

@Service
public class DITestService {
    @Autowired
    SetterService1 setterService2;  ---当接口只有一个实现子类,那么setterService2没限制
    @PostConstruct
    public void setterServiceWay1(){
        setterService2.way();
    }
}

从上面看SetterDao类、SetterDaoInterfaceImpl子类、SetterService1Impl类都没有注入到Spring的IOC容器,并且SetterService1Impl类依赖SetterDao类和SetterDaoInterfaceImpl子类。接下来采用setter方法注入------在applicationContext.xml中配置

<!--setter方式依赖注入-->
<bean id="setterDao_1" class="com.wwy.DI.DISetter.SetterDao"></bean>
<bean id="setterDaoInterface_1" class="com.wwy.DI.DISetter.SetterDaoInterfaceImpl"></bean>
<bean id="setterService" class="com.wwy.DI.DISetter.SetterService1Impl">
   <property name="setterDao" ref="setterDao_1"></property>
   <property name="setterDaoInterface" ref="setterDaoInterface_1"></property>
</bean>

-----说明:
<bean id=" " class=" "></bean> spring的依赖注入的特性,将对象注入到spring容器中管理。
ref="setterDao_1"对应的已经在xml中已经注入到spring容器中,所以将这个Bean当做参数传入setterService类中。
<property name="setterDao" ref="setterDao_1"></property> 配合SetterService1Impl 实现类的

public void setSetterDao(SetterDao setterDao) {
   this.setterDao = setterDao;
}

方法,是将IOC容器的Bean传入到对应的类中。

2.1.2 注入案例2

按照上面的分析,如果一个Bean已经在spring容器中【不是通过xml方式实现】,也是可以传入任何类中。

Dao层—一般类

@Component
public class SetterDao {
    public String operator1(){
        return "调用了operator1";
    }
}

service层—接口+实现类

public interface SetterService1 {
    public void way();
}

public class SetterService1Impl implements SetterService1 {
    @Autowired
    private SetterDao setterDao;
    /**
     * 接口声明
     */
    private SetterDaoInterface setterDaoInterface1;
    public void setSetterDaoInterface(SetterDaoInterface setterDaoInterface) {
        this.setterDaoInterface1 = setterDaoInterface;
    }
    @Override
    public void way() {
        System.out.println( setterDao.operator1());
        System.out.println( setterDaoInterface1.operator2());
    }
}

上面的例子,SetterDao 类是通过@Component注解注入到IOC容器,SetterService1Impl 类、SetterDaoInterface实现类都没注入。同样的原理,利用setter方式注入。

<!--setter方式依赖注入-->
<bean id="setterDaoInterface_1" class="com.wwy.DI.DISetter.SetterDaoInterfaceImpl"></bean>
<bean id="setterService" class="com.wwy.DI.DISetter.SetterService1Impl">
   <property name="setterDaoInterface" ref="setterDaoInterface_1"></property>
</bean>

2.1.3 实际应用场景解读

在spring+mybatis+mysql中,如果需要操作sql,那么在IOC容器中需要sqlSession实例【即sqlSessionFactory对象】,有了这个对象就可以执行sql任务。
同样,sqlSessionFactory又需要必须属性dataSource【“配置jdbc数据源”信息----理解setter依赖注入思想】,另外其还有一个通用属性configLocation(用来指定mybatis的xml配置文件路径)。

applicationContext.xml中<import resource="spring-mybatis.xml" />
spring-mybatis.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" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="configProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>
    <!-- 配置 jdbc 数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <!-- 使用properties来配置 -->
       <property name="driverClassName">
       <value>${jdbc.driverClassName}</value>
       </property>
       <property name="url">
       <value>${jdbc.url}</value>
       </property>
       <property name="username">
       <value>${jdbc.username}</value>
       </property>
       <property name="password">
       <value>${jdbc.password}</value>
       </property>
    </bean>

    <!-- 配置mybatis SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
    <!-- 配置mybatis mapper接口 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.wwy.Dao"/>
        <!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
    </bean>
</beans>

—查看SqlSessionFactoryBean源码发现,dataSource、configLocation就是采用的Setter方式注入。

2.2、xml–构造器注入

其他的类信息同“2.1.1 注入案例1”小章节信息。

public class SetterService2Impl implements SetterService1 {
    private SetterDaoInterface setterDaoInterface1;
    public SetterService2Impl(SetterDaoInterface setterDaoInterface1){
        this.setterDaoInterface1 =setterDaoInterface1;
    }
    @Override
    public void way() {
        System.out.println(setterDaoInterface1.operator2()+" SetterService2Impl");
    }
}

思想同setter方式类似,xml中配置细节如下:

<!-- 构造方法注入-->
  <bean id="setterDaoInterface_1" class="com.wwy.DI.DISetter.SetterDaoInterfaceImpl">
  
  <bean id="setterService2" class="com.wwy.DI.DIConstructor.SetterService2Impl">
        <constructor-arg ref="setterDaoInterface_1"></constructor-arg>
    </bean>

<constructor-arg ref="setterDaoInterface_1"></constructor-arg> 配合

 public SetterService2Impl(SetterDaoInterface setterDaoInterface1){
        this.setterDaoInterface1 =setterDaoInterface1;
    }

就可以将将IOC容器中已经存在的Bean对象,按照构造器函数方式传入到SetterService2Impl类中。另外,<bean id= 方式是将SetterService2Impl类注入到IOC容器。

2.3、xml–FactoryBean工厂类注入

FactoryBean:从容器中获取Bean的时候,返回的并不是类的一个实例,而是工厂Bean中getObject方法返回的对象。
FactoryBean是spring中工厂模式的实现,可以通过配置创建不同的对象。
通常可用在基于不同配置产生不同对象场景中,如数据库连接(线上、测试环境连接参数不一样)。
a)getObject(),返回生成的对象;【同一个Bean id多次获取,也只是第一次初始化的对象】
b)getObjectType(),返回对象类型;
c)isSingleton() ,是否是单例,true:是,false:不是。

public class StudentFB {
    private String id;
    private String name;
    StudentFB(){}
    StudentFB(String id,String name){
        this.id = id;
        this.name = name;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

实现FactoryBean接口的子类

public class StudentFactoryBean implements FactoryBean<StudentFB> {
    private String info;
    //返回生成的对象
    @Override
    public StudentFB getObject() throws Exception {
        return new StudentFB(info.split("_")[0],info.split("_")[1]);
    }
    //返回对象类型
    @Override
    public Class<?> getObjectType() {
        return StudentFB.class;
    }
    //是否是单例,true:是 false:否
    @Override
    public boolean isSingleton() {
        return true;
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
}

xml的配置:DIFactoryBean.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-3.1.xsd">
    <bean id="studentV1" class="com.wwy.DI.DIFactoryBean.StudentFactoryBean">
        <property name="info" value="001_wwy"></property>
    </bean>
    <bean id="studentV2" class="com.wwy.DI.DIFactoryBean.StudentFactoryBean">
        <property name="info" value="002_wwy"></property>
    </bean>
</beans>

测试的例子

public class FactoryBeanMain {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:DIFactoryBean.xml");
        StudentFB s1 = ctx.getBean("studentV1", StudentFB.class);
        StudentFB s2 = ctx.getBean("studentV1", StudentFB.class);
        System.out.println(s1 == s2); //同一个对象
        StudentFB s3 = ctx.getBean("studentV2", StudentFB.class);
        System.out.println(s1 == s3); //不同对象
    }
}

2.4、xml–FactoryMethod注入

public class PersonFM {
    private String id;
    PersonFM(String id){
        this.id = id;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}

创建静态和动态–FactoryMethod类

public class PersonFactoryMethod {
    //静态创建类
    public static PersonFM getStaticPer(String id){
        return new PersonFM(id);
    }
    //动态创建类
    public PersonFM getDynaPer(String id){
        return new PersonFM(id);
    }
}

xml的配置:DIFactoryMethod.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!--静态获取对象-->
    <bean id="staticPer" class="com.wwy.DI.DIFactoryMethod.PersonFactoryMethod"       factory-method="getStaticPer">
           <constructor-arg value="1"/>  <!--传入getStaticPer方法的参数-->
    </bean>
    <!--生成对象的工厂-->
    <bean id="personFactory" class="com.wwy.DI.DIFactoryMethod.PersonFactoryMethod"/>
    <!--动态获取对象-->
    <bean id="dynamicPer" factory-bean="personFactory" factory-method="getDynaPer">
       <constructor-arg value="11"/>   <!--传入getDynaPer方法的参数-->
    </bean>
</beans>

测试的类

public class FactoryMethodMain {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:DIFactoryMethod.xml");
        PersonFM s1 = ctx.getBean("staticPer", PersonFM.class);
        PersonFM s2 = ctx.getBean("dynamicPer", PersonFM.class);
        System.out.println(s2);
    }
}

2.4、注解–基于注解注入

@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

@Autowired
用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。
@Resource
其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。

@Resource 中有两个重要属性:name 和 type

Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。
如果指定 name 属性,则按实例名称进行装配;
如果指定 type 属性,则按 Bean 类型进行装配。
如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。

@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

三、几种依赖注入方式对比

3.1、xml–setter方法注入

优点
注入属性过多时,更轻便;
在对象需要被注入时才被注入依赖,不需要在初始化时注入;
方便在类实例化后重新对该属性进行配置或注入。
缺点
Setter过多导致代码冗余,维护麻烦;
会把循环依赖隐藏;
依赖不是不可变的【不能使用final修饰】。

3.2、xml–构造器方法注入

优点
依赖不可变:使用final关键字来修饰依赖字段。
依赖不为空:允许构造函数可以保证一些重要属性在Bean实例化时就设置好。
单一职责原则:当使用构造函数注入时,如果参数过多可能会促使你主动对类进行拆分。
完全初始化的状态:保证返回客户端的代码是完全初始化的状态。
更好的封装类变量:不需要为每个属性指定Setter方法,避免外部错误的调用。
更利于单元测试:其它两种方式注入,进行单元测试时需要初始化整个Spring的环境。
避免循环依赖:若存在循环依赖则启动会抛异常–【后续研究“循环依赖”】。

缺点
当注入对象特别多时,构造器显的繁琐,可读性和可维护性较差;
灵活性不强,对于不可选属性,参数值提供Null。
构造器不利于类的扩展和继承,子类需要引用父类复杂的构造函数。

3.3、注解–注解方法注入

优点
简单、便于添加新的依赖;
减少大量冗余代码;
新增依赖时不需要修改过多代码。
缺点
单一职责侵入:添加依赖过于简单,可能添加几十个依赖;
无法声明不可变的字段:字段不能使用final修饰;
隐藏了依赖关系
与依赖注入容器紧耦合

四、Bean、BeanDefinition、BeanFactory和FactoryBean关联

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DreamBoy_W.W.Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值