概要:spring入门,创建bean ,配置bean (主要是在class 这个节点中配置bean 的属性/基于XML 文件)
PART ONE
重点: Bean IOC() AOP(面向切面编程)
轻量级:非 侵入性的。不是jar 包的大小,而是使用spring 开发不用去实现spring 为我们提供的接口,不依赖spring 的API
依赖注入:
面向切面编程
容器: 包含并管理应用对象的生命周期
一站式: 可以整合各种优秀的第三方框架(spring 自身提供类优秀的展现层 springMVC 和持久层 spring JDBC)
Demo1
bean 配置:
<bean id="helloWorld" class="spring.bean.HelloWorld" id 是这个bean 的标识,使用反射创建helloworld ;所以HelloWorld 中要有无参构造器
<property name="name" value="spring"></property> 给属性赋值
</bean>
类的设计要符合javabean 的规范
使用:
1. 创建对象
HelloWorld helloWorld = new HelloWorld();
2. 设置属性
helloWorld.setName("spring");*/
使用spring 之后,这两步都是由spring 帮我们完成
---------------------------------------------------------------------------------------
1. 创建spring 的 IOC 容器对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("beanConfig.xml");
根据指定的xml 文件 创建IOC容器,配置文件中所有的bean 都会被创建实例
2. 从 IOC 容器中获取 Bean 实例
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
根据bean 实例的id 获取 bean 的实例
3. 调用方法
helloWorld.output();
注:
ApplicationContext 就代表IOC容器,这是一个接口
PART TWO
IOC
控制反转(Inversion Control):
反转资源的获取方向
,之前的资源获取是组件向容器索要资源,现在是容器主动将资源推送给它管理的组件,组件需要做的就是找一个合适的方法接受资源
DI(Denpendency Injection):
(其实是IOC的另一种表达方式) 依赖注入:就是依赖容器将资源注入到需要这个资源的组件中
例如:在Demo1 中 配置Bean 就是将spring 赋给name 其实就是将资源注入;
class A{
private B b ;
public void setB(B b){
B = b;
}
}
容器中有 A B 两个Bean(实例) ,传统的方式是:将B 取出来,通过set 方法赋值b 属性
在spring 解决的办法是:建立 B 实例 和b 之间的关系,将b 指向B 通过set 方法建立。我们在使用的时候只需要获取A 的实例就可以而不用同时获取A B 的实例
容器对组件按需分配
在编程中没有耦合是什么也做不了的。类之间要协作才能实现功能;过高的耦合度代码质量很差
一个类中使用其他类的情况是十分常见的(例如AB),最糟糕的耦合情况A 中直接定义b = new B(); 松耦合 :public void setB(B b);问题在于:
获取A 的实例:A a= (A) ctx.getBean(“a”);
问题在于: 在使用A 的过程中怎么将 B 赋值给A(不使用set 方法)
为什么这里会有不能使用set 方法:
暂时的理解:
比如在写 Controller 的时候要用到service 怎么办 ,不能使用new 这样耦合太高,只能是 Controller 中有一个引用 ;spring要做的事:将这个引用指向 service 这个bean ;实现依赖注入/类的装配
在IOC 中涉及到两点:bean 的创建 依赖如何注入(类之间如何进行装配)
PART THREE
Bean 的配置
一:XML 文件配置Bean
【产生Bean】
< bean id ="bean 的名字" class ="类的全类名" >
< property name="属性" value="属性的值">< /property>
< /bean>
通过class : 全类名
;通过反射在IOC容器中创建Bean 所以要求 对应的类中要有必须要有无参的构造器
通过 id
来标识 容器中Bean id 是唯一的
在spring IOC 创建Bean 之前我们要先实例化 IOC 容器
BeanFactory
是spring框架大的基础设施,是面向spring本身的,ApplicationContext
是面向使用spring框架开发者的,也就是说开发者使用ApplicationContext 创建IOC 容器实例,底层代码的实现是通过 BeanFactory
ApplicationContext 是一个接口,完成IOC容器实例的创建
主要实现类:
1. ClassPathXmlApplicationContext : 从类路径下加载配置文件
2. FileSystemAmlApplicationContext : 从文件系统中加载配置文件
ApplicationContext 在初始化上下文的时候就是话所有的单例Bean
【配置Bean 的属性/不包括依赖的对象】
使用value 属性 或者value 子标签
- 使用setter 方法注入Bean 的属性值,类中要有set 方法并且符合javabean 规范 ,配置中的name 的值要与之对应
<property name="name" value="spring"></property>
- 使用构造方法
首先类中要有构造函数,可以使用构造函数初始化所有的数据也可以初始化一部分,其他的使用默认初始化
使用构造注入:可以使用 参数的位置和参数的类型 来确定配置中的值赋给谁
<bean id="student1" class="spring.bean.Student">
<constructor-arg value="12345" index="0"></constructor-arg>
<constructor-arg value="你好" index="1"></constructor-arg>
<constructor-arg value="165" type="double"></constructor-arg>
</bean>
属性值注入的细节:
1. constructor-arg 可以直接使用它的value 属性 spring 会根据参数的类型将其转换。
也可以使用 constructor-arg 的子标签 <value></value> 将值的字面值 直接写在 标签中
<constructor-arg type="double">
<value>165</value>
</constructor-arg>
2. 如果值当中有特殊字符 要使用<![CDATA[值]]>
<value><![CDATA[nihao~~~~///]]></value>
【引用其他的Bean/引用的注入】
使用value 属性 或者value 子标签
3. 使用setter 方法
< property name =" " ref="引用的Bean 的id" ></property>
建立Bean 之间的引用关系,指向
4. 使用内部 bean 将bean 写在
<property ><bean></bean></property> 或者是 <constructor-arg><bean></bean></constructor-arg>
;
5. 使用 构造器
6. 赋空值 : 虽然没有什么意义,但是要知道这种写法 <null/ >
7. 级联属性赋值
Student 类中有属性 Car
在STudent的Bean 配置中配置car 的name 属性值,先要car 依赖注入(要有car 这个bean 才能进行 car 的属性赋值)
<property name="car.name" value ="baoma"></property>
- 集合属性赋值
< property>< /property > 的子标签 < list> /< set>/< map > 对集合属性赋值;然后在lis标签下进行value<value>值</value>
或者ref<ref bean ="bean 的id "></bean>
的配置
<property name=“”>
<list>
普通的的值 :<value></value>
引用 :<ref bean = "" /> 或者配置内部bean <ref><bean></bean></ref>
</list>
</property>
map 集合属性
<property>
<map>
<entry key =" " value-ref="其他Bean " ></entry>或者 value = "一般的值"
</map>
</property>
配置 properties 的属性值(hashMap 的一个子类)
<properties>
<props>
<prop key = " ">value</prop>
<prop key = " ">value</prop>
</props>
</properties>
注意:
如果你的类中有带参的构造那么一定要进行带参构造的配置
配置单例的集合Bean 让其他的Bean 能够使用它
<util :list id ="标志这个List 其他bean 使用时直接引用id ">
<ref bean =" " />
<value>值</value>
<util:list>
其他的bean 也可以使用这个list 的bean,将他从某个bean 的属性中剥离出来
spring2.5 中给出P属性 配置Bean 的属性
是 < bean > 的一个属性
<bean id=" " name = " 类的全类名" p:age = “值” p:cars-ref = “依赖的引用” p: 类的属性 = “值”>比传统的配置方式更加便捷
二:Bean 自动装配
在< bean>< /bean> 的autowire 的属性里指定自动装配的模式;自动配置主要针对引用属性
- ByType : 通过类型进行匹配
- ByName : 目标Bean 的id 和当前需要配置的bean 的属性名相同。
<bean id="car" class="spring.bean.Car" p:name="baima"></bean>
<bean id="student" class="spring.bean.Student" p:name="lisi" p:id="1" p:age="12" autowire="byName"></bean>
ByName 方式进行配置:已存在 bean 的id为car 这和当前这个bean 中的setter 风格属性名一致,所以可以进行自动配置。不存在这样的bean 就不进行装配
ByType 方式进行装配:已存在的bean 的类型和当前需要装配的bean 的这个属性的类型一致,但是如果配置文件中存在多个这样的bean 那配置就会报错
神之总结: 在实际的使用中很好使用自动转配。因为你一旦使用自动装配,所有未手动装配的属性都会是自动转配,失去了灵活性
三: Bean 配置之间的关系
这里的关系指的不是对象之间的关系,而是配置之间的关系
继承
- spring 允许继承bean 配置,被继承的bean 成为父bean ,继承这个父bean 的bean 称为 子bean ,子bean 继承父bean 中的所 属性配置,也可以覆盖父bean 中的属性配置
<!--测试bean配置继承-->
<bean id="car1" class="spring.bean.Car" p:name="baoma"></bean>
<!--继承 car1 -->
<bean id="car2" parent="car1"></bean> 使用属性 parent 表明 父bean
car2 的输出:Car{name='baoma'}
也可以覆盖 父bean ; 将子bean 的配置改为
<bean id="car2" p:name="biyadi" parent="car1"></bean>
car2 的输出为:Car{name='biyadi'}
- 父bean 可以作为模板 设置 bean 的
abstract="true"
模板可以不设置 class 属性 ,而让子bean 自己指定自己的类,并且模板bean 没有办法创建bean 实例
依赖
一个bean 中有一个引用属性,在创建这个bean 的时候要求,一定要有当前bean 的依赖,才能创建当前的bean
例如:student 类中有一个属性 car
在自动配置的时候:不存在car 对应的bean 不配置就行(自动配置为null)
在设置依赖之后如果配置文件中没有依赖的bean 就无法创建当前bean
<bean id="student2" class="spring.bean.Student" p:id="15" p:name="wangwu" p:age="18" depends-on="car" p:car-ref="car"></bean>
设置依赖给了你的bean 一个创建的必然满足的条件
Spring 允许用户通过depends-on 属性设置bean 的牵制依赖,牵制依赖的bean 会在本 bean 实例化之前创建好,如果有多个依赖则用逗号隔开
四:Bean 的生命周期
默认在IOC中配置bean 这个bean 是单例的:IOC只为这个bean 配置创建一个bean 对象。也就是说每一次getBean() 获取到的Bean 对象都是一样的。
例:默认 scope = "singletype" 单例
Car car1 = ctx.getBean("car")
Car car2 = ctx.getBean("car")
System.out.print(car1==car2) 输出结果:true 每次获取的bean 对象是同一个
可以通过bean 的 scope 属性进行配置
scope 默认为: scope = "singleton" 非单例
scope = "prototype" 每一次getBean() IOC会重新创建一个bean 对象
- Bean对象的创建:
单例模式:singletype
ApplicationContext ctx=new ClassPathXmlApplicationContext("beanConfig.xml")
在创建IOC 容器对象的时候,IOC容器就创建了配置文件中的所有bean 对象
非单例模式:prototype
在调用 getBean() 的创建相应的bean 对象,每次调用getBean() 都会创建一个相应bean 的对象
五:外部配置文件配置Bean
对于配置文件,学习到这其实自己也算做一个小小的总结: 首先是xml 文件的配置,然后是外部文件 properties 文件的配置。
外部文件的使用情景:
之前在Javaee JDBC 配置数据库的基本信息的时候 就使用过properties 文件配置数据库的基本信息,在程序中引入properties 文件,获取properties 文件中的值
配置一些基本信息的时候可以直接使用外部文件配置(.peroperties)
例如 : person 中的属性 id name age gender grade 【使用外部文件进行配置】
bean.properties
id=12345
name=xiaoming
age=12
gender=man
grade=125
在bean.xml配置文件中的引用外部文件中的属性
spring 提供了一个PropertyPlaceholderConfigurer 的BeanFactory后置处理器
,这个处理器允许用户在配置文件中使用${var} 的形式获取外部文件中的bean 属性
在配置文件中添加如下配置:<context:propertity-placeholder location=“classpath:properties 文件名”>
然后就可以在配置文件中使用${name} 获取到外部文件中的name 的值。
<!--引入外部文件配置bean -->
<context:property-placeholder location="classpath:bean.properties"/>
<bean id="person" class="spring.bean_properties.Person">
<property name="id" value="${id}"></property>
<property name="name" value="${name}"></property>
<property name="age" value="${age}"></property>
<property name="gender" value="${gender}"></property>
<property name="grade" value="${grade}"></property>
</bean>
可以联想到springboot ,虽然没有那么多的配置文件(没有bean 配置文件) 但是有Application.properties 文件。就是问了完成bean 的外部文件配置。
程序中是类,配置文件中是 bean 的配置。外部也有文件进行配置(保存bean 属性的值,便于配置)。这也解释了为什么在springboot 中配置bruid 数据连接池要在程序中配置声明一个bean :因为在springboot 默认的配置文件中没有bruid 对应的bean 配置。外部文件中配置bruid IOC容器中却没有bean 相对应
SpEL
Spring 的表达式语言(SqEL) :是一个支持运行时查询和操作对象的强大语言表达式;#{SqEL}
SqEL 表达式为bean 的属性动态赋值提供了便利
- 赋字面值 :
<property name ="id" value = #{5}>
<property name ="name" value = #{"小花"}>
如果是字符串要使用“”/‘’ (单引号/双引号) 赋字面值其实可以不用使用#{}
- 赋值对象引用
<property value = "car" value = #{car2}> 将当前bean 的car 引用指向 :car2 这个bean
相当于:<property value ="car" ref="car2">
- 获取另一个bean 的某个属性值,这是基本的bean 中的配置表达做不到的
<property value ="name " value = #{car2.name}> 将car2 这个bean 的name 属性赋值给当前的这个 bean 的name 属性
PART FOUR
六:IOC 容器中Bean 的生命周期方法
springIOC容器可以管理 Bean 的生命周期,管理过程
- 通过构造器或工厂方法创建bean 实例
- 为bean 的属性赋值和对其他Bean的用(完善Bean 实例)
- 调用bean 的初始化方法
- 此时的bean 就是可以使用的
- 当容器关闭时,调用Bean 的销毁方法
可以在bean 的指定bean 的初始化和销毁方法;init-method / destroy-menthod
容器对内部的东西进行管理的模式和Servlet容器 对Servlet的管理很相似
代码演示:创建,初始化,销毁
1. Bean:
public class Tree {
private String name;
private String type;
private Integer age;
Tree(){
System.out.println("调用构造器");
}
//省略大量的set/get和toString() 方法
销毁函数和初始化函数函数名,内容是自己随意定义的
1. 初始化函数
public void init(){
System.out.println("完成bean 的初始化");
}
2.销毁函数
public void destroy(){
System.out.println("完成bean 的销毁");
}
}
2. xml 配置文件:指定bean 的初始化/销毁函数
<bean id="tree1" class="spring.bean_cycle.Tree" init-method="init" destroy-method="destroy">
<property name="name" value="沙生树木"></property>
<property name="age" value="50"></property>
<property name="type" value="白杨"></property>
</bean>
3. 测试代码:
public static void main(String[] args){
测试初始化和销毁函数
创建容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
Tree tree = (Tree)ctx.getBean("tree1");
System.out.println(tree);
关闭IOC容器
ctx.close();
}
4. 运行结果:
调用构造器
完成bean 的初始化
Tree{name='沙生树木', type='白杨', age=50}
完成bean 的销毁
Bean的后置处理器
Bean的后置处理器(针对不同的处理还有不同的处理器)
允许在调用初始化方法的前后
对Bean 进行额外的处理,Bean 的后置处理器对IOC 容器里的所有Bean 都会逐一的进行处理;例如:bean属性后置处理器:检查bean 属性的正确性或根据特定的标准修改Bean 的属性
Bean 的后置处理器需要实现 interface BeanPostProcessor
在初始化方法执行前和后,Spring 会把每个Bean 实例 分别传递到上述接口的两个方法
postProcessAfterInitalization(Object bean,String beanName)
postProcessBeforeInitalization(Object bean,String beanName)
测试代码:
1. 自定义后置处理器
public class MyPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println(s+"初始化之前处理");
return o;
}
会将bean 和bean 的id 传入这个方法
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println(s+"初始化之后处理");
return o;
}
}
2. 配置文件
<!--配置bean 后置处理器,不需要设置id 扫描时会发现这是一个特殊的bean 会为她自定设置id-->
<bean class="spring.bean_cycle.MyPostProcessor"></bean>
3. 测试结果:
tree1初始化之前处理
完成bean 的初始化
tree1初始化之后处理
后置处理器的方法十分的强大,你可以在这个方法中对bean 做任意的处理和改变