Spring5之IOC
Spring 概述
- Spring 是轻量级且开源的 JavaEE 框架。
- Spring 可以解决企业应用开发的复杂性。
- Spring 有两个核心部分:IOC 和 AOP
IOC
:控制反转,把创建对象过程交给 Spring 管理AOP
:面向切面,不修改源代码进行功能增强- 注意:这两个只是核心部分,而Spring还有其它的部分!
Spring 特点
- 方便解耦,简化开发
- AOP 编程的支持
- 方便程序的测试
- 方便集成各种优秀框架(比如:Mybatis)
- 方便进行事务操作
- 降低 API 开发
入门案例
一、下载 Spring5
打开 https://spring.io/projects/spring-framework#learn 然后点击右上角的 GitHub,这里最新的稳定版本为:Spring5.3.13
进入 GitHub 后,找到并点击 Access to Binaries 中的链接
进入下载页面
找到对应目录下的 Spring 目录,然后点击打开对应的下载页面
下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring
二、创建一个java工程
- 创建 java 工程很简单,这里就直接省略了。。。
三、导入 Spring5 的相关 jar 包
将以下五个包(logging是日志包,在网上下一下就好了)导入到刚才创建好的 java 工程中
其它几个包都在 libs 目录中(这个目录在刚才下载的 Spring 中)
创建lib文件夹,然后把包丢进去
最后引入jar包就好了
四、编写代码进行测试
创建普通类和一个普通方法
package com.spring.pojo;
public class User {
public void add() {
System.out.println("add......");
}
}
创建 Spring 配置文件,在配置文件中配置创建的对象
<?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.xsd">
<!--
配置 User 对象创建
id:给对象取的名称
class:指定对象
-->
<bean id="user" class="com.spring.test.User" />
</beans>
说明:Spring 配置文件使用 xml 格式,文件名没什么要求,但是实际开发中还是建议取个比较能看懂的名字
创建测试类,编写测试方法
package com.spring.test;
import com.spring.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void testAdd() {
// 1.加载 Spring 的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 2.获取配置文件中创建的对象
User user = context.getBean("user", User.class);
System.out.println(user); // 打印地址值
user.add(); // 打印 add.....
}
}
目录结构
IOC 容器
什么是 IOC?
1、IOC:控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
2、使用 IOC 的目的:为了降低耦合度
3、上面的入门案例就是 IOC 实现
底层原理
使用的技术
1、xml 解析
2、工厂模式
3、反射
原始调用对象的缺点
在原始方式中,我们如果需要调用某个类中的方法或者变量,就需要先 new 对象,然后在通过这个对象去调用所需要的值。
为什么会导致耦合度较高呢?
根据上图来说,UserService 和 UserDao 是进行的直接引用,如果 UserDao 改变了存放的位置,UserService 就获取不到了。
工厂模式的解耦过程
解析:
在工厂模式中创建 UserDao 对象,然后 UserService 中使用工厂模式获取到 UserDao 对象。
在这个过程中 UserService 就不会直接去 new UserDao 了,从而就能达到一定的解耦效果(注意:虽然有一定的效果,但是还是存在一定的耦合度的,而我们的目的是要将耦合度降到最低)
IOC 过程
- 第一步:xml 配置文件,配置需要创建的对象
- 第二步:创建工厂类,通过反射创建对象
此时如果 UserDao 改变了存放路径,那么我们只需要更改 xml 文件中的对象路径即可。
IOC(接口)
-
IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂。
-
Spring 提供了 IOC 容器实现的两种方式(两个接口):
BeanFactoty
和ApplicationContext
-
BeanFactory
:是 IOC 容器中最基本的实现方式,该接口是 Spring 内部的使用接口,不提供给开发人员进行使用(但也可以用)特点:加载配置文件的时候不会创建对象,在获取对象(使用对象)的时候才去创建对象。
-
ApplicationContext
:是 BeanFactory 接口的子接口,提供更多更强大的功能,一般是提供给开发人员进行使用特点:加载配置文件的时候把在配置文件中的对象进行创建。
ApplicationContext 接口中的实现类说明
ClassPathXmlApplicationContext 实现类:
- 根据类路径(相对路径)获取对应的对象
FileSystemXmlApplicationContext 实现类:
- 根据磁盘路径(绝对路径)获取对应的对象
IOC 容器之Bean 管理
什么是Bean管理?
- Bean 管理指的是两个操作:Spring 创建对象 和 Spring 注入属性
Bean 管理是比较官方的说法,可以简单理解为上面的两个操作。
Bean 管理的两种操作方式
- 基于 xml 配置文件的方式实现
- 基于注解的方式实现
IOC 基于 xml 配置文件操作 Bean 管理
一、基于 xml 方式创建对象
<?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.xsd">
<!--配置 User 对象创建-->
<bean id="user" class="com.spring.pojo.User" />
</beans>
说明:
-
在 Spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
-
在 bean 标签中有很多属性,常用的属性说明:
id 属性
:唯一标识,可以理解为是一个不能重复的对象别名class 属性
:类的全路径name
:和 id 的作用差不多,但是 id 中的值不能添加一些符号,而 name 值中可以(比如 斜杆(/)之类的),但是 name 并不常用…
-
创建对象的时候,默认也是执行无参构造方法(大家可以测试一下,在User类中创建一个有参构成方法,然后再去执行测试方法,就会报:NoSuchMethodException)
二、基于 xml 方式注入属性
- DI:依赖注入,说白了也就是注入属性
第一种注入方式:使用 set 方法
原始写法:
package com.spring.pojo;
public class Book {
private String bname;
public void setBname(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
Book book = new Book();
book.setBname("小白");
}
}
Spring 写法:
- 创建类,并定于属性和对应的 set 方法
package com.spring.pojo;
/**
* @ClassName Book
* @Description: 使用 set 方法注入属性
* @Author Laoyang
* @Date 2021/12/16 12:32
*/
public class Book {
private String bname;
private String bauthor;
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
public void demo() {
System.out.println(bname + "---" + bauthor);
}
}
- 在 Spring 配置文件中配置对象创建,在配置属性注入
<?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.xsd">
<!-- 1. 配置对象创建 -->
<bean id="book" class="com.spring.pojo.Book">
<!--
2. set 方法注入属性(使用 property 标签实现)
name属性:类中的属性名称
value属性:向属性中注入的值
-->
<property name="bname" value="little Prince" />
<property name="bauthor" value="Anthony" />
</bean>
</beans>
- 进行测试
package com.spring.test;
import com.spring.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testDemo() {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
// 获取配置文件中创建的对象
Book book = context.getBean("book", Book.class);
System.out.println(book); // 地址值
book.demo(); // little Prince---Anthony
}
}
第二种注入方式:使用有参构造器
原始写法:
package com.spring.pojo;
public class Book {
private String bname;
public Book(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
Book book = new Book("小白");
}
}
Spring 写法:
- 创建类,定义属性,并创建对应的有参构造器
package com.spring.pojo;
/**
* @ClassName Order
* @Description: 使用有参构造注入属性
* @Author Laoyang
* @Date 2021/12/16 15:34
*/
public class Order {
private String oname;
private String address;
// 有参构造器
public Order(String oname, String address) {
this.oname = oname;
this.address = address;
}
}
- 在 Spring 的配置文件中进行配置
<?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.xsd">
<!-- 1. 配置创建对象 -->
<bean id="order" class="com.spring.pojo.Order">
<!--
2. 有参构造实现属性注入(使用 constructor-arg 标签实现)
name属性:类中的属性名称
value属性:向属性中注入的值
-->
<constructor-arg name="oname" value="Facial mask" />
<constructor-arg name="address" value="China" />
<!-- 通过参数索引值进行注入(不推荐)
<constructor-arg index="0" value="Facial mask" />
<constructor-arg index="1" value="China" />
-->
</bean>
</beans>
- 测试
package com.spring.test;
import com.spring.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class OrderTest {
@Test
public void testOrderDemo() {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
// 获取配置中创建的对象
Order order = context.getBean("order", Order.class);
System.out.println(order); // 地址值
order.orderDemo(); // Facial mask---China
}
}
第三种注入方式:p名称空间注入(了解)
- 使用 p 名称空间注入,可以简化基于 xml 配置方式(简化上面的 set 注入方式)
- 这种方式用的并不多,所以大家了解一下就好了
实现步骤:
-
添加 p 名称空间的在配置文件中:
xmlns:p="http://www.springframework.org/schema/p"
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
进行属性注入,在 bean 标签中进行操作
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- set 方法注入属性(使用 property 标签实现)--> <bean id="book" class="com.spring.pojo.Book" p:bname="神雕侠侣" p:bauthor="金庸"/> </beans>
-
测试
package com.spring.test; import com.spring.pojo.Book; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BookTest { @Test public void testDemo() { // 加载 Spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); // 获取配置文件中创建的对象 Book book = context.getBean("book", Book.class); System.out.println(book); // 地址值 book.demo(); // 神雕侠侣---金庸 } }
三、注入属性 - 字面量
注入空值
原始写法:
package com.spring.pojo;
public class Book {
// 空值
private String bname = null;
public getBname(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.getBname());
}
}
Spring 写法:
- 在 Book 类中添加 address 属性,并修改 demo 方法
package com.spring.pojo;
public class Book {
private String bname;
private String bauthor;
private String address;
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
public void setAddress(String address) {
this.address = address;
}
public void demo() {
System.out.println(bname + "---" + bauthor + "---" + address);
}
}
- 在 Spring 配置文件中进行配置
<?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.xsd">
<!-- 创建对象 -->
<bean id="book" class="com.spring.pojo.Book">
<!--
set 方法注入属性(使用 property 标签实现)
name属性:类中的属性名称
value属性:向属性中注入的值
-->
<property name="bname" value="little Prince"/>
<property name="bauthor" value="Anthony"/>
<!-- 在 Book 类中添加 address 字段,然后注入 null 值 -->
<property name="address">
<null/>
</property>
</bean>
</beans>
- 测试
package com.spring.test;
import com.spring.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testDemo() {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
// 获取配置文件中创建的对象
Book book = context.getBean("book", Book.class);
System.out.println(book); // 地址值
book.demo(); // 神雕侠侣---金庸---null
}
}
注入特殊字符
原始写法:
package com.spring.pojo;
public class Book {
// 特殊符号
private String character = "<>";
public getCharacter(String character) {
this.character = character;
}
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.getCharacter());
}
}
Spring 写法:
- 在 Spring 配置文件中进行配置
<?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.xsd">
<bean id="book" class="com.spring.pojo.Book">
<property name="bname" value="little Prince"/>
<property name="bauthor" value="Anthony"/>
<!--属性值中包含特殊字符-->
<!-- <property name="address" value="<<北上广深>>"/>-->
<!-- 方式一:把 <> 进行转义(大于:> 小于:<) -->
<!-- <property name="address" value="<<北上广深>>"/>-->
<!-- 方式二:把带特殊符号的内容写到 CDATA -->
<property name="address">
<value><![CDATA[<<北上广深>>]]></value>
</property>
</bean>
</beans>
- 测试
package com.spring.test;
import com.spring.pojo.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testDemo() {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
// 获取配置文件中创建的对象
Book book = context.getBean("book", Book.class);
System.out.println(book); // 地址值
book.demo(); // 神雕侠侣---金庸---<<北上广深>>
}
}
四、注入属性 - 外部 bean
原始写法:
- 创建 UserService、UserDao(接口)、UserDaoImpl
package com.spring.dao;
public interface UserDao {
public void update();
}
package com.spring.dao;
public class UserDaoImpl implements UserDao {
@Override
public void update() {
System.out.println("dao update.....");
}
}
- 在UserService中调用 UserDao 的方法
package com.spring.service;
import com.spring.dao.UserDao;
import com.spring.dao.UserDaoImpl;
public class UserService {
public void add() {
System.out.println("service add......");
// 原始方式:创建 UserDao 对象进行调用
UserDao dao = new UserDaoImpl();
dao.update();
}
}
Spring 写法:
- 在 UserService 类中添加 userDao 属性
package com.spring.service;
import com.spring.dao.UserDao;
import com.spring.dao.UserDaoImpl;
public class UserService {
// 创建 UserDao 类型属性,生成 set 方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add......");
// 原始方式:创建 UserDao 对象
// UserDao dao = new UserDaoImpl();
// dao.update();
// Spring方式:在配置文件中注入对象然后进行调用
userDao.update();
}
}
- 在 Spring 配置文件中进行配置
<?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.xsd">
<!-- 创建 service 和 dao 的对象 -->
<bean id="userService" class="com.spring.service.UserService">
<!--
注入 userDao 对象
name:类中的属性名称(这里指的是 UserService 中的属性名称)
ref:创建 userDao 对象 bean 标签 id 值
-->
<property name="userDao" ref="userDaoImpl" />
</bean>
<bean id="userDaoImpl" class="com.spring.dao.UserDaoImpl" />
</beans>
- 测试
package com.spring.test;
import com.spring.pojo.User;
import com.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void testServiceAdd() {
// 1.加载 Spring 的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
// 2.获取配置文件中创建的对象
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
/*
打印结果为:
地址值
service add......
dao update.....
*/
}
}
五、注入属性 - 内部 bean
1、一对多:部门和员工(部门是一,员工是多)
2、在实体类中表示部门和员工的关系(因为没使用数据库,所以员工使用对象类型来表示所属部门)
package com.spring.bean;
/**
* @ClassName Dept
* @Description: 部门
* @Author Laoyang
* @Date 2021/12/17 10:43
*/
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept{" +
"dname='" + dname + '\'' +
'}';
}
}
package com.spring.bean;
/**
* @ClassName Emp
* @Description: 员工
* @Author Laoyang
* @Date 2021/12/17 10:44
*/
public class Emp {
private String ename;
private String gender;
// 员工属于某一个部门,使用对象形式表示
private Dept dept;
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void add() {
System.out.println(ename + "---" + gender + "---" + dept);
}
}
3、在 Spring 配置文件中进行相关配置
<?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.xsd">
<!-- 内部 Bean -->
<bean id="emp" class="com.spring.bean.Emp">
<!-- 先设置两个普通的属性 -->
<property name="ename" value="小白"/>
<property name="gender" value="男" />
<!-- 设置对象类型的属性 -->
<!-- 内部注入 -->
<property name="dept">
<bean id="dept" class="com.spring.bean.Dept">
<property name="dname" value="研发部" />
</bean>
</property>
</bean>
</beans>
4、测试
package com.spring.test;
import com.spring.bean.Emp;
import com.spring.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmpTest {
@Test
public void testAdd() {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
// 获取配置中创建的对象
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp); // 地址值
emp.add(); // 小白---男---Dept{dname='研发部'}
}
}
六、注入属性 - 级联赋值
第一种写法
- 在 Spring 配置文件中进行相关配置
<?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.xsd">
<!-- 级联赋值 -->
<bean id="emp" class="com.spring.bean.Emp">
<!-- 先设置两个普通的属性 -->
<property name="ename" value="小白"/>
<property name="gender" value="男" />
<!-- 级联赋值 -->
<property name="dept" ref="dept" />
</bean>
<bean id="dept" class="com.spring.bean.Dept">
<property name="dname" value="人事部" />
</bean>
</beans>
- 测试
package com.spring.test;
import com.spring.bean.Emp;
import com.spring.pojo.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmpTest {
@Test
public void testAdd2() {
// 加载 Spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");
// 获取配置中创建的对象
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp); // 地址值
emp.add(); // 小白---男---Dept{dname='人事部'}
}
}
第二种写法
- 在 emp 类中设置 dept 的 get 方法
package com.spring.bean;
/**
* @ClassName Emp
* @Description: 员工
* @Author Laoyang
* @Date 2021/12/17 10:44
*/
public class Emp {
private String ename;
private String gender;
// 员工属于某一个部门,使用对象形式表示
private Dept dept;
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
// 生成 dept 的 get 方法
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void add() {
System.out.println(ename + "---" + gender + "---" + dept);
}
}
- 在 Spring 配置文件中进行相关配置
<?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.xsd">
<!-- 级联赋值 -->
<bean id="emp" class="com.spring.bean.Emp">
<!-- 先设置两个普通的属性 -->
<property name="ename" value="小白"/>
<property name="gender" value="男" />
<!-- 级联赋值 -->
<!-- 注意:这种写法必须要有对象属性的 get 方法,否则就获取不到 -->
<property name="dept.dname" value="财务部" />
</bean>
</beans>
七、注入集合属性
步骤
- 创建实体类(在里面编写集合类型的属性和set方法)
- 在 Spring 配置文件编写相关配置,注入相关属性
- 测试
相关类型:数组、List、Map、Set
具体实现
1、创建实体类(在里面编写集合类型的属性和set方法)
package com.laoyang.spring.collectiontype;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
/**
* 创建一个数组类型的属性
*/
private String[] courses;
/**
* 创建一个List集合类型的属性
*/
private List<String> list;
/**
* 创建一个Map集合类型的属性
*/
private Map<String, String> maps;
/**
* 创建一个Set集合类型的属性
*/
private Set<String> set;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSet(Set<String> set) {
this.set = set;
}
/**
* 测试方法
*/
public void add() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(maps);
System.out.println(set);
}
}
2、在 Spring 配置文件编写相关配置,注入相关属性
<?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.xsd">
<!-- 集合类型属性注入 -->
<bean id="stu" class="com.laoyang.spring.collectiontype.Stu" >
<!-- 注入数组类型的属性 -->
<property name="courses">
<array>
<value>java</value>
<value>mysql</value>
<!-- ..... -->
</array>
</property>
<!-- 注入List集合类型的属性 -->
<property name="list">
<list>
<value>小李</value>
<value>小王</value>
<!-- ..... -->
</list>
</property>
<!-- 注入Map集合类型的属性 -->
<property name="maps">
<map>
<entry key="世界第一" value="java" />
<entry key="世界第二" value="c++" />
<!-- ..... -->
</map>
</property>
<!-- 注入Set集合类型的属性 -->
<property name="set">
<set>
<value>嘿嘿嘿</value>
<value>哈哈哈</value>
<!-- ..... -->
</set>
</property>
</bean>
</beans>
3、测试
package com.laoyang.spring.test;
import com.laoyang.spring.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StuTest {
@Test
public void testAdd() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Stu stu = context.getBean("stu", Stu.class);
System.out.println(stu);
stu.add();
/*
打印结果为:
地址值
[java, mysql]
[小李, 小王]
{世界第一=java, 世界第二=c++}
[嘿嘿嘿, 哈哈哈]
*/
}
}
思考问题
- 上面的案例中,都是插入的 String 类型的数据,如果我想要插入一个对象类型,该如何实现呢?
- 上面的案例中,所有的集合属性都放在了 Stu 对象中,这样的话就不能给其它对象进行使用了,那么我们怎么把集合数据变为公共的呢?
解决问题
一、在集合里面设置对象类型的值
- 创建课程类,并在 Stu 类中创建课程集合属性
package com.laoyang.spring.collectiontype;
/**
* @ClassName Course
* @Description: 课程类
* @Author Laoyang
* @Date 2021/12/17 11:54
*/
public class Course {
/**
* 课程名称
*/
private String cname;
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "Course{" +
"cname='" + cname + '\'' +
'}';
}
}
package com.laoyang.spring.collectiontype;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
// ... 此处省略前面的代码
/**
* 课程集合属性
*/
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public void show() {
System.out.println(courseList);
}
}
- 在 Spring 配置文件中进行相关配置
<?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.xsd">
<!-- 集合类型属性注入 -->
<bean id="stu" class="com.laoyang.spring.collectiontype.Stu" >
<!-- ...此处省略前面的代码 -->
<!-- 注入课程集合类型的属性 -->
<property name="courseList">
<list>
<ref bean="course1" />
<ref bean="course2" />
</list>
</property>
</bean>
<bean id="course1" class="com.laoyang.spring.collectiontype.Course">
<property name="cname" value="Spring5课程" />
</bean>
<bean id="course2" class="com.laoyang.spring.collectiontype.Course">
<property name="cname" value="SpringBoot课程" />
</bean>
<!-- ...... -->
</beans>
- 测试
package com.laoyang.spring.test;
import com.laoyang.spring.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StuTest {
@Test
public void testShow() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Stu stu = context.getBean("stu", Stu.class);
System.out.println(stu);
stu.show(); // [Course{cname='Spring5课程'}, Course{cname='SpringBoot课程'}]
}
}
二、把集合提取出来,当做公共数据
- 创建Book类,用来测试效果
package com.laoyang.spring.collectiontype;
import java.util.List;
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
public void show() {
System.out.println(list);
}
}
- 在 Spring 配置文件中引入 util 名称空间
在xml文件头部声明中添加如下配置(具体配置位置,可看后面的代码):
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
- 使用 util 标签提取并使用 List 集合类型的属性
<?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: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"
>
<!-- 提取List集合类型的属性注入 -->
<util:list id="bookList">
<value>第一</value>
<value>第二</value>
<value>第三</value>
</util:list>
<!-- 提取出来的 List 集合该如何使用? -->
<bean id="book" class="com.laoyang.spring.collectiontype.Book">
<property name="list" ref="bookList" />
</bean>
</beans>
- 测试
package com.laoyang.spring.test;
import com.laoyang.spring.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testShow() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book);
book.show(); // [第一, 第二, 第三]
}
}
工厂 Bean(FactoryBean)
说明
- Spring 有两种 Bean,一种是普通 Bean,另一种就是工厂 Bean(FactoryBean)
- 普通 Bean:在配置文件中定义的 bean 类型就是返回类型。
- 工厂 Bean:在配置文件中定义的 bean 类型可以和返回类型不一样。
代码演示
- 第一步:创建一个类,让这个类作为工厂 Bean,实现 FactoryBean 接口
- 第二步:实现接口里面的方法,在实现的方法中定义返回的 Bean 类型
package com.laoyang.spring.factorybean;
import com.laoyang.spring.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;
/**
* 第一步:创建一个类,让这个类作为工厂 Bean,实现 FactoryBean 接口
* 第二步:实现接口里面的方法,在实现的方法中定义返回的 Bean 类型
*/
public class MyBean implements FactoryBean<Course> {
/**
* 定义返回的 bean 对象
*/
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
}
配置文件:
<?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.xsd">
<bean id="myBean" class="com.laoyang.spring.factorybean.MyBean" />
</beans>
测试:
package com.laoyang.spring.test;
import com.laoyang.spring.collectiontype.Book;
import com.laoyang.spring.collectiontype.Course;
import com.laoyang.spring.factorybean.MyBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyBeanTest {
@Test
public void testShow() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
// 未定义工厂 bean 时会返回 MyBean 的对象
// MyBean myBean = context.getBean("myBean", MyBean.class);
// System.out.println(myBean);
// 定义工厂 bean 后会返回工厂 bean 所返回的类型对象
Course myBean = context.getBean("myBean", Course.class);
System.out.println(myBean);
}
}
大家可以看到,配置文件中创建的是MyBean对象,而这里使用的是 Course 对象,因为工厂类中返回的就是Course 对象,如果这里还是使用 MyBean 就会报 BeanNotOfRequiredTypeException 异常
如果大家有兴趣尝试,可以先不让 MyBaen 类实现 FactoryBean 接口,然后测试查看效果
Bean 的作用域
- Bean 的作用域:在 Spring 当中设置创建 bean 的实例是单实例还是多实例
- 在 Spring 当中,默认情况下,创建的 bean 是单实例对象
验证当前是不是单实例
- 在测试方法中调用两次,然后进行打印
- 如果是单实例,此时打印的地址值应该是一样的
- 如果是多实例,此时打印的地址值应该是不一样的
package com.laoyang.spring.test;
import com.laoyang.spring.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testShow2() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Book book1 = context.getBean("book", Book.class);
Book book2 = context.getBean("book", Book.class);
/*
如果是单实例,此时打印的地址值应该是一样的
如果是多实例,此时打印的地址值应该是不一样的
*/
System.out.println(book1);
System.out.println(book2);
}
}
设置多实例
-
在 Spring 配置文件 bean 标签中有属性(
scope
)用于设置单实例还是多实例 -
scope 属性值:
singleton
:默认值,表示是单实例对象prototype
:表示是多实例对象 -
singleton 和 prototype 的区别:
1、singleton 表示单实例,prototype 表示多实例。
2、如果 scope 设置为 singleton,那么加载配置文件的时候就会创建单实例对象;设置为 prototype 的时候,不是加载 Spring 配置文件的时候创建的对象,而是在调用 getBean 方法时才会创建多实例对象。
修改 Spring 配置:
<?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: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"
>
<!-- 提取List集合类型的属性注入 -->
<util:list id="bookList">
<value>第一</value>
<value>第二</value>
<value>第三</value>
</util:list>
<!-- 将Book对象设置为多实例对象 -->
<bean id="book" class="com.laoyang.spring.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList" />
</bean>
</beans>
测试:
package com.laoyang.spring.test;
import com.laoyang.spring.collectiontype.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookTest {
@Test
public void testShow2() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Book book1 = context.getBean("book", Book.class);
Book book2 = context.getBean("book", Book.class);
/*
如果是单实例,此时打印的地址值应该是一样的
如果是多实例,此时打印的地址值应该是不一样的
*/
System.out.println(book1);
System.out.println(book2);
}
}
Bean 的生命周期
- 生命周期:从对象创建到对象销毁的过程就被称为生命周期。
Bean 生命周期的过程:
- 通过构造器创建 bean 实例(无参构造器)
- 为 bean 的属性设置值和对其它 bean 的引用(调用 set 方法)
- 调用 bean 的初始化方法(需要进行配置初始化的方法)
- 使用 bean (对象获取到了)
- 当容器关闭的时候,调用 bean 的销毁方法(需要进行配置销毁的方法)
演示 Bean 的生命周期
- 创建一个类,用来配合演示
package com.laoyang.spring.bean;
public class Orders {
private String oname;
public Orders() {
System.out.println("第一步:执行无参构造器创建 bean 实例");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步:执行set方法,设置属性值");
}
public void initMethod() {
System.out.println("第三步:执行初始化方法");
}
public void destroyMethod() {
System.out.println("第五步:执行销毁的方法");
}
}
- 在 Spring 配置文件中进行相关配置
<?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.xsd">
<!--
init-method:执行初始化方法
destroy-method:执行销毁方法
-->
<bean id="orders" class="com.laoyang.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="电脑" />
</bean>
</beans>
- 使用获取的 bean 对象,使用完后进行销毁
package com.laoyang.spring.test;
import com.laoyang.spring.bean.Orders;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class OrdersTest {
@Test
public void testInitMethod() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步:获取创建的对象");
System.out.println(orders);
/*
销毁方法并不会自动调用,所以需要我们手动让 bean 实例销毁
因为 ApplicationContext 接口不能直接调用 close(),所以可以强转为它的实现类然后在进行调用
因为在配置文件中进行了相关配置,所以调用 close() 销毁的时候就会自动去调用 destroyMethod() 方法
*/
((ClassPathXmlApplicationContext) context).close();
/*
最后的打印结果:
第一步:执行无参构造器创建 bean 实例
第二步:执行set方法,设置属性值
第三步:执行初始化方法
第四步:获取创建的对象
com.laoyang.spring.bean.Orders@569cfc36
第五步:执行销毁的方法
*/
}
}
Bean 的后置处理器
- 加上 Bean 的后置处理器之后,Spring 的生命周期会变为 7 步。
Bean 生命周期的过程:
- 通过构造器创建 bean 实例(无参构造器)
- 为 bean 的属性设置值和对其它 bean 的引用(调用 set 方法)
- 把 bean 的实例传递给 bean 的后置处理器的方法(
postProcessBeforeInitialization()
) - 调用 bean 的初始化方法(需要进行配置初始化的方法)
- 把 bean 的实例传递给 bean 的后置处理器的方法(
postProcessAfterInitialization()
) - 使用 bean (对象获取到了)
- 当容器关闭的时候,调用 bean 的销毁方法(需要进行配置销毁的方法)
演示添加后置处理器的效果
- 创建类,实现 BeanPostProcessor 接口,创建后置处理器
package com.laoyang.spring.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
public class MyBeanPost 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;
}
}
- 在配置文件中配置后置处理器
<?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.xsd">
<!--
init-method:执行初始化方法
destroy-method:执行销毁方法
-->
<bean id="orders" class="com.laoyang.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="电脑" />
</bean>
<!-- 配置后置处理器-->
<bean id="myBeanPost" class="com.laoyang.spring.bean.MyBeanPost" />
</beans>
- 测试效果
package com.laoyang.spring.test;
import com.laoyang.spring.bean.Orders;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class OrdersTest {
@Test
public void testInitMethod() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步:获取创建的对象");
System.out.println(orders);
/*
销毁方法并不会自动调用,所以需要我们手动让 bean 实例销毁
因为 ApplicationContext 接口不能直接调用 close(),所以可以强转为它的实现类然后在进行调用
*/
((ClassPathXmlApplicationContext) context).close();
/*
最后的打印结果:
第一步:执行无参构造器创建 bean 实例
第二步:执行set方法,设置属性值
在初始化之前执行的方法
第三步:执行初始化方法
在初始化之后执行的方法
第四步:获取创建的对象
com.laoyang.spring.bean.Orders@64f6106c
第五步:执行销毁的方法
*/
}
}
自动装配
- 自动装配:根据指定的装配规则(属性名称或属性类型),Spring 会自动将匹配的属性值进行注入。
演示自动装配的过程
- 创建两个类,用来配合测试
package com.laoyang.spring.autowire;
public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}
package com.laoyang.spring.autowire;
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
}
- 在 Spring 配置文件中进行相关配置
<?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.xsd">
<!--
实现自动装配(使用 bean 标签中的 autowire 属性实现)
autowire有两个常用值:
byName:根据属性名称进行注入,注入的值(bean 的 id)和类属性的名称要一样
byType:根据属性类型进行注入
-->
<!-- <bean id="emp" class="com.laoyang.spring.autowire.Emp" autowire="byName" />-->
<bean id="emp" class="com.laoyang.spring.autowire.Emp" autowire="byType" />
<!-- 手动装配 -->
<!-- <bean id="emp" class="com.laoyang.spring.autowire.Emp">-->
<!-- <property name="dept" ref="dept" />-->
<!-- </bean>-->
<bean id="dept" class="com.laoyang.spring.autowire.Dept" />
</beans>
- 测试
package com.laoyang.spring.test;
import com.laoyang.spring.autowire.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmpTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
}
引入外部属性文件
引入相关 jar 包
直接配置数据库信息(配置德鲁伊连接池)
- 直接在 Spring 配置文件中进行配置:
<?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.xsd">
<!-- 直接配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/userDb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
</beans>
引入外部属性文件配置数据库连接池
- 创建 .properties 文件,配置相关属性
prop.driverClassName=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.username=root
prop.password=123456
- 在 Spring 配置文件头部声明中引入 context 名称空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- 在 Spring 配置文件中引入 properties 属性文件
<?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: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:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClassName}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.username}"/>
<property name="password" value="${prop.password}"/>
</bean>
</beans>
IOC 基于注解操作 Bean 管理
注解复习
- 注解:注解是代码中的特殊标记,格式:
@注解名称(属性名称 = 属性值, 属性名称 = 属性值......)
- 注解可以使用在 类上、方法上、属性上
- 这里使用注解的主要目的:简化 xml 配置
注意:前两点说的是 JavaSE 中的注解,相当于是给大家巩固一下知识点。
Spring 相关注解
以下是 Spring 针对 Bean 管理中创建对象而提供的相关注解:
- @Component
- @Service
- @Controller
- @Repository
上面的四个注解功能是一样的,都可以用来创建 bean 实例。
基于注解方式实现对象创建
实现步骤
- 导入 spring-aop依赖
- 在 xml 配置文件中开启组件扫描(需要引入 context 名称空间)
- 创建类,在类上面添加创建对象的注解
建议重新弄个项目,用来测试效果
具体实现
导入 spring-aop依赖
在 xml 配置文件中开启组件扫描
<?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: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"
>
<!--
开启组件扫描(下面的两种方式是扫描多个包的写法,单个包直接写对应包名地址就好了)
方式一:如果想要扫描多个包,可以使用多个逗号隔开(com.laoyang.spring.dao,com.laoyang.spring.service)
方式二:扫描包的上层目录(com.laoyang.spring)
> 根据需求而定
-->
<context:component-scan base-package="com.laoyang.spring" />
</beans>
需要引入 context 名称空间
创建类,在类上面添加创建对象的注解
package com.laoyang.spring.service;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Component(value = "userService")
// @Service
public class UserService {
/*
@Component(value = "userService")
相当于
<bean id="userService" class="com.laoyang.spring.service.UserService" />
注解中的 value 属性值也可以省略不写,默认值是类的名称(首字母小写)
比如这里的 UserService,那么默认就会变为 userService
*/
public void add() {
System.out.println("service add...");
}
}
也可以把 @Component 设置为其它三个注解进行测试,最终效果都是一样的
测试效果
package com.laoyang.spring.test;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void testUserService() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
开启组件扫描的细节配置
- 刚才实现对象创建的时候我们配置过相关的组件扫描了,接下来我们可以把组件扫描写的更加细致
- 可以设置扫描内容的过滤器,只扫描我们需要的内容
案例一:
<?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: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"
>
<!--
示例一
use-default-filters="false":表示现在不使用默认 filter,自己配置 filter
context:include-filter:设置要扫描哪些内容
-->
<context:component-scan base-package="com.laoyang.spring" use-default-filters="false">
<!-- 只扫描有 @Controller 注解的类 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
案例二:
<?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: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"
>
<!--
示例二
下面的配置是扫描 base-package 设置的包路径下的所有内容
context:exclude-filter:设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.laoyang.spring">
<!-- 如果是使用了 @Controller 注解的类就不进行扫描 -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
基于注解方式实现属性注入
Spring 相关注解
- @AutoWired:根据属性类型进行自动装配(注入)
- @Qualifier:根据属性名称进行注入
- @Resource:可以根据类型注入,也可以根据名称注入
- @Value:注入普通类型属性
注意:以下的代码演示中,还是需要用到刚才的 xml 配置文件中的组件扫描!!!
@AutoWired 演示
步骤:
- 把 Service 和 Dao 对象进行创建,在 Service 和 Dao 类中添加创建对象的注解
- 在 Service 中注入 Dao 对象,在 Service 类中添加 Dao 类型属性,在属性上使用注解
- 测试效果
创建相关接口和类,用于待会配合测试
package com.laoyang.spring.dao;
public interface UserDao {
public void add();
}
package com.laoyang.spring.dao;
import org.springframework.stereotype.Repository;
// @Repository:创建 userDaoImpl 对象(相当于xml中的 bean 标签)
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add....");
}
}
在 service 中创建 dao 对象,并调用 dao 的 add 方法
package com.laoyang.spring.service;
import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 定义 dao 类型对象
* 不需要添加 set 方法
* @Autowired: 根据属性类型进行注入
*/
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add...");
userDao.add();
}
}
这里使用了 @Service,主要是为了给大家演示一下那四个注解都是相同效果,用哪个都可以,大家不要误解为只有 @Service 才能在这种时候使用!!!
测试效果
package com.laoyang.spring.test;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void testUserService() {
// 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService); // 地址值
userService.add(); // service 和 dao 中 add 方法的打印结果
}
}
正常测试完以后,大家可以在把 UserDao 中的 @Repository 注解注释掉,然后在测试,这个时候就会报错了…
@Qualifier 演示
- @Qualifier 注解需要和上面的 @AutoWired 一起使用
- 根据属性名称进行注入
在 @Repository 注解中定义对象属性名称
package com.laoyang.spring.dao;
import org.springframework.stereotype.Repository;
// 在 @Repository 注解中定义对象属性名称(相当于设置 bean 标签的 id 属性)
@Repository(value = "userDaoImpls")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add....");
}
}
在 Service 的 @Qualifier 注解中根据对象名称注入属性
package com.laoyang.spring.service;
import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 定义 dao 类型对象
* 不需要添加 set 方法
* @Qualifier(value = "属性名称"):根据属性名称进行注入
*/
@Autowired
@Qualifier(value = "userDaoImpls")
private UserDao userDao;
public void add() {
System.out.println("service add...");
userDao.add();
}
}
如果找不到对应的属性名称,那么就会报错
测试效果
package com.laoyang.spring.test;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void testUserService() {
// 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService); // 地址值
userService.add(); // service 和 dao 中 add 方法的打印结果
}
}
@Resource 演示
在 Service 中的 dao 属性对象上添加 @Resource 注解
package com.laoyang.spring.service;
import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
/**
* 定义 dao 类型对象
* 不需要添加 set 方法
*/
// @Resource // 根据类型进行注入
@Resource(name = "userDaoImpls") // 根据名称进行注入
private UserDao userDao;
public void add() {
System.out.println("service add...");
userDao.add();
}
}
注意:@Resource 注解是 javax 包提供的,所以 Spring 官方并不建议我们使用 @Resource,而是建议我们使用 @Autowired 和 @Qualifier 注解
测试效果
package com.laoyang.spring.test;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void testUserService() {
// 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService); // 地址值
userService.add(); // service 和 dao 中 add 方法的打印结果
}
}
@Value 演示
在 UserService 类中创建一个属性,并使用 @Value 注解进行属性注入
package com.laoyang.spring.service;
import com.laoyang.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource(name = "userDaoImpls") // 根据名称进行注入
private UserDao userDao;
@Value(value = "abc")
private String name;
public void add() {
System.out.println("service add...");
userDao.add();
System.out.println(name);
}
}
测试效果
package com.laoyang.spring.test;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void testUserService() {
// 需要使用到 xml 配置文件中的组件扫描,如果没有配置组件扫描,则会报错
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService); // 地址值
userService.add();
/**
最后的打印结果为:
地址值
service add...
dao add....
abc
*/
}
}
完全注解开发
- 在上面的示例中,我们还是需要在 xml 配置文件中配置组件扫描,否则就没办法找到用注解创建的对象属性。
实现步骤
- 编写配置类,替代 xml 配置文件
- 编写测试类
注释掉 xml 文件中的组件扫描配置
<?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: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.laoyang.spring" />-->
</beans>
编写配置类,替代 xml 配置文件
package com.laoyang.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// @Configuration:把当前类作为配置类,替代 xml 配置文件
@Configuration
@ComponentScan(basePackages = {"com.laoyang.spring"})
public class SpringConfig {
/*
@ComponentScan(basePackages = {"com.laoyang.spring"})
相当于
<context:component-scan base-package="com.laoyang.spring" />
*/
}
编写测试类
package com.laoyang.spring.test;
import com.laoyang.spring.config.SpringConfig;
import com.laoyang.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBeanTest {
@Test
public void testUserService2() {
// 1.加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
// 2.获取创建的对象
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService); // 地址值
userService.add(); // service 和 dao 中 add 方法的打印结果
/**
最后的打印结果为:
地址值
service add...
dao add....
abc
*/
}
}