spring IOC 详解

本文深入探讨Spring框架中Bean的配置与管理,包括构造器注入、属性注入、构造方法注入、内部bean、null值赋值、集合属性赋值、自动装配、bean配置的继承、抽象bean以及bean之间的依赖关系等内容。

Hello World

wKioL1QDH7Ozya2jAAEfLCtXWVM593.jpg

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下自动扫描、侦测和实例化具有特定注解的组件。

特定组件包括:

-@Component 基本注解,标识了一个受Spring管理的组件

-@Repository 标识持久层组件

-@Service 标识服务层(业务层)组件

-@Controller 标识表现层组件

对于扫描到的组件,Spring有默认的莫名策略:使用非限定类名,第一个字母小写,也可以在注解中通过value属性值标识组件的名称。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值