spring IOC 详解
Hello World
HelloWorld.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.test.spring01;
public class HelloWorld {
private String name;
public HelloWorld() {
System.out.println( "调用构造函数..." );
}
public String getName() {
return name;
}
public void setName2(String name) { //对应name="name2"
this .name = name;
}
public void sayHello() {
System.out.println( "Hello," + name);
}
} |
Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.test.spring01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//ClassPathXmlApplicationContext是ApplicationContext接口的实现类,实现从类路径下加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "ApplicationContext.xml" );
System.out.println( "创建对象" );
HelloWorld helloWorld = (HelloWorld)applicationContext.getBean( "a1" );
//HelloWorld helloWorld = applicationContext.getBean(HelloWorld.class);
//这种方式要求在配置文件中只配置了一个HelloWorld的bean
helloWorld.sayHello();
}
} |
applicationContext.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-3.0.xsd">
< bean id = "a1" class = "com.test.spring01.HelloWorld" >
< property name = "name2" value = "spring" />
<!--set方法注入-->
</ bean >
</ beans >
|
运行结果
调用构造函数...
创建对象
Hello,spring
也可以通过构造方法注入
1
2
3
4
5
6
|
<!--通过构造方法来配置bean属性--> < bean id = "car" class = "com.atguigu.spring.beans.Car" > <!--参数和构造函数的参数对应-->
< constructor-arg value = "Audi" index = "0" ></ constructor-arg >
< constructor-arg value = "Shanghai" index = "1" ></ constructor-arg >
< constructor-arg value = "300000" type = "double" ></ constructor-arg >
</ bean >
|
1
2
3
4
5
6
|
<!--使用构造器注入属性值可以指定参数的位置和参数的类型,以区分重载的构造器--> < bean id = "car2" class = "com.atguigu.spring.beans.Car" >
< constructor-arg value = "Baoma" type = "java.lang.String" ></ constructor-arg >
< constructor-arg value = "Shanghai" type = "java.lang.String" ></ constructor-arg >
< constructor-arg value = "400000" type = "int" ></ constructor-arg >
</ bean >
|
有特殊标记时也可以下面这么写
1
2
3
4
5
6
7
8
9
|
< bean id = "car2" class = "com.atguigu.spring.beans.Car" >
< constructor-arg value = "Baoma" type = "java.lang.String" ></ constructor-arg >
< constructor-arg type = "java.lang.String" >
< value > <![CDATA[<Shanghai^>]]> </ value >
</ constructor-arg >
< constructor-arg type = "int" >
< value >400000</ value >
</ constructor-arg >
</ bean >
|
引用类型
1
2
3
4
5
|
< bean id = "person" class = "com.atguigu.spring.beans.Person" >
< property name = "name" value = "Tom" ></ property >
< property name = "age" value = "34" ></ property >
< property name = "car" ref = "car2" ></ property > <!--引用car2-->
</ bean >
|
内部bean,不能被外部引用,只能在内部使用
1
2
3
4
5
6
7
8
|
<!--内部bean--> < property name = "car" > <!--上面的car可以这么写-->
< bean class = "com.atguigu.spring.beans.Car" >
< constructor-arg value = "Ford" ></ constructor-arg >
< constructor-arg value = "Changan" ></ constructor-arg >
< constructor-arg value = "200000" type = "double" ></ constructor-arg >
</ bean >
</ property >
|
赋null值
1
2
|
<!--测试值赋null--> < constructor-arg >< null /></ constructor-arg >
|
为级联属性赋值(必须有set方法) 要先创建car才能赋值
1
2
3
|
< constructor-arg ref = "car" ></ constructor-arg >
<!--为级联属性赋值--> < property name = "car.macSpeed" value = "240" ></ property >
|
为集合属性赋值(list set map)
1
2
3
4
5
6
7
8
9
10
|
< bean id = "person3" class = "com.atguigu.spring.collections.Person" >
< property name = "name" value = "Tom" ></ property >
< property name = "age" value = "34" ></ property >
< property name = "cars" > <!--cars是Person类的一个list类型的属性-->
< list >
< ref bean = "car" >
< ref bean = "car2" >
</ list >
</ property >
</ bean >
|
1
2
3
4
5
6
7
8
9
10
11
|
<!--配置Map属性值--> < bean id = "person4" class = "com.atguigu.spring.collections.Person" >
< property name = "name" value = "Tom" ></ property >
< property name = "age" value = "34" ></ property >
< property name = "cars" > <!--cars是Person类的一个map类型的属性-->
< map >
< entry key = "AA" value-ref = "car" ></ entry >
< entry key = "BB" value-ref = "car2" ></ entry >
</ map >
</ property >
</ bean >
|
1
2
3
4
5
6
7
8
9
10
11
|
<!--配置Peoperties属性值--> < bean id = "dataSource" class = "com.atguigu.spring.collections.dataSource" >
< property name = "peoperties" >
< props >
< prop key = "user" >root</ prop >
< prop key = "password" >123456</ prop >
< prop key = "jdbcUrl" >jdbc:mysql:///test</ prop >
< prop key = "driverClass" >com.mysql.jdbc.Driver</ prop >
</ props >
</ property >
</ bean >
|
1
2
3
4
5
6
7
8
9
10
|
<!--配置单例的集合bean,以供多个bean进行引用,需要导入util命名空间--> < util:list id = "cars" >
< ref bean = "car" >
< ref bean = "car2" >
</ util:list >
< bean id = "person4" class = "com.atguigu.spring.collections.Person" >
< property name = "name" value = "Jack" ></ property >
< property name = "age" value = "29" ></ property >
< property name = "cars" ref = "cars" ></ property > <!--引用上面配置的cars-->
</ bean >
|
1
2
3
|
<!--通过P命名空间为bean的属性赋值,需要先导入P命名空间--> < bean id = "person5" class = "com.atguigu.spring.collections.Person" p:age = "35" p:name = "Shang" p:cars-ref = "cars" >
</ bean >
|
自动装配(Person类中有address属性和car属性)
byName根据bean的名字和当前bean的setter风格的属性名进行自动装配。
byType根据bean的类型和当前bean的数学的类型进行自动装配。(如果有两个类型一样的bean就会出错)
缺点:在bean配置文件里设置autowire属性进行自动装配将会装配bean的所有属性。若只希望装配个别属性时,autowire属性就不够灵活了。autowire属性要么根据类型自动装配,要么根据名称自动装配,不能两者兼而有之。
一般情况下,在是寄到 项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些。
1
2
3
4
5
|
< bean id = "address" class = "com.atguigu.spring.beans.autowire.Address" p:city = "Beijing" p:street = "Huilongguan" ></ bean >
< bean id = "car" class = "com.atguigu.spring.beans.autowire.Car" p:brand = "Audi" p:price = "30000" ></ bean >
< bean id = "person" class = "com.atguigu.spring.beans.autowire.Person" p:name = "Tom" p:autowire = "byName" ></ bean >
|
bean配置的继承
1
2
3
4
|
< bean id = "address" class = "com.atguigu.spring.beans.autowire.Address" p:city = "Beijing" p:street = "Wudaokou" >
</ bean >
<!--bean配置的继承:使用bean的parent属性指定继承哪个bean的配置--> < bean id = "address2" p:street = "Dazhongsi" parent = "address" ></ bean >
|
抽象bean,只能被继承
1
2
3
4
|
<!--抽象bean:bean的abstract属性为true的bean,不能被IOC容器实例化 若一个bean的class属性没有指定,则该bean必须是一个抽象的bean。类似抽象类 --> < bean id = "address" p:city = "Beijing" p:street = "Wudaokou" abstract = "true" ></ bean >
|
bean之间的依赖关系
Spring允许用户通过depends-on属性设定Bean前置依赖的Bean,前置依赖的Bean会在本Bean实例化之前创建好。如果前置依赖于多个Bean,则可以通过逗号、空格的方式配置多个Bean。
1
2
3
|
< bean id = "car" class = "com.atguigu.spring.autowire.Car" p:brand = "Audi" p:price = "30000" ></ bean >
<!--要求在配置person时,必须有一个关联的car。person这个bean依赖于car这个bean--> < bean id = "person" class = "som.atguigu.spring.beans.autowire.Person" p:name = "Tom" p:address-ref = "address2" depends-on = "car" ></ bean >
|
bean配置的作用域
默认为单例singleton,容器初始化时创建bean实例,在整个容器的生命周期里只创建一个bean。
prototype原型,容器初始化时不创建bean实例,在每次请求时创建一个新的bean实例并返回。
1
2
3
4
|
< bean id = "car" class = "com.atguigu.spring.autowire.Car" scope = "prototype" >
< property name = "brand" value = "Audi" ></ property >
< property name = "price" value = "300000" ></ property >
</ bean >
|
引入外部配置文件
不引入配置文件时这么写. beans-properties.xml
1
2
3
4
5
6
|
< bean id = "dataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = "user" value = "root" ></ property >
< property name = "password" value = "123456" ></ property >
< property name = "driverClass" value = "com.mysql.jdbc.Driver" ></ property >
< property name = "jdbcUrl" value = "jdbc:mysql:///test" ></ property >
</ bean >
|
1
2
3
|
ApplicationContext ctx = new ClassPathXmlApplicationContext( "beans-properties.xml" );
DataSource dataSource = (DataSource)ctx.getBean( "dataSource" );
System.out.println(dataSource.getConnection()); //建立连接
|
引入外部配置文件
db.properties
1
2
3
4
|
user=root password= 123456
driverClass=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql: ///test
|
beans-properties.xml写法
1
2
3
4
5
6
7
8
|
<!--导入属性文件--> <!--要导入context命名空间-->
< context:property-placeholder location = "classpath:db.properties" />
< bean id = "dataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = "user" value = "${user}" ></ property >
< property name = "password" value = "${password}" ></ property >
< property name = "driverClass" value = "${driverClass}" ></ property >
< property name = "jdbcUrl" value = "${jdbcUrl}" ></ property >
</ bean >
|
Spring表达式语言:SpEL。支持运行时查询和操作对象。
字面量的表示
整数
1
|
< property name = "count" value = "#{5}" />
|
小数
1
|
< property name = "frequency" value = "#{89.7}" />
|
科学计数法
1
|
< property name = "capacity" value = "1e4" />
|
String可以使用单引号或者双引号作为字符串的定界符号
1
2
|
< property name = "name" value = "#{'chuck'}" />
< property name = "name" value = '#{"chuck"}' />
|
Boolean
1
|
< property name = "enabled" value = "#{false}" />
|
引用其他对象,等同ref
1
2
|
<!--通过value属性和SpEL配置bean之间的引用关系--> < property name = "prefix" value = "#{prefixGernerator}" />
|
引用其他对象的属性
1
|
< property name = "suffix" value = "#{prefixGernerator.suffix}" />
|
调用其他方法,还可以链式操作
1
|
< property name = "suffix" value = "#{prefixGernerator.toString()}" />
|
1
2
|
<!---方法的连缀--> < property name = "suffix" value = "#{prefixGernerator.toString().toUpperCase()}" />
|
1
|
< property name = "price" value = "#{T(java.jang.Math).PI*80}" ></ property >
|
1
2
3
4
5
6
|
<!--使用SpEL表达式来引用其他的bean--> < property name = "car" value = "#{car}" ></ property >
<!--使用SpEL表达式来引用其他的bean的属性--> < property name = "city" value = "#{address.city}" ></ property >
<!--在SpEL中使用运算符--> < property name = "info" value="#{car.price>300000?'金玲':'白领'}"></ property >
|
为bean指定init方法和destroy方法
1
2
3
4
5
|
< bean id = "car" class = "com.atguigu.spring.bean.Car"
init-method = "init" destroy-method = "destroy" > <!--init和destroy对应Car类中的init destroy方法-->
< property name = "brand" value = "Audi" ></ property >
</ bean >
<!--setBrand之后调用init方法,ctx.close();时调用destroy方法--> |
Bean后置处理器允许在调用初始化方法前后对bean进行额外的处理。Bean后置处理器对IOC容器里的所有的Bean实例逐一处理,而非单一实例。其典型应用是:检查Bean属性的正确性或根据特定的标准更改Bean属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class MyBeanPostProcessor implements BeanPostProcessor{
//处理所有bean
@override
public Object postProcessBeforeInitialization(Object bean, Stirng beanName) throws BeansException{
//可以在这里进行一些操作
if ( "car" .equals(beanName)){
//...
}
System.out.println( "postProcessBeforeInitialization..." );
return bean;
}
@override
public Object postProcessAfterInitialization(Object bean, Stirng beanName) throws BeansException{
System.out.println( "postProcessAfterInitialization..." );
//可以在这里对bean的属性进行更改...安全隐患
return bean;
}
} |
1
2
3
4
5
6
7
8
9
10
|
<!--配置文件中配置bean的后置处理器。不需要配置id,IOC容器自动识别是一个BeanPostProcessor--> <!-- 实现BeanPostProcessor接口,并且提供实现 Object postProcessBeforeInitialization(Object bean, Stirng beanName) init-method之前调用 Object postProcessAfterInitialization(Object bean, Stirng beanName) init-method之后调用 bean: bean实例本身 beanName:IOC容器配置的bwan的名字 返回值:实际上返回给用户的bean。可以在以上两个方法中修改返回的bean,甚至返回一个新的bean --> < bean class = "com.atguigu.spring.MyBeanPostProcessor" ></ bean >
|
Spring IOC容器对Bean的生命周期进行管理的过程
-通过构造器或工厂方法创建Bean实例
-为bean的属性设置值和对其他bean的引用
-将bean实例传递给bean后置处理器的postProcessBeforeInitialization方法
-调用bean的初始化方法
-将bean实例传递给bean后置处理器的postProcessAfterInitialization方法
-bean可以使用了
-容器关闭时,调用bean的销毁方法
静态工厂方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** *静态工厂方法:直接调用某一个类的静态方法就可以返回bean的实例 */ public class StaticCarFactory{
private static Map<String, car> cars = new HashMap<String, Car>();
static {
cars.put( "audi" , new Car( "audi" , 300000 ));
cars.put( "ford" , new Car( "ford" , 400000 ));
}
//静态工厂方法
public static Car getCar(String name){
return cars.get(name);
}
} |
1
2
3
4
5
6
7
8
9
10
11
|
<!--通过静态工厂方法来配置bean--> <!-- class属性:指向静态工程方法的全类名
factory-method:静态工厂方法的名字
constructor-arg:如果工厂方法需要传入参数,使用constructor-arg来配置参数
--> < bean id = "car1"
class = "com.atguigu.spring.StaticCarFactory"
factory-method = "getCar" >
< constructor-arg value = "audi" ></ constructor-arg >
</ bean >
|
1
|
Car car = (Car)ctx.getBean( "car1" ); //取对象
|
实例工厂方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/** *实例工厂方法,即先需要创建工厂,然后调用工厂的实例方法来返回bean的实例 */ public class InstanceCarFactory{
private Map<String, car> cars = null ;
public InstanceCarFactory(){
cars = new HashMap<String, Car>();
cars.put( "audi" , new Car( "audi" , 300000 ));
cars.put( "ford" , new Car( "ford" , 400000 ));
}
public Car getCar(String name){
return cars.get(name);
}
} |
1
2
3
4
5
6
7
8
9
10
|
< bean id = "carFactory" class = "com.atguigu.spring.InstanceCarFactory" ></ bean >
<!--通过实例工厂方法来配置bean--> <!-- factory-bean属性:指向实例工程方法的全类名
factory-method:方法的名字
constructor-arg:如果工厂方法需要传入参数,使用constructor-arg来配置参数
--> < bean id = "car2" factory-bean = "carFactory" factory-method = "getCar" >
< constructor-arg value = "audi" ></ constructor-arg >
</ bean >
|
1
|
Car car = (Car)ctx.getBean( "car2" ); //取对象
|
通过FactoryBean配置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
|
public class CarFactoryBean implements FactoryBean{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
@Override
public Object getObject() throws Exception {
return new Car(name, "beijing" ); //调用构造函数
}
@Override
public Class getObjectType() {
return Car. class ;
}
@Override
public boolean isSingleton() {
return true ;
}
} |
1
2
3
|
< bean id = "car" class = "com.test.spring04.CarFactoryBean" >
< property name = "name" value = "BMW" ></ property >
</ bean >
|
1
2
|
Car car = (Car) applicationContext.getBean( "car" );
System.out.println(car); |
在IOC容器中通过注解配置Bean
组件扫描(component scanning):Spring可以从classpath下自动扫描、侦测和实例化具有特定注解的组件。
特定组件包括:
[email protected] 基本注解,标识了一个受Spring管理的组件
[email protected] 标识持久层组件
[email protected] 标识服务层(业务层)组件
[email protected]er 标识表现层组件
对于扫描到的组件,Spring有默认的莫名策略:使用非限定类名,第一个字母小写,也可以在注解中通过value属性值标识组件的名称。