Spring6-IoC(learning)

Spring-IoC

A. 控制反转

  • IoC(Inversion of Control):控制反转(设计思想)
  • Spring通过IoC容器管理所有Java对象的实例化与初始化,控制对象间的依赖关系。将由IoC容器管理的Java对象称为Spring Bean,它与使用new关键字创建的Java对象没有任何区别。IoC容器是Spring框架最重要的核心组件之一。
    • 将创建对象的权力交给第三方容器
    • 将维护对象间关系的权利交给第三方容器

B. 依赖注入

  • 依赖注入(DI , Dependency Injection):实现了控制反转思想,指在Spring对象创建过程中,将对象依赖属性通过配置注入
  • 注入方式:
    • setter注入
    • 构造注入

C. IoC在Spring中的实现

  • BeanFactory:IoC的基本实现,是Spring内部使用的接口,不提供给外界使用
  • ApplicationContext:是BeanFactory的子接口,面向Spring使用者
  • ApplicationContext拥有一系列具体的实现类

D. 基于xml管理Bean

1. 使用xml定义和获取bean

<?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="user" class="org.spring6Demo.iocxml.User"></bean>
</beans>
public static void main(String[] args) {
    	// 加载Spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

        // 1.根据id获取bean
        User user1 = (User) context.getBean("user");
        System.out.println("user1 " + user1.getInfo());

        // 2.根据类型获取bean
        User user2 = context.getBean(User.class);
        System.out.println("user2 " + user2.getInfo());

        // 3.根据id和类型获取bean
        User user3 = context.getBean("user", User.class);
        System.out.println("user3 " + user3.getInfo());
    }
  • 当上述2根据类型获取bean时,要求IoC容器中指定类型的bean只能有一个

    • 有超过一个时,会抛出异常
    • 应当改用1/3来获取bean
  • 接口有一个或多个实现类时(Liscov)

    • 一个接口有一个实现类时,可以根据接口类型获取bean(bean应当唯一)

      UserDaoImpl implements UserDao

      <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="userDaoImpl" class="org.spring6Demo.iocxml.bean.UserDaoImpl"></bean>
      </beans>
      
      public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
              // 根据接口类型获取bean
              UserDao userDao = context.getBean(UserDao.class);
              System.out.println(userDao); // org.spring6Demo.iocxml.bean.UserDaoImpl@14ec4505
          }
      
    • 一个接口有多个实现类时,不可以根据接口类型获取bean(bean不唯一)

      UserDaoImpl implements UserDao , UserDaoImpl2 implements UserDao

      <!--    接口有多个实现类-->
      <bean id="userDaoImpl" class="org.spring6Demo.iocxml.bean.UserDaoImpl"></bean>
      <bean id="userDaoImpl2" class="org.spring6Demo.iocxml.bean.UserDaoImpl2"></bean>
      

      Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.spring6Demo.iocxml.bean.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2

    • 根据类型获取bean时,在满足bean唯一性的前提下,当 对象 instanceof 指定类型 为true时就可认定为与类型匹配,可以获取到对象

2. 依赖注入

2.1 set注入(setter)
  • 创建类,定义属性,设置属性的setter方法

    public class Book {
        private String name;
        private String author;
        
        public Book() {}
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    }
    
  • Spring配置文件

    <!--set注入-->
    <bean id="book1" class="org.spring6Demo.iocxml.di.Book">
        <property name="name" value="Head First Java"></property>
        <property name="author" value="Sierra Bates"></property>
    </bean>
    

    标签即调用类中已定义的setter方法,name指定属性,value为属性赋值

2.2 构造注入(有参constructor)
  • 创建类,定义属性,设置有参构造方法

    public class Book {
        private String name;
        private String author;
    
        public Book(String name, String author) {
            this.name = name;
            this.author = author;
        }
    }
    
  • Spring配置文件

    <!--构造注入-->
    <bean id="book2" class="org.spring6Demo.iocxml.di.Book">
        <constructor-arg name="name" value="Head First Java"></constructor-arg>
        <constructor-arg name="author" value="Sierra Bates"></constructor-arg>
    </bean>
    

    标签即调用类中已定义的有参构造方法

2.3 特殊值处理
  • null

    <bean id="null_test" class="org.spring6Demo.iocxml.di.Book">
        <property name="name" value="几何原本"></property>
        <property name="author">
            <null></null>
        </property>
    </bean>
    
  • xml实体:采用转义符号(&lt;  &gt;)

  • CDATA节:用于表示纯文本数据,xml解析器遇到CDATA则认为是纯文本而不再对这段文本进行xml解析,从而可以直接在CDATA节中书写特殊符号

    <bean id="null_test" class="org.spring6Demo.iocxml.di.Book">
        <property name="name" value="几何原本"></property>
        <property name="author">
            <value><![CDATA[
            <<欧几里得>>
            ]]></value>
        </property>
    </bean>
    

3. 特殊类型属性注入

3.1 对象类型属性注入
  • 引用外部Bean

    <!--注入外部bean : 部门Department 员工Employee 向Employee的bean中注入Department的bean
            1 分别创建两个类的对象dpm emp
            2 在emp的bean中使用property引入dpm的bean-->
        <bean id="dpm" class="org.spring6Demo.iocxml.objDI.Department">
            <property name="name" value="security"></property>
        </bean>
        <bean id="emp" class="org.spring6Demo.iocxml.objDI.Employee">
            <!--普通属性注入-->
            <property name="name" value="Leo"></property>
            <property name="age" value="20"></property>
            <!--对象类型属性注入-->
            <property name="dept" ref="dpm"></property>
        </bean>
    
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-obj-di.xml");
        Employee emp = context.getBean("emp", Employee.class);
        System.out.println(emp); // Employee{name='Leo', age=20, dept=Department{name='security'}}
        // 创建了Employee类型bean:emp  同时也为其dept属性创建并赋值了Department类型bean:dpm 
    }
    
  • 内部Bean

    <!--注入内部bean-->
        <bean id="emp2" class="org.spring6Demo.iocxml.objDI.Employee">
            <property name="name" value="David"></property>
            <property name="age" value="21"></property>
            <!--内部bean-->
            <property name="dept">
                <bean id="dpm2" class="org.spring6Demo.iocxml.objDI.Department">
                    <property name="name" value="finance"></property>
                </bean>
            </property>
        </bean>
    
    @Test
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-obj-di.xml");
        Employee emp2 = context.getBean("emp2", Employee.class);
        System.out.println(emp2); // Employee{name='David', age=21, dept=Department{name='finance'}}
    }
    
  • 级联属性赋值

    <bean id="dpm" class="org.spring6Demo.iocxml.objDI.Department">
        <property name="name" value="security"></property>
    </bean>
    <bean id="emp" class="org.spring6Demo.iocxml.objDI.Employee">
        <property name="name" value="Leo"></property>
        <property name="age" value="20"></property>
        <property name="dept" ref="dpm"></property>
        <!--级联赋值-->
        <property name="dept.name" value="test"></property>
    </bean>
    <!-- Employee{name='Leo', age=20, dept=Department{name='test'}} -->
    
3.2 数组类型属性注入
<!--注入数组类型属性-->
<bean id="emp3" class="org.spring6Demo.iocxml.objDI.Employee">
    <property name="name" value="Tom"></property>
    <property name="age" value="22"></property>
    <property name="dept">
        <bean id="dpm3" class="org.spring6Demo.iocxml.objDI.Department">
            <property name="name" value="HR"></property>
        </bean>
    </property>
    <!--数组类型属性赋值-->
    <property name="loves">
        <array>
            <value>eating</value>
            <value>sleeping</value>
            <value>coding</value>
        </array>
    </property>
</bean>
<!-- Employee{name='Tom', age=22, dept=Department{name='HR'}, loves=[eating, sleeping, coding]} -->
3.3 List类型属性注入
    <!-- List类型属性注入 : 向Department的List<Employee>属性注入值 -->
    <bean id="emp4-1" class="org.spring6Demo.iocxml.objDI.Employee">
        <property name="name" value="Judy"></property>
        <property name="age" value="21"></property>
    </bean>
    <bean id="emp4-2" class="org.spring6Demo.iocxml.objDI.Employee">
        <property name="name" value="Jerry"></property>
        <property name="age" value="23"></property>
    </bean>
    
    <bean id="dpm4" class="org.spring6Demo.iocxml.objDI.Department">
        <property name="name" value="security"></property>
        <!--List类型属性赋值-->
        <property name="employees">
            <list>
                <ref bean="emp4-1"></ref>
                <ref bean="emp4-2"></ref>
            </list>
        </property>
    </bean>
<!-- Department{name='security', employees=[Employee{name='Judy', age=21, dept=null, loves=null}, Employee{name='Jerry', age=23, dept=null, loves=null}]} -->
3.4 Map类型属性注入
    <bean id="teacher1" class="org.spring6Demo.iocxml.mapDI.Teacher">
        <property name="tid" value="001"></property>
        <property name="tName" value="刘钦"></property>
    </bean>
    <bean id="teacher2" class="org.spring6Demo.iocxml.mapDI.Teacher">
        <property name="tid" value="002"></property>
        <property name="tName" value="刘嘉"></property>
    </bean>

    <bean id="student1" class="org.spring6Demo.iocxml.mapDI.Student">
        <property name="sid" value="201250001"></property>
        <property name="sName" value="Tommy"></property>
        <!--Map类型属性赋值-->
        <property name="teacherMap">
            <map>
                <entry>
                    <key>
                        <value>001</value>
                    </key>
                    <!--定义的Map为<String,Teacher>类型,因此value部分应使用ref-->
                    <ref bean="teacher1"></ref>
                </entry>
                <entry>
                    <key>
                        <value>002</value>
                    </key>
                    <ref bean="teacher2"></ref>
                </entry>
            </map>
        </property>
    </bean>
<!--Student{sid='201250001', sName='Tommy', teacherMap={001=Teacher{tid='001', tName='刘钦'}, 002=Teacher{tid='002', tName='刘嘉'}}}-->
3.5 引用集合类型的bean注入
  • 在命名空间约束中,应加入与util相关的部分才能使用util标签
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util.xsd">
    
    <!--Student bean-->
    <bean id="student" class="org.spring6Demo.iocxml.mapDI.Student">
        <property name="sid" value="201250002"></property>
        <property name="sName" value="zy"></property>
        <property name="courses" ref="courseList"></property>
        <property name="teacherMap" ref="teacherMap"></property>
    </bean>
    
    <!--util:list courses-->
    <util:list id="courseList">
        <ref bean="course1"></ref>
        <ref bean="course2"></ref>
    </util:list>
    
    <!--util:map teacherMap-->
    <util:map id="teacherMap">
        <entry>
            <key>
                <value>001</value>
            </key>
            <ref bean="teacher1"></ref>
        </entry>
        <entry>
            <key>
                <value>002</value>
            </key>
            <ref bean="teacher2"></ref>
        </entry>
    </util:map>
    
    <!--Teacher bean-->
    <bean id="teacher1" class="org.spring6Demo.iocxml.mapDI.Teacher">
        <property name="tid" value="001"></property>
        <property name="tName" value="刘钦"></property>
    </bean>
    <bean id="teacher2" class="org.spring6Demo.iocxml.mapDI.Teacher">
        <property name="tid" value="002"></property>
        <property name="tName" value="刘嘉"></property>
    </bean>
    
    <!--Course bean-->
    <bean id="course1" class="org.spring6Demo.iocxml.mapDI.Course">
        <property name="cName" value="OS"></property>
    </bean>
    <bean id="course2" class="org.spring6Demo.iocxml.mapDI.Course">
        <property name="cName" value="SE"></property>
    </bean>
</beans>
<!--Student{sid='201250002', sName='zy', teacherMap={001=Teacher{tid='001', tName='刘钦'}, 002=Teacher{tid='002', tName='刘嘉'}}, courses=[Course{cName='OS'}, Course{cName='SE'}]}-->

4. 引入外部属性文件(以mysql数据库连接为例)

  • 当外部配置发生改变时,只需修改外部属性文件,而无需对代码直接进行修改

  • 数据库表准备

    mysql> select * from bill;
    +----+------+--------+---------+----------+
    | id | type | amount | balance | info     |
    +----+------+--------+---------+----------+
    |  1 |    1 |   9000 |    9000 | 初始收入  |
    +----+------+--------+---------+----------+
    
  • 引入相关依赖(pom.xml)

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.31</version>
    </dependency>
    
  • 外部属性文件(jdbc.properties)

    jdbc.user=root
    jdbc.password=*************
    jdbc.url=jdbc:mysql://localhost:3306/mysql_demo1?serverTimezone=UTC
    jdbc.driver=com.mysql.cj.jdbc.Driver
    
  • 创建spring配置文件,引入context命名空间,引入外部属性文件,使用表达式完成注入( ${...}

    <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:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <!--完成配置信息注入-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.user}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="driverClassName" value="${jdbc.driver}"></property>
        </bean>
    </beans>
    
  • 连接测试

    public class JDBCTest {
        @Test
        public void test() throws SQLException {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml");
            DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class);
            testDatabase(dataSource);
        }
    
        private void testDatabase(DruidDataSource dataSource) throws SQLException {
            Connection connection = dataSource.getConnection();
            String sqlQuery = "SELECT * FROM bill;";
            PreparedStatement preparedStatement = connection.prepareStatement(sqlQuery);
            ResultSet resultSet = preparedStatement.executeQuery();
            System.out.println("id\tamount\tinfo");
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                int amount = resultSet.getInt("amount");
                String info = resultSet.getString("info");
                System.out.println(id + "\t" + amount + "\t" + info);
            }
    
            resultSet.close();
            preparedStatement.close();
            connection.close();
        }
    }
    

    输出:

    id amount info
    1 9000 初始收入

5. bean作用域scope

Spring中可以通过配置bean标签的scope属性,来指定bean的作用域范围

取值 含义 创建对象的时机
singleton(默认) 此bean对象在IoC容器中始终为单例 IoC容器初始化时
prototype 此bean对象在IoC容器中有多个实例 获取bean时
    <!--单实例-->
    <bean id="user1" class="org.spring6Demo.iocxml.bean_scope.User" scope="singleton">
        <property name="user_name" value="zy"></property>
    </bean>
    <!--多实例-->
    <bean id="user2" class="org.spring6Demo.iocxml.bean_scope.User" scope="prototype">
        <property name="user_name" value="zyx"></property>
    </bean>
public class BeanScopeTest {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-scope.xml");
        User user_singleton1 = context.getBean("user1", User.class);
        System.out.println(user_singleton1); // org.spring6Demo.iocxml.bean_scope.User@65987993
        User user_singleton2 = context.getBean("user1", User.class);
        System.out.println(user_singleton2); // org.spring6Demo.iocxml.bean_scope.User@65987993
    }

    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-scope.xml");
        User user_prototype1 = context.getBean("user2", User.class);
        System.out.println(user_prototype1); // org.spring6Demo.iocxml.bean_scope.User@4d1bf319
        User user_prototype2 = context.getBean("user2", User.class);
        System.out.println(user_prototype2); // org.spring6Demo.iocxml.bean_scope.User@6f53b8a
    }
}

6. bean生命周期

  • bean对象创建
  • 给bean对象设置相关属性
  • bean后置处理器 BeanPostProcessor(初始化前)
  • bean对象初始化(调用指定初始化方法 init-method=
  • bean后置处理器 BeanPostProcessor(初始化后)
  • bean对象完成创建
  • bean对象销毁(配置指定销毁方法 destroy-method=
  • IoC容器关闭

7. FactoryBean

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是其getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

package org.spring6Demo.iocxml.factoryBean;

import org.springframework.beans.factory.FactoryBean;

public class MyFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
<bean id="user" class="org.spring6Demo.iocxml.factoryBean.MyFactoryBean"></bean>
public class TestFactory {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-factorybean.xml");
        User user = (User) context.getBean("user");
        System.out.println(user); // org.spring6Demo.iocxml.factoryBean.User@18271936
    }
}

8. 自动装配

根据指定策略(bean标签的autowire属性),在IoC容器中匹配某一个bean,自动为匹配的bean中所依赖的类类型或接口类型属性赋值,完成自动装配

  • byType:根据类型匹配IoC容器中某个兼容类型的bean并为属性自动赋值

    • 若IoC容器中没有兼容类型可以用于赋值,则该属性不装配,值为null
    • 若IoC容器中有多个兼容类型可以用于赋值,则抛出异常NoUniqueBeanDefinitionException
    • 若IoC容器有多个此兼容类型的bean,则会抛出异常。此时不能使用autowire属性进行自动装配,而要在xml文件中使用<qualifier value="xxx">指定要注入的bean,或采用注解。
  • byName:将自动装配的属性的属性名作为bean的id在IoC容器中匹配相应的bean,进行赋值

<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="userController" class="org.spring6Demo.iocxml.autoConfig.controller.UserController" autowire="byType">
    </bean>
    <bean id="userService" class="org.spring6Demo.iocxml.autoConfig.service.UserServiceImpl" autowire="byName">
    </bean>
    <bean id="userDao" class="org.spring6Demo.iocxml.autoConfig.dao.UserDaoImpl">
    </bean>
</beans>
public class UserController {

    private UserService service;

    public void setService(UserService service) {
        this.service = service;
    }

    public void addUser() {
        System.out.println("UserController::addUser()");
        this.service.addUserService();
    }
}

public class UserServiceImpl implements UserService{
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUserService() {
        System.out.println("UserServiceImpl::addUserService()");
        this.userDao.addUserDao();
    }
}

public class UserDaoImpl implements UserDao{
    @Override
    public void addUserDao() {
        System.out.println("UserDaoImpl::addUserDao()");
    }
}
public class AutoConfigTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");
        UserController userController = context.getBean("userController", UserController.class);
        userController.addUser();
    }
}

输出:

UserController::addUser()
UserServiceImpl::addUserService()
UserDaoImpl::addUserDao()

E. 基于注解管理Bean

注解(Annotation)是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。我们可以使用注解来实现自动装配,简化Spring的xml配置。

1. 开启组件扫描

Spring默认不使用注解装配Bean,因此我们需要在Spring的xml配置中,通过context.component-scan元素开启Spring Beans的自动扫描功能。开启此功能后,Spring会默认自动从扫描指定的包(base-package属性设置)及其子包下的所有类,如果类上使用了@Component注解,就将该类装配到容器中。

<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 base-package="org.spring6Demo"></context:component-scan>
</beans>
  • <context:component-scan> 标签中,可以通过 <context:exclude-filter> 指定组件扫描排除规则,通过 <context:include-filter> 指定在原有组件扫描规则上追加的规则

  • type属性值:

    • annotation:根据注解排除,expression中设置要排除的注解的全类名
    • assignable:根据类型排除,expression中设置要排除的类型的全类名
  • 要设置include-filter仅扫描指定组件,必须设置use-default-filters属性为false以关闭默认扫描规则(默认扫描规则为扫描指定包下所有类)

2. 使用注解定义bean

以下注解可以直接标注在Java类上,将他们定义为Spring Bean

注解 说明
@Component 该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应类上即可。
@Repository 该注解用于将数据访问层(Dao层)的类标识为Spring中的Bean,其功能与@Component相同。
@Service 该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
@Controller 该注解通常作用在控制层(如SpringMVC 的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。
@Component(value = "user") // <bean id="user" ... >  (value缺省为类名首字母小写)
public class User {
}

@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    User user = context.getBean(User.class);
    System.out.println(user); // org.spring6Demo.bean.User@5a37d3ed
}

3. 依赖注入:@Autowired

单独使用@Autowired注解时,默认根据类型装配(byType)

  • 该注解可以标注于:构造方法上、方法上、形参上、属性上、注解上

  • 在注入时,默认要求被注入的bean必须存在,否则报错(默认required=true)

3.1 属性注入

将@Autowired注解直接标注在属性上,此时会自动根据类型找到相应对象并完成注入,无需像xml管理bean时一样需要声明setter。此种注入方法基于 setter 方法的隐式调用,Spring 会为带有 @Autowired注解的属性生成一个 setter 方法(如果还没有定义的话),并在创建 bean 时调用这个 setter 方法来完成注入。

@Controller
public class UserController {
    // 属性注入
    @Autowired
    private UserService userService;
    public void add() {
        System.out.println("UserController.add()");
        userService.add();
    }
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public void add() {
        System.out.println("UserServiceImpl.add()");
        userDao.add();
    }
}

@Repository
public class UserDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("UserDaoImpl.add()");
    }
}

public class AutoUserTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserController userController = context.getBean("userController", UserController.class);
        userController.add();
    }
// 输出:
// UserController.add()
// UserServiceImpl.add()
// UserDaoImpl.add()
}
3.2 set注入

可以将@Autowired注解标注在属性的setter上,此时会自动进行注入

@Controller
public class UserController {
    private UserService userService;
    // set注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void add() {
        System.out.println("UserController.add()");
        userService.add();
    }
}
3.3 构造注入

通过在构造方法上使用@Autowired注解来实现构造方法的自动装配。当使用@Autowired注解标注构造方法时,Spring容器会在初始化bean时自动调用带有@Autowired注解的构造方法,并将所需的依赖项注入到构造方法的参数中。

@Controller
public class UserController {
    private UserService userService;
    // 构造方法注入
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
    public void add() {
        System.out.println("UserController.add()");
        userService.add();
    }
}

当类中有多个构造方法时,只有一个构造方法可以被标记为@Autowired。如果有多个构造方法都使用了@Autowired注解,Spring容器将抛出异常,此时需要使用@Qualifier注解来指定要使用的构造方法(3.6)。

3.4 形参上注入
@Controller
public class UserController {
    private UserService userService;
    // 形参上注入
    public UserController(@Autowired UserService userService) {
        this.userService = userService;
    }
    public void add() {
        System.out.println("UserController.add()");
        userService.add();
    }
}
3.5 单一构造函数,无注解

当只有一个有参数构造函数时,@Autowired注解可以省略

3.6 @Autowired与@Qualifier 联合注解

当Spring容器中存在多个相同类型的Bean时,自动装配可能会产生歧义;或即使只有一个Bean符合依赖注入的要求,开发者也可能希望更精确地控制Bean的注入。此时需要使用@Qualifier注解明确指定要注入的Bean (的名称 value)。

@Repository
public class UserDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("UserDaoImpl.add()");
    }
}

@Repository
public class UserRedisDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("UserRedisDaoImpl.add()");
    }
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier(value = "userRedisDaoImpl")
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("UserServiceImpl.add()");
        userDao.add();
    }
}

4. 依赖注入:@Resource

@Resource注解也可以完成属性注入,其与@Autowired注解区别为:

  • @Resource注解是JDK扩展包中的,属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)而@Autowired注解是Spring框架自己的。

  • @Resource注解默认根据名称装配byName,未指定name时使用属性名作为name,通过name找不到的话会自动启动通过类型byType装配。@Autowired注解默认根据类型装配byType,如果想根据名称装配则需要配合@Qualifier注解一起使用。

  • @Resource注解用在属性上、setter方法上。@Autowired注解用在属性上、setter方法上、 构造方法上、构造方法参数上。

4.1 引入依赖
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
4.2 根据name注入
@Controller("resourceUserController")
public class UserController {
    @Resource(name = "userService1")
    private UserService userService;

    public void add() {
        System.out.println("UserController.add()");
        userService.add();
    }
}

@Service("userService1")
public class UserServiceImpl implements UserService {
    @Resource(name = "userDao1")
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("UserServiceImpl.add()");
        userDao.add();
    }
}

@Repository("userDao1")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("UserDaoImpl.add()");
    }
}
4.3 name未知时注入

已知name时会直接根据name寻找匹配的bean,未知时则按类型寻找进行装配

@Controller("resourceUserController")
public class UserController {
    @Resource
    private UserService userService;

    public void add() {
        System.out.println("UserController.add()");
        userService.add();
    }
}

5. 全注解开发

不再使用Spring配置文件(bean.xml),而是使用配置类代替配置文件

  • 配置类

    @Configuration
    @ComponentScan("org.spring6Demo.resource")
    // <context:component-scan base-package="org.spring6Demo.resource"></context:component-scan>
    public class SpringConfig {
    }
    
  • 测试类

    public class ResourceConfigUserTest {
        @Test
        public void test(){
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //
            UserController controller = context.getBean(UserController.class);
            controller.add();
        }
    }
    
posted @   CodingSaltFish  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示