您现在的位置是:首页 >技术杂谈 >Spring详解(超全面)网站首页技术杂谈
Spring详解(超全面)
一、Spring
Spring是SSM(Spring、SpringMVC、Mybatis)框架之一。所谓框架,就是高度抽取可重用代码的一种设计,具有高度通用性;
1、 概述
(1)Spring是一个开源框架;
(2)Spring为简化企业级开发而生,使用Spring,JavaBean就可以使代码非常的优雅和简洁;
(3)Spring是一个IoC(DI)和AOP容器框架;
(4)Spring的优良特性
非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
依赖注入:DI(Dependency Injection),控制反转(IoC)最经典的实现;
面向切面编程:AOP(Aspect Oriented Programming)
容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
(5)组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用,在 Spring 中可以使用XML和Java注解组合这些对象;
(6)一站式:在IoC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC);
(7)Spring模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TE3J19zB-1682512561625)(C:UsersadminDesktopssmspring组件图.png)]
解释:
每一个绿色的框,代表一个功能模块;绿色框中的黑色框,代表使用这个功能模块需要哪些jar包。
2、为什么要用Spring
(1)方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
(2)方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
(3)降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
(4)方便程序的测试
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
(5)AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
(6)声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程;
作为 Java 程序员,对 Spring 的理解和掌握程度,基本上就是编程能力的体现。
3、Spring初体验
-
创建Maven项目
勾选通过archetype构建,选择maven-archetype-quickstart
-
导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
- 创建接口和实现类
public interface UserDao {
void queryAll();
}
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("UserDaoImpl对象创建了");
}
@Override
public void queryAll() {
System.out.println("查询了所有用户信息");
}
}
- 创建spring配置文件
在工程resource目录下创建spring-config.xml,idea中可以直接在resource目录下使用菜单New–>XML Configuration File–> Spring Config创建。
<?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指定创建出来的对象在核心容器中的名字(name),用作唯一标识
class指定创建对象的类的全类名
-->
<bean id="userDaoImpl" class="com.hqyj.cl.dao.impl.UserDaoImpl"/>
</beans>
- 测试
public class UserDaoTest {
@Test
public void selectAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
UserDao userDaoImpl = ac.getBean("userDaoImpl", UserDao.class);
userDaoImpl.selectAll();
}
}
二、IoC
1、概念
IoC(Inversion of Control)即控制反转。在使用Spring之前,我们获取对象几乎都是用过new的方式,但是呢,在使用Spring之后,我们就不在使用new的方式来创建对象,而是交给Spring来给我们创建,这种对对象控制权的转移就叫做控制反转。
2、IoC容器
-
IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。
-
Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。
-
Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。
-
Spring 提供 2 种不同类型的 IoC 容器,即 BeanFactory 和 ApplicationContext 容器。
2.1 BeanFactory
BeanFactory容器,Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;BeanFactory在启动的时候不会去实例化Bean,当有从容器中拿Bean的时候才会去实例化;
BeanFactory ac = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
2.2 ApplicationContext
应用上下文,继承BeanFactory接口,它是Spring的一个更高级的容器,提供了更多的有用的功能;ApplicationContext 在 BeanFactory 的基础上增加了很多企业级功能,例如 AOP、国际化、事务支持等;ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化; ApplicationContext 接口有两个常用的实现类:
2.2.1 ClassPathXmlApplicationContext
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示:
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
2.2.2 FileSystemXmlApplicationContext
该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示:
ApplicationContext ac = new FileSystemXmlApplicationContext("D:\workspace\spring_demo\src\main\resources\spring-config.xml");
三、Spring Bean的创建
由 Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息创建。一般情况下,我们都是在Spring的配置文件中来管理Bean。
Spring的配置文件中,由一组<beans></beans>
标签包裹,在其中使用<bean></bean>
标签来管理每一个对象。
1、无参构造创建
使用此方式创建bean一定要有无参构造,否则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指定创建出来的对象在核心容器中的名字(name),用作唯一标识
class指定创建对象的类的全类名
-->
<bean id="userDaoImpl" class="com.hqyj.dao.impl.UserDaoImpl" lazy-init="true"/>
</beans>
1.1 bean标签属性
属性名称 | 描述 |
---|---|
id | Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成。id 的值必须以字母开始,可以使用字母、数字、下划线等符号。 |
name | name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开。Spring 容器可以通过 name 属性配置和管理容器中的 Bean。 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。 |
scope | 用于设定 Bean 实例的作用域,属性值可以为 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg | 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型 |
property | 元素的子元素,用于调用 Bean 实例中的 setter 方法来属性赋值,从而完成依赖注入。该元素的 name 属性用于指定 Bean 实例中相应的属性名 |
ref | 和 等元素的子元索,该元素中的 bean 属性用于指定对某个 Bean 实例的引用 |
value | 和 等元素的子元素,用于直接指定一个常量值 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型的依赖注入 |
map | 用于封装 Map 类型的依赖注入 |
entry | |
init-method | 容器加载 Bean 时调用该方法,类似于 Servlet 中的 init() 方法 |
destroy-method | 容器删除 Bean 时调用该方法,类似于 Servlet 中的 destroy() 方法。该方法只在 scope=singleton 时有效 |
lazy-init | 懒加载,值为 true,容器在首次请求时才会创建 Bean 实例;值为 false,容器在启动时创建 Bean 实例。该方法只在 scope=singleton 时有效 |
2、动态工厂模式创建
- 创建UserDaoFactory工厂
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
- Spring配置文件
<!-- 通过普通工厂创建bean-->
<!-- 第一步,将工厂类本身加入ioc容器-->
<bean id="userDaoFactory" class="com.hqyj.cl.dao.impl.UserDaoFactory"/>
<!-- 第二步,配置bean
factory-bean指定由ioc容器创建的工厂bean
factory-method指定的该工程的工厂方法 -->
<bean id="userDaoImpl" factory-bean="userDaoFactory" factory-method="getUserDao"/>
3、静态工厂模式创建
- 创建UserDaoFactory工厂
public class UserDaoFactory {
public static UserDao getUserDao02(){
return new UserDaoImpl();
}
}
- Spring配置文件
<!-- 第二步,配置bean
id指定创建出来的对象在核心容器中的名字(name),用作唯一标识
class指定创建对象的类的全类名
factory-method指定的该工程的静态工厂方法 -->
<bean id="userDaoImpl" class="com.hqyj.cl.dao.impl.UserDaoFactory" factory-method="getUserDao02"/>
四、Spring Bean的作用域
Scope | Description |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。 |
application | 8将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。 |
4.1 示例代码
4.1.1 singleton
- Spring配置文件
<bean id="userDaoImpl" class="com.hqyj.cl.dao.impl.UserDaoImpl" scope="singleton"/>
- 测试类
public class UserDaoTest {
@Test
public void selectAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
UserDao userDaoImpl = ac.getBean("userDaoImpl", UserDao.class);
UserDao userDaoImpl1 = ac.getBean("userDaoImpl", UserDao.class);
System.out.println(userDaoImpl==userDaoImpl1);
}
}
结果输出为true,之前在UserDaoImpl类中写的无参构造输出语句,只会输出一遍,说明对象只创建了一次。
4.1.2 prototype
- Spring配置文件
<bean id="userDaoImpl" class="com.hqyj.cl.dao.impl.UserDaoImpl" scope="prototype"/>
- 测试类
public class UserDaoTest {
@Test
public void selectAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
UserDao userDaoImpl = ac.getBean("userDaoImpl", UserDao.class);
UserDao userDaoImpl1 = ac.getBean("userDaoImpl", UserDao.class);
System.out.println(userDaoImpl==userDaoImpl1);
}
}
结果输出为false,之前在UserDaoImpl类中写的无参构造输出语句,会输出两遍,说明对象创建了两次。
五、Spring Bean的生命周期
-
在传统的 Java 应用中,Bean 的生命周期很简单,使用关键字 new 实例化 Bean,当不需要该 Bean 时,由 Java 自动进行垃圾回收。
-
Spring 中 Bean 的生命周期较复杂,可以表示为:Bean 的定义 -> Bean 的初始化 -> Bean 的使用 -> Bean 的销毁。
-
Spring 根据 Bean 的作用域来选择管理方式。对于 singleton 作用域的 Bean,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁;而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
-
在 XML 中配置Bean时,可以在Bean标签中使用 init-method 属性和 destory-method属性来分别定义该bean的初始化回调方法和销毁回调方法。
5.1 示例代码
- userDao
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("UserDaoImpl对象创建了");
}
@Override
public void selectAll() {
System.out.println("执行了selectAll方法");
}
public void init(){
System.out.println("init");
}
public void destroy(){
System.out.println("destroy");
}
}
- Spring配置文件
<bean id="userDaoImpl1" class="com.hqyj.cl.dao.impl.UserDaoImpl" scope="prototype"
init-method="init" destroy-method="destroy"/>
- 测试类
public class UserDaoTest {
@Test
public void selectAll() {
AbstractApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
ac.registerShutdownHook();
}
}
在非Web应用的单例模式中,手工加载Spring IoC容器,要用AbstractApplicationContext接口继承了ApplicationContext接口,其中方法registerShutdownHook/close,可以手动关闭容器,让destroy-method去执行对应的方法
六、DI
DI(Dependency Injection),即“依赖注入”:是组件之间依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
依赖注入主要有两种实现方式,分别是 setter 注入、构造函数注入。
6.1 setter 注入
指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 Bean 后,调用该 Bean 的 setter 方法,即可实现基于 setter 的 依赖注入。
在 Spring 实例化 Bean 的过程中,首先会调用默认的构造方法实例化 Bean 对象,然后通过 Java 的反射机制调用 setXxx() 方法进行属性的注入。因此,setter 注入要求 Bean 的对应类必须满足以下两点要求:
-
必须提供一个默认的无参构造方法;
-
必须为需要注入的属性提供对应的 setter 方法。
使用 setter 注入时,在 Spring 配置文件中,需要使用
<bean>
元素的子元素<property>
为每个属性注入值;使用构造注入
时,在配置文件中,主要使用 <constructor-arg>
标签定义构造方法的参数,使用其 value 属性(或子元素)设置该参数的值。
6.1.1 示例代码
<property>
标签包含 name、ref、value 等属性。name 用于指定参数名称;value 属性用于注入基本数据类型以及字符串类型的值;ref 属性用于注入已经定义好的 Bean
- 创建一个Java类User
public class User {
private int id;
private String username;
private String password;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", password='" + password + ''' +
'}';
}
}
- 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="user" class="com.hqyj.cl.pojo.User">
<property name="id" value="1"/>
<property name="username" value="chenlei"/>
<property name="password" value="111"/>
</bean>
</beans>
- 测试类
public class UserDaoTest {
@Test
public void selectAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
User user = ac.getBean("user", User.class);
System.out.println(user);
}
}
6.2 构造函数
指 IoC 容器使用构造函数注入被依赖的实例。可以通过调用带参数的构造函数实现依赖注入,每个参数代表一个依赖。
通过使用构造函数注入被依赖的实例有三种方式:通过下标注入、通过属性注入、通过参数名注入。
6.2.1 定义一个User类
public class User {
private int id;
private String username;
private String password;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", password='" + password + ''' +
'}';
}
}
6.2.2 通过下标注入
当构造函数有多个参数时,可以使用 index 属性指定参数的位置,index 属性值从 0 开始,如果不指定index,默认使用标签先后顺序。
<bean id="user" class="com.hqyj.cl.pojo.User">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="chenchen"/>
<constructor-arg index="2" value="23"/>
</bean>
6.2.3 通过属性注入
name属性通过指定构造方法中的参数名来进行注入,最为常用,重点掌握
<bean id="user" class="com.hqyj.cl.pojo.User">
<constructor-arg type="int" value="2"/>
<constructor-arg type="java.lang.String" value="chenchen"/>
<constructor-arg type="java.lang.String" value="123"/>
</bean>
6.2.4 通过参数名注入
<bean id="user" class="com.hqyj.cl.pojo.User">
<constructor-arg name="id" value="3"/>
<constructor-arg name="username" value="chenchen"/>
<constructor-arg name="password" value="123"/>
</bean>
6.3 setter注入和构造函数注入的比较
构造函数注入:
优点:创建时必须要指定构造函数中的全部参数,bean才能被创建,保证了对象创建出来之后,成员变量一定都有值。
缺点:必须要指定全部参数,否则对象无法创建,使用该方式改变对象的创建过程。
setter注入:
优点:对象创建时,无需指定参数,不会限制对象的创建。
缺点:对象具有多个属性可以注入时,无法保证全部或某些属性一定被注入。
setter注入方式较为灵活,使用更方便。
6.4 setter注入对象
有几种情况:普通类型,List集合,Map集合,String类型,对象引用类型,Set集合,数组,Properties,null类型
- Student类
public class Student {
private String name;
private Counselor counselor;
private String[] courses;
private List<String> books;
private Map<String, String> idCard;
private Set<String> studentNumber;
private String wifeName;
private Properties properties;
public Student() {
}
public Student(String name, Counselor counselor, String[] courses, List<String> books, Map<String, String> idCard, Set<String> studentNumber, String wifeName, Properties properties) {
this.name = name;
this.counselor = counselor;
this.courses = courses;
this.books = books;
this.idCard = idCard;
this.studentNumber = studentNumber;
this.wifeName = wifeName;
this.properties = properties;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Counselor getCounselor() {
return counselor;
}
public void setCounselor(Counselor counselor) {
this.counselor = counselor;
}
public String[] getCourses() {
return courses;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public List<String> getBooks() {
return books;
}
public void setBooks(List<String> books) {
this.books = books;
}
public Map<String, String> getIdCard() {
return idCard;
}
public void setIdCard(Map<String, String> idCard) {
this.idCard = idCard;
}
public Set<String> getStudentNumber() {
return studentNumber;
}
public void setStudentNumber(Set<String> studentNumber) {
this.studentNumber = studentNumber;
}
public String getWifeName() {
return wifeName;
}
public void setWifeName(String wifeName) {
this.wifeName = wifeName;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", counselor=" + counselor +
", courses=" + Arrays.toString(courses) +
", books=" + books +
", idCard=" + idCard +
", studentNumber=" + studentNumber +
", wifeName='" + wifeName + ''' +
", properties=" + properties +
'}';
}
}
- Counselor类
public class Counselor {
private String name;
public Counselor() {
}
public Counselor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Counselor{" +
"name='" + name + ''' +
'}';
}
}
- Spring配置文件
<!--引用注入-->
<bean id="counselor" class="com.hqyj.cl.pojo.Counselor">
<property name="name" value="张老师"/>
</bean>
<!--注入学生类-->
<bean id="student" class="com.hqyj.cl.pojo.Student">
<!--普通值注入-->
<property name="name" value="chenchen"/>
<!--引用注入-->
<property name="counselor" ref="counselor"/>
<!--数组注入-->
<property name="courses">
<array>
<value>计算机组成原理</value>
<value>计算机网络</value>
<value>数据结构</value>
</array>
</property>
<!--List集合注入-->
<property name="books">
<list>
<value>Java入门到放弃</value>
<value>Python大数据挖掘与分析</value>
</list>
</property>
<!--Map集合注入-->
<property name="idCard">
<map>
<entry key="idCard" value="29121821212"/>
<entry key="phone" value="123121212121"/>
</map>
</property>
<!--Set集合注入-->
<property name="studentNumber">
<set>
<value>20200301123</value>
</set>
</property>
<!--null注入-->
<property name="wifeName">
<null/>
</property>
<!--Properties注入-->
<property name="properties">
<props>
<prop key="username">chenchen</prop>
<prop key="password">112112</prop>
</props>
</property>
</bean>
- 测试类
public class UserDaoTest {
@Test
public void selectAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = ac.getBean("student", Student.class);
System.out.println(student);
}
}
七、基于注解的配置方式
在 Spring 中,尽管可以使用 XML 配置文件实现 Bean 的装配工作,但如果应用中 Bean 的数量较多,会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。
Java 从 JDK 5.0 以后,提供了 Annotation(注解)功能,Spring 2.5 版本开始也提供了对 Annotation 技术的全面支持,我们可以使用注解来配置依赖注入。
Spring 默认不使用注解装配 Bean,因此需要在配置文件中添加标签 <context:annotation-config/>
,启用注解支持;
<context:annotation-config/>
:仅能够在已经在已经注册过的bean上面起作用。对于没有在spring容器中注册的bean,它并不能执行任何操作
<context:component-scan/>
:除了兼具 <context:annotation-config/
> 功能外,还具有自动将带有@component
、@service
、@Repository
等注解的对象注册到spring容器中的功能
配置包扫描注解:
<context:component-scan base-package=“com.hqyj”/>
1、创建Bean相关的注解
1.1 @Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
1.2 @Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
1.3 @Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
1.4 @Controller
通常作用在控制层(如 Struts2 的 Action、SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
2、Bean生命周期相关的注解
2.1 @Scope
注解在类名上,用于指定Bean作用范围,等同于xml文件中配置Bean时使用scope属性。常用值singleton、prototype
2.2 @PostConstruct
注解在方法上,指定Bean创建时回调方法。相当于xml配置文件中<bean>
标签中使用init-method属性的功能。
2.3 @PreDestroy
注解在方法上,指定Bean销毁时回调方法。相当于xml配置文件中<bean>
标签中使用destroy-method属性的功能。
2.4 @Configuration
全注解实现,@Configuration类似xml配置文件beans标签@bean类似于bean标签
import com.hqyj.pojo.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBean { //方法返回值就是定义bean的类型 方法名就是id
@Bean
public Dog dog(){
return new Dog("哈士奇","黑白色");
}
}
public static void main(String[] args) {
//通过注解Component
ApplicationContext ac = new AnnotationConfigApplicationContext(MyBean.class);
Dog dog = ac.getBean("dog", Dog.class);
System.out.println(dog);
}
3、依赖注入相关的注解
3.1 @Autowired
自动装配:
自动装配就是指 Spring 容器在不使用 <constructor-arg>
和<property>
标签的情况下,可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。
自动装配只需要较少的代码就可以实现依赖注入。但不能自动装配简单数据类型,比如 int、boolean、String 等,相比较显式装配,自动装配不受程序员控制。
@Autowired注解可以应用到 Bean 的属性变量、属性的 setter 方法、非 setter 方法及构造函数等,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。自动装配可以不用到对应的set方法。
在配置文件的bean标签中也有autowired属性
byType:保证 bean 的 id 是唯一的,且该 bean 的类型也是唯一的
byName:保证 bean 的 id 是唯一的,且该 bean 的 id 是此类属性对应的 set方法的方法名
User类
@Data //提供了getter setter方法
@AllArgsConstructor //提供了有参构造方法
@NoArgsConstructor //提供了无参构造方法
@Component
public class User {
@Value("zhangsan")
private String username;
@Value("111")
private String password;
@Autowired
private Address address;
}
Address类
@Component
public class Address {
@Value("chengdu")
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" +
"addr='" + addr + ''' +
'}';
}
}
配置文件bean.xml
<bean id="User" class="com.hqyj.pojo.User">
</bean>
<bean id="address" class="com.hqyj.pojo.Address">
</bean>
测试类
public class UserTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
User user = ac.getBean("User", User.class);
System.out.println(user);
}
}
3.2 @Resource
作用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。
@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
User类
@Data //提供了getter setter方法
@AllArgsConstructor //提供了有参构造方法
@NoArgsConstructor //提供了无参构造方法
@Component
public class User {
@Value("zhangsan")
private String username;
@Value("111")
private String password;
/*@Autowired
@Qualifier("address1")*/
@Resource(name = "address1") //相当于@Autowired+@Qualifier
private Address address;
}
3.3 @Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。(可以指定注入的bean)
Address类(去掉@Component注解)
public class Address {
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" +
"addr='" + addr + ''' +
'}';
}
}
配置文件
<context:component-scan base-package="com.hqyj.pojo"/>
<bean id="User" class="com.hqyj.pojo.User">
</bean>
<bean id="address" class="com.hqyj.pojo.Address">
<property name="addr" value="chengdu"></property>
</bean>
<bean id="address1" class="com.hqyj.pojo.Address">
<property name="addr" value="haerbin"></property>
</bean>
User类
@Data //提供了getter setter方法
@AllArgsConstructor //提供了有参构造方法
@NoArgsConstructor //提供了无参构造方法
@Component
public class User {
@Value("zhangsan")
private String username;
@Value("111")
private String password;
@Autowired
@Qualifier("address1") //指定要注入的bean
private Address address;
}
测试类
public class UserTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
User user = ac.getBean("User", User.class);
System.out.println(user); //拿到haerbin
}
}
额外说明:
<context:component-scan base-package="com.hqyj.pojo"/>
<bean id="User" class="com.hqyj.pojo.User" autowire="byType">
</bean>
<bean id="address" class="com.hqyj.pojo.Address" >
<property name="addr" value="chengdu"></property>
</bean>
<!--<bean id="address1" class="com.hqyj.pojo.Address">
<property name="addr" value="haerbin"></property>
</bean>-->
可以在user中去掉@autowire注解 在配置文件中加入,可以byType注入,但是注入的bean需要唯一。
<context:component-scan base-package="com.hqyj.pojo"/>
<bean id="User" class="com.hqyj.pojo.User" autowire="byName">
</bean>
<bean id="address" class="com.hqyj.pojo.Address" >
<property name="addr" value="chengdu"></property>
</bean>
<bean id="address1" class="com.hqyj.pojo.Address">
<property name="addr" value="haerbin"></property>
</bean>
autowire=“byName”,是根据pojo中get和set方法的参数名获取对应的值(可以手动编写User类的getter和setter方法测试)
想要注入对应的bean,需要修改set方法的方法名,例如:
public void setAddress1(Address address) {
this.address = address;
}
3.4 @Value
@Autowired注入另外的bean,而@Value注解则是注入值。由于在java源代码中注入常量值会带来硬编码问题,该注解使用较少。
4、示例代码
- UserService
public interface UserService {
void selectAll();
}
- UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
// 注入值
@Value("张三")
private String username;
@Override
public void selectAll() {
System.out.println(username);
userDao.selectAll();
}
}
- Spring配置文件
<context:component-scan base-package="com.hqyj.cl" />
<bean id="man" class="com.hqyj.cl.pojo.Man" />
<bean id="student" class="com.hqyj.cl.pojo.Student">
<property name="name" value="student1"/>
</bean>
<bean id="student1" class="com.hqyj.cl.pojo.Student">
<property name="name" value="student1"/>
</bean>
<bean id="counselor" class="com.hqyj.cl.pojo.Counselor">
<property name="name" value="张老师" />
</bean>
- Man类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Man {
@Value("张三")
private String name;
/* @Autowired
@Qualifier("student1")*/
@Resource(type = Student.class)
private Student student;
@Autowired
private Counselor counselor;
}
- 测试类
public class UserDaoTest {
@Test
public void selectAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
Man man = ac.getBean("man", Man.class);
System.out.println(man);
}
}