1.Spring核心概念
IOC(Inversion of Control)是Spring框架的核心概念,每个概念的提出总是为了解决一些问题:
//业务层实现
public class BookServiceImp2 implements BookService{
private BookDao bookDao = new BookDaoImp2
bookDao.save();
}
//数据层实现
public class BookDaoIml2 implemnets BookDao{
public void save(){}
}
在面向接口编程中,业务层需要调用数据层的方法,就需要在业务层new一个数据层的对象。
那么就会存在一个问题:高耦合度,即如果数据层的实现类发生变化,那么业务层的代码也需要跟着改变,发生变更后,都需要进行编译打包和重部署。
解决问题:不在业务层new数据层的对象,实现低耦合。
新的问题:业务层如何使用数据层的对象?
这时候就需要用到IOC思想:IOC(控制反转)用于消减计算机程序的耦合问题,具体体现为:使用对象时,由主动new转换为由外部(容器)提供对象,我们只需要提供一个入口即可。此时对象的创建控制权由程序转移到外部,此即控制反转。
DI(依赖注入)是IoC的另外一个说法,只是从不同角度描述相同的概念。它体现在:IoC容器负责建立bean与bean之间的关系,即IoC容器负责解决某个bean需要注入到哪个类当中。
那么这个外部(容器)又是什么?Spring技术实现了IOC思想,并提供了一个容器,称为IOC容器,负责对象的创建、初始化/注入等一系列工作。而被创建或被管理的对象在IOC容器中统称为Bean,IOC容器中放的就是一个个的Bean对象。
2.IoC案例
好了,了解了Spring IoC以及其他一些相关概念后,我们基于注解的方式完成一个需求:
在Controller层中,依赖注入Service层,在Service层中,依赖注入Dao层。
(0)需求思路分析:
- Spring使用IoC容器管理bean,bean在需求中具体指什么?
- 项目中类的实例化对象,比如Controller、Dao
- 如何将被管理的对象交给IOC容器?
- 在配置文件中配置或使用注解,我们使用注解形式
- 我们要使用bean先要获取IoC容器,如何获取IoC容器?
- Spring框架提供对应的接口
- 如何从IoC容器中获取bean?
- 调用Spring框架提供的接口中对应的方法
(1)创建Maven工程同时在pom.xml文件中导入Spring相关坐标
<dependencies>
<!-- Spring依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- junit依赖包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
(2)创建项目结构,编写业务,使用注解将这些类配置为bean,交给IoC容器管理
controller--UserController
@Controller
public class UserController {
@Autowired
private UserService userService;
public void save(){
System.out.println("我是UserController");
}
}
dao--UserDao
@Repository
public class UserDao {
public void save(){
System.out.println("我是UserDao");
}
}
service--UserService
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void save(){
System.out.println("我是Service");
userDao.save();
}
}
(3)在resource目录下创建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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描,扫描类上的注解-->
<context:component-scan base-package="com.example"/>
</beans>
(4)在测试类中获取IoC容器,拿到bean进行测试
public class App {
public static void main(String[] args) {
//使用Spring框架提供的接口获取IoC容器
//将配置文件作为参数传递进去以获取参数中的上下文对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("springconfig.xml");
//使用接口中的方法获取bean,将bean的id传递进去
//获取到的bean是个对象,Object类型的,需要转换成BookDao类型的
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
}
}
(5)观察运行结果
3.谈谈基于注解配置bean
在上方代码中不难发现,我们使用了@Controller、@Service、@Repository分别对controller层、service层、dao层的类进行了bean的配置。这是怎么回事呢?
这就是基于注解的spring开发,通过在Class上使用特定的注解进行标注,然后让Spring去扫描这些特定的注解,并把它们当做一个bean进行定义。这些特定注解如下:
注解 | 说明 | 位置 |
---|---|---|
@Component | 声明bean的基本注解 | 类定义上方 |
@Controller | @Component的衍生注解 | 标注在控制层类上 |
@Service | @Component的衍生注解 | 标注在业务层类上 |
@Repository | @Component的衍生注解 | 标注在数据访问层类上 |
值得一提的是,在IOC容器中,每一个Bean都有一个属于自己的名字,可以通过注解的value属性指定bean的名字。如果没有指定,默认为类名首字母小写,所以也可以按照名称获取bean。
@Component("bean1")
public class UserDaoImpl{...}
而只标明注解还不行,还需要配置包扫描路径,让Spring能扫描到这些写在类上的注解。
//base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
<context:component-scan base-package="com.bookiejb"/>
4.谈谈自动装配
Spring注解开发中的东西是为了让我门加速开发的,他对原始的内容进行了阉割,也就是没有必要的功能他就没有做,只做了最快速,最好用的,也就是用自动装配替代了Setter与构造器注入
1.使用@Autowired注解开启自动装配模式
@Autowired可以对成员变量、方法以及构造方法进行标注,完成自动装配工作,其原理为基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,消除了setter方法。
该注解默认按照类型注入,所以当该类型存在两个bean时,无法完成注入。
@Service
public class UserService {
//为userDao注入类型为UserDao的bean
@Autowired
private UserDao userDao;
}
2.使用@Resource注解开启自动装配模式
该注解与@Autowired注解功能相同,区别在于该注解默认按照名称注入,只有当找不到与名称相匹配的bean时才会按照类型注入
@Service
public class UserService{
//为userDao注入名称为userDaoImpl的bean
@Resource(name = "userDaoImpl")
private UserDao userDao;
}
3.使用@Qualifier注解开启指定名称装配bean
该注解需配合@Autowired注解使用,当@Autowired注解需要按名称注入时,在该注解的属性中指定bean的名称。
@Service
public class UserService{
//为userDao注入名称为userDao1的bean
@Autowired
@Qualifier("userDao1")
private UserDao userDao;
}