Spring - IoC/DI

目录

Spring概述

        Spring是什么

        Bean是什么

        Spring的优势

IoC

DI

Spring中的IoC和DI

Spring环境搭建

        添加Spring相关依赖

Spring - IoC 基于xml进行管理

        配置文件

        获取对象

Spring - DI 基于xml进行依赖注入

        Setter方法注入

        构造器注入

        数组注入

        集合注入

        对象注入

        引入外部文件

        自动装配 - autowire

Spring 注释管理

        添加context命名空间

        开启组件扫描

        使用注释定义bean

        Autowire进行依赖注入

        Resource注解

Spring 纯注解


Spring概述

        Spring是什么

        Spring是Java的一个设计层面的开发框架,它将对象的管理权力移交给Spring容器,以达到降低设计出来的程序耦合度的目标

        在Spring中用bean来表示一个对象

        Bean是什么

        Bean就是Spring中的一个对象,在Spring中配置后可以在Java代码中获取这个bean来获取对象,一个bean的生命周期如下

        bean创建 - 设置bean属性 - bean后置处理器 - bean对象初始化 - bean后置处理器 - 对象创建完成 - bean对象销毁 - 关闭IoC容器

        Spring的优势

        1. 使用Spring设计出来的代码耦合度低。将对象之间的依赖关系以及管理交给Spring处理,避免开发者在编码过程中导致的过度耦合

        2. AOP支持。Spring支持AOP,能够很方便地进行面向切面的编程,例如性能检测、事务管理、日志等等

        3. 具有很多的API以供集成其他框架

        4. 低侵入式设计,代码的可读性好、污染性低

    在继续讲Spring之前,有两个概念需要先提到,一个是IoC-控制反转,一个是DI-依赖注入

IoC

        IoC - Inversion of Control,中文翻译为控制反转,这是一种设计思想,也就是将对象实体交给容器控制,这个容器在Spring中就是一个Map

        在传统的设计模式下,开发者需要自己创建业务需要的类以及进行相关属性的注入,但是当有了IoC后,开发者可以将管理对象的权力移交给IoC容器,则不需要主动去创建这些对象,只需要从IoC容器中拿取所需要的对象即可        

        利用IoC设计出的程序具有松耦合的特性,同时便于测试和复用,并且使得整个体系变得更加灵活!

DI

        DI - Dependency Injection,中文翻译为依赖注入,就是由IoC容器动态地将某个依赖关系注入到组件中,只需要通过简单的配置,就可以指定目标需要的资源,完成业务需求

        在Spring下,程序需要的资源都由IoC来提供,也就是说程序依赖IoC容器,IoC容器可以注入程序中某个对象所需要的资源,比如对象、字面量等

        接下来就讲讲使用Spring的正确姿势

Spring中的IoC和DI

        Spring通过IoC容器管理所有Java对象的实例化和初始化,可以通过.getBean("objname")从IoC容器中获取对象,在创建对象过程中将对象依赖属性通过配置进行注入,可以说IoC是一种思想,而DI是具体实现方法

Spring环境搭建

        添加Spring相关依赖

        在新建的Maven工程中添加使用Spring所需的依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.2</version>
        </dependency>

        等待Maven将依赖下载、配置完成后即可使用Spring进行开发了!

Spring - IoC 基于xml进行管理

        首先为了演示方便,编写一个User类,其中只有一个方法add

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

        配置文件

        配置bean.xml文件,在其中有spring的一些规范,以及用到的命名空间

        在配置文件中加入与我们刚刚创建的User类对应的bean,其中id是自定义的一个唯一标识这个bean的,class是你这个bean对应的Java代码中的类的类全路径名

<bean id="user" class="com.aki.sping6.User"></bean>

        获取对象

        在测试类中添加如下代码,这里的getBean是用id来获取,如果同类下有唯一的bean,也可以直接使用该类.class来获取

public class TestUser {
    @Test
    public void test_user(){
        //加载spring配置文件,对象创建
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //从容器中获取创建的对象
        User user = (User) context.getBean("user");
        System.out.println(user);
        user.add();
    }
}

       这里是一个最简单的Spring IoC的例子,创建context时就已经将bean创建了,后面只是从其中拿出这个bean来获取对象,这是单实例的情况

        如果要有多实例,则可以通过scope="prototype"来配置,每次获取都创建一个不同对象

<bean id="user" class="com.aki.spring6iocxml.bean.User"
          scope="prototype"></bean>

Spring - DI 基于xml进行依赖注入

        为了演示方便,这里先创建一个Book类,其中有属性也有构造器也有set方法

public class Book {
    private String book_name;
    private String author;

    public void setBook_name(String book_name) {
        this.book_name = book_name;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Book() {
    }

    public Book(String book_name, String author) {
        this.book_name = book_name;
        this.author = author;
    }
}

        Setter方法注入

        这种情况下需要在类中添加属性的setter方法,例如Book类中的setBook_name

        随后在xml配置文件中添加这个类,并且用<property>标签进行set方法的依赖注入

    <bean id="book" class="com.aki.spring6iocxml.di.Book">
        <!--1. set 方法依赖注入 <property>-->
        <property name="book_name">
            <value><![CDATA[a<b]]></value>
        </property>
        <property name="author" value="aki"></property>
    </bean>

        在property中name与要注入的属性名称一致,value内容是你要注入的内容

        这里用到了![CDATA[...]],由于xml有一些符号是关键字,因此要使用这些符号时,除了用转义,也可以使用cdata区,其中...是用户自定义的,可以直接使用xml的关键字

        构造器注入

        这种情况下需要在类中添加构造器

        随后在xml配置文件中添加这个类,并且用<constructor-arg>标签进行注入

    <bean id="bookgoucaoqi" class="com.aki.spring6iocxml.di.Book">
        <!--2. constructor 方法依赖注入 <constructor-arg>-->
        <constructor-arg name="book_name" value="Java2"></constructor-arg>
        <constructor-arg name="author" value="aki2 &lt;"></constructor-arg>
    </bean>

        注入完成后在getBean时获取到的对象就是已经注入属性的对象了!

        这里的两个例子都是常规类型的注入,如果碰到了数组或者对象,要如何注入呢?

        创建一个Employee和Department类

public class Employee {
    private String ename;
    private int age;
    private String[] hobby;
    private Department department;
    public void work(){
        System.out.println(ename+" "+age+" is working loving "+Arrays.toString(hobby));
    }
}


public class Department {
    private String dname;
    private List<Employee> emplist;
    private Map<String,Employee> map;
    public void info(){
        System.out.println(dname);
        for(Employee emp:emplist){
            System.out.println(emp.getEname());
        }
    }
    public void map(){
        map.get("2").work();
    }
}

        数组注入

        在配置文件中添加bean,并且在hobby这个property中加入array标签,将需要的依赖注入即可

    <!--数组注入 <array>
                    <value>a</value>
                </array>
    -->
    <bean id="emp" class="com.aki.spring6iocxml.di_obj.Employee">
        <property name="ename" value="lucy"></property>
        <property name="age" value="40"></property>
        <property name="department" ref="dept"></property>
        <property name="hobby">
            <array>
                <value>"a"</value>
                <value>b</value>
                <value>'c'</value>
            </array>
        </property>
    </bean>

        集合注入

        在配置文件中添加bean,并且在emplist集合property中加入list标签,其中map比较特殊,需要用<entry>标签代表一个kv,并且在entry下用key和value描述kv值

        <!--
        注入list
        <property>
            <list>
                <ref bean="..."></ref>  集合中是 类
                <value>...</value> 集合中是 常规类型
            </list>
        </property>

        注入map
        <property>
            <map>
                <entry>
                    <key>
                        <value>...</value>
                    </key>
                    <value>...</value> v是 常规类型
                    <ref bean="..."></ref> v是 类
                </entry>
            </map>
        </property>
    -->
    <bean id="department" class="com.aki.spring6iocxml.di_obj.Department">
        <property name="dname" value="tech department"></property>
        <property name="emplist">
            <list>
                <ref bean="emp1"></ref>
                <ref bean="emp2"></ref>
            </list>
        </property>
        <property name="map" ref="map"></property>
    </bean>

        1. 除了直接这样硬写,还可以用util来进行注入,需要加入util的命名空间

        xmlns:util="http://www.springframework.org/schema/util
    <util:map id="map">
        <entry>
            <key>
                <value>2</value>
            </key>
            <ref bean="emp2"></ref>
        </entry>
    </util:map>

       这样在外部定义了id为"map"的map后,可以在property中用ref属性直接引用这个map

         2. 此外,还可以用p命名空间进行注入,同样需要引入p的命名空间

        xmlns:p="http://www.springframework.org/schema/p"
    <bean id="dpt_p" class="com.aki.spring6iocxml.di_obj.Department"
    p:dname="ppppp" p:map-ref="map">
    </bean>

        在p:属性名后就可以直接用了!,p命名空间下同样可以引用util定义的集合,使用-ref

        对象注入

        对象的注入也很简单,有三种方法

        1. 引入外部的bean,这种方法最好理解,直接上代码

    <!-- 注入对象变量
    一、引入外部bean
        1. 创建两个类对象 department 和 employee
        2. 在employee的bean中 用property引入dept的bean
    -->
    <bean id="department" class="com.aki.spring6iocxml.di_obj.Department">
        <property name="dname" value="fire department"></property>
    </bean>

    <bean id="employee" class="com.aki.spring6iocxml.di_obj.Employee">
        <property name="ename" value="aki"></property>
        <property name="age" value="20"></property>
        <!--对象类型属性 private Department department 注入
            用ref="..."
            其中的 ... 是在xml文件中创建的该对象的bean的id
        -->
        <property name="department" ref="department"></property>
    </bean>

        这样在employee对象中的department属性就被注入了上面定义的department对象!

        2. 用内部bean,这就相当于在Java中的 在内部直接new一个对象出来

    <!-- 注入对象变量
        二、用内部bean
           在bean的property中 写内部bean
           其中这个内部bean也可以直接拿来用
        -->
    <bean id="department2" class="com.aki.spring6iocxml.di_obj.Department">
        <property name="dname" value="police department"></property>
    </bean>

    <bean id="employee2" class="com.aki.spring6iocxml.di_obj.Employee">
        <property name="ename" value="bully"></property>
        <property name="age" value="30"></property>
        <!-- 内部bean -->
        <property name="department">
            <bean id="department2" class="com.aki.spring6iocxml.di_obj.Department">
                <property name="dname" value="police department"></property>
            </bean>
        </property>
    </bean>

        这种方法在对象的property标签内bean了一个department对象

        3. 级联赋值,这种方法可以修改对象的值,利用对象.属性可以修改!

    <!-- 注入对象变量
        三、级联赋值
            property下可用 对象.属性 进行值的修改和赋值
        -->
    <bean id="department3" class="com.aki.spring6iocxml.di_obj.Department">
        <property name="dname" value="medical department"></property>
    </bean>

    <bean id="employee3" class="com.aki.spring6iocxml.di_obj.Employee">
        <property name="age" value="80"></property>
        <property name="ename" value="cindy"></property>
        <property name="department" ref="department3"></property>
        <property name="department.dname" value="医疗"></property>
    </bean>

        引入外部文件

        在上面的处理过程中,各种属性的值都是我们在配置文件中手写的,如果要更改则需要该很多地方,导致耦合度高,我们可以使用引入外部文件的方式来降低耦合度

        这里利用context命名空间,同样需要引入

    xmlns:context="http://www.springframework.org/schema/context

        然后利用context引入外部文件

    <!--利用context 引入外部文件-->
    <context:property-placeholder 
    location="classpath:jdbc.properties"></context:property-placeholder>

        引入这个外部文件后就可以使用其中的值了! 注意格式 value="${...}",...是文件中的名称

    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${url}"></property>
    </bean>

        自动装配 - autowire

        之前讲到的对象都是手动装配,很麻烦,spring提供了一种自动装配办法,在bean标签内的autowire属性可以实现对象的自动装配

        1. autowire = "byType" 根据类型装配,如果在IoC中没有能够匹配的类型,最终装配为null,如果在IoC中有多个能够匹配的bean,则会报错

        2. autowire = "byName" 根据名称装配,这种情况下<bean id>中的id要对应java代码中装配目标的属性的名称,否则报错

        例如,在UserController中有UserService属性,UserService中有UserDao属性,则可以自动装配,如下配置xml文件,实现对象的自动装配

    <bean id="controller" class="com.aki.spring6iocxml.auto_bean.controller.UserController" autowire="byType">
    </bean>

    <bean id="service" class="com.aki.spring6iocxml.auto_bean.service.UserServiceImpl" autowire="byName">
    </bean>

    <bean id="userDao" class="com.aki.spring6iocxml.auto_bean.dao.UserDaoImpl"></bean>

        到这里都是通过xml文件进行依赖注入,这很麻烦,因此spring提供了注释管理IoC

Spring 注释管理

        添加context命名空间

        Spring可以通过注释进行依赖注入,首先要引入context命名空间,这个在上面讲过了,就不再赘述了

        开启组件扫描

        利用context:component-scan开启组件扫描,这里需要指定扫描的包,基本写法会扫描包内全部的注释,在下面给出了排除或者只选择某些的写法,可以规定要哪些注释或者要哪些类

<!--    基本写法-->
    <context:component-scan base-package="com.aki.spring6.bean"></context:component-scan>

<!--    有排除或者只选择某些的写法-->
    <context:component-scan base-package="com.aki">
<!--        <context:exclude-filter type="" expression=""/>
            type:设置排除或包含的依据
            type="annotation" 根据注解排除 expression中设置要排除的注解的全类名
                下面Controller这个注解不会扫描
            type="assignable" 根据类型排除 expression中设置要排除的类型的全类名
                下面com.aki.spring6.User这个类的注解不会扫描

            <context:include-filter> 同理
-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--        <context:exclude-filter type="assignable" expression="com.aki.spring6.bean.User"/>-->
        
    </context:component-scan>

        使用注释定义bean

        这里有四种定义bean的注释,其功能都是一样的,只是名字不同

        @Component 泛化的概念

        @Repository 数据访问层

        @Service 业务层

        @Controller 控制层

        在UserController类上添加注释@Controller,则可以扫描这个注释创建UserController的一个对象,不需要再进行配置文件的编写了!

@Controller
public class UserController {
    private UserService userService;
}

        同样的,可以再UserService类上添加注释@Service

@Service
public class UserServiceImpl implements UserService{
    private UserDao userDao;
}

        Autowire进行依赖注入

        @Autowire可以添加在属性、setter方法、构造方法、形参上,都可以达到自动装配

    //注入service
    @Autowired  //1. 属性注入 根据类型找到对应对象 完成注入
    private UserService userService;


    //2. set方法注入
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }


    //3. 构造方法注入
    private UserDao userDao;
    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    //4. 形参上注入  在参数前加入@autowired,如果有多个参数 写一个就可以
    private UserDao userDao;
    private UserController userController;

    public UserServiceImpl(@Autowired UserDao userDao, UserController userController) {
        this.userDao = userDao;
        this.userController = userController;
    }

        Autowired 默认根据类型进行装配,如果想要根据名称进行装配,需要搭配@Qualifier注释

   @Autowired
    @Qualifier(value = "dao3")
    private UserDao userDao;

        其中@Qualifier的value要与目标类的创建bean注释中的 value一致

        如果只有一个有参数的构造函数,则无需注解,可以省略@Autowired;如果有多个构造函数,需要注解在构造方法上,而不是在形参上,并且只有一个构造方法可以被注解!

        Resource注解

        除了Autowired,JDK还提供了Resource注解进行依赖注入

        默认根据名称装配,未指定name时使用属性名为name,当name找不到时用类型装配,可以用于属性和setter方法上

        使用@Resource需要引入依赖

<!--        @Resource依赖-->
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.0</version>
        </dependency>

        引入依赖后就可以直接使用了,可以达到与autowired一样的效果

    @Resource
    private ResourceTest resourceTest;

Spring 纯注解

        Spring可以实现纯注解,只要定义一个配置类即可,其中的@Configuration表示这是一个配置类,@ComponentScan表示了扫描空间

//配置类 可以全注解开发
@Configuration
@ComponentScan("com.aki.spring6")
public class SpringConfig {
}

        随后在获取contex时使用的是AnnotationConfigApplicationContext(SpringConfig.class),用这个配置类为参数来获取context

public class TestConfig {
    public static void main(String[] args) {
        //加载配置类
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController userController = context.getBean(UserController.class);
        userController.add();
    }
}

        至此,Spring的IoC和DI就已经学习完毕啦!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值