前言
上篇文章简单介绍了IOC, 本文则是重点讲述如何使用Spring5里的IOC进行Bean管理, 有两种方式, 分别是基于xml文件和注解, 我们都会一一讲到。
BeanFactory接口
IOC容器的底层就是对象工厂
Spring提供了IOC容器的两种实现方式(两个接口):
-
BeanFactory: IOC容器基本实现, 是Spring内部使用的接口, 不提供给开发人员使用, 加载文件的时候不会船舰对象, 在获取对象的时候才会创建对象。
-
ApplicationContext: BeanFactory接口的子接口, 提供更多更强大的功能, 加载配置文件的时候就会把在配置文件中配置好的对象进行创建。
ApplicationContext的四个常用实现类:
- **FileSystemXmlApplicationContext:**加载配置文件的时候采用的是项目的路径, 也就是绝对路径。
- **ClassPathXmlApplicationContext:**加载配置文件的时候根据ClassPath位置, 也就是项目相对路径。
- **XmlWebApplicationContext:**在Web环境下初始化监听器的时候会加载该类。
- **AnnotationConfigApplicationContext:**根据注解的方式启动Spring 容器。
SpringDI的方式
Spring提供了三种方式来依赖注入,有构造方法注入, setter方法注入以及接口注入。其中Spring以往推荐使用Setter的方法现在改成推荐构造方法注入。使用构造方法注入需要注意的一点就是要避免循环依赖。所谓的循环依赖指的就是在A对象的构造方法中Spring要注入B,而在B对象中Spring要注入A。这个时候会形成一个闭环因为Spring不知道该先注入哪一个接着会抛出异常。而Spring建议的处理方式是说如果遇到这种情况的话就改用Setter方式注入。
Bean管理(基于xml)
基于xml创建对象:
- 在Spring配置文件中, 使用bean标签创建对象
- 在bean标签中有多个属性, 常用的有:
- id: 唯一标识, 用于获取对象
- class: 类的全路径
- 创建对象的时候, 默认的是执行无参构造方法完成对象创建
<!--配置User对象的创建-->
<bean id="user" class="com.ayu.User"></bean>
基于xml方式注入属性:
-
使用set方法进行注入
在bean标签内使用property标签完成属性注入:
name: 类中属性名称
value: 属性中注入的值
<!--使用set方法进行参数注入--> <bean id="user1" class="com.ayu.User"> <property name="name" value="Tom"></property> <property name="age" value="11"></property> </bean>
-
使用有参构造器进行注入
在bean标签内使用constructor-arg标签完成属性注入:
name: 类中属性名称
value: 属性中注入的值
<!--使用有参构造方法进行参数注入--> <bean id="user2" class="com.ayu.User"> <constructor-arg name="name" value="Jerry"></constructor-arg> <constructor-arg name="age" value="11"></constructor-arg> </bean>
xml注入其他属性:
-
字面量
-
null值
使用
标签
<!--null值--> <property name="name"> <null/> </property>
-
属性值包含特殊符号
使用CDATA
<!--特殊字符--> <property name="name"> <value> <![CDATA[<<时生>>]]> </value> </property>
-
-
注入外部bean
使用ref属性注入外部创建好的bean对象
<!--外部bean--> <bean id="userService" class="com.ayu.service.UserService"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.ayu.dao.UserDao"></bean>
-
注入内部bean
直接在property标签里创建bean标签
<!--内部bean--> <bean id="emp" class="com.ayu.bean.Emp"> <property name="name" value="张三"></property> <property name="dept"> <bean id="dept" class="com.ayu.bean.Dept"> <property name="deptNo" value="101"></property> </bean> </property> </bean>
-
级联赋值
对属性里的bean对象的属性进行赋值操作
<!--级联赋值--> <bean id="emp1" class="com.ayu.bean.Emp"> <property name="name" value="张三"></property> <property name="dept" ref="dept1"></property> <property name="dept.deptNo" value="102"></property> </bean> <bean id="dept1" class="com.ayu.bean.Dept"></bean>
xml注入集合属性:
- array标签对数组进行赋值
- list标签对List集合进行赋值
- set标签对Set集合进行赋值
- map标签对Map集合进行赋值
<bean id="stu" class="com.ayu.collection.Student">
<property name="courses">
<array>
<value>语文</value>
<value>数学</value>
<value>英语</value>
</array>
</property>
<property name="list">
<list>
<value>java</value>
<value>go</value>
<value>c++</value>
</list>
</property>
<property name="map">
<map>
<entry key="java" value="88"></entry>
<entry key="c++" value="78"></entry>
</map>
</property>
<property name="set">
<set>
<value>MySQL</value>
<value>SQLServer</value>
</set>
</property>
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<bean id="course1" class="com.ayu.collection.Course">
<property name="name" value="Spring5"></property>
</bean>
<bean id="course2" class="com.ayu.collection.Course">
<property name="name" value="SpringMVC"></property>
</bean>
使用util标签共享bean:
<!-- 配置命名空间 -->
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<!-- 使用util标签来配置共享的bean-->
<util:list id="bookList">
<value>大话数据结构</value>
<value>程序是怎样跑起来的</value>
<value>操作系统导论</value>
</util:list>
<bean id="book" class="com.ayu.collection.Book">
<property name="list" ref="bookList"></property>
</bean>
使用p命名空间配置bean:
<!-- 配置命名空间 -->
xmlns:p="http://www.springframework.org/schema/p"
<!--使用p命名空间来配置bean-->
<bean id="user" class="com.ayu.User" p:name="Tom" p:age="11"></bean>
Bean作用域:
Spring中可以使用scope属性来配置bean的作用域:
- singleton: 单例, 在初始化配置文件时生成单例bean对象**(默认)**
- prototype: 原型的, 在初始化配置文件时不生成bean对象, 使用时返回不同的bean对象
- request: web环境下每一个request请求都会返回一个不同的bean, 只在本次请求中有效
- session: web环境下每一个request请求都会返回一个不同的bean, 在session中有效
<!--设置scope属性-->
<bean id="user" class="com.ayu.User" scope="singleton"></bean>
Bean生命周期:
- 通过构造方法生成bean的实例
- 为bean注入属性
- 调用初始化方法**(通过init-method属性配置)**
- bean的使用
- IOC容器关闭时,调用销毁方法**(通过destroy-method属性配置)**
创建一个测试用的Order类:
public class Order {
private String name;
public Order() {
System.out.println("1.无参构造");
}
public void setName(String name) {
this.name = name;
System.out.println("2.set方法");
}
public void initMethod() {
System.out.println("3.初始化方法");
}
public void destroyMethod() {
System.out.println("5.销毁方法");
}
@Test
public void testOrder() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Order order = context.getBean("order", Order.class);
System.out.println("4.获取到bean对象");
System.out.println(order);
((ClassPathXmlApplicationContext) context).close();
}
}
配置文件:
<bean id="order" class="com.ayu.bean.Order" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="手机"></property>
</bean>
测试结果:
1.无参构造
2.set方法
3.初始化方法
4.获取到bean对象
com.ayu.bean.Order@69b0fd6f
5.销毁方法
Bean的后置处理器:
使用后置处理器后的生命周期:
- 通过构造方法生成bean的实例
- 为bean注入属性
- 将bean传给后置处理器的postProcessBeforeInitialization方法
- 调用初始化方法**(通过init-method属性配置)**
- 将bean传给后置处理器的postProcessAfterInitialization方法
- bean的使用
- IOC容器关闭时,调用销毁方法**(通过destroy-method属性配置)**
创建一个后置处理器类实现BeanPostProcessor接口:
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
在配置文件中配置BeanPostProcessor:
<bean id="myBeanPost" class="com.ayu.bean.MyBeanPost"></bean>
测试结果:
1.无参构造
2.set方法
初始化之前
3.初始化方法
初始化之后
4.获取到bean对象
com.ayu.bean.Order@66d1af89
5.销毁方法
Bean的自动装配
bean标签的autowire属性实现自动装配, autowire有两个常用的值:
-
byName: 根据属性名称注入, 注入值bean的id值和类属性值名称需一样
<bean id="emp" class="com.ayu.autowrite.Emp" autowire="byName"> <!--<property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.ayu.autowrite.Dept"></bean>
-
byType: 根据属性类型注入
<bean id="emp" class="com.ayu.autowrite.Emp" autowire="byType"> <!--<property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.ayu.autowrite.Dept"></bean>
注: 通过byType自动装配,已配置的bean中有多个该类型的bean时会报错
Bean管理(基于注解)
Spring针对bean管理提供的注解
下面四个注解功能是一样的,都可以用来创建 bean 实例
- @Controller: 控制器, 推荐给controller层添加此注解。
- @Service: 业务逻辑, 推荐给业务逻辑层添加此注解。
- @Repository: 仓库管理, 推荐给数据访问层添加此注解。
- @Component: 给不属于以上基层的组件添加此注解。
基于注解实现对象创建
-
开启组件扫描:
如果扫面多个包, 可以用逗号隔开, 或是扫描包的上层目录
<context:component-scan base-package="com.ayu"></context:component-scan>
-
创建类, 在类上添加对象注解
//value属性可以不写, 会默认类名首字母小写为value的值 @Repository(value="userDao") public class UserDaoImpl implements UserDao{ @Override public void hello() { System.out.println("hello dao"); } }
开启组件扫描细节配置
<!--示例一:use-default-filters表示现在不使用默认filter,自己配置filter
content:include-filter 设置扫面那些内容-->
<content:component-scan base-package="com.ayu" use-default-filters="false">
<content:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</content:component-scan>
<!--示例二:下面配置扫面包所有内容
content:exclude-filter 设置哪些内容不进行扫描-->
<content:component-scan base-package="com.ayu" use-default-filters="false">
<content:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</content:component-scan>
基于注解实现属性注入
-
@Autowired: 根据属性类型进行自动装配
@Service public class UserService { //不需要set方法 @Autowired private UserDao userDaO; public void hello() { System.out.println("hello service"); userDaO.hello(); } }
-
@Qualifier: 根据属性名称进行注入, 通常和@Autowired一起使用
@Service public class UserService { //不需要set方法 //区别同一接口下多个实现类 @Autowired @Qualifier(value="userDaoImpl") private UserDao userDaO; public void hello() { System.out.println("hello service"); userDaO.hello(); } }
-
@Resource: 可以根据类型注入,也可以根据名称注入(这个注解是JDK提供的)
@Resource(value="userDaoImpl") private UserDao userDaO;
-
@Value: 注入普通类型属性
@Value(value = "service") private String name;
完全注解开发
创建配置类, 代替xml配置文件
@Configuration
@ComponentScan(basePackages = {"com.ayu"})
public class SpringConfig {}
此时应使用AnnotationConfigApplicationContext这个实现类:
//AnnotationConfigApplicationContext需要将配置类传给它
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.hello();
最后
我的个人主页: www.ayu.link
本文连接: ┏ (゜ω゜)=☞