Spring_IOC

Spring_IOC

七月 24, 2019

IOC的相关功能

1.set方式注入(必须依靠set方法)

可以注入的内容有:
    基本类型(8中基本类型+字符串)的装配
    对象类型的装配
    集合的装配

基本类型的装配

方式: 配置元素<value/>
例子:
1
2
3
4
5
6
7
8
public class HelloBean {
private String name;
private int age;
public String sayHello(){
return "hello "+name +",your age is" + age;
}
.............
}

配置文件set.xml

1
2
3
4
5
6
7
<bean id="helloBean" class="ioc.HelloBean">
<property name="name">
<value>tom</value>
</property>
<property name="age" value="20">
</property>
</bean>
<!--
id是Bean的唯一标识,要求在整个配置文件中要唯一,也可使用name属性,bean标签里面的id和name属性都可以用来标识这个配置的对象,但是id会帮我们检查给对象起的名字是否规范(名字不能重复、不能用数字开头、不能有空格等等),如果检查出来了那么就会报错name属性不会帮检查这些东西
 -->
<!--property 对于所有用set方式来注入的必须使用该标签-->
<!--value    是对以基本类型,都用value(标签/属性)来注入,可以实现自动的数据类型转换-->

测试类:

1
2
3
4
5
6
  main:
ApplicationContext ac =
new ClassPathXmlApplicationContext("set.xml");
//获取容器的一个实例
HelloBean hb = (HelloBean) ac.getBean("helloBean");
System.out.println(hb.sayHello());
对象类型的装配
    使用property的ref属性引用    

集合的装配

    方式:配置元素<list> <set> <map> <props>
例如:
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
40
41
42
43
44
45
46
47
48
49
50
51
public class SomeBean {
private List listProperty;
private Set setProperty;
private Map mapProperty;
private Properties<String,String> property;
public List getListProperty() {
return listProperty;
}
public void setListProperty(List listProperty) {
this.listProperty = listProperty;
}
public Set getSetProperty() {
return setProperty;
}
public void setSetProperty(Set setProperty) {
this.setProperty = setProperty;
}
public Map getMapProperty() {
return mapProperty;
}
public void setMapProperty(Map mapProperty) {
this.mapProperty = mapProperty;
}
public Properties getProperty() {
return property;
}
public void setProperty(Properties property) {
this.property = property;
}
public void printInfo(){
System.out.println("listProperty");
System.out.println(listProperty);
System.out.println("setProperty");
System.out.println(setProperty);
Set set = mapProperty.entrySet();
Iterator it = set.iterator();
while(it.hasNext()){
Map.Entry entry = (Entry) it.next();
System.out.println("Key " +entry.getKey() );
System.out.println("value "+entry.getValue());
}
System.out.println("props: ");
Set set2 = property.entrySet();
Iterator it2 = set2.iterator();
while(it2.hasNext()){
Map.Entry entry= (Entry) it2.next();
System.out.println("key "+entry.getKey());
System.out.println("value "+entry.getValue());
}
}
}

applcationContext.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
<bean id="someBean" class="ioc.SomeBean">
<property name="listProperty">
<list>
<value>list1</value>
<value>list1</value>
<value>list3</value>
</list>
</property>
<property name="setProperty">
<set>
<value>set1</value>
<value>set1</value>
<value>set3</value>
</set>
</property>
<property name="mapProperty">
<map>
<entry key="key1">
<value>value1</value>
</entry>
<entry key="key2">
<value>value2</value>
</entry>
</map>
</property>
<property name="property">
<props>
<prop key="key1">prop1</prop>
<prop key="key2">prop2</prop>
<prop key="key3">prop3</prop>
</props>
</property>
</bean>

测试类:

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
40
41
42
43
main:
String path = "ioc/applicationContext.xml";
ApplicationContext ac =
new ClassPathXmlApplicationContext(path);
SomeBean sb = (SomeBean) ac.getBean("someBean");
sb.printInfo();


2.基于构造器注入
方式: 配置<constructor-arg>元素
在Bean中不用写set方法,但是要有相应的构造器

构造器注入有俩种形式 一个是根据参数类型 一个是根据参数位置的下标
<constructor-arg type="int" value="">
<constructor-arg index="0" value="">

例如:
<bean name="student" class="com.briup.bean.Student">
<constructor-arg type="int" value="25">
</constructor-arg>

<constructor-arg type="java.lang.String" value="tom">
</constructor-arg>

<constructor-arg type="long" value="100">
</constructor-arg>

</bean>

或者:
<bean name="student" class="com.briup.bean.Student">
<constructor-arg index="2">
<value>30</value>
</constructor-arg>

<constructor-arg index="0">
<value>200</value>
</constructor-arg>

<constructor-arg index="1">
<value>lily</value>
</constructor-arg>
</bean>

3.自动注入 :容器依照一些规则去装配bean中的一个属性

注意:自动装配只对[对象类型]起作用,对基本类型不起作用.
第一种情况:
    在beans标签中配置装载方式:default-autowire="byName"

    在根元素beans中加入这个属性,那么下面所有的bean都会
    使用byName的方式进行自动注入,如果在下面的某一个bean
    里面想使用其他的方式进行注入,可以用autowire=""属性进行
    说明,或者某一个bean不想使用任何自动注入就使用autowire="no"

第二种情况:
    在bean标签中指定配置方式

    autowire="byName":
    spring容器会到当前的类中找property的名字,然后
    再根据这个名字去spring容器中找有没有和这个property
    名字相同的对象,有的话,就把这个对象当做参数放到
    setXxxx这个方法里面注入进来.
    注意:了解property指的类中的什么东西

    autowire="byType":
    spring容器会根据当前类中的set方法里面参数的类型,
    去容器中找相匹配的对象,如果没找到就算了,如果找到
    一个就注入进来,如果找到多个,那么就会报错了.

    autowire="constructor" 
    根据构造器的参数类型去匹配

4.继承

配置文件中,一个bean的配置可以继承另一个bean的配置

bean标签中的俩个属性:
1
2
3
4
5
6
abstract属性
<bean name=".." class=".." abstract="true">
注:bean中一旦定义为了abstract="true",则说明该bean是用来被继承的,不能通过该bean获得对象了

parent属性,指定继承哪个bean的配置
<bean name=".." parent="另一个bean的名字">
例子:
1
2
3
4
5
<bean name="student" class="com.briup.bean.Student">
<property name="name">
<value>zhangsan</value>
</property>
</bean>
<!-- 
    abstract="true" 表示当前的配置是一个抽象的配置,
    这时候我们在代码中就不能通过这个bean的名字teacher来
    获得相应的对象了(和java中的抽象类不能直接new对象的道理一样)

    但是我们可以在写一个配置去继承这个抽象的配置,当然即使当前
    这个配置不是抽象的,也能够被继承(和java中继承一样)
-->

<bean name="teacher" class="com.briup.bean.Teacher" abstract="true">
    <property name="student" ref="student"></property>
</bean>

<!-- 
    parent="teacher" 表示当前配置是继承了另外一个名字叫
    teacher的bean的配置,配置和配置的继承像java中的类和类
    直接的继承一样,子类会把父类中的对象继承过来.
    当然在子配置里面依然是可以覆盖父配置中已经写的配置信息.
-->
1
2
3
4
5
6
7
8
9
10
<bean name="t" parent="teacher">

<property name="id">
<value>11</value>
</property>

<property name="name">
<value>TeacherWang</value>
</property>
</bean>

8 Bean对象的在IOC容器中的生命周期

例如:我们需要从容器中获得一个Bean的对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  1、容器创建该Bean的对象

2、容器给这个Bean对象注入依赖

3、如果这个Bean已经实现了BeanNameAware接口,容器会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

4、如果这个Bean已经实现了BeanFactoryAware接口,容器会调用它实现的setBeanFactory方法
该方法接收的参数就是容器本身(可以用这个方式来获取其它Bean)

5、如果这个Bean已经实现了ApplicationContextAware接口,容器会调用setApplicationContext方法,该步和第四步类似,ApplicationContext是BeanFactory的子接口,有更多的实现方法

6、如果spring容器中配置了BeanPostProcessor接口的实现类,容器将会调用该实现类中的postProcessBeforeInitialization方法,表示可以在Bean对象执行 初始化方法之前 做点事情

7、如果Bean在Spring配置文件中配置了init-method属性则会自动调用其配置的初始化方法

8、如果spring容器中配置了BeanPostProcessor接口的实现类,容器将会调用该实现类中的postProcessAfterInitialization方法,表示可以在Bean对象执行 初始化方法之后 做点事情

注:以上工作完成以后就可以使用这个Bean对象了

9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
但是一般情况下,我们定义的Bean都是些普通的类,没有用到任何spring的接口,所以大多数时候我们只考虑Bean对象的创建、依赖注入、初始化、使用、销毁即可,并且可以把Bean分为单例和非单例俩种情况:

单例管理的对象:

1.默认情况下,spring在读取xml文件的时候,就会创建对象
2.进行依赖注入,如果有依赖的话
3.会去调用init-method=".."属性值中所指定的方法,如果有该配置的话
4.Bean对象可以被正常使用
5.对象在被销毁的时候,会调用destroy-method="..."属性值中所指定的方法,如果有该配置的话

注意1:调用container.destroy()方法会销毁单例对象
注意2:lazy-init="true",可以让这个Bean对象在第一次被访问的时候创建,而不是读取xml文件就被创建
注意3:因为是单例,所以该对象只会被创建一次

非单例管理的对象:

1.使用这个对象的时候,spring容器会创建这个对象
2.进行依赖注入,如果有依赖的话
3.会去调用init-method=".."属性值中所指定的方法,如果有该配置的话
4.Bean对象可以被正常使用

注意1:spring容器不会销毁非单例对象
注意2:因为是非单例,所以每次使用都会创建一个新的Bean对象

9 xml文件导入其他xml文件配置

如果我们在spring框架中配置了多个xml文件,我们可以在读取配置文件的时候把这些xml文件一下全都读取,也可以只读一个总的xml文件,在这个总的xml文件中把其他的xml全都都导入进来。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
student.xml文件:
<bean name="student" class="com.briup.bean.Student">
<property name="id">
<value>25</value>
</property>
</bean>

teacher.xml文件:
<bean name="teacher" class="com.briup.bean.Teacher">
<property name="student" ref="student"></property>
</bean>

import.xml文件:
<import resource="teacher.xml"/>
<import resource="student.xml"/>

main:
String[] path = {"com/briup/ioc/imp/import.xml"};
ApplicationContext container =
new ClassPathXmlApplicationContext(path);

Teacher t = (Teacher) container.getBean("teacher");
System.out.println(t.getStudent());

10 spring容器创建bean对象的方式

1)xml文件中有bean的配置,而且这个bean所对应的java类中存在一个无参构造器,那么这个时候spring容器就可以使用反射调用无参构造器来创建实例了(常规的方式)


2)通过工厂类获得实例(工厂类实现了接口FactoryBean<?>)
    注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可
    例如:
//工厂类实现指定接口并且实现接口中的三个抽象方法:
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
  public class ConnectionFactory implements FactoryBean<Connection>{
private String driver;
private String url;
private String username;
private String password;

@Override
public Connection getObject() throws Exception {
Class.forName(driver);
Connection conn =
DriverManager.getConnection(url,username,password);
return conn;
}

@Override
public boolean isSingleton() {
return false;
}

@Override
public Class<Connection> getObjectType() {
return Connection.class;
}
set/get
....
}
xml文件:

<!-- 
    下面配置的这个类,可以自动的帮我们去读取指定的properties文件的内容,文件中用key-value的形式存放数据,读完之后我们就可以用${key}这种形式去拿文件中的value值了

    在htmlsingle中直接搜索类名即可得到相关配置样例

    classpath指的是从src下面找.
-->
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
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:oracle.properties</value>
</property>
</bean>

因为这个类是一个工厂类,所以我们用名字conn在容器中拿对象的时候,
拿到并不是这个工厂类对象,而是这个工厂类对象调用完工厂方法后所返回的对象.

<bean name="conn" class="com.briup.ioc.factory.ConnectionFactory">
<property name="driver">
<value>${driver}</value>
</property>

<property name="url">
<value>${url}</value>
</property>

<property name="username">
<value>${username}</value>
</property>

<property name="password">
<value>${password}</value>
</property>
</bean>

main:
String path = "com/briup/ioc/factory/factory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
Connection conn =
(Connection)container.getBean("conn");

System.out.println(conn);
3)通过实例工厂获得bean对象(不需要实现或者继承任何接口或者父类),就是一个普通的工厂实例instanceFactory
//一个普通的工厂类 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  public class ConnectionFactory{
private String driver;
private String url;
private String username;
private String password;

public Object getConnection() throws Exception {
Class.forName(driver);
Connection conn =
DriverManager.getConnection(url,username,password);
return conn;
}
get/set
....
}
xml文件:

<!-- 读取properties文件 -->
1
2
3
4
5
   <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:oracle.properties</value>
</property>
</bean>
<!-- 配置工厂类 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<bean name="factory" class="com.briup.ioc.instanceFactory.ConnectionFactory">
<property name="driver">
<value>${driver}</value>
</property>

<property name="url">
<value>${url}</value>
</property>

<property name="username">
<value>${username}</value>
</property>

<property name="password">
<value>${password}</value>
</property>
</bean>
<!-- 
    将来通过这个conn来拿对象,拿到的是名字为factory的工厂类调用完
    名字为getConnection方法之后所返回的对象。
-->
1
<bean name="conn" factory-bean="factory" factory-method="getConnection"></bean>
main:
1
2
3
4
5
6
7
8
String path = "com/briup/ioc/instanceFactory/instanceFactory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);

Connection conn =
(Connection)container.getBean("conn");

System.out.println(conn);
4)通过静态工厂获得实例

    例如
//静态工厂类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  public class ConnectionFactory{
private static String driver =
"oracle.jdbc.driver.OracleDriver";
private static String url =
"jdbc:oracle:thin:@127.0.0.1:1521:XE";
private static String username = "briup";
private static String password = "briup";

public static Object getConnection() throws Exception {
Class.forName(driver);
Connection conn =
DriverManager.getConnection(url,username,password);
return conn;
}
}
xml文件:
    <!-- 这样配置一定要求getConnection方法是静态方法 -->
1
<bean name="conn" class="com.briup.ioc.staticFactory.ConnectionFactory" factory-method="getConnection"></bean>
main:
1
2
3
4
5
6
7
8
String path = "com/briup/ioc/staticFactory/staticFactory.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);

Connection conn =
(Connection)container.getBean("conn");

System.out.println(conn);

11 自定义属性编辑器PropertyEditor

Spring中我们可以使用属性编辑器来将特定的字符串转换为对象
    String--转换-->object

java.beans.PropertyEditor(JDK中的接口)用于将xml文件中字符串转换为特定的类型,同时JDK为我们提供一个实现类java.beans.PropertyEditorSupport

Spring在注入时,如果遇到类型不一致(例如需要Address类型但是用户传了个String)则会去调用相应的属性编辑器进行转换

spring会调用属性编辑器的setAsText(String str)进行处理用户传的字符串,并调用getValue()方法获取处理后得到的对象
在代码中处理完后需要调用setValue方法,要不然spring调用getValue方法拿不到处理后转换成的对象


//自定义编辑器类
1
2
3
4
5
6
  public class AddressEditor extends PropertyEditorSupport {

@Override
public String getAsText() {
return super.getAsText();
}
//Spring遇到数据类型不一致并且不能自己处理的时候会调用这个方法处理字符串
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
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] str = text.split(",");
String city = str[0];
String street = str[1];
String country = str[2];
Address add = new Address(city, street, country);
setValue(add);
}

}

//Address类
public class Address {
private String city;
private String street;
private String country;
set/get
.....
}

//Student类
public class Student {
private long id;
private String name;
private boolean gender;
private int age;
private Address address;
get/set
...
}
1
2
3
4
5
6
7
8
9
<bean id="student" class="com.briup.ioc.proEdit.Student">
<property name="id" value="1"/>
<property name="name" value="tom"/>
<property name="age" value="45"/>
<property name="gender" value="true"/>
<property name="address">
<value>kunshan,xueyuan,China</value>
</property>
</bean>

12 自定义事件

在spring中我们可以自定义事件,并且可以使用ApplicationContext类型对象(就是spring容器container)来发布这个事件
事件发布之后,所有的ApplicaitonListener(监听器)实例都会被触发并调用指定方法onApplicationEvent()来处理.

例如:
自定义事件类RainEvent:
1
2
3
4
5
public class RainEvent extends ApplicationEvent {
public RainEvent(Object source) {
super(source);
}
}
监听器类RainListener1
1
2
3
4
5
6
7
8
public class RainListener1 implements ApplicationListener<RainEvent>{

@Override
public void onApplicationEvent(RainEvent event) {
System.out.println("唐僧大喊:" + event.getSource() + "赶快收衣服喽!");
}

}
监听器类RainListener2
1
2
3
4
5
6
public class RainListener2 implements ApplicationListener<RainEvent>{

public void onApplicationEvent(RainEvent event) {
System.out.println("我们:" + event.getSource() + "太好了不用上课了!");
}
}
xml文件:
1
2
3
<!-- 只需要把这俩个监听器类交给spring容器管理就可以了 -->
<bean class="com.briup.ioc.event.RainListener1"></bean>
<bean class="com.briup.ioc.event.RainListener2"></bean>
main:
1
2
3
4
String path = "com/briup/ioc/event/event.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
container.publishEvent(new RainEvent("下雨了!"));

13 ioc中的annotation配置

@Autowired 注入(不能缺失初始化Bean)
1) @Autowired使用后需要在xml文件加入以下配置才能生效: <context:annotation-config/>

2) @Autowired注解可以写在成员变量(最常用)、setter方法、构造器函数上面

3) @Autowired默认按照byType匹配的方式进行注入,如果没有一个bean的类型是匹配的则会抛异常,
如果有多个bean的类型都匹配成功了,那么再按byName方式进行选择。如果name没有找到,则也会抛出异常

4) @Autowired如果最终匹配不成功(注意一定是一个都没有找到的情况)则会抛出异常,但是如果设置为 @Autowired(required=false),则最终匹配不成功没有不会抛出异常。

5) @Autowired可以结合@Qualifier("beanName")来使用,则可以达到byName的效果

@Resource(java提供)
1) @Resource使用后需要在xml文件加入以下配置才能生效:<context:annotation-config/>

2) @Resource的作用和@Autowired差不多,只不过 @Resource是默认先用byName,如果找不到合适的就再用byType来注入

3) @Resource有俩个属性,name和type,使用name属性则表示要byName匹配,使用type属性则表示要byType匹配



@PostConstruct和@PreDestroy
1) 标注了@PostConstruct注解的方法将在类实例化后调用
2) 标注了@PreDestroy注解的方法将在类销毁之前调用


@Component
1) @Component注解可以直接定义bean,而无需在xml定义。但是若两种定义同时存在,xml中的定义会覆盖类中注解的Bean定义

2) @Component注解直接写在类上面即可

3) @Component有一个可选的参数,用于指定bean的名称
    @Component("boss")
    public class Boss{}

4) @Component如果不指定参数,则bean的名称为当前类的类名小写
    //和上面例子的相关相同
    @Component
    public class Boss{}

5) @Component使用之后需要在xml文件配置一个标签
    <context:component-scan/>

6) <context:component-scan base-package="com.briup.ioc.annotation" /> 
    表示spring检查指定包下的java类,看它们是否使用了 @Component注解

7) @Component定义的bean默认情况下都是单例模式的,如果要让这个bean变为非单例,可以再结合这个@Scope注解来达到目标@Scope("prototype")


@Component是Spring中所有bean组件的通用形式, @Repository @Service @Controller 则是 @Component的细化,用来表示更具体的用例,分别对应了持久化层、服务层和表现层。但是至少到现在为止这个四种注解的实质区别很小(甚至几乎没有),都是把当前类注册为spring容器中的一个bean


注意:
1.component-scan标签默认情况下自动扫描指定路径下的包(含所有子包)

2.component-scan标签将带有@Component @Repository @Service @Controller注解的类自动注册到spring容器中

3.component-scan标签对标记了@Required @Autowired @PostConstruct @PreDestroy @Resource @WebServiceRef @EJB   @PersistenceContext @PersistenceUnit等注解的类进行对应的操作,使注解生效

4.component-scan标签包含了annotation-config标签的作用