您现在的位置是:首页 >技术杂谈 >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);
    }
}
                
            




U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结