spring(二)IoC控制反转

一、概念

IoC:Inversion of Controller,即控制反转,不是一种技术,而是一种设计思想。

IoC是指在程序开发中,实例的创建不再由调用者管理,而是由spring容器管理。Spring容器负责控制程序之间的关系,而不是由程序代码直接控制。控制权由程序代码转移到了Spring容器,控制权发生了反转,这就是Spring的IoC思想。

二、案例

2.1、创建项目,添加pom依赖

<dependencies>
        <!--   单元测试    -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--   spring依赖    -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!--   编译插件    -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.2、创建一个实体类

public class Team {

    private Integer id;
    private String name;
    private String location;

    public Team(){
        System.out.println("Team的默认构造方法被调用:id="+id+",name="+name+",location="+location);
    }
}

2.3、添加一个配置文件application.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--spring的配置文件 1、beans: 根标签,spring中java的对象成为bean
    2、spring-beans.xsd 是约束文件(约束XML文件中能编写哪些标签)-->
<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要创建哪个类的对象
    一个bean标签声明一个对象: id="自定义的对象名称" ,要求唯一
     class="类的完全限定名" 包名+类名,spring底层是反射机制创建对象,所以必须使用类名
      相当于 Team team1=new Team();
      创建好的对象放入一个集合Map中 例如:springMap.put("team1",new Team()); -->
    <bean id="team1" class="com.jsonliu.test.bean.Team"/>
    <!--  创建非自定义对象  -->
    <bean id="date" class="java.util.Date"/>
</beans>

2.4、获取Spring容器

Spring提供了两种容器,BeanFactory和ApplicationContext

2.4.1、BeanFactory

BeanFactory的基础类型是IoC容器,是管理Bean的工厂,它负责初始化各种Bean,并调用它们的生命周期方法。

BeanFactory接口有多个实现类,最常见的是org.springframework.beans.factory.xml.XmlBeanFactory,它是根据XML中的定义装配Bean。

创建对象时机:调用getBean时创建对象

BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("application.xml"));

2.4.2、ApplicationContext

ApplicationContext是BeanFactory的子接口,也称为应用上下文。不仅提供了BeanFactory的所有功能,还添加了对i18n(国际化)、资源访问、事件传播等方面的良好支持。

创建对象时机:初始化applicationContext容器时

ApplicationContext接口的两个常用类:

ClassPathXmlApplicationContext,从类路径ClassPath中查找指定xml配置文件,找到后装载完成ApplicationContext的实例化。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

FileSystemXmlApplicationContext,它与ClassPathXmlApplicationContext的区别在于:读取Spring配置文件时,FileSystemXmlApplicationContext不再从类路径中读取配置文件,而是通过参数指定配置文件位置,它可以获取类路径之外的资源,如:“D:\application.xml”

ApplicationContext applicationContext2 = new FileSystemXmlApplicationContext("application.xml");

2.5、通过上下文获取容器中对象

    @Test
    public void test(){
        //使用spring容器创建对象
        //1、指定spring配置文件名称
        String springConfig="application.xml";
        //2、创建spring容器对象
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
        //3、获取容器中的对象
        Team team1 = (Team) applicationContext.getBean("team1");
        //4、容器的其他API
        int count = applicationContext.getBeanDefinitionCount();
        System.out.println("容器中对象个数:"+count);
        String[] names = applicationContext.getBeanDefinitionNames();
        for(String name:names){
            System.out.println("容器中对象的名称:"+name);
        }
        //5、获取日期
        Date date = (Date) applicationContext.getBean("date");
        System.out.println("当前时间:"+date);
    }

三、Bean的标签属性

属性说明
class指定bean对应类的全路径
namebean对应对象的一个标识
scopebean对象的创建模式和生命周期,singleton和prototype
idbean对应对象的唯一标识,不能添加特别字符
lazy-init是否延迟加载,默认false。延迟加载,指对象被调用时才会加载,使用时需要通过getbean()方法获得对象。不延迟加载时,只需加载配置文件即可。
init-method加载配置文件即可进行的对象初始化的方法
destory-method对象销毁时调用方法

Team中补充方法:

    public void init(){
        System.out.println("初始化...");
    }

    public void destroy(){
        System.out.println("销毁...");
    }

application.xml中添加:

<!--bean标签的属性:
    id="自定义的对象名称" ,要求唯一
    name="bean对于的一个标识“,一般使用id居多
    class="类的完全限定名"
    scope="singleton/prototype" 单例/多例
    singleton:默认值, 单例:在容器启动的时候就已经创建了对象,而且整个容器只有唯一 的一个对象
    prototype:多例,在使用对象的时候才创建对象,每次使用都创建新的对象
    lazy-init="true/false" 是否延迟创建对象,只针对单例有效
    true:不延迟创建对象,容器加载的时候立即创建 false:默认加载,使用对象的时候才去创建对象
    init-method="创建对象之后执行的初始化方法"
    destroy-method="对象销毁方法,调用容器destroy方法的时候执行" --> 
    <bean id="team2" class="com.jsonliu.test.bean.Team" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"/>
    <bean id="team3" class="com.jsonliu.test.bean.Team" scope="prototype"/>

修改测试方法:

 @Test
    public void test(){
        //使用spring容器创建对象
        //1、指定spring配置文件名称
        String springConfig="application.xml";
        //2、创建spring容器对象
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
        System.out.println("----------容器初始化创建对象------------");
        //3、获取容器中的对象
        Team team1 = (Team) applicationContext.getBean("team1");
        Team team11 = (Team) applicationContext.getBean("team1");
        System.out.println(team1);
        System.out.println(team11);

        //4、容器的其他API
        int count = applicationContext.getBeanDefinitionCount();
        System.out.println("容器中对象个数:"+count);
        String[] names = applicationContext.getBeanDefinitionNames();
        for(String name:names){
            System.out.println("容器中对象的名称:"+name);
        }
        //5、获取日期
        Date date = (Date) applicationContext.getBean("date");
        System.out.println("当前时间:"+date);

        System.out.println("----------调用时创建对象------------");
        Team team2 = (Team) applicationContext.getBean("team2");
        Team team22 = (Team) applicationContext.getBean("team2");
        System.out.println(team2);
        System.out.println(team22);

        System.out.println("----------调用时创建对象:创建了多个对象------------");
        Team team3 = (Team) applicationContext.getBean("team3");
        Team team33 = (Team) applicationContext.getBean("team3");
        System.out.println(team2);
        System.out.println(team22);
        applicationContext.close();
    }

在这里插入图片描述

四、Spring容器创建对象方式

  • 使用默认构造方法
  • 使用带参构造方法
  • 使用工厂类

4.1、创建实体类Team

public class Team {

    private Integer id;
    private String name;
    private String location;

    public Team(){
        System.out.println("Team的默认构造方法被调用:id="+id+",name="+name+",location="+location);
    }

    public Team(Integer id, String name, String location) {
        this.id = id;
        this.name = name;
        this.location = location;
        System.out.println("Team的全参数构造方法被调用:id="+id+",name="+name+",location="+location);
    } 
}

4.2、创建工厂类

public class MyFactory {

    public Team instanceFun(){
        System.out.println("MyFactory-------instanceFun");
        return new Team(1,"皇家马德里","马德里");
    }

    public static Team staticFun(){
        System.out.println("MyFactory-------staticFun");
        return new Team(2,"拜仁慕尼黑","慕尼黑");
    }

    /**
     * 测试两个方法
     * @param args
     */
    public static void main(String[] args) {
        MyFactory myFactory=new MyFactory();
        Team team = myFactory.instanceFun();
        Team team1 = MyFactory.staticFun();
    } 
}

4.3、createType.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">

<!--
    spring容器创建对象方法:
    1、无参构造方法
    2、有参构造方法
    3、工厂类:实例方法、静态方法
-->
    <!--1、默认构造方法-->
    <bean id="team1" class="com.jsonliu.test.bean.Team"/>
    <!--2、有参构造方法-->
    <bean id="team2" class="com.jsonliu.test.bean.Team">
        <constructor-arg name="id" value="3"/>
        <constructor-arg name="name" value="巴塞罗那"/>
        <constructor-arg name="location" value="巴塞罗那"/>
    </bean>
    <!--3、工厂类:静态方法-->
    <bean id="staticTeam" class="com.jsonliu.test.config.MyFactory" factory-method="staticFun"/>
    <!--4、工厂类:实例方法-->
    <bean id="myFactory" class="com.jsonliu.test.config.MyFactory" />
    <bean id="instanceTeam" factory-bean="myFactory" factory-method="instanceFun" />

</beans>

4.4、测试类

public class CreateType { 
    @Test
    public void test(){ 
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("createType.xml"); 
    } 
} 

运行结果:

在这里插入图片描述

五、基于XML的DI

DI-Dependency Injection,依赖注入。组件之间的依赖关系由容器在运行期间决定,即容器动态的将某个依赖关系注入到组件中。依赖注入的目的并非为软件系统带来更多的功能,而是提升组件重用频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要简单的配置,而无需任何代码就可以指定目标需要的资源,完成自身业务逻辑,不需要关系具体资源来于何处,由谁实现。

IoC是一种思想,一个概念,实现方式多种多样。依赖注入就是其中用的比较多的一种方式。

IoC和DI是同一个概念的不同角度描述。IoC是一个概念,一种思想,DI是实现它的手段。Spring框架使用依赖注入实现IoC。

Spring容器是一个超级大工厂,负责创建、管理所有java对象,这些java对象被称为bean。Spring容器管理着Bean之间的依赖关系(使用依赖注入方式),使用IoC实现对象之间解耦。

5.1、注入分类

bean实例再调用无参构造器创建对象后,就要对bean对象的属性进行初始化。初始化由容器自动完成,被称为注入。

5.1.1、通过set方法

set注入也称为设值注入,通过setter方法传入被调用者实例。这种方法简单直观,因而在Spring依赖注入中大量使用。

5.1.2、通过构造方法

构造注入:在构造调用者实例同时,完成被调用者实例化。使用构造器设置依赖关系。

5.1.3、自动注入

对于引用类型属性的注入,也可以不再配置文件中显示注入。可以通过为标签设置autowire属性值,为引用类型属性进行隐式注入(默认不自动注入引用类型属性)根据自动注入判断标准不同,可以分为:

  • byName:根据名称自动注入
  • byType:根据类型自动注入

1、byName:当配置文件中被调用者bean的id与代码中调用者bean类属性名相同时,使用byName方式,让容器自动将被调用者bean注入给调用者bean。容器是通过调用者bean类属性名与配置文件中被调用者bean的id进行比较而实现自动注入的。

2、byType:要求配置文件中被调用者bean的class指定的类,要与代码中调用者bean类的某引用类型属性的类型同源。要么相同,要么is-a关系(子类或实现类)。但这样的同源的被调用bean只能有一个,超过一个会出现错误。

5.2、示例

5.2.1、创建TeamDao

public class TeamDao { 
    public void add(){
        System.out.println("TeamDao---------add");
    } 
}

5.2.2、创建TeamService

public class TeamService {

    private TeamDao teamDao; //=new TeamDao();

    public void add(){
        System.out.println("TeamService------------add");
        teamDao.add();
    }

    public TeamService() {
    }

    public TeamService(TeamDao teamDao) {
        this.teamDao = teamDao;
    }

    public void setTeamDao(TeamDao teamDao) {
        this.teamDao = teamDao;
    }
}

5.2.3、DI.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 id="teamDao" class="com.jsonliu.test.dao.TeamDao"/>

    <bean id="teamService" class="com.jsonliu.test.service.TeamService">
        <!--   使用set方法注入属性值     -->
        <property name="teamDao" ref="teamDao"/>
    </bean>

    <bean id="teamService1" class="com.jsonliu.test.service.TeamService">
        <!--   使用构造方法注入属性值     -->
        <constructor-arg name="teamDao" ref="teamDao"/>
    </bean>

    <!--   按照名称自动注入:查找容器中id名与属性名一致的对象进行注入     -->
    <bean id="teamService2" class="com.jsonliu.test.service.TeamService" autowire="byName"/>

    <!--   按照类型自动注入:查找容器中类型与属性类型相同或者符合is-a关系的对象进行注入,但是要求类型相同的对象唯一,否则抛出异常:不知道匹配哪一个     -->
    <bean id="teamService3" class="com.jsonliu.test.service.TeamService" autowire="byType"/>

</beans>

5.2.4、测试类

public class DITest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("DI.xml");
        TeamService teamService = (TeamService) applicationContext.getBean("teamService");
        teamService.add();

        TeamService teamService1 = (TeamService) applicationContext.getBean("teamService1");
        teamService1.add();

        TeamService teamService2 = (TeamService) applicationContext.getBean("teamService2");
        teamService2.add();

        TeamService teamService3 = (TeamService) applicationContext.getBean("teamService3");
        teamService3.add();
    }
}

六、基于注解实现IoC

对于DI使用注解,将不再需要在Spring配置文件中声明bean实例。Spring中使用注解,需要在原有Spring运行环境基础上做一些改变。

6.1、声明Bean的注解@Component

在类上添加注解@Component表示该类创建对象的权限交给Spring容器,注解的value属性用于指定bean的id值,可以省略。
@Component不指定value属性,bean的id是类名首字母小写。

//@Component标识在类上,表示该对象由Spring容器创建 value属性表示创建的id值,value可以省略,值也可以省略,默认为类名的首字母小写
@Component("teamDao")
//相当于xml配置文件中:<bean id="teamDao" class="com.jsonliu.test.dao.TeamDao" />
public class TeamDao {
    public void add() {
        System.out.println("TeamDao---------add");
    }

    public TeamDao() {
        System.out.println("TeamDao默认构造方法");
    }
}

@Component标识属于“万能”注解,Spring针对不同类型的类还提供了其他注解:

  • @Repository:用于dao实现类注解
  • @Service:用于service实现类注解
  • @Controller:用于controller实现类注解

@Repository、@Service、@Controller注解是对@Component注解的细化,标注数据持久层、业务实现层、访问控制层的注解。

@Repository
public class TeamDao {
    public void add() {
        System.out.println("TeamDao---------add");
    }

    public TeamDao() {
        System.out.println("TeamDao默认构造方法");
    }
}

@Service
public class TeamService {

    private TeamDao teamDao; //=new TeamDao();

    public void add(){
        System.out.println("TeamService------------add");
        teamDao.add();
    }

    public TeamService() {
    }

    public TeamService(TeamDao teamDao) {
        this.teamDao = teamDao;
    }

    public void setTeamDao(TeamDao teamDao) {
        this.teamDao = teamDao;
    }
}

@Controller
public class TeamController {

    public TeamController() {
        System.out.println("TeamController默认的构造方法");
    }
}

6.2、包扫描

需要在Spring配置文件中配置组件扫描器,用于指定在基本包中扫描注解。如果没有进行包扫描,添加的注解是不生效的。

多个包扫描方式:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

<!--
     context:component-scan 告知spring要扫描的包
     这些包以及子包如果添加了@Component注解,这些添加了注解的类就由spring进行对象创建
     注意:需要在beans标签中添加  xmlns:context="http://www.springframework.org/schema/context"
     http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
-->
    <!-- 包扫描方式1:使用多个component-scan -->
    <context:component-scan base-package="com.jsonliu.test.dao" />
    <context:component-scan base-package="com.jsonliu.test.service" />
    <context:component-scan base-package="com.jsonliu.test.controller" />
    <!-- 包扫描方式2:使用分隔符 , ; 空格 其中空格不推荐 -->
    <context:component-scan base-package="com.jsonliu.test.dao,com.jsonliu.test.service,com.jsonliu.test.controller"/>
    <!-- 包扫描方式3:扩大包扫描范围,扫描上层的父包: 不建议使用顶级父包,扫描路径变多,容易导致容器启动变慢,指定到注解所在包是比较合适的 -->
    <context:component-scan base-package="com.jsonliu.test"/>

</beans>

6.3、属性注入@Value

在属性上使用注解@Value,该注解的value属性用于指定需要注入的值。使用该注解进行注入,类中无需setter方法;如果存在setter方法,注解添加到setter方法上也可以。

在这里插入图片描述

6.4、byType自动注入@Autowired

在引用属性上加@Autowired,该注解默认按使用类型进行装配Bean。使用该注解完成属性注入,类中无需setter。当然,若属性有setter,也可以将其加入到setter上。

6.5、byName自动注入@Autowired和@Qualifier

需要在属性上联合使用注解@Autowired和@Qualifier,@Qualifier的属性value用于指定要匹配的bean的id。类中无需setter方法,也可加到setter方法上。

@Autowired还有一个属性required,默认值为true,表示匹配失败后,会终止程序运行。若值为false,表示匹配失败后,将会忽略,未匹配值为null。

在这里插入图片描述

6.6、自动注入@Resource

jdk中提供了@Resource注解,该注解即可按照名称匹配Bean,也可以按照类型匹配Bean。 默认按照名称注入。 要求JDK6+ @Resource可以在属性上,也可以在set方法上。

6.6.1、byType注入引用类型属性

@Resource不带任何参数,默认按照名称的方式注入,按照名称不能注入再按照类型进行Bean匹配注入。

6.6.2、byName注入引用类型属性

@Resource指定其name属性,则按照name属性匹配Bean的id。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

笑谈子云亭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值