Spring

Spring

Ioc控制反转

参考博客

DI(依赖注入)是实现Ioc的一种方法

Ioc就是将得到对象的方式反转,不是由我们自身主动创建(new)对象,而是将创建对象的过程交给spring容器完成,我们只负责从容器中得到(get)对象。

Ioc是Spring的核心。可以使用xml,注解的方式实现Ioc,也可以零配置实现。

Spring容器在初始化时会先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

用XML方式配置Bean时,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

配置文件Bean.xml

认识

bean.xml:

1
2
3
4
5
6
7
8
9
10
11
12
<?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就是java对象,由spring管理,id是外部获得对象的唯一id,class是实现类的包路,property中name是类内部变量,value是赋予name的值-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>

</beans>

Hello.java:

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
public class Hello {
private String name;
private String sz;
public Hello(){
System.out.println("无参构造方法");
}

public Hello(String name){
this.name = name;
System.out.println("有参构造方法name="+name);
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

public String getSz() {
return sz;
}
public void setSz(String sz) {
this.sz = sz;
}

public void show(){
System.out.println("Hello,"+ name );
}
}

测试:

1
2
3
4
5
6
7
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}

用法

在配置文件加载时,bean中管理的对象就已经初始化了。

  • 当没有定义任何构造方法时会默认调用无参构造方法

  • 有参构造方法最常用:

    1
    2
    3
    4
    5
    <!-- 根据参数名字设置 -->
    <bean id="hello" class="com.kuang.pojo.Hello">
    <!-- name指参数名 -->
    <constructor-arg name="name" value="cz"/>
    </bean>

id是bean的唯一标识符,如果bean中没有设置id值,那么name默认为标识符,如果设置了id,name就是别名,别名可设置多个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--bean就是java对象,由Spring创建和管理-->

<!--
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
ref用来传入引用
class是bean的全限定名=包名+类名
-->

<bean id="student" name="hello2 h2,h3;h4" class="com.kuang.pojo.Student">
<property name="sz" value="czczcz"/>
</bean>

<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="schatten.cz"/>
<property name="student" ref="student"/>
</bean>

<import resource="{path}/beans.xml"/> 多人import导入其他配置文件可以合并。

DI依赖注入

Bean注入

1. 注意点:这里的值是一个引用,ref

1
2
3
4
5
6
7
8
<bean id="addr" class="com.kuang.pojo.Address">
<property name="address" value="重庆"/>
</bean>

<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
</bean>

2. 数组注入

1
2
3
4
5
6
7
8
9
10
11
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</array>
</property>
</bean>

3. List注入

1
2
3
4
5
6
7
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
<value>爬山</value>
</list>
</property>

4. Map

1
2
3
4
5
6
<property name="card">
<map>
<entry key="中国邮政" value="456456456465456"/>
<entry key="建设" value="1456682255511"/>
</map>
</property>

5. set

1
2
3
4
5
6
7
<property name="games">
<set>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</set>
</property>

6. Null

1
<property name="wife"><null/></property>

7. Properties

1
2
3
4
5
6
7
<property name="info">
<props>
<prop key="学号">20190604</prop>
<prop key="性别"></prop>
<prop key="姓名">小明</prop>
</props>
</property>

拓展

User.java : 【注意:这里没有有参构造器!】

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
public class User {
private String name;
private int age;

public User(String name,int age){
this.name = name;
this.age = age;
}

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
  1. P注入(set方法注入) : 需要在头文件中加入约束文件
1
2
3
4
导入约束 : xmlns:p="http://www.springframework.org/schema/p"

<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
  1. c 注入(构造器注入) : 需要在头文件中加入约束文件
1
2
3
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
1
2
3
4
5
6
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}

Bean作用域

Bean自动装配

隐式的bean发现机制和自动装配

  1. 组件扫描
  2. 自动装配

自动装配xml配置(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>

<bean id="user" class="com.kuang.pojo.User">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="str" value="qinjiang"/>
</bean>
</beans>

user类拥有猫和狗两个宠物属性,猫狗都有一个sout方法。

byName:

按名称自动装配

1
<bean id="user" class="com.kuang.pojo.User autowire="byName"></bean>

当一个bean节点带有 autowire byName的属性时。

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  2. 去spring容器中寻找是否有此字符串名称id的对象。
  3. 如果有,就取出注入;如果没有,就报空指针异常。

缺点:当id名与setXXX不同时就会出现空指针报错。

byType:

按类型自动装配

使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

1
2
3
4
5
6
7
<bean class="com.kuang.pojo.Dog"/>
<bean class="com.kuang.pojo.Cat"/>
<bean class="com.kuang.pojo.Cat"/>

<bean id="user" class="com.kuang.pojo.User" autowire="byType">
<property name="str" value="qinjiang"/>
</bean>

上面这个就会报错,因为有猫类在上面定义了两个,所以会报错 ``NoUniqueBeanDefinitionException`

去掉id也可以。

小结:

  • byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性set方法的值一致。
  • bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。

使用注解自动装配(推荐)

用注解的方式注入属性

  1. 在spring配置文件中引入context文件头
1
2
3
4
xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
  1. 开始属性注解支持

    1
    <context:annotation-config/>

@Autowired

需要导入spring-aop包

@Autowired 是按bytype类型自动转配的,不支持id匹配,如果要id匹配可以配合@Qualifier(value="<id>") 来匹配id。例子:

将User类中的set方法去掉,使用@Autowired注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String str;

public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}

配置文件:

1
2
3
4
5
<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"/>

补充:@Autowired(required=false),注解默认为返回true,说明:false是对象允许为null,true是对象不能为空。

还有一个java本身的 @Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

@Autowired与@Resource异同:

  1. @Autowired@Resource 都可以用来装配bean。都可以写在字段上,或写在setter方法上。
  2. @Autowired 默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
  3. @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired 先byType,@Resource先byName

0%