1 装配的概念

1.1 Bean 装配的概念

指在 Spring 容器中把 Bean 组装到一起,前提是容器需要知道 Bean 的依赖关系,如何通过依赖注入来把它们装配到一起。

1.2 Bean 自动装配的概念

Spring 容器可以在不使用 <constructor-arg><property> 元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。

1.3 Bean 自动装配的局限性

自动装配的局限性是:

  • 覆盖的可能性:仍然可以使用 <constructor-arg><property> 设置指定依赖项,从而覆盖自动装配。
  • 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String 字符串,和类。
  • 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

2 基于 XML 的装配

通过 set 方法注入;通过构造器注入 (通过 index 或 type 设置参数);静态工厂注入;实例工厂注入。

2.1 属性注入

2.1.1 简单类型属性注入

<property name = "age" value = "">

该语句会调用类中的 setAge 方法,并将 value 作为参数传入进去。Name 值为 email,就调用 setEmail 方法。也可以使用注解来设置 set 方法。

需要注意,仅仅会调用 setAge 方法,对于方法内部如何实现不关心。即,set 方法不赋值,那么对应值就是 null。类中没有 email 属性,但是有 setEmail 方法,也是会调用 setEmail 方法。如果没有对应的 set 方法,那么会报错。

SetAge 方法必须只有一个参数,且 value 值可以转换成传入参数类型。

<bean id="myStudent" class="com.bjpowernode.ba01.Student" >
	<property name="name" value="李四" /><!--setName("李四")-->
	<property name="age" value="22" /><!--setAge(21)-->
	<property name="email" value="lisi@qq.com" /><!--setEmail("lisi@qq.com")-->
</bean>

2.1.2 构造器装配 Bean

<constructor-arg name="myage" value="20" />

  • name: 表示构造方法的形参名
  • index : 表示构造方法的参数的位置,下标从 0 开始。Name 和 index 都不存在时,标签顺序为参数顺序。
  • value :构造方法的形参类型是简单类型的,使用 value
  • ref :构造方法的形参类型是引用类型的,使用 ref,属性值为其他 bean 对象的 ID,如果不存在 or 存在多个,那么将报错。
<!--使用name属性实现构造注入-->
<bean id="myStudent" class="com.bjpowernode.ba03.Student" >
 <constructor-arg name="myage" value="20" />
 <constructor-arg name="mySchool" ref="myXueXiao" />
 <constructor-arg name="myname" value="周良"/>
</bean>
<!--使用index属性-->
<bean id="myStudent2" class="com.bjpowernode.ba03.Student">
 <constructor-arg index="1" value="22" />
 <constructor-arg index="0" value="李四" />
 <constructor-arg index="2" ref="myXueXiao" />
</bean>
<!--省略index-->
<bean id="myStudent3" class="com.bjpowernode.ba03.Student">
 <constructor-arg value="张强强" />
 <constructor-arg value="22" />
 <constructor-arg ref="myXueXiao" />
</bean>

2.1.3 引用类型属性注入

当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。Ref 的值必须为某 bean 的 id 值。

<bean id="myStudent" class="com.bjpowernode.ba02.Student" >
	<property name="name" value="李四" />
	<property name="age" value="26" />
	<!--引用类型-->
	<property name="school" ref="mySchool" /><!--setSchool(mySchool)-->
</bean>
 
<!--声明School对象-->
<bean id="mySchool" class="com.bjpowernode.ba02.School">
	<property name="name" value="北京大学"/>
	<property name="address" value="北京的海淀区" />
</bean>

另一种方式 —— 内部 Bean

<bean id="studentFour" class="com.atguigu.spring6.bean.Student">
    <property name="id" value="1004"></property>
    <property name="name" value="赵六"></property>
    <property name="age" value="26"></property>
    <property name="sex" value="女"></property>
    <property name="clazz">
        <!-- 在一个bean中再声明一个bean就是内部bean -->
        <!-- 内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性 -->
        <bean id="clazzInner" class="com.atguigu.spring6.bean.Clazz">
            <property name="clazzId" value="2222"></property>
            <property name="clazzName" value="远大前程班"></property>
        </bean>
    </property>
</bean>

2.1.4 特殊值处理

<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>
<!-- null值 -->
<property name="name">  <null />  </property>
<!-- 下面这种是 “null” 字符串 -->
<property name="name" value="null"></property
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a &lt; b"/>
 
 <property name="expression">
     <!-- 解决方案二:使用CDATA节 -->
     <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
     <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
     <!-- 所以CDATA节中写什么符号都随意 -->
     <value><![CDATA[a < b]]></value>
 </property>

2.1.5 集合属性注入

public class User {
	 private List<GirlFriend> lists;
	 private Set<GirlFriend> sets;
	 private Map<String, GirlFriend> maps;
	 private Properties properties;
	 private String[] array;
	 //getter、setter、toString方法省略......
}

XML 配置如下:
<ref bean=””><value >…</value> 来表示集合中的引用类型和简单类型。
<entry key="..." value-ref="..."/> 来表示 Map 中的属性
<prop key="k1">v1</prop> 来表示 Properties 中的属性
如果是 Set 的话,所有重复的值都会被忽略掉。

<bean id="compactDisc" class="soundsystem.BlankDisc">
	<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
	<constructor-arg value="The Beatles" />
	<constructor-arg><null/></constructor-arg>  <!--传递一个 null -->
</bean>
 
<!--实例化User-->
<bean id="user2" class="com.thr.pojo.User">
	<!--注入List集合-->
	<property name="lists">
		<list>
			<ref bean="girlFriend1"/>
			<ref bean="girlFriend2"/>
			<ref bean="girlFriend3"/>
		</list>
	</property>
	<!--注入Set集合-->
	<property name="sets">
		<set>
			<ref bean="girlFriend1"/>
			<ref bean="girlFriend2"/>
			<ref bean="girlFriend3"/>
		</set>
	</property>
	<!--注入Map集合-->
	<property name="maps">
		<map>
			<entry key="正牌女友" value-ref="girlFriend1"/>
			<entry key="备胎1" value-ref="girlFriend2"/>
			<entry key="备胎2" value-ref="girlFriend3"/>
		</map>
	</property>
	<!--注入Properties-->
	<property name="properties">
		<props>
			<prop key="k1">v1</prop>
			<prop key="k2">v2</prop>
		</props>
	</property>
	<!--注入数组-->
	<property name="array">
		<array>
			<value>value1</value>
			<value>value2</value>
			<value>value3</value>
		</array>
	</property>
</bean>

2.1.6 命名空间装配 Bean

Spring 3.0 命名空间

2.2 属性自动注入

Spring 容器中给我们提供了完成 Bean 之间的自动装配的功能(但是只针对对象类型的自动装配),这样的好处就是有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量,因为在稍微大一点的项目中,一个被引用的 Bean 的 ID 改变了,那么需要修改所有引用了它的  ID 。Spring 框架默认是不支持自动装配的,可以使用 Spring 的配置文件中< bean >元素的 autowire 属性为一个 bean 定义指定自动装配模式。其中<bean>元素中的 autowire 属性有 5 个可选值,如下:

属性描述
no默认的设置,表示不启用自动装配,需要我们手动通过”ref”属性手动完成装配
byName通过属性名称自动装配,如果一个 JavaBean 中的属性名称与 Bean 的 id 相同,则自动装配这个 Bean 到 JavaBean 的属性中。Spring 会查找该 JavaBean 中所有的 set 方法名,获得将 set 去掉并且首字母小写的字符串,然后去 Spring 容器中寻找是否有此字符串名称 id 的 Bean。如果有则就注入,如果没有则注入动作将不会执行
byType通过属性类型自动装配。Spring 会在容器中查找 JavaBean 中的属性类型与 Bean 的类型一致的 Bean,并自动装配这个 Bean 到 JavaBean 的属性中,如果容器中包含多个这个类型的 Bean,Spring 将抛出异常。如果没有找到这个类型的 Bean,那么注入动作将不会执行
constructor类似于 byType,也是通过类型自动装配,但是它是通过构造方法的参数类型来匹配。Spring 会寻找与该 JavaBean 构造方法的各个参数类型相匹配的 Bean,然后通过构造函数注入进来。如果在 Spring 容器中没有找一个构造函数参数类型的 Bean,则会报错
autodetect表示在 constructor 和 byType 之间自动的选择注入方式 (spring 5. X 已经没有了)。首先尝试通过 constructor 来自动装配,如果它不执行,则 Spring 尝试通过 byType 来自动装配
default由上级标签 beans 的 default-autowire 属性确定

引用类型属性也可以不用在配置文件中显式的注入,可以通过为 <bean> 标签设置 autowire 属性值,就可以进行隐式自动注入(默认是不自动注入引用类型属性)。

隐式自动注入分为两种:

  1. autowire = "byName" :根据名称自动注入
  2. autowire = "byType" :根据类型自动注入

2.2.1 byName 方式自动注入

Java 类中 set 方法的后缀和 Spring 容器中(配置文件)<bean> 的 id 名称一样,且数据类型是一致的。

public class Student {
	private String name;
	// 声明一个引用类型
	private School school;
	// set方法的后缀与<bean>对象的id相同,因此调用setSchool2方法
	public void setSchool2(School sc){}
}
<bean id="myStudent" class="com.bjpowernode.ba04.Student" autowire="byName">
	 <property name="age" value="26" />
</bean>
<bean id="school2" class="com.bjpowernode.ba04.School">
	 <property name="name" value="清华大学"/>
</bean>

2.2.2 byType 方式自动注入

要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即,要么相同,要么是子类 or 实现类。但是同源的被调用 bean 只能有一个。多于一个,容器就不知道匹配哪个。

public class Student {
	private String name;
	private int age;
	//声明一个引用类型
	private School school;
	//private School school2;
}
<!--byType-->
<bean id="myStudent" class="com.bjpowernode.ba05.Student" autowire="byType">
	<property name="name" value="张飒" />
</bean>
 
<!--下面只能声明一种,要不只声明父类/接口,要不只声明子类/实现类-->
<!--否则student bean 会报错,提示有两种school供选择-->
<!--声明School对象-->
<bean id="mySchool" class="com.bjpowernode.ba05.School">
	<property name="name" value="人民大学"/>
</bean>
 
<!--声明School的子类-->
<!--<bean id="primarySchool" class="com.bjpowernode.ba05.PrimarySchool">
 <property name="name" value="北京小学" />
 <property name="address" value="北京的大兴区" />
</bean>-->

2.2.3 constructor 装配

2.2.4 default 装配

Default 装配表示由父级标签 beans 的 default-autowire 属性来配置。如果 beans 标签上设置了 default-autowire 属性,那么 default-autowire 属性会统一配置当前 beans 中的所有 bean 的自动装配方式。

image

  • 如果子标签<bean>没有单独的设置 autowire 属性,那么将采用父标签的 default-autowire 属性的模式。
  • 如果子标签<bean>单独设置了 autowire 属性,则采用自己的模式。

2.2.5 Bean 自动装配的补充

①、上述的讲到 byType 和 constructor 装配是支持数组和强类型集合(即指定了集合元素类型)。如 bean A 有个属性定义是 List<Foo>类型,Spring 会在容器中查找所有类型为 Foo 的 bean,注入到该属性。记住是 Foo,不是 List。另外如果集合是 Map 集合,那么 Map 的 key 必须是 String 类型,Spring 会根据 value 的类型去匹配。例如有属性 bean A 中有一个属性为 Map<String, Foo> p,容器中有 bean B 和 C 类型均为 Foo,那么 A 实例化完成后,p 属性的值为:{“B”:B 的实例对象,“C”:C 的实例对象}。

②、虽然 autowrie 给我们带来配置的便利性,但是也有缺点,比如会导致 bean 的关系没那么显而易见,所以用 autowire 还是 ref 还是需要根据项目来决定。

③、autowire-candidate:前面我们说到配置有 autowire 属性的 bean,Spring 在实例化这个 bean 的时候会在容器中查找匹配的 bean 对 autowire bean 进行属性注入,这些被查找的 bean 我们称为候选 bean。作为候选 bean,我凭什么就要被你用,老子不给你用。所以候选 bean 给自己增加了 autowire-candidate=“false”属性(默认是 true),那么容器就不会把这个 bean 当做候选 bean 了,即这个 bean 不会被当做自动装配对象。同样,标签可以定义 default-autowire-candidate=“false”属性让它包含的所有 bean 都不做为候选 bean。我的地盘我做主。

2.3 引入外部配置文件

2.3.1 引入 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:c="<http://www.springframework.org/schema/c>"
  xsi:schemaLocation="<http://www.springframework.org/schema/beans>
  <http://www.springframework.org/schema/beans/spring-beans.xsd>">
	<!-- 引入 JavaConfig -->
  <bean class="soundsystem.CDConfig" />
  <!-- 引入 XML -->
  <import resource="cdplayer-config.xml" />
  <import resource="classpath:ba06/spring-student.xml" />
	<import resource="classpath:ba06/spring-*.xml" />
</beans>

2.3.2 引入 properties 配置文件

jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

在 Spring 配置文件中添加 Context 名称空间:

<?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:property-placeholder location="classpath:jdbc.properties"/>
 
	<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
	    <property name="url" value="${jdbc.url}"/>
	    <property name="driverClassName" value="${jdbc.driver}"/>
	    <property name="username" value="${jdbc.user}"/>
	    <property name="password" value="${jdbc.password}"/>
	</bean>
</beans>

2.4 FactoryBean

35.容器:IoC-基于 XML 管理 Bean-FactoryBean_哔哩哔哩_bilibili

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

将来我们整合 Mybatis 时,Spring 就是通过 FactoryBean 机制来帮我们创建 SqlSessionFactory 对象的。

② 创建类 UserFactoryBean

 package com.atguigu.spring6.bean;
 public class UserFactoryBean implements FactoryBean<User> {
     @Override
     public User getObject() throws Exception {
         return new User();
     }
 ​
     @Override
     public Class<?> getObjectType() {
         return User.class;
     }
 }
 

③ 配置 bean

 <bean id="user" class="com.atguigu.spring6.bean.UserFactoryBean"></bean>

④ 测试

 @Test
 public void testUserFactoryBean(){
     //获取IOC容器
     ApplicationContext ac = new ClassPathXmlApplicationContext("spring-factorybean.xml");
     User user = (User) ac.getBean("user");
     System.out.println(user);
 }

3 基于注解的装配

3.1 使用注解创建 Bean 对象

3.1.1 @Component

  • 属性 value:对象的名称 ID,唯一
//@Component(value="myStudent")
//@Component("myStudent") value 字段可以省略,直接指定 value 值
//@Component  不指定 value 属性, bean 的 id 是类名的首字母小写
public class Student { }

Spring 中和 @Component 功能一致,创建对象的注解还有:

  • @Repository (用在持久层类的上面) : 放在 dao 的实现类上面,表示创建 dao 对象,dao 对象是能访问数据库的。
  • @Service (用在业务层类的上面):放在 service 的实现类上面,创建 service 对象,service 对象是做业务处理,可以有事务等功能的。
  • @Controller (用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接受用户提交的参数,显示请求的处理结果。

使用 @Component 创建对象后,还需要增加组件扫描来放到 Spring 容器中。当前有两种方案:

  1. 在配置文件中增加组件扫描器,扫描哪里使用了 @Component 注解。
  2. 扫描组件注解 @ComponentScan - @ComponentScan

3.1.2 组件扫描器

在配置文件中增加组件扫描器,扫描哪里使用了 @Component 注解。
另外参考 @ComponentScan 关于 filter 等其他属性配置。

  1. 多组组件扫描器,扫描不同的包
  2. 使用分隔符 (;,空格(不推荐)) 分割多个包名
  3. 指定父包,但是会使得扫描包数目增加,启动时间变慢
<!--指定多个包的三种方式-->
<!--第一种方式:使用多次组件扫描器,指定不同的包-->
<context:component-scan base-package="com.bjpowernode.ba01"/>
<context:component-scan base-package="com.bjpowernode.ba02"/>
<!--第二种方式:使用分隔符 ; 或 , 分隔多个包名-->
<context:component-scan base-package="com.bjpowernode.ba01;com.bjpowernode.ba02" />
<!--第三种方式:指定父包-->
<context:component-scan base-package="com.bjpowernode" />

3.1.3 扫描组件注解

(通过这种方法可以实现 Spring 全注解开发,不用再写 Spring 配置文件)

在任意一个类上添加 @ComponentScan 注解即可,这里用一个新建的 JavaConfig 来告诉 Spring 容器怎么扫描,就是指定扫描哪个包。

package com.thr.pojo;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan
public class PojoConfig { }

@ComponentScan 注解如果不指定扫描哪个包的话,默认是扫描当前类的包路径 (这里是 com. Thr. Pojo),将当前包下的所有被 @Component 注解的 Bean 对象加入到 Spring 容器。

@ComponentScan 两个属性配置项 basePackages 和 basePackageClasses :

  • Value:等价于 basePackages。
  • BasePackages:表示扫描的包路径,单个路径用 String,多个路径用 String 数组?。
  • BasePackageClasses:表示扫描的类,也可以配置多个。

通过上述配置项来扫描其他包路径下的 Bean 对象。

package com.thr.config;
import org.springframework.context.annotation.ComponentScan;
@Configuration
//@ComponentScan(basePackages = "com.thr.pojo")
//@ComponentScan(basePackages={"soundsystem", "video"})
//@ComponentScan(basePackageClasses =User.class)
@ComponentScan(basePackages = "com.thr.pojo",basePackageClasses = User.class)
public class PojoConfig { }

3.2 属性注入

简单类型属性通过 @Value 注解进行注入。

@Value("..")
简单类型的属性赋值。可以在属性定义上面,也可以在 set 方法上面。Value 值为要注入的值。标记在属性上时,setter 方法可以省略。

下述方法中,先创建 Student 对象。创建对象时,读取配置文件中的 myname 和 myage 数据并赋值。之后,调用 setAge 方法,传入注解中提供的 value 值。即,最后 age 为 30.

@Component("myStudent")
public class Student {
	//@Value("李四")
	@Value("${myname}") //使用属性配置文件中的数据
	private String name;
	@Value("${myage}") //使用属性配置文件中的数据
	private Integer age;
	@Value("30")
	public void setAge(Integer age) {
		System.out.println("setAge:"+age);
		this.age = age;
	}
}

Spring 配置文件

<!--添加组件扫描器-->
<context:component-scan base-package="com.bjpowernode.ba02" />
<!--加载属性配置文件-->
<context:property-placeholder location="classpath:test.properties" />

3.3 属性自动注入

3.3.1 @Autowired 自动注入

在构造器方法、Setter 方法上增加 @Autowired 注解,那么 Spring 就可以从容器中自动搜索并装配到对应位置。注解也可以直接配置到引用类型属性上,但是官方并不推荐

@Autowired 注解代码:

package org.springframework.beans.factory.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

注解可以使用的位置:构造方法上、方法上、形参上、属性上、注解上。

@Service
public class UserServiceImpl implements UserService {
	// 构造器注入
    @Autowired
    public UserServiceImpl(UserDao userDao) { this.userDao = userDao; }
	// 形参注入
	public UserServiceImpl(@Autowired UserDao userDao) { this.userDao = userDao; }
	// 属性注入
    @Autowired
    private UserDao userDao;
	// set 注入
    @Autowired
    public void setUserDao(UserDao userDao) { this.userDao = userDao; }

@Autowired 还有一个属性 required,默认值为 true,表示当自动装配失败后,会终止程序运行。设置为 false,自动装配失败后,对应属性为 null。

使用 @Autowired 注解来自动装配指定的 Bean。在使用@Autowired 注解之前需要在 Spring 配置文件进行配置,<context:annotation-config />

在启动 spring IoC 时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,当容器扫描到@Autowied、@Resource 或@Inject 时,就会在 IoC 容器自动查找需要的 Bean,并装配给该对象的属性。在使用@Autowired 时,首先在容器中查询对应类型的 Bean:

  • 如果查询结果刚好为一个,就将该 Bean 装配给@Autowired 指定的数据;
  • 如果查询的结果不止一个,那么@Autowired 会根据名称来查找;
  • 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用 required=false。

与 @Qualifier 注解配合,按照 byName 装配:

@Autowired(required = false)
@Qualifier("mySchool")
private School school;

疑问:

Spring 什么时候去根据 @Autowired 注解寻找 Bean 对象的依赖?

怎么去找 Bean 的依赖呢,查看所有 @Autowired 注解修饰的方法或字段?

会不会存在冲突呢,如下面同时有构造器+Setter 注入?

如果有多个 Setter 注入呢?有没有顺序呢?

3.3.2 JDK 注解 @Resource 自动注入

@Resource 注解也可以完成属性注入。

@Autowired 和@Resource 都可用于:属性上、setter 方法上,都可以用来装配 bean。

@Autowired 和@Resource 之间的区别
一是,@Autowired 默认按类型装配,属于 Spring 注解;@Resource 默认按照名称装配,属于 Java 注解。
二是,@Autowired 默认按类型装配;实现按名称装配需要@Autowired + @Qualifier 注解;默认对象必须存在,若可以为 null,required 属性为 false。
三是,@Resource 根据 name 和 type 属性的有无,四种不同的装配方式。

@Resource 注解属于 JDK 扩展包,所以不在 JDK 当中,需要额外引入以下依赖:【如果是 JDK 8 的话不需要额外引入依赖。高于 JDK 11 或低于 JDK 8 需要引入以下依赖。补充:JDK 11 将 javax. Annotation 这个包移除了,如果想继续使用可以通过 maven 或者其他方式导入。

 <dependency>
     <groupId>jakarta. Annotation</groupId>
     <artifactId>jakarta. Annotation-api</artifactId>
     <version>2.1.1</version>
 </dependency>

@Resource 注解:默认 byName 注入,没有指定 name 时把属性名当做 name,根据 name 找不到时,才会 byType 注入。ByType 注入时,某种类型的 Bean 只能有一个。

使用范例:

@Service
Public class UserServiceImpl implements UserService {
	// 指定 name
    @Resource (name = "myUserDao")
    Private UserDao myUserDao;
    // 根据属性名作为 name
    @Resource
    Private UserDao myUserDao;
}

4 基于 Java 类的装配

如果要使用外部包的 Java 对象,将其简单修改后注入到 Spring 容器时,自动化装配就无法使用。此时,推荐采用 Java 代码装配 Bean 对象,涉及到 @Configuration 与 @Bean 注解。

又或者是,想要创建同一个类型的不同属性的 Java Bean,也可以使用这种方法来完成。

4.1 @Configuration 与 @Bean 介绍

@Configuration :标注在类上,作用:配置 Spring 容器 (应用上下文),被它修饰的类表示可以使用 Spring IoC 容器作为 bean 定义的来源。

@Bean :标注在方法上,作用:注册 bean 对象,被标记的方法的返回值会作为 bean 被加入到 Spring IoC 容器之中,bean 的名称默认为方法名。@Bean 的配置项中包含 5 个配置项:

  • Value:等同于下面的 name 属性
  • Name:相当于 bean 的 id,它是一个字符串数组,允许配置多个 BeanName
  • autowire:标志是否是一个引用的 Bean 对象,默认值是 Autowire.NO
  • InitMethod:自定义初始化方法
  • DestroyMethod:自定义销毁方法

4.2 @Configuration 与 @Bean 举例

Java 代码装配 Bean 使用示例如下:

@ComponentScan (basePackages = "com. Thr. Pojo")
@Configuration
Public class PojoConfig {
	 Public PojoConfig () { 		 System. Out. Println ("PojoConfig 容器初始化成功...");	 }
 
	 //实例化 User 对象并且装配值
	 @Bean (name = "user")
	 Public User user (){
		 User user = new User ();
		 User. SetUserId (2021);
		 User. SetUserName ("菜逼小唐");
		 Return user;
	 }
 
	 //实例化 GirlFriend 对象并且装配值
	 @Bean (name ="girlFriend" )
	 Public GirlFriend girlFriend (){
		 GirlFriend girlFriend = new GirlFriend ();
		 GirlFriend. SetGirlName ("陈美丽");
		 Return girlFriend;
	 }
		//实例化 GirlFriend 对象,创建同类不同属性的对象
	 @Bean (name ="girlFriend 1" )
	 Public GirlFriend girlFriend 1 (){
		 GirlFriend girlFriend 1 = new GirlFriend ();
		 GirlFriend 1. SetGirlName ("何美丽");
		 Return girlFriend 1;
	 }
		// 方法可以是有参方法,此时 Spring 会自动寻找对象注入到方法的入参中
		@Bean
		Public CDPlayer cdPlayer (CompactDisc compactDisc) {
		  Return new CDPlayer (compactDisc);
		}
}
 

如果要获取它们,我们可以使用 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类进行扫描,并用于构建 bean 定义,初始化 Spring 容器。

Public class SpringTest 1 {
 Public static void main (String[] args) {
	 //1. 初始化 Spring 容器,通过注解加载
	 ApplicationContext applicationContext = new AnnotationConfigApplicationContext (PojoConfig. Class);
	 //2. 通过容器获取实例
	 User user = applicationContext. GetBean ("user", User. Class);
	 //3. 调用实例中的属性
	 System. Out. Println (user. GetUserId ()+"----"+
	   User. GetUserName ()+"----"+
	   User. GetUserAge ()+"----"+
	   User. GetGirlFriend ());
	 }
}

4.3 @Component 和 @Bean 的区别是什么?

  1. @Component 注解作用于类,而 @Bean 注解作用于方法。
  2. @Component 通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 Bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 Bean, @Bean 告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
  3. @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 Bean。比如当我们引用第三方库中的类需要装配到 Spring 容器时,则只能通过 @Bean 来实现

4.4 @import 注解

4.5 JavaConfig 导入 JavaConfig/XML 配置

JavaConfig 导入 JavaConfig 配置

PojoConfig 类中导入 GirlFriendConfig 配置,也可以如 CDPlayerConfig 一次导入多个配置。

@Configuration
Public class GirlFriendConfig{...}
 
@ComponentScan (basePackages = "com. Thr. Pojo")
@Configuration
@Import (GirlFriendConfig. Class)
Pubiic class PojoConfig {...}
 
@Configuration
@Import ({CDPlayerConfig. Class, CDConfig. Class})
Public class CDPlayerConfig {...}

JavaConfig 导入 XML 配置

假设 BlankDisc 定义在名为 cdconfig. Xml 的文件中,该文件位于根类路径下,那么可以修改 SoundSystemConfig,让它使用 @ImportResource 注解,如下所示:

Package soundsystem;
 
Import org. Springframework. Context. Annotation. Configuration;
Import org. Springframework. Context. Annotation. Import;
Import org. Springframework. Context. Annotation. ImportResource;
 
@Configuration
@Import (CDPlayerConfig. Class)
@ImportResource ("classpath: ")
Public class SoundSystemConfig { ... }