一.课程引入
上一讲,我们采用XML配置文件的方式使用Spring容器管理Bean对象,最后给出一个思考题:“如果我们有几十个类要创建Bean,采用XML配置方式,会不会让Spring配置文件显得很臃肿,怎么解决这个问题呢?”,这一讲,我们准备利用组件注解符精简Spring配置文件。
- 配置:configuration
- 注解:annotation
二.打开第一讲项目
三、利用组件注解符精简Spring配置文件
1.创建net.tzj.spring.lesson02包
2.将lesson01子包的四个类拷贝到lesson02子包
3.修改杀龙任务类 - SlayDragonQuest
- 业务Bean的配置可用注解符:
@Component
- 组件 (@Service
- 服务、@Repository
-
仓库、@Mapper
- 映射器、@Controller
- 控制器)
@component (把普通pojo实例化到spring容器中,相当于配置文件中的)
在探究@component前先了解一下注解?何为注解?注解本质上就是一个类,开发中我们可以使用注解 取代 xml配置文件
package net.tzj.spring.lesson02;
import org.springframework.stereotype.Component;
/**
* 功能:杀龙任务类
* 作者:唐梓杰
* 日期:2021年3月31日
*/
@Component //添加组件注解符,交给spring容器管理,没有设置参数,那么组件默认名称为:slayDragonQuest
public class SlayDragonQuest {
public void embark(){
System.out.println("执行杀龙任务");
}
}
注:添加组件注解符@component,交给spring容器管理,没有设置参数,那么组件默认名称为:slayDragonQuest
4.修改救美任务类 - RescueDamselQuest
package net.tzj.spring.lesson02;
import org.springframework.stereotype.Component;
/**
* 功能:救美任务类
* 作者:唐梓杰
* 日期:4.7
*/
@Component
public class RescueDamselQuest {
public void embark(){
System.out.println("执行救美任务");
}
}
5.修改勇敢骑士类 - BraveKnight
- 注意:删除setSlayDragonQuest()方法,因为已经通过自动装配注解设置了勇敢骑士的杀龙任务属性。
- 查看@Component注解源码
package net.tzj.spring.lesson02;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 功能:勇敢骑士类
* 作者:唐梓杰
* 日期:2021年03月31日
*/
@Component("Mike") //添加组件解注符,设置参数"mike",对应的就是spring配置文件里bean元素的id值
public class BraveKnight {
@Autowired //主动装配注解符(可用@Resource或@Inject注解符替换)
private SlayDragonQuest slayDragonQuest;
// public void setSlayDragonQuest(SlayDragonQuest slayDragonQuest){
//
// this.slayDragonQuest = slayDragonQuest;
// }
public void embarkOnQuest(){
slayDragonQuest.embark();
}
}
@Component(“Mike”) //添加组件解注符,设置参数"mike",对应的就是spring配置文件里bean元素的id值(不设置默认就是类的名称,设置就可以使用mike来调用)
@Autowired 自动装配程序,就不用写装配了 //主动装配注解符(可用@Resource或@Inject注解符替换)
6.修改救美骑士类 - DamselRescuingKnight
package net.tzj.spring.lesson02;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 功能:救美骑士类
* 作者:唐梓杰
* 日期:4.7
*/
@Component
public class DamselRescuingKnight {
@Resource
private RescueDamselQuest rescueDamselQuest;
// public DamselRescuingKnight(RescueDamselQuest rescueDamselQuest){
// this.rescueDamselQuest = rescueDamselQuest;
// }
public void embarkOnQuest(){
rescueDamselQuest.embark();
}
}
注意:删除了构造方法,因为已经通过资源注解符将救美任务属性注入了救美骑士组件。
@Resource已经注入了救美方法,所以删除了构造方法
7.创建Spring配置文件
在resources目录创建xml_annotation
子目录,然后在里面创建Spring配置文件 -spring-config.xml
@componentscan注解 即告诉spring 该去哪里扫描bean。
spring boot 如果你的其他包都在使用了@SpringBootApplication注解的main
类所在的包及其下级包,则你什么都不用做,SpringBoot会自动帮你把其他包都扫描了 如果你有一些bean所在的包,不在main
的包及其下级包,那么你需要手动加上@ComponentScan注解并指定那个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: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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描,扫描指定包下添加了注解符的类或接口,将其生成Bean对象-->
<context:component-scan base-package="net.tzj.spring.lesson02" />
</beans>
组件扫描:扫描指定包下添加了注解符的类(@Component、@Service、@Repository、@Mapper、@Controller),将其生成Bean对象(如下)
<context:component-scan base-package="net.tzj.spring.lesson02" />
8.创建测试类 - TestKnight
- 在test/java里创建net.tzj.spring.lesson2包,在包里创建TestKnight类
package net.tzj.spring.lesson02;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/*
功能:测试骑士类
作者:tzj
时间:4.12
*/
package net.tzj.spring.lesson02;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 功能:测试骑士类
* 作者:tzj
*/
public class TestKnight {
private ClassPathXmlApplicationContext context; // 基于类路径XML配置文件的应用容器
@Before
public void init() {
// 基于Spring配置文件创建应用容器
context = new ClassPathXmlApplicationContext("xml_annotation/spring-config.xml");
}
@Test
public void testKnight() {
// 根据名称从应用容器里获取勇敢骑士对象
BraveKnight knight1 = (BraveKnight) context.getBean("Mike");
// 勇敢骑士执行任务
knight1.embarkOnQuest();
// 根据名称从应用容器里获取救美骑士对象
DamselRescuingKnight knight2 = (DamselRescuingKnight) context.getBean("damselRescuingKnight");
// 救美骑士执行任务
knight2.embarkOnQuest();
}
@After
public void destroy() {
// 关闭应用容器
context.close();
}
}
四、程序优化 - 面向接口
- Spring框架可以方便地管理Bean及其相互依赖。为了模块之间实现松耦合,一般采用面向接口的方式。多种骑士,多种任务,可以任意搭配。为了实现这个效果,我们应该抽象出两个接口:骑士接口(
Knight
)和任务接口(Quest
)。骑士接口有两个实现类:BraveKnight
和DamselRescuingKnight
;任务接口有两个实现类:SlayDragonQuest
和RescueDamselQuest
。
(一)创建任务接口 - Quest
package net.tzj.spring.lesson02;
/*
功能:任务接口
作者:tzj
日期:4.12
*/
public interface Quest {
void embark();
}
(二)创建骑士接口 - Knight
package net.tzj.spring.lesson02;
/*
功能:骑士接口
作者:tzj
日期:4.12
*/
public interface Knight {
void embarkOnQuest();
}
(三)修改杀龙任务类 - SlayDragonQuest
- 让杀龙任务类实现任务接口
(四)修改救美任务类 - RescueDamselQuest
- 让救美任务类实现任务接口
(五)修改勇敢骑士类 - BraveKnight
注意:SlayDragonQuest类改成了Quest接口,这样就可以给该骑士自动装配任何任务(实现了Quest接口的Bean),增加了程序的灵活性。
(六)修改救美骑士类 - DamselRescuingKnight
- 让救美骑士类实现骑士接口
(七)修改测试类 - TestKnight
(八)再修改测试类 - TestKnight
package net.tzj.spring.lesson02;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/*
功能:测试骑士类
作者:tzj
时间:4.12
*/
public class Testknight {
private ClassPathXmlApplicationContext context; //基于路径xml设置文件的应用容器
@Before
public void init(){
//基于Spring配置文件创建应用容器
context = new ClassPathXmlApplicationContext("xml_annotation/spring-config.xml");
}
@Test
public void testBraveKnight(){
//根据名称从应用容器里获取勇敢骑士对象
Knight knight = (BraveKnight) context.getBean("Mike"); //父接口变量指向实现类对象
// 勇敢骑士执行任务
knight.embarkOnQuest();
}
@Test
public void testDamselRescuingKnight(){
//根据名称从应用容器里获取救美骑士对象
Knight knight = (DamselRescuingKnight) context.getBean("damselRescuingKnight"); //父接口变量指向实现类对象
//救美骑士执行任务
knight.embarkOnQuest();
}
@After
public void destory(){
//关闭应用容器
context.close();
}}
运行结果:
五、课堂练习
-
任务1、两种骑士交换执行任务。 要求勇敢骑士去救美,要求救美骑士去杀龙。
-
任务2、两种骑士都执行两项任务。 要求勇敢骑士先执行杀龙任务,再执行救美任务。 要求救美骑士先执行救美任务,再执行杀龙任务。
-
任务3、两种骑士再交换执行任务。 要求勇敢骑士完成杀龙任务,救美骑士完成救美任务。