Spring学习笔记(一)bean的配置
Spring学习笔记(一)bean的配置
1.IOC与DI
- IOC(Inversion of Control):其思想是。传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适时的返回资源。而应用了IOC之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。
- DI(Dependency injection)-IOC的另一种表述方式:即。
2.配置bean
2.1基于xml文件配置bean
- 基于xml配置bean
<!-- 配置bean:
class:bean的全类名,通过反射的方式在IOC容器中创建bean。所以要求bean中必须有无参构造的构造器。
id:标识容器中的bean。id唯一。
-->
<bean id="user" class="com.nl.spring.bean.User">
<property name="name" value="abc"></property>
</bean>
-
Spring容器
Spring提供了两种类型的IOC容器实现
-
BeanFactory:IOC容器的基本实现
-
ApplicationContext:是BeanFactory的子接口
-
几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory
-
-
ApplicationContext
ApplicationContext的主要实现类:
- ClassPathXmlApplicationContext:从类路径下加载配置文件
- FileSystemXmlApplicationContext:从文件系统中加载配置文件
ConfigurableApplicationContext扩展与ApplicationContext,新增两个主要方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭上下文的能力。
ApplicationContext在初始化上下文时就实例化所有单例的bean。
-
获取bean
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//1.利用id查找到容器中的bean
User user = context.getBean("user", User.class);
//2.通过类型返回容器中的bean。若有多个相同类型时会报错。
context.getBean(User.class);
2.2依赖注入的方式:属性注入、构造器注入
User类:
package com.nl.spring.bean;
public class User {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
-
属性输入
- 属性注入即通过setter方法注入Bean的属性值或依赖的对象。
- 属性注入使用
<property>
元素,使用name属性指定bean的属性名称,value属性或<value>
子节点指定属性值。
<bean id="user" class="com.nl.spring.bean.User"> <property name="name" value="abc"></property> </bean>
-
构造方法注入
- 通过构造方法注入bean的属性值或依赖对象,它保证了Bean实例在实例化后就可以使用。
- 构造器注入在
<constructor-arg>
元素里声明属性,<constructor-arg>
中没有name属性。
Student类:
package com.nl.spring.bean;
public class Student {
private String id;
private String name;
private Double avgScore;
public Student() {
}
public Student(String id, String name, Double avgScore) {
this.id = id;
this.name = name;
this.avgScore = avgScore;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAvgScore(Double avgScore) {
this.avgScore = avgScore;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", avgScore=" + avgScore +
'}';
}
}
- 普通属性赋值
配置:
<bean id="student" class="com.nl.spring.bean.Student">
<!--通过构造器参数索引-->
<constructor-arg value="0001" index="0"></constructor-arg>
<constructor-arg value="Andy" index="1"></constructor-arg>
<constructor-arg value="89.5" index="2"></constructor-arg>
</bean>
<bean id="student1" class="com.nl.spring.bean.Student">
<!--通过构造器参数类型-->
<constructor-arg type="java.lang.String" value="0002"></constructor-arg>
<constructor-arg type="java.lang.String" value="Sun"></constructor-arg>
<constructor-arg type="java.lang.Double" value="80"></constructor-arg>
</bean>
两种方式可以混合使用。
若在Student类中aveScore属性类型为double,而在配置文件中type为java.lang.Double时,并不会自动拆装箱。
若value中含有特殊字符,如<>
时,可使用<![CDATA[]]>
<bean id="student1" class="com.nl.spring.bean.Student">
<constructor-arg type="java.lang.String" value="0002"></constructor-arg>
<constructor-arg type="java.lang.String" >
<value><![CDATA[<gress>]]></value>
</constructor-arg>
<constructor-arg type="java.lang.Double" value="80"></constructor-arg>
</bean>
- 引用类型赋值
当bean的属性含有引用类型时,可使用ref
属性或ref元素:
<bean id="user1" class="com.nl.spring.bean.User">
<property name="name" value="root"></property>
<!--引用外部bean
<property name="student">
<ref bean="student1"></ref>
</property>
-->
<!--内部bean。不能被外部引用
<property name="student">
<bean class="com.nl.spring.bean.Student">
<constructor-arg type="java.lang.String" value="00006"/>
<constructor-arg type="java.lang.String" value="alice"/>
<constructor-arg type="java.lang.Double" value="78"/>
</bean>
</property>
-->
<property name="student" ref="student1"/>
</bean>
赋值为null
<bean id="user1" class="com.nl.spring.bean.User">
<property name="name">
<value><null/></value>
</property>
</bean>
级联属性赋值:
<constructor-arg ref="stu"></constructor-arg>
<!--级联属性赋值-->
<property name="stu.avgScore" value="89.0"></property>
- List属性赋值
<property name="roles">
<list>
<value>student</value>
<value>son</value>
</list>
</property>
- Map属性赋值
<property name="bookInfo">
<map>
<entry key="《世纪》" value="89.5"/>
<entry key="《外星人》" value="45.8"/>
</map>
</property>
- Properties属性赋值
<property name="properties">
<props>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/</prop>
<prop key="user">root</prop>
<prop key="password">123456</prop>
</props>
</property>
- 将List等集合属性分离出来
<util:list id="list">
<value>teacher</value>
<value>father</value>
</util:list>
需导入util命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">
</beans>
- 通过p命名空间对属性赋值
<bean id="pp" class="com.nl.spring.bean.Person" p:name="tom" p:roles-ref="list">
</bean>
需导入p命名空间。
2.3自动装配(基于xml文件)
- 使用:在
<bean>
的autowire属性指定自动装配的模式 - byType(根据类型自动装配):若有多个相同类型时,则无法进行自动装配。
<bean id="p1" class="com.nl.spring.bean.autowire.Person" p:name="Bot"
autowire="byType"></bean>
- byName(根据名称自动装配):需目标bean的名称与属性名相同。
<bean id="person" class="com.nl.spring.bean.autowire.Person"
p:name="Ancy" autowire="byName"></bean>
2.4通过调用静态工厂方法创建bean
- 通过调用静态工厂方法创建bean,将对象的创建封装到静态方法中。
- 在创建bean时,用class属性指定工厂类;
factory-method
指定获取bean的静态方法。若静态方法有参数,则使用constructor-arg
子节点添加参数。 - 具体实现:
工厂类:
package com.nl.spring.bean.bean_factory;
import java.util.HashMap;
import java.util.Map;
/**
* 静态工厂方法:直接调用某一个类的静态方法返回bean实例
*/
public class StaticPersonFactory {
private static Map<String,Person> personMap = new HashMap<>();
static{
personMap.put("zhangsan" ,new Person("zhangsan"));
personMap.put("lisi" ,new Person("lisi"));
}
public static Person getPerson(String name){
return personMap.get(name);
}
}
配置文件:
<bean id="p1" class="com.nl.spring.bean.bean_factory.StaticPersonFactory"
factory-method="getPerson">
<constructor-arg value="zhangsan"></constructor-arg>
</bean>
2.5通过实例工厂方法创建bean
- 将对象的创建封装到另外一个对象实例的方法中。
- 具体实现:
工厂类:
package com.nl.spring.bean.bean_factory;
import java.util.HashMap;
import java.util.Map;
/**
* 实例工厂方法:先创建工厂,再调用工厂的实例方法
*/
public class InstancePersonFactory {
private Map<String,Person> personMap = null;
public InstancePersonFactory(){
personMap = new HashMap<String, Person>();
personMap.put("zhangsan",new Person("zhangsan"));
personMap.put("wangwu",new Person("wangwu"));
}
public Person getPerson(String name){
return personMap.get(name);
}
}
配置文件:
<!-- 配置工厂实例-->
<bean id="personFactory" class="com.nl.spring.bean.bean_factory.InstancePersonFactory"></bean>
<!-- 通过实例工厂方法来配置person-->
<!-- factory-bean:实例工厂bean-->
<!-- factory-method:实例工厂方法-->
<bean id="p2" factory-bean="personFactory" factory-method="getPerson">
<constructor-arg value="wangwu"></constructor-arg>
</bean>
2.6通过FactoryBean配置bean
具体实现:
自定义的FactoryBean:
package com.nl.spring.bean.factorybean;
import org.springframework.beans.factory.FactoryBean;
//自定义的FactoryBean需要实现FactoryBean接口
public class PersonFactoryBean implements FactoryBean<Person> {
private String name;
public void setName(String name) {
this.name = name;
}
//返回bean对象
@Override
public Person getObject() throws Exception {
return new Person("Tom");
}
//返回bean的类型
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
配置文件:
<!-- 通过FactoryBean来配置bean-->
<!-- class:FactoryBean的全类名-->
<!-- property:FactoryBean的属性-->
<!-- 实际返回的是FactoryBean类的getObject()方法得到的实例-->
<bean id="person" class="com.nl.spring.bean.factorybean.PersonFactoryBean">
<property name="name" value="Tom"></property>
</bean>
2.7 基于注解配置bean
在classpath下扫描组件
-
组件扫描(component scanning):Spring能从classpath下自动扫描,实例化具有特定注解的组件。
-
特定组件:
- @Component Spring管理的组件。
- @Repository 持久层组件
- @Service 业务层组件
- @Controller 表现层组件
-
对于扫描到的组件命名规范:使用类名,且第一个字母小写。也可通过注解的value属性标识组件名称。
-
使用注解后,还需在配置文件中配置
<context:component-scan>
节点,配置该节点需要导入context命名空间。base-package属性指定需要扫描的基类包,Spring会自动扫描该包及子包的所有类。
有多个包需要扫描时,可用逗号分割。
resource-pattern属性指定扫描的资源。
<ontext:include-filter>
子节点表示要包含的目标类。<context:exclude-filter>
子节点表示排除在外的目标类。
<!-- resource-pattern指定扫描的资源-->
<context:component-scan
base-package="com.nl.spring.bean.annotation"
resource-pattern="repository/*.class">
</context:component-scan>
- type属性为annotation
<!-- exclude-file节点排除哪些指定表达式的组件-->
<context:component-scan base-package="com.nl.spring.bean.annotation">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
<!-- include-filter节点表示包含哪些指定组件,需配合use-default-filters属性使用-->
<context:component-scan base-package="com.nl.spring.bean.annotation"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
- type属性为assignable
<!-- 不包含指定接口和实现类-->
<context:component-scan base-package="com.nl.spring.bean.annotation">
<context:exclude-filter type="assignable"
expression="com.nl.spring.bean.annotation.repository.AnimalRepository"/>
</context:component-scan>
<context:component-scan base-package="com.nl.spring.bean.annotation"
use-default-filters="false">
<context:include-filter type="assignable"
expression="com.nl.spring.bean.annotation.repository.AnimalRepository"/>
</context:component-scan>
组件装配
-
<context:component-scan>
元素会自动注册AutowiredAnnotationBeanPostProcessor
实例,该实例可以自动装配具有@Autowired
和@Resource
注解的属性。
使用
@Autowired
自动装配bean
- 该注解可在属性或者
setXxx()
方法上。
@Autowired
private UserDao userDao;
- 若某个字段有多个实现类时,可配合
@Qualifier("")
使用,括号内即为指定实例bean。
@Repository("userDao1")
public class UserDaoImpl1 implements UserDao {}
@Repository
public class UserDaoImpl implements UserDao {}
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDao1")
private UserDao userDao;
}
-
@Autowired
注解中若required
属性为false,则表明该字段为null值。也可直接使用@Nullable
注解。
@Service
public class UserServiceImpl implements UserService {
@Autowired(required=false)
private UserDao userDao;
}
使用
@Resource
进行自动装配
- 该注解相当于
@Autowired
、@Qualifier
联合使用,若要指定实例bean可通过name属性。
@Resource(name = "userDao1")
private UserDao userDao;
2.9 bean之间的关系(继承、依赖)
- 继承
<bean id="addr1" class="com.nl.spring.bean.bean_relation.Address"
p:city="Beijing" p:road="Zhangshan"></bean>
<!-- <bean id="addr2" class=" com.nl.spring.bean.bean_relation.Address"-->
<!-- p:city="Guangzhou" p:road="Zhangshan"></bean>-->
<!-- bean配置的继承:使用bean的parent属性指定继承哪个bean的配置-->
<bean id="addr2" parent="addr1" p:city="Guangzhou"></bean>
- 抽象bean
<!-- 抽象bean:bean的abstract属性为true的bean,这样的bean不能被容器实例化,只能用来继承。autowire属性不会被继承-->
<!-- 若某一个bean没有指定class属性,则该bean必须是一个抽象bean-->
<bean id="addr" class="com.nl.spring.bean.bean_relation.Address"
p:city="Lijiang" p:road="souki" abstract="true"></bean>
- 依赖
2.10 bean的作用范围
使用bean的scope属性配置bean的作用范围。
singleton:默认值。容器初始化时,创建实例。在整个生命周期内只创建一次。单例的。
prototype:原型。容器初始化时不创建该实例。在获取bean实例时,会创建一个新的bean实例。
- singleton(单例)
<bean id="addr" class="com.nl.spring.bean.bean_scope.Address"
p:city="Qingdao" p:road="xxx" scope="singleton"></bean>
每次从IOC容器中获取的都是同一个bean。
- prototype(原型)
<bean id="addr" class="com.nl.spring.bean.bean_scope.Address"
p:city="Qingdao" p:road="xxx" scope="prototype"></bean>
每次都会产生一个新的bean实例。
2.11 外部属性文件
外部属性文件db.properties:
driverclass=com.mysql.jdbc.Driver
jdbcurl=jdbc:mysql://localhost:3306/test
user=root
password=123450
xml配置文件:
<!-- 导入属性文件-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 使用外部文件的属性:-->
<property name="driverClass" value="${driverclass}"></property>
<property name="jdbcUrl" value="${jdbcurl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
2.12 Spring表达式语言(SpEL)
- 字面值赋值
<bean id="addr" class="com.nl.spring.bean.spel.Address">
<!-- 1.使用spel为字面值赋值-->
<property name="city" value="#{'Beijing'}"></property>
<property name="road" value="Wangfujin"></property>
</bean>
- 引用类的静态属性
<bean id="car" class="com.nl.spring.bean.spel.Car">
<property name="brand" value="baoma"></property>
<property name="price" value="200000"></property>
<property name="typePerimeter" value="#{T(java.lang.Math).PI * 80}"></property>
</bean>
- 引用其他bean及属性
<bean id="person" class="com.nl.spring.bean.spel.Person">
<property name="name" value="zhangdan"></property>
<property name="car" value="#{car}"></property>
<!-- 引用其他bean的属性-->
<property name="address" value="#{addr.city}"></property>
</bean>
3.bean的生命周期
3.1bean生命周期的流程
- 通过构造器或工厂方法创建bean实例
- 调用bean的初始化方法
- 使用bean
- 关闭容器,调用bean的销毁方法
在bean的声明中设置init-method
和destory-method
属性,为bean指定初始化方法和销毁方法。
<bean id="car" class="com.nl.spring.bean.bean_cycle.Car"
init-method="init" destroy-method="destory">
<property name="brand" value="baoma"></property>
</bean>
3.2创建bean后置处理器
- bean后置处理器允许在调用初始化方法前后对bean进行额外的处理。
- bean后置处理器对IOC容器里的所有bean实例逐一处理。
- 需实现
org.springframework.beans.factory.config.BeanPostProcessor
接口。在初始化方法被调用前后,Spring会把bean实例传递给该接口的如下两个方法中:
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
具体实现
Car类:
package com.nl.spring.bean.bean_cycle;
/**
* bean的生命周期
*/
public class Car {
private String brand;
public Car() {
System.out.println("Car constructor...");
}
public void setBrand(String brand) {
System.out.println("setBrand()");
this.brand = brand;
}
public void init(){
System.out.println("init...");
}
public void destory(){
System.out.println("destory");
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
'}';
}
}
后置处理器类:
package com.nl.spring.bean.bean_cycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization:" + bean + "," +beanName);
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization:" + bean + "," +beanName);
return bean;
}
}
可以在两个方法中修改bean,或是返回一个新的bean。
自定义后置处理器的配置:
<bean class="com.nl.spring.bean.bean_cycle.MyBeanPostProcessor"></bean>
无需配置id,IOC会自动识别
测试类:
package com.nl.spring.bean.bean_cycle;
import org.junit.Test;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest implements BeanPostProcessor {
@Test
public void beanCycleTest(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean_cycle.xml");
Car car = context.getBean("car", Car.class);
System.out.println(car);
//关闭IOC容器
context.close();
Car constructor...
//执行结果:
//setBrand()
//postProcessBeforeInitialization:Car{brand='baoma'},car
//init...
//postProcessAfterInitialization:Car{brand='baoma'},car
//Car{brand='baoma'}
//destory
}
}
推荐阅读
-
Spring的配置,XML提示的配置,Bean的相关配置
-
详解Spring Cloud Feign 熔断配置的一些小坑
-
python网络编程学习笔记(五):socket的一些补充
-
品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”
-
Redis学习笔记(一):Redis的数据类型
-
spring如何使用命名空间p简化bean的配置
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
-
Spring学习之Bean的装配多种方法
-
spring cloud配置高可用eureka时遇到的一些坑
-
java 学习笔记(入门篇)_java的安装与配置