Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simplicity, and productivity has made it the world’s most popular Java framework.
Spring 包括了用来简化 java 开发的多种框架,用于不同的场景,其中最为基础的是 Spring Framework,功能有:
Spring Framework 在开发中的作用:
Spring Boot 可以帮助我们开发【基于Spring的】、【独立的】、【生产级的】应用程序
它的主要目标是:
传统的 Spring 课程教学顺序一般为
利弊
本课程的设计理念
Spring Boot 提供了方便的手段来快速生成项目骨架
步骤1 - 生成项目骨架
https://start.spring.io/
步骤2 - 解压压缩包,用 IDEA 打开 pom.xml 文件
步骤3 - 编写控制器类,见下面的代码片段
步骤4 - 运行引导类(程序的入口),此时可以用浏览器访问 http://localhost:8080/hello 来进行测试
步骤5 - 运行 mvn package 打 jar 包,即可作为最终产品发布
控制器类需要我们自己编写
@RestController public class HelloController { @RequestMapping("/hello") public String hello() { return "Hello, Spring Boot!"; } }
其中
引导类代码如下,由骨架向导生成
@SpringBootApplication public class Demo1Application { public static void main(String[] args) { SpringApplication.run(Demo1Application.class, args); } }
如果使用 idea 编辑器,步骤1和步骤2可以进一步简化,利用自带的向导完成
注意 starter 地址有如下选择
- https://start.spring.io 官网向导地址,网络不好时会连接失败
- http://start.aliyun.com 国内地址,速度快,依赖全,提供的 Spring Boot 版本稍低,生成的 pom 文件有差异
Spring 的一大作用就是能够降低代码耦合,让程序的扩展性更好。
某业务接口
public interface UserService { public void foo(); }
其中一种实现 A
public class UserServiceImplA implements UserService{ @Override public void foo() { System.out.println("UserServiceImplA.foo()..."); } }
业务功能使用者
public class UserController { private UserService userService = new UserServiceImplA(); public void foo() { // 调用业务方法 userService.foo(); } }
测试
UserController controller = new UserController(); controller.foo();
如果业务功能发生了调整,变成了实现 B
public class UserServiceImplB implements UserService{ @Override public void foo() { System.out.println("UserServiceImplB.foo()..."); } }
可以看到,业务功能的使用者(UserController)也会受到影响,使用实现 A 的地方都需要修改:
public class UserController { // 这里要修改为实现 B private UserService userService = new UserServiceImplB(); public void foo() { userService.foo(); } }
之前老师应该都这么讲:为了降低耦合,需要在分层调用时去使用接口,不要直接使用实现类。但看看下面例子,你会发现,Controller 使用 Service 的实现类是无法避免的!上面的问题在于 Controller 与 Service 产生了耦合:即一方的改变影响了另一方。能否达到如下的效果,Service 发生变更,但 Controller 不受影响?
Service 对象创建该不该由 Controller 自己来做?
一个新的问题是,对于 Controller 来说,应该主动去容器获取依赖对象,还是被动从容器接收依赖对象?
名词解释:容器
- 英文对应 Container,指对象的运行环境,同时负责管理这些对象的创建、初始化、销毁等
- 见过的容器有 Tomcat 容器,管理 Servlet,Filter 等对象
- 而 Spring 也是一种容器,管理 Controller,Service,Dao 等对象
Spring 的解决方法是,将刚才的 UserServiceImplA 或 UserServiceImplB 的创建权就交给 Spring 容器,这样就无需我们来关心对象的创建
让 Spring 来管理 Service 的创建,通过在 Service 的实现类A上添加 @Service 注解可以让它被 Spring 管理起来
@Service public class UserServiceImplA implements UserService { @Override public void foo() { System.out.println("UserServiceImplA.foo()..."); } }
测试通过 Spring 的容器获取到该 Service 对象,ConfigurableApplicationContext 即是容器类型
@SpringBootApplication public class Demo1Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Demo1Application.class, args); System.out.println(context.getBean(UserService.class)); } }
会输出
com.itheima.demo.service.impl.UserServiceImplA@495b0487
如果要切换成另一个实现类,只需要在另一个实现类B上添加 @Service 注解即可,记得去掉实现类A上的 @Service
@Service public class UserServiceImplB implements UserService { @Override public void foo() { System.out.println("UserServiceImplB.foo()..."); } }
注意
- 在 UserServiceImplB 上添加 @Service,需要在 UserServiceImplA 上去掉 @Service,即暂时不能同时把他俩交给 Spring 管理,否则会报错
- 让他俩同时交给 Spring 管理的解决办法,后面再讲
此时会输出
com.itheima.demo.service.impl.UserServiceImplB@3212a8d7
此步达到的效果
- 可以看到,通过 @Service 注解 UserServiceImplA 或 UserServiceImplB 对象的创建已经交给了 Spring 容器
把控制器也交给 Spring 管理起来,这回用 @Controller 注解标注,注意此时已无需自己创建 UserService 的实现类,改由用 @Autowired 指明该成员变量的取值来自于 Spring 容器,@Autowired 会根据类型去找容器中的对象
@Controller public class UserController { @Autowired private UserService userService; public void foo() { // 调用业务方法 userService.foo(); } }
测试一下控制器功能是否改变
@SpringBootApplication public class Demo1Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Demo1Application.class, args); System.out.println(context.getBean(UserService.class)); context.getBean(UserController.class).foo(); } }
输出
com.itheima.demo.service.impl.UserServiceImplB@76012793 UserServiceImplB.foo()...
此步达到的效果
- Service 切换实现,Controller 的代码再也无需改动,达到了低耦合
检查自己理解程度
- 来回切换实现类,体会【低耦合】含义
- 如果实现类A、实现类B 的 @Service 都去掉,会发生什么,设想一下
Spring 可以结合 JUnit 完成单元测试,测试用例中也可以利用 @Autowired 来注入待测对象,只要它被 Spring 所管理
@SpringBootTest class Demo1ApplicationTests { @Autowired private UserController controller; // 注入任意由容器管理的对象 @Test void test1() { // 使用 controller } }
注意
- Spring 中用了 JUnit5,因此测试方法不必是 public 的
容器对象自身也可以使用 @Autowired 注入,容器类型可以使用接口类型:ApplicationContext
@SpringBootTest class Demo1ApplicationTests { @Autowired private ApplicationContext context; // 注入容器自身 @Test void test2() { // 使用 context } }
刚才解耦的例子中,涉及到 Spring 的两个核心概念