IoC是Spring的核心思想之一,常用的实现IoC的技术就是依赖注入
Beer:
package pojo;
public class Beer {
public void toDrink(){
System.out.println("喝啤酒");
}
}
Person(注入Beer对象):
package pojo;
public class Person {
private Beer beer;
public void setBeer(Beer beer) {
this.beer = beer;
}
public void drink(){
beer.toDrink();
}
}
application.xml(Spring配置文件):
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="pojo.Person">
<property name="beer" ref="beer"/>
</bean>
<bean id="beer" class="pojo.Beer"/>
</beans>
MyTest:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Person;
public class MyTest {
@Test
public void test(){
//ClassPathXmlApplicationContext解析配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
Person person = (Person) applicationContext.getBean("person");
person.drink();
}
}
这就是一个简单的Spring依赖注入的案例
依赖注入:由容器将对象注入到需要的地方,我们仅需要被动接受即可(案例中将Beer对象注入到Person类中)
这是常用的IoC的实现方法
Spring官网:Core详细介绍了依赖注入
后续一一解析
基于XML文件的依赖注入是最基础的注入方式
我们上面的案例就是基于XML文件的注入
需要了解Spring对应的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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="pojo.Person">
<property name="beer" ref="beer"/>
</bean>
<bean id="beer" class="pojo.Beer"/>
</beans>
我们点入该xsd约束的bean标签(ctrl+鼠标左键点击标签)
总之,bean标签表示的就是我们的对象
通过
将id=beer的bean注入到了person中
property标签中,name对应的是Person类的成员变量名,ref表示引用其他bean
如果只是一个简单属性可以通过Value值
这里的property标签就3个属性
这是一种setter注入
关于Spring.xml配置文件,有很多值得注意的细节,后续深入
通过constructor-arg
子标签构造器注入
通过property
子标签Setter注入
我们可以通过一个案例简单查看:
修改前面的Person类:
package pojo;
public class Person {
private Beer beer;
public Person() {
}
public Person(Beer beer) {
this.beer = beer;
System.out.println("有参构造方法");
}
public void setBeer(Beer beer) {
this.beer = beer;
System.out.println("Setter注入");
}
public void drink(){
beer.toDrink();
}
}
再运行
确实是Setter方法注入
然后修改一下xml中的配置:
<bean id="person" class="pojo.Person">
<constructor-arg name="beer" ref="beer"/>
</bean>
<bean id="beer" class="pojo.Beer"/>
ok,验证了上面的结论正确
property
标签是Setter方法注入constructor-arg
标签是有参构造方法注入从上面的案例的XML文件的配置,可以清晰的看出两个Bean之间的关系(不管是property
还是constructior-arg
)
表现了父子的依赖关系,显然,这很麻烦,类结构表现了依赖关系,xml中又要表现一次,而且当新增属性,又要多加子标签
XML提供了全局自动装配和局部自动装配,自动装配就是Spring自己根据类结构将表现bean的依赖关系
末尾加default-autowire=""
配置这样,整个beans标签内的bean都会自动装配
autowire=""
no:默认值,不开启自动装配
default:默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的
byName:Spring容器查看XML配置文件中autowire属性设置为byName的bean(全局或局部)。然后,它尝试将其属性与配置文件中相同名称定义的bean进行匹配并连接。如果找到匹配,它将注入这些bean。否则,bean将不会被注入
如上面案例中,byName可以注入,但当修改属性名就无法注入
原因是属性名不对,Setter方法名不对public void setBee(Beer bee)
,自然无法注入,可以通过在成员变量上加上标签@Qualifier自定义别名解决
Car继承体系:
package pojo.bytype;
interface Car {
}
class FirstCar implements Car{}
class SecondCar implements Car{}
Person类:
package pojo.bytype;
public class Person {
//自动注入,但是以byType方法,变量名需要对齐类名
private Car car;
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person{" +
"car=" + car +
'}';
}
}
在xml配置文件中:
<bean id="person" class="pojo.bytype.Person" autowire="byType"/>
<bean id="firstCar" class="pojo.bytype.FirstCar"/>
这样就可以运行成功,使用byType,不用关心实体类成员变量name与bean的id
但如果有两个同类型的bean就无法识别
<bean id="person" class="pojo.bytype.Person" autowire="byType"/>
<bean id="firstCar" class="pojo.bytype.FirstCar"/>
<bean id="secondCar" class="pojo.bytype.SecondCar"/>
因为我们在person类中的属性是Car car
,有两个指向car类型的bean,无法识别
小结:
标签添加属性default-autowire=""
标签添加属性autowire
基于注解的依赖注入,Spring提供了注解的方式简便XML配置
我们前面需要
指定特定的实体类,而通过在实体类上注解,Spring自动扫描装配即可代替
@Component、@Service、@Controller
等等,最基础的是@Component,其他的标记注解都是基于这个基础注解,这个注解表示类是Spring的组件bean,即会被Spring扫描成beanpackage pojo.bytype;
import org.springframework.stereotype.Component;
interface Car {
}
@Component
class FirstCar implements Car{}
class SecondCar implements Car{}
@Autowired
,类似与前面的自动装配,这个是自动注入<context:component-scan base-package="pojo.bytype"/>
的使用隐式启用 的功能。使用 时,通常无需包含 元素。
在使用注解时,需要在Spring中配置AnnotationBeanPostProcessor,如autowired注解需要
,当然,这是一种完整的做法
而
是隐式地向Spring容器注册AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor这4个BeanPostProcessor,也就是可以直接使用注解
运行:
@Test
public void test3(){
//ClassPathXmlApplicationContext解析配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
pojo.bytype.Person person = (pojo.bytype.Person) applicationContext.getBean("person");
System.out.println(person.toString());
}
那么是使用什么自动装配方式呢?byName、byType、constructor?
修改一下Car的名字private Car myCar;
很明显,不是byName
再运行:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'pojo.bytype.Car' available: expected single matching bean but found 2: firstCar,secondCar
明显,就是byType自动注入方式,但当我们把属性名该为对应的注入类的类名
可以注入,注入的确实是FirstCar
当byType失败时,会尝试byName,都失败就报错
如果不想改属性名,Spring还提供了别名注解@Qualifier("firstCar")
基于Java类的依赖注入
上面还是需要在XML配置文件中配置标签,可不可以完全抛弃XML文件呢?
基于Java类+注解可以实现(SpringBoot就是这种方式)
需要两个注解:
@Configuration
:表示这个Java类是Spring配置类,替代XML@ComponentScan(value = "pojo.bytype")
表示该配置类扫描哪个包,替代XML中的
基于Java的依赖注入步骤:
package pojo.bytype;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "pojo.bytype")
public class Config {
}
ClassPathXmlApplicationContext
类解析了,Spring提供了AnnotationConfigApplicationContext
解析注解配置文件@Test
public void test4(){
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
pojo.bytype.Person person = (pojo.bytype.Person) context.getBean("person");
System.out.println(person.toString());
}
这样,就完全不需要XML配置文件了
当然,如果不使用自动扫描,也可以在JavaConfig里配置Bean
package pojo.bytype;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public Car car(){
return new FirstCar();
}
@Bean
public Person person(){
return new Person();
}
}
一样可以注入
default-autowire=""
,局部自动装配autowire=""
@Component
注解,需要注入的地方添加@Autowire
(属性名、构造方法、Setter方法都可以),在XML配置文件中只需要标记需要扫描的包@Configuration
表示该Java类为配置类,代替XML @ComponentScan(value = "pojo.bytype")
表示扫描的包,测试类需要AnnotationConfigApplicationContext
类解析注解配置类学海无涯苦作舟
都看到这了,点个赞呗(^_−)☆