1.Spring简介
1.1 什么是Spring框架?
Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inversion of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。
1.2 Spring的特性
- 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
- 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们自己在代码中new创建。而使用 Spring 之后。对象的创建都是给了 Spring 框架。
- 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值。
- 面向切面编程:Aspect Oriented Programming——AOP
- 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
- 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
- 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了表现层的 SpringMVC 和持久层的 Spring JDBC)
1.3.Spring的体系结构
Spring 框架根据功能的不同大体可分为 Data Access/Integration(数据访问与集成)
、Web
、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)
、Core Container(核心容器)
和 Test
。
Spring框架的组成结构图如下所示:
2.程序的耦合与解耦
-
**耦合:**耦合指的就是对象之间的依赖关系。对象之间的耦合越高,维护成本越高。
-
**案例:**没有引入IOC容器时系统的Web层、业务层、持久层存在耦合
/**
* 模拟表现层
*/
public class Client {
private UserService userService = new UserServiceImpl();
public static void main(String[] args) {
//硬编码:此处有依赖关系
userService.addUser();
}
}
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
//硬编码:此处有依赖关系
private UserDao userDao = new UserDaoImpl();
@Override
public void addUser(){
userDao.addUser();
}
}
/**
* 持久层实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
-
问题分析:
上边的代码service层在依赖dao层的实现类,此时如果更改dao了层的实现类或此时没有dao层实现类,编译将不能通过。
-
IOC(工厂模式)解耦:
- 把所有的dao和service对象使用配置文件配置起来
- 当服务器启动时读取配置文件
- 把这些对象通过反射创建出来并保存在容器中
- 在使用的时候,直接从工厂拿
3.工厂模式解耦
3.1 基础版本
每次调用,在工厂中直接new对象
/**
* bean工厂
*/
public class BeanFactory_v1 {
/**
* 获得UserServiceImpl对象
* @return
*/
public static UserService getUserService(){
return new UserServiceImpl();
}
/**
* 获得UserDaoImpl对象
* @return
*/
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
3.1 进阶版本
每次调用,在工厂中通过反射创建对象,多例模式
工厂模式创建对象思路:
- 用反射的方式来创建对象
- 通过读取配置文件的方式得到能反射的全类名
#配置文件beans.properties:
#配置要使用的dao和service
UserDao=com.by.dao.UserDaoImpl
UserService=com.by.service.UserServiceImpl
/**
* bean工厂
*/
public class BeanFactory_v2 {
private static Properties prop = new Properties();
/**
* 根据全类名获取bean对象
* @param beanName
* @return
* @throws ClassNotFoundException
*/
public static Object getBean(String beanName) {
try {
//不能使用:web工程发布后没有src目录
//InputStream is = new FileInputStream("src/bean.properties");
InputStream is =
BeanFactory_v2.class.getClassLoader()
.getResourceAsStream("bean.properties");
prop.load(is);
return Class.forName(prop.getProperty(beanName)).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
System.out.println(prop.get("UserService"));
System.out.println(getBean("UserService"));
}
}
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao = (UserDao) BeanFactory.getBean("UserDao");
public void addUser(){
userDao.addUser();
}
}
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
//直接引用接口实现类
for (int i = 0; i < 5; i++) {
UserService userService =
(UserService)BeanFactory.getBean("UserService");
System.out.println(userService);
}
}
}
3.3 最终版本
- 加载类时直接读取配置文件,通过反射创建配置文件中所有类的实例并存到map中,单例模式
- 调用方法获取对象时直接从map中读取
/**
* bean工厂
*/
public class BeanFactory_v3 {
//1.定义一个容器,用于存放对象
private static Map<String, Object> beans = new HashMap<>();
/**
* 加载配置文件
*/
static {
try {
//2、读取配置文件
//不能使用:web工程发布后没有src目录
//InputStream is = new FileInputStream("src/bean.properties");
InputStream is = BeanFactory_v3.class.getClassLoader().getResourceAsStream("bean.properties");
//3、通过反射创建对象,把对象存到容器中
Properties prop = new Properties();
prop.load(is);
Set<Map.Entry<Object, Object>> entrySet = prop.entrySet();
for (Map.Entry<Object, Object> entry : entrySet) {
String key = entry.getKey().toString();
String beanName = entry.getValue().toString();
Object value = Class.forName(beanName).newInstance();
beans.put(key, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 4、在使用的时候,直接从工厂拿
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
try {
return beans.get(beanName);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
System.out.println(getBean("UserService"));
}
}
4.Spring IoC解耦
4.1 什么是 IoC?
IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。
简单来说就是把本来在类内部控制的对象,反转到类外部进行创建后注入,不在由类本身镜像控制,这就是IOC的本质。不过, IoC 并非 Spring 特有,在其他语言中也有应用。
- 为什么叫控制反转?
控制:指的是对象创建(实例化、管理)的权力
反转:控制权交给外部环境(Spring 框架、IoC 容器)
-
普通方式获取对象:
我们在获取对象时,都是采用new的方式。是主动的。
-
工厂模式获取对象:
我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
4.2 IoC和工厂模式区别
Spring IOC(Inversion of Control,控制反转)与工厂模式的主要区别体现在实现方式、依赖管理和解耦程度上。
-
实现方式:
- Spring IOC 通过依赖注入(DI)实现,容器负责创建对象、管理对象以及对象之间的依赖关系。这意味着,当容器创建一个对象时,会自动处理对象之间的依赖关系。
- 工厂模式 通过专门定义一个类(工厂类)负责创建对象,将对象创建逻辑集中在一个地方。这意味着对象之间的依赖关系需要手动在工厂类中处理。
-
依赖管理:
- Spring IOC 容器负责管理对象之间的依赖关系,当容器创建一个对象时,会自动处理这些依赖。
- 工厂模式 由工厂类负责管理对象之间的依赖关系,需要手动在工厂类中处理这些依赖。
-
解耦程度:
- Spring IOC 通过依赖注入实现了解耦,使得对象之间不需要直接进行依赖,而是通过容器进行依赖管理,提高了系统的灵活性和可维护性。
- 工厂模式 虽然也能实现一定程度的解耦,但相较于Spring IOC,其解耦程度较低,对象之间的依赖关系仍然需要在工厂类中进行处理。
4.3 IoC解耦
4.3.1 创建工程
-
工程结构
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.by</groupId>
<artifactId>Spring_IOC_Xml</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 项目源码及编译输出的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 项目编译JDK版本 -->
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
</dependencies>
</project>
注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。
Spring常用功能的Jar包依赖关系 |
---|
核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4个模块组成。
- dao
/**
* 持久层实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
- service
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
//此处有依赖关系
private UserDao userDao = new UserDaoImpl();
public void addUser(){
userDao.addUser();
}
}
4.3.2 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--
2、把对象交给spring来创建
id:给对象在容器中提供一个唯一标识。用于获取对象
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl"></bean>
</beans>
注意:命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml
4.3.3 测试
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
//1.使用ApplicationContext接口,就是在获取spring容器
// ac对象就类似工厂模式中的工厂
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据bean的id获取对象
UserDao userDao = (UserDao) ac.getBean("userDao");
System.out.println(userDao);
UserService userService = (UserService) ac.getBean("userService");
System.out.println(userService);
userService.addUser();
}
}
- 问题:
service层仍然耦合
4.4 依赖注入(DI)
4.4.1依赖注入概述
DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
- 为什么需要依赖注入?
IoC虽然能帮我们创建对象,但是要怎么获取对象呢?
如果每次获取对象都使用getBean方法来获取,那么与工厂(spring容器)直接又形成耦合了。而依赖注入就可以解决这个问题。
- IOC和DI的关系
ioc就是容器,di就是注入这一行为,那么di确实就是ioc的具体功能的实现。控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式。
-
依赖注入的类型:
- 基本类型和String
- 其它类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型
-
依赖注入的方式:
在 Spring 中实现依赖注入的常见方式有以下 3 种:- 属性注入(Field Injection)
- Setter 注入(Setter Injection)
- 构造方法注入(Constructor Injection)
4.4.2 构造函数注入
- 在XML配置方式中,
<constructor-arg>
是通过构造函数参数注入,比如下面的xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:类中需要提供一个对应参数列表的构造函数。
标签:constructor-arg
==给谁赋值:==
name:指定参数在构造函数中的名称
==赋什么值:==
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<!--注入bean对象-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
</beans>
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。具体代码如下:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
//需要提供构造方法
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
4.4.3 setter方式注入
- 在XML配置方式中,property都是setter方式注入,比如下面的xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:实现类中必须包含对应的set方法
标签:property
==给谁赋值:==
name:找的是类中set方法后面的部分
==赋什么值:==
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
本质上包含两步:
- 第一步,通过反射创建对象,相当于new UserServiceImpl(), 所以需要默认构造函数
- 第二步,调用setUserDao()函数注入userDao的值, 所以需要setUserDao()函数
所以对应的service类是这样的:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
//需要提供setter方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
4.4.4 属性注入——自动注入
自动注入不需要在 标签中添加其他标签注入属性值,而是自动从容器中找到相应的bean对象设置为属性值。
自动注入有两种配置方式:
- 全局配置:在
<beans>
中设置 default-autowire 属性可以定义所有bean对象的自动注入策略。 - 局部配置:在
<bean>
中设置 autowire 属性可以定义当前bean对象的自动注入策略。
autowire的取值如下:
- no:不会进行自动注入。
- default:全局配置default相当于no,局部配置default表示使用全局配置
- byName:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供set方法。
- byType:在Spring容器中查找类型与属性类型相同的bean,并进行注入。需要提供set方法。
- constructor:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供构造方法。
具体代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<!--autowire="byType":按照类型自动注入值-->
<bean id="userService" class="com.by.service.UserServiceImpl" autowire="byType">
</bean>
</beans>
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
//需要提供setter方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
4.4.5 注入集合类型的属性
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map。具体代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:property
标签:constructor-arg
==给谁赋值:==
name:找的是类中set方法后面的部分
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<property name="userDao" ref="userDao"></property>
<!-- 给mySet集合注入数据 -->
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<!-- 注入array数组数据 -->
<property name="myArray">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<!-- 注入list集合数据 -->
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<!-- 注入Map数据 -->
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB" value="bbb"></entry>
</map>
</property>
</bean>
</beans>
service类具体代码:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void addUser(){
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
userDao.addUser();
}
}
4.5 Spring的工厂类
-
工厂类 BeanFactory 和 ApplicationContext 的区别。
-
ApplicationContext 是 BeanFactory 的子接口,提供了比父接口更多的功能。
-
在生成 bean 实例的时候,生成的时机是不一样的。
- BeanFactory 在工厂实例化后,在
调用 getBean 时创建实例
。 - ApplicationContext 在一加载配置文件的时候,将
配置文件中所有单例模式生成的类全部实例化
。
- BeanFactory 在工厂实例化后,在
-
现在一般使用 ApplicationContext,不建议使用 BeanFactory。
4.6 bean的介绍
4.6.1 什么是bean?
在Spring框架中,“Bean” 是一个概念,它表示由Spring IoC容器管理的对象
。Bean是Spring框架的基本构建块,它可以是一个Java对象、一个组件、一个服务,或者是你的应用程序中的任何一个对象。
bean是计算机自动生成的类,bean是一个由Spring IoC容器实例化、组装和管理的对象。也就是说,bean并不是程序员编辑的,而是程序运行时,由spring通过反射生成的
。
4.6.2 五种作用域
-
目前Spring Bean的作用域或者说范围主要有五种:
作用域 说明 singleton 默认值, IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。 prototype 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。 request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。 session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。 application (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。 websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。 -
可以通过
<bean>
标签的scope
属性控制bean的作用范围,其配置方式如下所示:
<bean id="..." class="..." scope="singleton"/>
-
需要根据场景决定对象的单例、多例模式
单例:Service、DAO、SqlSessionFactory(或者是所有的工厂)
多例:Connection、SqlSession
4.6.3 将一个类声明为 Bean 的注解有哪些?
@Component
:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
4.6.4 Bean的生命周期
Bean生命周期可以粗略的划分为五大步:
- 第一步:实例化Bean
- 第二步:Bean属性赋值
- 第三步:初始化Bean
- 第四步:使用Bean
- 第五步:销毁Bean
案例:
<!--
singleton:单例
prototype:多例
-->
<bean id="userService" class="com.by.service.UserServiceImpl"
scope="singleton" init-method="init" destroy-method="destroy">
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
System.out.println("调用构造方法创建bean...");
}
public void setUserDao(UserDao userDao) {
System.out.println("调用set方法注入值...");
this.userDao = userDao;
}
public void init(){
System.out.println("调用init方法初始化bean...");
}
public void destroy(){
System.out.println("调用destroy方法销毁bean...");
}
public void addUser(){
userDao.addUser();
}
}
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
//使用对象
ac.getBean("userService");
/**
*singleton:对象在容器中,关闭容器时执行bean的销毁方法
*prototype:对象不在容器中,而是受jvm管理,jvm回收bean时执行销毁方法
*/
ac.close();
}
}
- 生命周期
- 单例(singleton):[容器启动]—>构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[容器关闭]—>destroy方法(销毁)
- 多例(prototype):[使用对象]---->构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[JVM垃圾回收]—>destroy方法(销毁)
5.基于注解的IOC配置
学习基于注解的IOC配置,大家脑海里首先得有一个认知,即注解配置和xml配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
5.1 创建工程
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.by</groupId>
<artifactId>Spring_IOC_Annotation</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
</dependencies>
</project>
- dao
/**
* 持久层实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
- service
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public void addUser(){
userDao.addUser();
}
}
5.2 IOC
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<!-- 告知spring框架,在读取配置文件,创建容器时扫描包,依据注解创建对象,并存入容器中 -->
<context:component-scan base-package="com.by"></context:component-scan>
</beans>
- dao
@Repository
public class UserDaoImpl implements UserDao {
... ...
}
- service
@Service
public class UserServiceImpl implements UserService {
... ...
}
5.3 DI
- service
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void addUser() {
userDao.addUser();
}
}
- 测试
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = ac.getBean("userServiceImpl",UserService.class);
userService.addUser();
}
}
5.3 常用注解
5.3.1 用于创建bean的
以下四个注解的作用及属性都是一模一样的,都是针对一个的衍生注解只不过是提供了更加明确的语义化。
5.3.1.1 @Controller
-
作用:把资源交给spring来管理,相当于:
<bean id="" class="">
;一般用于表现层。 -
属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;
5.3.1.2 @Service
-
作用:把资源交给spring来管理,相当于:
<bean id="" class="">
;一般用于业务层。 -
属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;
-
案例
//@Service("userService")声明bean,且id="userService"
@Service//声明bean,且id="userServiceImpl"
public class UserServiceImpl implements UserService {
...
}
5.3.1.3 @Repository
-
作用:把资源交给spring来管理,相当于:
<bean id="" class="">
;一般用于持久层。 -
属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;
-
案例
//@Repository("userDao")声明bean,且id="userDao"
@Repository//声明bean,且id="userDaoImpl"
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
5.3.1.4 @Component
-
作用:把资源交给spring来管理,相当于:
<bean id="" class="">
;通用。 -
属性:value:指定bean的id;如果不指定value属性,默认bean的id是当前类的类名,首字母小写;
@Component、@Controller、@Repositor和@Service 的区别?
- @Component:最普通的组件,可以被注入到spring容器进行管理。
- @Controller:将类标记为 Spring Web MVC 控制器。
- @Service:将类标记为业务层组件。
- @Repository:将类标记为数据访问组件,即DAO组件。
5.3.1.5 @Scope
-
作用:指定bean的作用域范围。
-
属性:value:指定范围的值,singleton prototype request session。
5.3.2 用于属性注入的
以下四个注解的作用相当于:<property name="" ref="">
。
5.3.2.1 @Autowired
-
作用:自动按照类型注入。set方法可以省略。
-
案例:
@Service
public class UserServiceImpl implements UserService {
@Autowired //注入类型为UserDAO的bean
private UserDao userDao;
public void addUser(){
userDao.addUser();
}
}
5.3.2.2 @Qualifier
-
作用:注入指定名称的bean
-
案例:
@Service
public class UserServiceImpl implements UserService {
@Autowired //注入类型为UserDAO的bean
@Qualifier("userDaoImpl")//有多个相同类型的bean时,可使用Qualifier指定bean
private UserDao userDao;
public void addUser(){
userDao.addUser();
}
}
5.3.2.3 @Resource
-
作用:自动按照名字注入。set方法可以省略。
-
属性:name:指定bean的id。
-
案例:
@Service
public class UserServiceImpl implements UserService {
@Resource(name="userDaoImpl")//注入id=“userDaoImpl”的bean
private UserDao userDao;
public void addUser(){
userDao.addUser();
}
}
5.3.2.4 @Value
-
作用:注入基本数据类型和String类型数据的
-
属性:value:用于指定值
-
案例一
@Service
public class UserServiceImpl implements UserService {
@Resource(name="userDaoImpl") //注入id=“userDaoImpl”的bean
private UserDao userDao;
@Value("张三")//注入String
private String name;
@Value("18")//注入Integer
private Integer age;
public void addUser(){
System.out.println(name+","+age);
userDao.addUser();
}
}
-
案例二
- 创建config.properties
name=张三
age=18
- 加载配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<!--加载config.properties-->
<context:property-placeholder location="config.properties"/>
<context:component-scan base-package="com.by"></context:component-scan>
</beans>
- 注入属性值
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Value("${name}")//注入String
private String name;
@Value("${age}")//注入Integer
private Integer age;
public void addUser() {
System.out.println(name+","+age);
userDao.addUser();
}
}