0%

spring基础-1

依赖查找、依赖注入

IoC的实现分为两种:依赖查找和依赖注入

依赖查找和依赖注入

依赖查找:创建的 Bean 都是不带属性的
byName
1
2
3
4
5
6
7
<bean id="person"    	class="com.linkedbear.spring.basic_dl.a_quickstart_byname.bean.Person"></bean>

public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("quickstart-byname.xml");
Person person = (Person) ctx.getBean("person");
System.out.println(person);
}
byType
1
2
3
4
5
6
7
8
9
10
11
12
<bean class="com.linkedbear.spring.basic_dl.b_bytype.bean.Person"></bean>

public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("quickstart-bytype.xml");
Person person = ctx.getBean(Person.class);//一次拿一个DemoDao的实现类
Map<String, DemoDao> beans = ctx.getBeansOfType(DemoDao.class);//一次拿多个DemoDao的实现类
System.out.println(person);
ObjectProvider<Dog> dogProvider = ctx.getBeanProvider(Dog.class);//延迟查找类
//假设容器中没有Dog的Bean,使用这种方式,运行 main 方法后发现并没有报错
//只有调用 dogProvider 的 getObject ,真正要取包装里面的 Bean 时,才会报异常。
//getIfAvailable ,它可以在找不到 Bean 时返回 null 而不抛出异常
}
通过接口与实现类
1
2
3
4
5
6
7
8
9
10
<bean class="com.linkedbear.spring.basic_dl.b_bytype.dao.impl.DemoDaoImpl"/>
//DemoDaoImpl是接口的实现类,在启动类 QuickstartByTypeApplication 中
//借助 BeanFactory 取出 DemoDao ,并打印 findAll 方法的返回数据
//其实是有注入的意思
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("quickstart-bytype.xml");
//BeanFactory 可以根据接口类型,找到对应的实现类。
DemoDao demoDao = ctx.getBean(DemoDao.class);
System.out.println(demoDao.findAll());
}
依赖注入:
xml配置注入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Person {
private String name;
private Integer age;
// getter and setter ......
}
public class Cat {
private String name;
private Person master;
// getter and setter ......
}

<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Person">
<property name="name" value="test-person-byset"/>
<property name="age" value="18"/>
</bean>
<bean id="cat" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Cat">
<property name="name" value="test-cat"/>
<!-- ref引用上面的person对象 -->
<property name="master" ref="person"/>
</bean>

public class QuickstartInjectBySetXmlApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("inject-set.xml");
Person person = ctx.getBean(Person.class);
System.out.println(person);
Cat cat = ctx.getBean(Cat.class);
System.out.println(cat);
}
}

output:
Person{name='test-person-byset', age=18}
Cat{name='test-cat', master=Person{name='test-person-byset', age=18}}
注解注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class QuickstartConfiguration {
//4.3.3之后可以直接写value,不需要再在xml中配置<bean id=" " />
@Bean(name = "aaa") //注解显式声明
public Person person() {
return new Person();
}
}
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("秦疆")
// 相当于配置文件中 <property name="name" value="秦疆"/>
public String name;
}

public class AnnotationConfigApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(QuickstartConfiguration.class);
Person person = ctx.getBean(Person.class);
System.out.println(person);
}
}
组件扫描

因为Bean可能需要添加很多,所以为了简化,就可以在类上添加@Component。如果不指定 Component的名称,它的默认规则是 “类名的首字母小写”(例如 Person 的默认名称是 personDepartmentServiceImpl 的默认名称是 departmentServiceImpl )。

1
2
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
xml启用组件扫描
1
<context:component-scan base-package="com.linkedbear.spring.annotation.c_scan.bean"/>

为了迎合咱在进行 Web 开发时的三层架构,它额外提供了三个注解:@Controller@Service@Repository ,分别代表表现层、业务层、持久层。这三个注解的作用与 @Component 完全一致,其实它们的底层也就是 @Component

xml引入注解(在 xml 中要引入注解配置,需要开启注解配置,同时注册对应的配置类)
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 开启注解配置 -->
<context:annotation-config />
<bean class="com.linkedbear.spring.annotation.d_importxml.config.AnnotationConfigConfiguration"/>
</beans>
依赖查找与依赖注入区别:
  • 作用目标不同
    • 依赖注入的作用目标通常是类成员
    • 依赖查找的作用目标可以是方法体内,也可以是方法体外
  • 实现方式不同
    • 依赖注入通常借助一个上下文被动的接收
    • 依赖查找通常主动使用上下文搜索

几种注入方式:

setter属性注入:
  • xml:
1
2
3
4
<bean id="person" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Person">
<property name="name" value="test-person-byset"/>
<property name="age" value="18"/>
</bean>
  • 注解:
1
2
3
4
5
6
7
@Bean
public Person person() {
Person person = new Person();
person.setName("test-person-anno-byset");
person.setAge(18);
return person;
}
构造器注入
  • xml
1
2
3
4
<bean id="person" class="com.linkedbear.spring.basic_di.b_constructor.bean.Person">
<constructor-arg index="0" value="test-person-byconstructor"/>
<constructor-arg index="1" value="18"/>
</bean>
  • 注解
1
2
3
4
@Bean
public Person person() {
return new Person("test-person-anno-byconstructor", 18);
}
注解式属性注入
@Component下的属性注入
1
2
3
4
5
6
7
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("秦疆")
// 相当于配置文件中 <property name="name" value="秦疆"/>
public String name;
}
外部配置文件引入-@PropertySource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//文件名:red.properties
red.name=red-value-byproperties
red.order=1

@Configuration
@PropertySource("classpath:basic_di/value/red.properties")
public class InjectValueConfiguration {
@Value("${red.name}")
private String name;
@Value("${red.order}")
private Integer order;
}

//上面情况与下面情况二选一即可
<bean class="com.linkedbear.spring.basic_di.c_value_spel.bean.Red">
<property name="name" value="${red.name}"/>
<property name="order" value="${red.order}"/>
</bean>
SpEL属性注入
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class Green {
@Value("#{'copy of ' + blue.name}")
private String name;
@Value("#{blue.order + 1}")
private Integer order;
}

<bean class="com.linkedbear.spring.basic_di.c_value_spel.bean.Green">
<property name="name" value="#{'copy of ' + blue.name}"/>
<property name="order" value="#{blue.order + 1}"/>
</bean>
自动注入:
xml中的autowire
1
2
3
4
5
6
7
8
9
<bean id="user" class="com.kuang.pojo.User" autowire="byName">
<property name="str" value="qinjiang"/>
</bean>
<bean id="user" class="com.kuang.pojo.User" autowire="byType">
//id可以删除
//使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。
//如果不唯一,会报不唯一的异常。
<property name="str" value="qinjiang"/>
</bean>
注解中的autowire
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class User {
@Autowired
private Cat cat;
//@Autowired是按类型自动转配的,不支持id匹配,即唯一
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Dog dog;
private String str;
//getter方法...
}

<context:annotation-config/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
注解中的@Qualifier
1
2
3
4
5
6
7
8
9
10
11
12
13
//@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
//@Qualifier不能单独使用。
<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

@Autowired //不加会报错
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
注解中的@Resource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
//其次再进行默认的byName方式进行装配;
//如果以上都不成功,则按byType的方式自动装配。
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

public class User {
//如果允许对象为null,设置required = false,默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
1
2
3
4
5
6
7
8
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>

@Resource
private Cat cat;
@Resource
private Dog dog;
//先进行byName查找,失败;再进行byType查找,成功。
一些问题:
@Autowired注入的原理逻辑

先拿属性对应的类型,去 IOC 容器中找 Bean ,如果找到了一个,直接返回;如果找到多个类型一样的 Bean , 把属性名拿过去,跟这些 Bean 的 id 逐个对比,如果有一个相同的,直接返回;如果没有任何相同的 id 与要注入的属性名相同,则会抛出 NoUniqueBeanDefinitionException 异常。

依赖注入的目的和优点?

首先,依赖注入作为 IOC 的实现方式之一,目的就是解耦,我们不再需要直接去 new 那些依赖的类对象(直接依赖会导致对象的创建机制、初始化过程难以统一控制);而且,如果组件存在多级依赖,依赖注入可以将这些依赖的关系简化,开发者只需要定义好谁依赖谁即可。

除此之外,依赖注入的另一个特点是依赖对象的可配置:通过 xml 或者注解声明,可以指定和调整组件注入的对象,借助 Java 的多态特性,可以不需要大批量的修改就完成依赖注入的对象替换(面向接口编程与依赖注入配合近乎完美)。

谁把什么注入给谁了?

由于组件与组件之间的依赖只剩下成员属性 + 依赖注入的注解,而注入的注解又被 SpringFramework 支持,所以这个问题也好回答:IOC 容器把需要依赖的对象注入给待注入的组件