Spring核心

Spring的两个核心

IOC控制反转

  • Inverse of Control的简写,为 控制反转,指是将对象的创建和管理交由Spring框架来完成,而不是由开发人员手动创建和管理

  • 即:反转资源获取方向,把自己创建资源、向环境索取资源的方式变为环境自动将资源准备好,我们享受资源注入

IOC容器

  • IoC容器是用来实现IoC思想的一个工具或者说技术手段

  • 它能够自动扫描应用程序中的对象,将它们实例化,并自动注入它们所需要的依赖对象,使应用程序的开发人员能够更加专注于业务逻辑的实现,而不用关心对象的创建和管理。Spring通过IoC容器来管理所有的Java对象的实例化和初始化,控制着对象与对象之间的依赖关系

依赖注入DI

  • DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想,是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
  • 所以 IoC 是一种控制反转的思想,而依赖注入 DI 是对 IoC 的一种具体实现
  • Bean管理:指Bean对象的创建,以及Bean对象中属性的赋值

IOC容器实现

Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先需要创建IoC容器,Spring提供了IoC容器的两种实现方式

  • BeanFactory
    这是IoC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发人员使用。

  • ApplicationContext
    BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者,几乎所有场合都使用 ApplicationContext,而不使用底层的BeanFactory。

ApplicationContext的主要实现类

类型说明
AnnotationConfigApplicationContext使用注解方式构建IoC容器
ClassPathXmlApplicationContext使用XML配置文件方式构建Spring IoC容器

注解管理Bean

Bean对象定义

在Spring框架规范中,所有由spring管理的对象都称之为Bean对象。
Spring提供了以下多个注解,这些注解可以直接标注在java类上,将它们定义成Spring Bean。

注解说明
@Component该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅标识容器中的一个组件(Bean),并且可以作用在任何层次,例如Service层、Dao层等,使用时只需将该注解标注在相应的类上即可。
@Repository该注解用于数据访问层(Dao层)的类标识为Spring中的Bean,功能与@Component相同。
@Service该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
@Controller该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。
Bean对象获取
ApplicationContext context = new AnnotationConfigApplicationContext("包扫描路径");
User user = context.getBean(类名.class);

Bean对象作用域分析@Scope

在Spring框架中,Bean是按照作用域来创建的
常见的作用域有两种:Singleton和Prototype

Singleton (单例)
  • 是指整个应用中只有一个实例,并在第一次请求时创建实例
  • 单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如数据库连接。
Prototype(多例)
  • 是指每次请求都会创建一个新的实例并返回,每个实例之间是相互独立的
  • 多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。

在Spring中可以通过 @Scope 注解来指定bean的作用域范围,具体如下

取值含义
@Scope(“singleton”)在IoC容器中,这个bean的对象为单实例
@Scope(“prototype”)这个bean在IoC容器中有多个实例

Bean对象生命周期管理

  • 程序中的每个对象都有生命周期,对象的创建、初始化、应用、销毁的整个过程称之为对象的生命周期;
  • 在对象创建以后需要初始化,应用完成以后需要销毁时执行的一些方法,可以称之为是生命周期方法;

在spring中,可以通过 @PostConstruct@PreDestroy 注解实现bean对象生命周期的初始化和销毁时的方法。

引用外部属性文件

  • 实际开发中,很多情况下我们需要对一些变量或属性进行动态配置,而这些配置可能不应该硬编码到我们的代码中,因为这样会降低代码的可读性和可维护性。

  • 我们可以将这些配置放到外部属性文件中,比如database.properties文件,然后在代码中引用这些属性值,例如jdbc.url和jdbc.username等。这样,我们在需要修改这些属性值时,只需要修改属性文件,而不需要修改代码,这样修改起来更加方便和安全。

  • 通过将应用程序特定的属性值放在属性文件中,我们还可以将应用程序的配置和代码逻辑进行分离,这可以使得我们的代码更加通用、灵活。

自动扫描配置

  • 自动扫描配置是 Spring 框架提供的一种基于注解(Annotation)的配置方式,用于自动发现和注册 Spring 容器中的组件。当我们使用自动扫描配置的时候,只需要在需要被 Spring 管理的组件(比如 Service、Controller、Repository 等)上添加对应的注解,Spring 就会自动地将这些组件注册到容器中,从而可以在其它组件中使用它们。

  • 在 Spring 中,通过 @ComponentScan 注解来实现自动扫描配置。
    @ComponentScan 注解用于指定要扫描的包或类。
    Spring 会在指定的包及其子包下扫描所有添加 @Component(或 @Service、@Controller、@Repository 等)注解的类,把这些类注册为 Spring Bean,并纳入 Spring 容器进行管理。

请添加图片描述

AOP面向切面编程

Aspect Oriented Programming 的简写,为 面向切面编程。AOP用来封装多个类的公共行为,将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少系统的重复代码。
请添加图片描述

添加依赖

<dependencies>
    <!-- Maven坐标:
    https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <!-- 引入spring context依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.24</version>
    </dependency>
</dependencies>

入门案例

注解

  • @Component
    注解描述的类,表示此类交给Spring框架管理
package cn.tedu.spring.example;
import org.springframework.stereotype.Component;

@Component
public class User {
    public void userRun(){
        System.out.println("User is do something~~");
    }
}

通过Spring创建Java bean对象

  • AnnotationConfigApplicationContext扫描这个包中所有带有@Component注解的类,并根据这些类创建相应的Spring组件
package cn.tedu.spring.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestUser {
    public static void main(String[] args) {
    
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.example");
        User user1 = context.getBean(User.class);
        System.out.println("user1 = " + user1);
        user1.userRun();
    }
}

请添加图片描述

xml管理Bean案例

  • 在Spring框架中,Bean的配置可以通过 XML 文件来完成。这个文件通常被称为 Spring 配置文件或 Spring XML 文件。
package cn.tedu.spring.example;

public class UserXml {
    private String username;
    private String password;
    public void run(){
        System.out.println("今天天气不错挺风和日丽的~");
    }
}

main下创建bean.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userXml" class="cn.tedu.spring.example.UserXml"></bean>
</beans>
package cn.tedu.spring.example;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUserXml {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserXml userXml = (UserXml)context.getBean("userXml");
        userXml.run();
    }
}

请添加图片描述

DI依赖注入案例

package cn.tedu.spring.dibase;

public class Book {
     private String bookName;
     private String bookAuthor;

     public void setBookName(String bookName) {
          this.bookName = bookName;
     }

     public void setBookAuthor(String bookAuthor) {
          this.bookAuthor = bookAuthor;
     }

     @Override
     public String toString() {
          return "Book{" +
                  "bookName='" + bookName + '\'' +
                  ", bookAuthor='" + bookAuthor + '\'' +
                  '}';
     }
}

创建Spring配置文件 bean-di.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="book" class="cn.tedu.spring.dibase.Book">
        <property name="bookName" value="倚天屠龙记" />
        <property name="bookAuthor" value="金庸"/>
    </bean>
</beans>
package cn.tedu.spring.dibase;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBook {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
        Book book = context.getBean(Book.class);
        System.out.println("book = " +book);
    }
}

请添加图片描述

注解管理Bean案例

@Value

​ @Value注入是将属性值直接注入到bean中,主要用于注入一些简单类型的属性(如字符串、基本类型等);
使用时需要注意属性的类型和格式,否则会导致注入失败。

@Autowired

@Autowired注入是将对象注入到bean中,并且在注入对象时会根据依赖注入容器中 bean的类型 进行匹配。
如果容器中有多个类型匹配的bean存在,则会抛出异常。因此,@Autowired注入常用于注入复杂对象、接口类型的属性或其他bean实例。

package cn.tedu.spring.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    @Value("jdbc:mysql://localhost:3306/tedu")
    private String dbUrl;
    @Value("root")
    private String username;
    private String password;
    
    @Value("qwertyuiop")
    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "UserDao{" +
                "dbUrl='" + dbUrl + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
package cn.tedu.spring.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Value("注册业务")
    private String serveName;

    @Autowired
    private UserDao userDao;

    @Override
    public String toString() {
        return "UserService{" +
                "serveName='" + serveName + '\'' +
                ", userDao=" + userDao +
                '}';
    }
}
package cn.tedu.spring.bean;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestUserDao {

    @Test
    public void testBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.bean");
        UserDao userDao = context.getBean(UserDao.class);
        System.out.println("userDao = " + userDao);
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.bean");
        UserService userService = context.getBean(UserService.class);
        System.out.println("userService:"+ userService);
    }
}

请添加图片描述

根据接口类型注入

当一个接口有一个唯一的实现类时,Spring框架会通过接口找到该接口对应的实现类,并进行bean对象的创建以及DI注入操作

package cn.tedu.spring.auto;

public interface Cache {
}
package cn.tedu.spring.auto;

import org.springframework.stereotype.Component;

@Component
public class CacheImpl1 implements Cache{
}
package cn.tedu.spring.auto;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class UserCache {

    @Autowired
    private Cache cache;
}
package cn.tedu.spring.auto;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestUserCache {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.auto");
        UserCache userCache = context.getBean(UserCache.class);
        System.out.println("userCache:" +userCache);
    }
}

当一个接口有多个实现类时,Spring无法确定注入哪个实现类对象,因此会报错:
No qualifying bean of type ‘cn.tedu.spring.auto.Cache’ available: expected single matching bean but found 2: cacheImpl1,cacheImpl2
可以结合 @Qualifier注解 来解决这个问题

package cn.tedu.spring.auto;

import org.springframework.stereotype.Component;

@Component
public class CacheImpl2 implements Cache{
}

@Qualifier

@Qualifier注解是用于限定一个接口有多个实现类时,根据指定的限定条件来选择具体的实现类的注解;

当Spring容器中存在多个实现同一接口的bean时,在注入时,由于不能确定注入哪一个实现类,就需要通过@Qualifier注解来明确指定要注入的bean的名称。

package cn.tedu.spring.auto;

import org.springframework.stereotype.Component;

@Component(value = "AAAA")
public class CacheImpl1 implements Cache{

}
package cn.tedu.spring.auto;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

@Repository
public class UserCache {

    @Autowired
    @Qualifier(value = "AAAA")
    private Cache cache;
}

请添加图片描述

@Resource

注解是JavaEE提供的注解之一,也支持在Spring Framework中使用。在Spring中,它可以用来注入Bean实例,与@Autowired注解的作用类似,但其也有自己的一些特点。

  • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该解释是标准注解 @Resource 注解默认根据名称装配byName;
    当未指定 name 时,则使用属性名作为 name 进行装配;
    如果通过name也未找到,则会自动启动通过类型byType装配。
  • @Autowired注解是Spring框架自己的,
    @Autowired注解默认根据类型装配byType,如果想根据名称匹配,需要配合@Qualifier注解一起使用。
package cn.tedu.spring.resource;

public interface ResMapper {
}
package cn.tedu.spring.resource;

import org.springframework.stereotype.Component;

@Component
public class ResMapperImpl1 implements ResMapper {
}
package cn.tedu.spring.resource;

import org.springframework.stereotype.Component;

@Component
public class ResMapperImpl2 implements ResMapper{
}
package cn.tedu.spring.resource;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class WeiboMapper {
    @Resource(name = "resMapperImpl1")
    private ResMapper resMapper;
}
package cn.tedu.spring.resource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestWeiboMapper {
    public static void main(String[] args) {
        ApplicationContext context = new
                AnnotationConfigApplicationContext("cn.tedu.spring.resource");
        WeiboMapper weiboMapper = context.getBean(WeiboMapper.class);
        System.out.println("weiboMapper:" + weiboMapper);

    }
}

总结

  1. 指定@Resource中的name,则根据名称装配
  2. 未指定name时,则根据属性名装配
  3. 未指定name,属性名也不一致,则根据类型装配

请添加图片描述

Bean对象作用域分析@Scope

package cn.tedu.spring.scope;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Scope(value = "singleton")
//@Scope(value = "protoatype")
@Component
public class Order {
    private String status;
}
package cn.tedu.spring.scope;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestOrder {
    public static void main(String[] args) {
        ApplicationContext context = new
                AnnotationConfigApplicationContext("cn.tedu.spring.scope");
                
        Order order1 = context.getBean(Order.class);
        System.out.println(order1);

        Order order2 = context.getBean(Order.class);
        System.out.println(order2);
    }
}

总结

  • 当为单例模式 singleton 时,多次获取bean实例的地址是相同的
    单例模式适用于需要共享数据并且需要避免重复创建实例的情况
  • 当为多例模式 prototype 时,多次获取bean实例的地址是不同的
    多例模式适用于需要动态地创建对象并提供独立实例的情况
    请添加图片描述

Bean对象生命周期管理

  • @PostConstruct 注解
    生命周期初始化方法,在对象构建以后执行。

  • @PreDestroy 注解
    生命周期销毁方法,比如此对象存储到了spring容器,那这个对象在spring容器移除之前会先执行这个生命周期的销毁方法(注:prototype作用域对象不执行此方法)。

package cn.tedu.spring.life;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class Life {
    private String lifeTime;

    public Life() {
        System.out.println("1.实例化(构造方法)");
    }

    @Value("高科技你咬我")
    public void setLifeTime(String lifeTime) {
        this.lifeTime = lifeTime;
        System.out.println("2.属性赋值(set方法)");
    }

    @PostConstruct
    public void init(){
        System.out.println("3.初始化方法执行");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("5.销毁阶段:销毁之前需要执行的操作");
    }
}
package cn.tedu.spring.life;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestLife {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext("cn.tedu.spring.life");
        Life life = context.getBean(Life.class);
        System.out.println("4.使用阶段:开发者");
        context.close();
    }
}

总结

  1. 实例化阶段(bean对象创建)
    在这个阶段中,IoC容器会创建一个Bean的实例,并为其分配空间。这个过程可以通过构造方法完成。

  2. 属性赋值阶段
    在实例化完Bean之后,容器会把Bean中的属性值注入到Bean中,这个过程可以通过set方法完成。

  3. 初始化阶段(bean对象初始化)
    在属性注入完成后,容器会对Bean进行一些初始化操作;

  4. 使用阶段
    初始化完成后,Bean就可以被容器使用了

  5. 销毁阶段
    容器在关闭时会对所有的Bean进行销毁操作,释放资源。
    请添加图片描述

引用外部属性文件

在 resources 目录下创建文件 :application.properties

databaseUrl=jdbc:mysql://localhost:3306/tedu
username=root
password=root
package cn.tedu.spring.file;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Repository;

@PropertySource(value = "classpath:application.properties")
@Repository
public class Database {
    @Value("${databaseUrl}")
    private String url;
    @Value("${username}")
    private String uname;
    @Value("${password}")
    private String pwd;

    @Override
    public String toString() {
        return "Database{" +
                "url='" + url + '\'' +
                ", uname='" + uname + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
package cn.tedu.spring.file;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestDatabase {
    public static void main(String[] args) {
        ApplicationContext context = new
                AnnotationConfigApplicationContext("cn.tedu.spring.file");
        Database database = context.getBean(Database.class);
        System.out.println(database);
    }
}

请添加图片描述

自动扫描配置

  • @Configuration注解:
    标识此类为Spring的配置类,Spring在启动时会自动加载此类;
  • @ComponentScan注解:
    自动扫描注解;

指定包路径为:cn.tedu.spring, 扫描该包及子孙包中所有的类,把所有添加相关注解的类注册为Spring Bean;
未指定包路径,则扫描该配置文件[SpringConfig.java]所在包以及子孙包中的类;

package cn.tedu.spring.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value = "cn.tedu.spring")
public class SpringConfig {
}

package cn.tedu.spring.config;

import cn.tedu.spring.bean.UserService;
import cn.tedu.spring.file.Database;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestSpringConfig {
    public static void main(String[] args) {
        ApplicationContext context = new
                AnnotationConfigApplicationContext(SpringConfig.class);
 
        UserService userService = context.getBean(UserService.class);
        System.out.println(userService);

        Database database = context.getBean(Database.class);
        System.out.println(database);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值