学习内容来自 B站尚硅谷视频
IOC 概念
IOC 全称 Inversion of Control,也就是控制反转,是面向对象编程的一种设计原则,可以降低代码的耦合度
通俗的说,就是把创建对象、调用对象的过程交给 Spring 进行管理,这样做的目的是为了降低代码的耦合度
学习笔记一的入门案例就是 IOC 实现
IOC 底层原理
主要使用的技术:
- XML 解析
- 工厂模式
- 反射
例子
原始方式,Service 需要关联 Dao,Dao 关联具体类,这样耦合度较高,如果中间有类的路径改变,则需要挨个改变相关联的类
使用工厂模式解耦合:
IOC 过程:
- 创建 XML 配置文件,配置创建的对象
- 创建工厂类,在工厂类中通过解析配置文件获得类的全类名,然后使用反射创建对象实例
IOC 接口(BeanFactory)
IOC 主要基于 IOC 容器,而 IOC 容器底层就是对象工厂
Spring 提供 IOC 容器的两种实现方式,即两个接口
- BeanFactory:IOC 容器基本实现方式,Spring 内部使用接口,不建议开发人员使用
BeanFactory 在加载配置文件的时候并不会创建对象,只有在获取使用对象时才会创建(懒汉式)
- ApplicationContext:BeanFactory 的子接口,提供更多定制功能,建议开发人员使用
ApplicationContext 在加载配置文件时就会创建对象(饿汉式)
ApplicationContext 实现类
ApplicationContext 有一些实现类:
其中有两个重要的实现类:
- FileSystemXmlApplicationContext
使用该实现类加载 Xml 需要制定文件在系统盘中的路径
- ClassPathXmlApplicationContext
使用该实现类主要是相对于当前项目代码文件的路径,即 src 下的路径
BeanFactory 也有这两个实现类
IOC 操作 Bean 管理
Bean 管理
Bean 管理主要包括两个操作:
- Spring 创建对象,如入门案例使用 ApplicationContext 读取配置文件然后创建对象
- Spring 注入属性,即根据类的 set 方法设置对象相关属性值
主要包括两种实现方式
- 继续 XML 配置文件
- 基于注解
基于 XML 配置文件
基于 XML 创建对象
- 准备配置文件
在配置文件中使用 bean 标签配置
<bean id="user" class="com.sict.spring5.User" name=""></bean>
bean 标签常用属性
- id:给对象起一个标识符
- class:类的全类名
- name:和 id 功能类似,不过该属性中可以有特殊符号,不常用
- 创建对象
创建对象的时候默认使用对象的无参构造器
基于 XML 注入属性
依赖注入
IOC 中有一个概念为 DI,表示依赖注入,也就是注入属性
注入方式有两种:
- 使用相应属性的 set 方法注入
- 使用类的有参构造器注入
set 方法注入
- 在对象中设置属性的 set 方法
public class Book {
private String name;
private String author;
public void setAuthor(String author) {
this.author = author;
}
public void setName(String name) {
this.name = name;
}
}
- 在 Spring 配置文件中配置对象创建,以及属性注入
<bean id="book" class="com.sict.spring5.Book">
<!-- 使用 property 标签完成属性注入
name:属性名
value:属性要注入的值
-->
<property name="name" value="猎魔人"></property>
<property name="author" value="杰洛特"></property>
</bean>
- 测试
public void testBook1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
Book book = context.getBean("book", Book.class);
book.testDemo();
}
有参构造器注入
- 设置好有参构造器
public class Order {
private String name;
private String address;
public Order(String name, String address) {
this.name = name;
this.address = address;
}
public void testDemo() {
System.out.println(name + ", " + address);
}
}
- 在 Spring 配置文件中配置对象创建,以及属性注入
<bean id="order" class="com.sict.spring5.Order">
<!-- 使用 constructor-arg 标签完成属性注入
name:属性名
value:属性要注入的值
-->
<constructor-arg name="name" value="羊肉汤"></constructor-arg>
<constructor-arg name="address" value="洛阳"></constructor-arg>
</bean>
- 测试
public void testOrder1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
Order order = context.getBean("order", Order.class);
order.testDemo();
}
set 注入简化 – p 名称空间注入
配置文件头部依赖加上
xmlns:p="http://www.springframework.org/schema/p"
在配置文件中配置创建对象方式:
<bean id="book" class="com.sict.spring5.Book" p:author="单的列宁" p:name="66666"></bean>
这里使用标签 p:属性名=属性值 注入对应属性
xml 注入其他类型数据
null 值
在配置文件中配置属性时,可以通过以下方法将属性设置为空值
<bean id="book" class="com.sict.spring5.Book">
<property name="publish">
<null></null>
</property>
</bean>
带特殊符号的内容
在配置文件中配置属性时,属性值中不可以包含特殊符号(如 “<” 、 “>”)可以通过以下方法使属性值包含特殊符号
- 使用转义字符代替,如
< >
表示 “<” 、 “>” - 把带特殊符号的内容写到
<![CDATA[特殊内容]]>
中
<bean id="book" class="com.sict.spring5.Book">
<!-- 属性值包含特殊符号
1. 在特殊符号前加上转义 < >
2. 把带特殊符号的内容写到 <![CDATA[特殊内容]]> 中
-->
<property name="author">
<value><![CDATA[<<南京>>]]></value>
</property>
</bean>
注入属性–外部 bean
假设现在有两个类:service 类和 dao 类
其中,service 类中调用 dao 类中的方法
dao 接口
public interface UserDao {
void update();
}
dao 实现类
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update sth");
}
}
service 类
public class UserService {
public void add() {
System.out.println("service add sth");
// 创建 UserDao 对象
// 常规方法
UserDao userDao = new UserDaoImpl();
userDao.update();
}
}
也可以在 spring 配置文件中进行配置
首先,需要更改 service 类,加上属性 UserDao
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add sth");
// 创建 UserDao 对象
// 常规方法
UserDao userDao = new UserDaoImpl();
userDao.update();
userDao.update();
}
}
然后在配置文件中配置:
<!-- service 和 dao 对象创建-->
<bean id="userService" class="com.sict.spring5.service.UserService">
<!-- 注入 userDao 对象
name 属性名
ref 属性值对应的对象在配置文件中的 id
-->
<property name="userDao" ref="userDao"></property>
</bean>
编写测试:
public void testAdd() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
注入属性–内部 bean
假设现在有两个类
部门类
public class Dept {
private String name;
public void setName(String name) {
this.name = name;
}
}
员工类
public class Empl {
private String name;
private String gender;
// 员工所属部门
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void add() {
System.out.println(name + ", " + gender + ", " + dept);
}
}
其中,一个部门可以有多个员工,这两个类是一对多的关系
于是可以在 spring 配置文件中进行如下配置:
<bean id="emp" class="com.sict.spring5.bean.Empl">
<!-- 设置普通属性(字面量属性) name 和 gender-->
<property name="gender" value="男"/>
<property name="name" value="jerry"/>
<!-- 设置对象属性-->
<property name="dept">
<!-- 内部 bean-->
<bean id="dept" class="com.sict.spring5.bean.Dept">
<property name="name" value="系统集成"/>
</bean>
</property>
</bean>
这里使用内部 bean 配置对象属性
编写测试代码
@Test
public void testBean2() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Empl emp = context.getBean("emp", Empl.class);
emp.add();
}
注入属性–级联赋值
级联赋值就是给属性的属性赋值
首先,需要在有对应属性的属性的 get 方法
修改员工类如下,添加了 dpet 的 get 方法:
public class Empl {
private String name;
private String gender;
// 员工所属部门
private Dept dept;
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void add() {
System.out.println(name + ", " + gender + ", " + dept);
}
}
在配置文件中配置:
<bean id="emp" class="com.sict.spring5.bean.Empl">
<!-- 设置普通属性(字面量属性) name 和 gender-->
<property name="gender" value="男"/>
<property name="name" value="jerry"/>
<!-- 级联赋值 设置对象属性-->
<property name="dept" ref="dept"/>
<property name="dept.name" value="办公室"/>
</bean>
<bean id="dept" class="com.sict.spring5.bean.Dept">
<property name="name" value="School"/>
</bean>
注入属性–集合
- 注入数组类型
- 注入 List 集合
- 注入 Set 集合
假设有一个类 Stu
public class Stu {
private String[] courses;
private Set<String> friends;
private List<String> bills;
private Map<String, Integer> score;
public void testBean() {
for (String cours : courses) {
System.out.println(cours + " ");
}
System.out.println();
for (String friend : friends) {
System.out.println(friend + " ");
}
System.out.println();
for (String bill : bills) {
System.out.println(bill + " ");
}
System.out.println();
score.forEach((s, integer) -> System.out.println(s + ": " + integer));
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setFriends(Set<String> friends) {
this.friends = friends;
}
public void setBills(List<String> bills) {
this.bills = bills;
}
public void setScore(Map<String, Integer> score) {
this.score = score;
}
}
编写配置文件:
<!-- 集合类型注入-->
<bean id="stu" class="com.sict.spring5.collectionType.Stu">
<!-- 数组类型注入-->
<property name="courses">
<array>
<value>计算机组成原理</value>
<value>计算机系统结构</value>
</array>
</property>
<!-- set 类型注入-->
<property name="friends">
<set>
<value>张三</value>
<value>李四</value>
</set>
</property>
<!-- list 类型注入-->
<property name="bills">
<list>
<value>刚刚花了一块钱</value>
<value>现在花了五块钱</value>
</list>
</property>
<!-- map 类型注入-->
<property name="score">
<map>
<entry key="数据结构" value="66"/>
<entry key="算法设计" value="76"/>
</map>
</property>
</bean>
编写测试代码
public void testBead01() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.testBean();
}
集合中注入对象类型
在上面的例子中,集合中存储的都是字符串或者基本类型这些字面量,如果集合中存储的是对象的话,需要用如下方法进行配置
假设现在有一个课程类:
public class Course {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
'}';
}
}
修改类 Stu 为:
public class Stu {
private String[] courses;
private Set<String> friends;
private List<String> bills;
private Map<String, Integer> score;
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public void testBean() {
for (Course cours : courseList) {
System.out.println(cours + " ");
}
System.out.println();
for (String friend : friends) {
System.out.println(friend + " ");
}
System.out.println();
for (String bill : bills) {
System.out.println(bill + " ");
}
System.out.println();
score.forEach((s, integer) -> System.out.println(s + ": " + integer));
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setFriends(Set<String> friends) {
this.friends = friends;
}
public void setBills(List<String> bills) {
this.bills = bills;
}
public void setScore(Map<String, Integer> score) {
this.score = score;
}
}
于是配置 courseList 方法为:
<!-- 集合类型注入-->
<bean id="stu" class="com.sict.spring5.collectionType.Stu">
<!-- 数组类型注入-->
<property name="courses">
<array>
<value>计算机组成原理</value>
<value>计算机系统结构</value>
</array>
</property>
<!-- set 类型注入-->
<property name="friends">
<set>
<value>张三</value>
<value>李四</value>
</set>
</property>
<!-- list 类型注入-->
<property name="bills">
<list>
<value>刚刚花了一块钱</value>
<value>现在花了五块钱</value>
</list>
</property>
<!-- map 类型注入-->
<property name="score">
<map>
<entry key="数据结构" value="66"/>
<entry key="算法设计" value="76"/>
</map>
</property>
<!-- list 类型注入对象-->
<property name="courseList">
<list>
<ref bean="course01"/>
<ref bean="course02"/>
<ref bean="course03"/>
<ref bean="course04"/>
</list>
</property>
</bean>
<bean id="course01" class="com.sict.spring5.collectionType.Course">
<property name="name" value="数据结构"/>
</bean>
<bean id="course02" class="com.sict.spring5.collectionType.Course">
<property name="name" value="算法设计"/>
</bean>
<bean id="course03" class="com.sict.spring5.collectionType.Course">
<property name="name" value="组成原理"/>
</bean>
<bean id="course04" class="com.sict.spring5.collectionType.Course">
<property name="name" value="操作系统"/>
</bean>
提取集合注入部分
将配置文件中集合配置部分提取出来,作为一个公共部分
现在有一个类 Extraction
public class Extraction {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
}
- 首先,需要在 spring 配置文件头部引入名称空间 util
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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.xsd">
- 使用 util 标签完成 list 集合初始化
<!--提取 list 集合作为公共部分-->
<util:list id="extractList">
<!-- 如果元素是对象,需要使用标签 <ref> -->
<value>杰瑞狗</value>
<value>jerry dog</value>
<value>Jerry</value>
</util:list>
- 注入提取后的 list 集合作为属性
<!-- 注入公共部分的 list 集合-->
<bean id="extract" class="com.sict.spring5.collectionType.Extraction">
<property name="list" ref="extractList"/>
</bean>
这样就完成了 list 集合的提取和注入
FactoryBean
Spring 有两种类型的 bean,一种是普通 bean,一种是 FactoryBean
- 普通 bean 就是之前提到的配置文件,在配置文件中 <bean> 标签的属性 class 对应的就是要返回类的全类名
- FactoryBean 配置中的定义类型与返回类型可以不一致
使用方式
- 创建类,让该类作为 FactoryBean(工厂 bean),实现接口 FactoryBean
- 实现接口里的方法,在实现的方法中定义返回的 bean 的类型
public class MyBean implements FactoryBean<Course> {
// 定义返回 bean 的类型
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setName("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return Course.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
然后在配置文件中配置工厂 bean
<bean id="myBean" class="com.sict.spring5.factoryBean.MyBean">
</bean>
编写测试
public void testBead03() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Course myBean = context.getBean("myBean", Course.class);
System.out.println(myBean);
}
输出结果:
Course{name='abc'}
Bean 的作用域
在 Spring 里面,可以设置创建 bean 实例是单实例还是多实例
默认情况下,都是单实例
比如,之前提到的 Extraction 类的测试,修改为下:
public void testBead02() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
Extraction extract1 = context.getBean("extract", Extraction.class);
Extraction extract2 = context.getBean("extract", Extraction.class);
System.out.println(extract1);
System.out.println(extract2);
}
输出结果:
com.sict.spring5.collectionType.Extraction@2a65fe7c
com.sict.spring5.collectionType.Extraction@2a65fe7c
可以看到两个对象地址一致,也就是说明返回的 bean 是单实例的
单实例、多实例设置方法
在配置文件中 bean 标签中有属性 scope 用于设置单实例还是多实例
scope 有两个值可以设置:
- singleton 表示单实例,默认值
- prototype 表示多实例
于是可以配置文件中写上:
<!--提取 list 集合作为公共部分-->
<util:list id="extractList">
<!-- 如果元素是对象,需要使用标签 <ref> -->
<value>杰瑞狗</value>
<value>jerry dog</value>
<value>Jerry</value>
</util:list>
<!-- 注入公共部分的 list 集合-->
<bean id="extract" class="com.sict.spring5.collectionType.Extraction" scope="prototype">
<property name="list" ref="extractList"/>
</bean>
重新运行测试代码,结果为:
com.sict.spring5.collectionType.Extraction@2a65fe7c
com.sict.spring5.collectionType.Extraction@4135c3b
可以看到两个对象地址不同,也就是返回两个不一样的对象
singleton 和 prototype 区别
- singleton 单实例、prototype 多实例
- scope 为 singleton,加载 spring 配置文件的时候就会创建单实例对象
- scope 为 prototype,调用 getBean() 方法时才会创建对象
bean 生命周期
对象创建到销毁的过程就是对象的生命周期
bean 的生命周期包含:
- 通过参数构造器(无参构造器)创建 bean 实例
- 为 bean 的属性设置值或对其它 bean 的引用(调用 set 方法)
- 调用 bean 的初始化方法(需要进行配置)
- 使用 bean
- 容器关闭时,调用销毁 bean 的销毁方法(需要进行配置)
代码演示:
- 先创建一个 Orders 类
public class Orders {
private String name;
public void setName(String name) {
this.name = name;
System.out.println("2. set 方法被调用,用来设置值");
}
public Orders() {
System.out.println("1. 无参构造器被调用,用来创建 bean 实例");
}
// 创建 bean 执行的初始化的方法
public void initMethod() {
System.out.println("3. 执行初始化的方法");
}
// 销毁 bean 执行的销毁方法
public void destoryMethod() {
System.out.println("5. 执行销毁方法");
}
}
- 配置 bean
<!-- init-method 即初始化方法
destroy-method 即销毁方法
-->
<bean id="orders" class="com.sict.spring5.bean.Orders" init-method="initMethod" destroy-method="destoryMethod">
<property name="name" value="饺子"/>
</bean>
- 编写测试代码
public void testBead04() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("4. 使用 bean");
System.out.println(orders);
// 手动销毁 bean 实例
// close 方法是 ClassPathXmlApplicationContext 类的方法,需要强转
((ClassPathXmlApplicationContext) context).close();
}
输出结果为:
1. 无参构造器被调用,用来创建 bean 实例
2. set 方法被调用,用来设置值
3. 执行初始化的方法
4. 使用 bean
com.sict.spring5.bean.Orders@515aebb0
5. 执行销毁方法
bean 的后置处理器
bean 的生命周期还有更完整的两个步骤(bean 的后置处理器),补充之前的步骤后如下:
- 通过参数构造器(无参构造器)创建 bean 实例
- 为 bean 的属性设置值或对其它 bean 的引用(调用 set 方法)
- 把 bean 实例传递给 bean 的后置处理器的方法
postProcessBeforeInitialization
- 调用 bean 的初始化方法(需要进行配置)
- 把 bean 实例传递给 bean 的后置处理器的方法
postProcessAfterInitialization
- 使用 bean
- 容器关闭时,调用销毁 bean 的销毁方法(需要进行配置)
添加的代码演示:
- 添加自定义的 bean 后置处理器类,实现 BeanPostProcessor 接口
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
// 初始化之前调用的方法
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前执行的方法");
return bean;
}
@Override
// 初始化之后调用的方法
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后执行的方法");
return bean;
}
}
- 在配置文件中配置后置处理器,该后置处理器应用于整个配置文件
<!-- 配置后置处理器-->
<bean id="myBeanPostProcessor" class="com.sict.spring5.bean.MyBeanPostProcessor"></bean>
执行测试代码后的结果为:
1. 无参构造器被调用,用来创建 bean 实例
2. set 方法被调用,用来设置值
初始化之前执行的方法
3. 执行初始化的方法
初始化之后执行的方法
4. 使用 bean
com.sict.spring5.bean.Orders@6c64cb25
5. 执行销毁方法
XML 自动装配
之前提到的配置方法,通过指定属性名称和值配置初始化 bean 实例的方法就是手动配置
自动装配是指根据指定装配规则(属性名称或者类型),Spring 自动将匹配的属性值进行注入
bean 标签有属性 autowire,配置自动装配
autowire 有两个常用属性值:
- byName 根据属性名称注入,注入值 bean 的 id 值和类的属性名称要一致
- byType 根据属性类型注入
根据属性名称自动装配
假设有两个类:
public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
public void test() {
System.out.println(dept);
}
}
在配置文件中配置:
<bean id="dept" class="com.sict.spring5.autowire.Dept">
</bean>
<!-- 实现自动装配
bean 标签有属性 autowire,配置自动装配
autowire 有两个常用属性值:
byName 根据属性名称注入,注入值 bean 的 id 值和类的属性名称要一致
byType 根据属性类型注入
-->
<bean id="emp" class="com.sict.spring5.autowire.Emp" autowire="byName">
<!-- 手动装配-->
<!-- <property name="dept" ref="dept"/>-->
</bean>
编写测试:
public void testBead05() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean5.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
根据属性类型自动装配
在配置文件中进行配置:
<bean id="dept" class="com.sict.spring5.autowire.Dept">
</bean>
<!-- 实现自动装配
bean 标签有属性 autowire,配置自动装配
autowire 有两个常用属性值:
byName 根据属性名称注入,注入值 bean 的 id 值和类的属性名称要一致
byType 根据属性类型注入
-->
<bean id="emp" class="com.sict.spring5.autowire.Emp" autowire="byType">
<!-- 手动装配-->
<!-- <property name="dept" ref="dept"/>-->
</bean>
注意,根据属性类型自动装配,在配置文件中只能由一个对应类型的 bean,如果有多个将会出错
引入外部属性文件
在配置文件中可以引入外部文件来配置属性
比如配置 druid 数据库连接池有两种方式
- 直接配置数据库信息
<!-- 直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.driver"/>
<property name="url" value="jdbc:mysql://localhost/userDb"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
- 引入外部属性文件配置数据库连接池
先在配置文件头部引入 context 名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
准备一个 properties 文件在 src 目录下:
prop.driverClass=com.mysql.jdbc.Driver
prop.url=com.mysql.jdbc.driver
prop.username=root
prop.password=root
然后在配置文件中引入:
<!-- 引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.username}"/>
<property name="password" value="${prop.password}"/>
</bean>
基于注解方式
什么是注解
- 注解是代码特殊标记,格式
@注解名称(属性名称=属性值...)
- 注解可以作用在类、方法、属性上面
- 注解的目的是为了简化 XML 配置
Spring 针对 Bean 管理提供了四种注解:
- @component 普通组件
- @Service 用在 service 层或者业务逻辑层
- @Controller 用在 web 层
- @Repository 用在 dao 层或者持久层
这四个注解都可以用来创建 bean 实例,不用注解一般用在不同层中
基于注解创建对象
-
引入依赖
-
开启组件扫描,使 Spring 扫描加注解的类
引入 context 名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
开启组件扫描
<!-- 开启组件扫描-->
<!-- 多个包开启扫描可以使用 , 隔开-->
<!-- <context:component-scan base-package="com.sict.spring5.dao, com.sict.spring5.service"/>-->
<!-- 也可以直接写上层目录-->
<context:component-scan base-package="com.sict.spring5"/>
- 创建类,在类名上加上注解
// 等价于 <bean id="userService" class="..."/>
// value 属性值可以忽略,默认值为 首字母小写后的类名称
@Service(value = "userService")
public class UserService {
public void add() {
System.out.println("service add....");
}
}
- 编写测试代码
public void testService() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
输出结果:
service add....
组件扫描的细节配置
在上面的例子中,开启组件扫描时会扫描包下所有的类
实际上可以配置具体扫描的类
<!-- 示例 1
use-default-filters="false" 表示不使用默认扫描规则,即不使用全包扫描
context:include-filter 表示要扫描的内容
-->
<context:component-scan base-package="com.sict" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!-- 示例 2
默认扫描全包
context:exclude-filter 表示不进行扫描的内容
-->
<context:component-scan base-package="com.sict">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
即默认规则即为全包扫描,可以通过use-default-filters="false"
关闭全包扫描,然后通过context:include-filter
表示要扫描的内容;
也可以在全包扫描时通过context:exclude-filter
过滤不进行扫描的内容
通过注解实现属性注入
三种常用的属性注入的注解为:
- @AutoWired 根据属性类型自动注入
- @Qualifier 根据属性名称自动注入
- @Resource 可以根据属性类型,也可根据属性名称
- @Value 注入字面量
@AutoWired
- 创建 service 和 dao 类,在类中加入对象注解
@Service(value = "userService")
public class UserService {
public void add() {
System.out.println("service add....");
}
}
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("dao add...");
}
}
- 在 service 添加 dao 属性,在属性上加上注解
@Service(value = "userService")
public class UserService {
// 使用该注解不需要加上 set 方法
@Autowired
private UserDao userDao;
public void add() {
userDao.add();
System.out.println("service add....");
}
}
@Qualifier
该注解需要和 @Autowired 一起使用
在上面的例子中,如果属性对应接口有多个实现类,就可能会引发错误
这时候就可以使用 @Qualifier 注解指定具体实现类
首先给 UserDao 加上自定义名称
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("dao add...");
}
}
然后在 UserService 中加上 @Qualifier 注解
// 等价于 <bean id="userService" class="..."/>
// value 属性值可以忽略,默认值为 首字母小写后的类名称
@Service(value = "userService")
public class UserService {
@Autowired
@Qualifier(value = "userDaoImpl1")
private UserDao userDao;
public void add() {
userDao.add();
System.out.println("service add....");
}
}
@Resource
直接在属性上加上 @Resource 注解,默认即为根据类型注入
@Service(value = "userService")
public class UserService {
// 根据类型注入
@Resource
private UserDao userDao;
public void add() {
userDao.add();
System.out.println("service add....");
}
}
如果在 @Resource 后面加上属性 name 那么即为根据名称注入
@Service(value = "userService")
public class UserService {
// 根据名称注入
@Resource(name = "userDaoImpl1")
private UserDao userDao;
public void add() {
userDao.add();
System.out.println("service add....");
}
}
注意,@Resource 注解不是 spring 官方的包,而是 javax.annotation 包下
@Value
修改 UserService 类
@Service(value = "userService")
public class UserService {
// 根据名称注入
@Resource(name = "userDaoImpl1")
private UserDao userDao;
// 注入字符串 abc
@Value(value = "abc")
private String name;
public void add() {
userDao.add();
System.out.println("service add...." + name);
}
}
全注解开发
即不使用配置文件,仅使用注解开发
步骤如下:
- 创建配置类,替代配置文件
@Configuration // 加上该注解,该类即为配置类,可以替代 xml 配置文件
@ComponentScan(basePackages = {"com.sict"}) // 开启组件扫描
public class SpringConfig {
}
- 和之前步骤一致,创建 service 和 dao 类,加上相应注解