Spring中的IOC与AOP的理解(1)

什么是Spring?

它是一个轻量级,非入侵式的控制反转(IoC)和面向切面(AOP)的容器框架.

Spring是一个生态:可以构建企业级应用程序所需的一切基础设施

通常Spring指的就是Spring Framework,它有两大核心:IOC和AOP

1.IoC和DI的支持

Spring的核心就是一个大的工厂容器,可以维护所有对象的创建和依赖关系,Spring工厂用于生成Bean,并管理Bean,实现高内聚低耦合的设计理念。

通俗的说,通过IOC的方式,如果在一个对象中使用另外的对象,从前我们需要手动地new对象,比如Connection还需要销毁,对象始终会和其他接口或类耦合,而使用IOC,对象的管理与创建都不用我们操心,也可以实现解耦的目的.

IOC(Inversion of control)是什么?

IOC 是一种编程思想,它通过控制反转的方式降低了代码之间的耦合度。在传统的程序设计中,我们通常会直接调用其他对象的方法。但在 IoC 框架中,控制权被反转,对象之间的调用由框架负责管理。这使得代码更易于测试和维护。IOC的目的是DI

DI(Dependency Injection)

DI即依赖注入,DI的前提是IOC,依赖注入的核心思想是将对象的依赖关系从硬编码中解耦出来,使得代码之间的耦合度降低。DI 可以通过构造器注入和Setter方法进行属性注入两种方式实现。

Bean的说明:
  • 在Spring框架中,"bean" 是一个被框架实例化、组装和管理的对象。
  • Beans可以通过XML配置文件、Java注解或Java配置类配置。
配置bean的方式:
  1. XML配置文件:通过<bean>标签在XML文件中定义。
  2. 注解:如@Component, @Service, @Repository, @Controller等。这些注解自动将类注册为Spring应用程序上下文中的bean。
  3. Java配置:使用@Configuration和@Bean注解在Java类中定义。
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标签:表示配置bean
    	id属性:表示给bean起名字
    	class属性:表示给bean定义类型
    <bean id="" class=""></bean>
	-->
    <bean id="userDao" class="com.yaorange.dao.impl.UserDaoImpl"/>
    <bean id="userService" class="com.yaorange.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>

对应的java类

UserDao接口

public interface UserDao {
    void getList();
}

UserDao实现类UserDaoImpl

public class UserDaoImpl implements UserDao {

    @Override
    public void getList() {
        System.out.println("用户列表");
    }
}

UserService接口以及实现类代码:

public interface UserService {
    void selectUser();
}

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    @Override
    public void selectUser() {
        System.out.println("执行service...");
        userDao.getList();
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

通过IOC容器获取bean对象

public class Main {
    public static void main(String[] args) {
        //创建IOC容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IOC容器中获取bean对象
        
        UserService userService = ctx.getBean("userService", UserService.class);
        userService.selectUser();

    }
}
Bean注解配置案例
@Service
//开启事务管理
@Transactional(rollbackFor = Exception.class)
public class BookServiceImpl implements BookService {
    @Autowired
    private BooksMapper booksMapper;

    @Override
    public List<Books> selectAll() {
        return booksMapper.selectAll();
    }
}
java配置类:
@Configuration
//加载资源路径
@PropertySource("classpath:props/jdbc.properties")
public class JdbcConfig {
    @Value("${jdbc.driverClass}")
    private String driverClass;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.url}")
    private String url;

    /**配置Druid数据源*/
    /**@Bean将返回的dataSource标识为被Spring管理的bean对象*/
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }
}
Bean的生命周期:
  1. 实例化(Instantiation):在这个阶段,Spring容器会根据配置信息或注解来创建Bean的实例。可以通过构造函数实例化,也可以通过工厂方法实例化。
  2. 属性赋值(Population):在实例化后,Spring容器会将配置的属性值或注解中的属性值注入到Bean实例中。这个过程可以通过setter方法注入,也可以通过字段注入。
  3. 初始化(Initialization):在属性赋值完成后,Spring容器会调用Bean的初始化方法。在这个阶段,可以进行一些初始化操作,如数据加载、资源准备等。
  4. 使用(In Use):在初始化完成后,Bean就可以被应用程序使用了。在这个阶段,Bean会被注入到其他Bean中,或者通过Spring容器获取并调用其方法。
  5. 销毁(Destruction):当应用程序不再需要Bean时,Spring容器会负责销毁Bean。
IOC容器的加载过程:
1.资源定位:

提供bean的配置文件,容器通过BeanDefinitionReader读取配置文件或配置类

2.解析配置文件:

解析描述Bean的定义和依赖关系的配置文件,Bean定义包含了Bean的类名、作用域、构造函数参数、属性值等信息。根据解析的配置信息创建相应的BeanDefinition对象

3.bean的配置和初始化

IOC容器根据解析得到的BeanDefinition对象进行bean配置和初始化;在bean配置的过程中会进行依赖注入;在依赖注入的实现中,会使用反射来动态地创建对象实例

通过资源定位,配置文件解析,容器初始化以及依赖注入这些过程,IOC容器就完成了对象的创建/配置和依赖管理;

IOC容器BeanFactory和ApplicationContext的区别:
  • 容器类型与启动时机:BeanFactory是Spring的底层容器,只提供了基本的IoC和DI(依赖注入)功能。当调用getBean方法时,只有在使用到某个Bean时,BeanFactory才会对该Bean进行加载实例化,类似单例模式的懒汉模式。而ApplicationContext作为BeanFactory的子接口,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能。在启动时,ApplicationContext会预先实例化所有的单例Bean,通过设置bean的属性lazy-init=true来让Bean延迟实例化,类似单例模式的饿汉模式。
  • 核心实现:BeanFactory的一个核心实现是XMLBeanFactory,而ApplicationContext的一个核心实现是ClassPathXmlApplicationContext。Web容器的环境我们使用WebApplicationContext并且增加了getServeltContext方法。

实现类:

BeanFactory的实现类:

  • XmlBeanFactory:从XML配置文件中加载Bean的定义,并实例化Bean。
  • ListableBeanFactory:BeanFactory的子接口,提供了列出所有Bean定义的方法。
  • AutowireCapableBeanFactory:自动装配Bean的容器,可以根据Bean的名称、类型或名称模式进行自动装配。
  • ConfigurableListableBeanFactory:可配置的ListableBeanFactory,提供了更多的配置选项。

ApplicationContext的实现类

  • ClassPathXmlApplicationContext:从类路径下的XML配置文件加载Bean的定义。
  • FileSystemXmlApplicationContext:从文件系统中的XML配置文件加载Bean的定义。
  • WebApplicationContext:为Web应用程序提供的ApplicationContext实现,可以与Servlet容器集成。
  • AnnotationConfigApplicationContext:从Java配置类加载Bean的定义,支持@Configuration和@Bean注解。
  • AnnotationBeanNameAutoProxyCreator:自动为带有@Controller或@Repository注解的Bean创建代理。
依赖注入的方式:
setter方式注入

通过类中属性的set方法实现属性赋值

代码示例:

public class UserService {  
    private UserRepository userRepository;  
  
    // 默认的构造函数  
    public UserService() {  
    }  
  
    // UserRepository的setter方法  
    public void setUserRepository(UserRepository userRepository) {  
        this.userRepository = userRepository;  
    }  
  
    public User getUserById(int id) {  
        return userRepository.findById(id);  
    }  
}

public class UserRepositoryImpl implements UserRepository {  
    @Override  
    public User findById(int id) {  
        // 假设这里是从数据库中查找用户  
        return new User(id, "Username" + id);  
    }  
}

在xml中通过<property>标签进行setter依赖注入

UserService类有一个名为setUserRepository的setter方法,它接受一个UserRepository类型的参数。在Spring的XML配置文件中,<property>元素用于指定要注入的属性名称(即setter方法的名称去掉set前缀)和对应的bean引用。当Spring容器创建UserService的bean时,它会调用setUserRepository方法并将userRepository bean传递给它,从而完成依赖注入。

<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">  
  
    <!-- 定义UserRepository的bean -->  
    <bean id="userRepository" class="com.example.UserRepositoryImpl"/>  
  
    <!-- 定义UserService的bean,并通过setter注入UserRepository -->  
    <bean id="userService" class="com.example.UserService">  
        <property name="userRepository" ref="userRepository"/>  
    </bean>  
  
</beans>
构造器方式注入

通过类中构造函数实现属性赋值

代码示例:

public class UserService {  
    private final UserRepository userRepository;  
  
    public UserService(UserRepository userRepository) {  
        this.userRepository = userRepository;  
    }  
  
    public User getUserById(int id) {  
        return userRepository.findById(id);  
    }  
}

xml通过<constructor-arg >标签进行依赖注入

UserService类的依赖项UserRepository通过XML配置进行了注入。通过使用<constructor-arg>元素,将UserRepository类型的bean引用传递给UserService的构造函数。这样,当Spring容器创建UserService的实例时,会自动将UserRepository注入到构造函数中。

<!-- UserService的bean定义 -->  
<bean id="userService" class="com.example.UserService">  
    <constructor-arg ref="userRepository"/>  
</bean>  
  
<!-- UserRepository的bean定义 -->  
<bean id="userRepository" class="com.example.UserRepository"/>
依赖注入方式选择
  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  2. 可选依赖使用setter注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. ==自己开发的模块推荐使用setter注入(后期基于注解开发),只有第三方提供的类中没有set方法时才选择构造器注入==
自动装配

自动装配是Spring框架中的一种机制,用于自动将依赖项注入到应用程序对象中。通过自动装配,开发人员可以专注于应用程序的业务逻辑,而不需要手动创建和管理依赖关系。

自动装配的方式:
  1. byType:根据类型进行自动装配。Spring容器会查找类型与注入点匹配的bean,并将其注入到目标属性或构造函数中。如果找到多个匹配的bean,则会抛出异常。
  2. byName:根据名称进行自动装配。Spring容器会查找名称与注入点匹配的bean,并将其注入到目标属性或构造函数中。
  3. constructor:通过构造函数进行自动装配。Spring容器会查找与构造函数参数类型匹配的bean,并将其作为参数传递给构造函数。
  4. autodetect:自动检测需要注入的bean。Spring容器会根据bean的属性和方法,自动推断需要注入的依赖项,并进行装配。

除了基于XML的配置方式,自动装配还可以通过注解的方式实现。例如,可以使用@Autowired注解来自动注入依赖项。

@AutoWired与@Qualifier的说明

在使用@Autowired注解时,需要注意以下几点:

  • @Autowired默认按照类型进行匹配,如果存在多个相同类型的bean实例,会产生歧义。
  • 如果需要指定特定的bean实例,可以使用@Qualifier注解。
  • @Autowired可以标注在类成员变量、方法及构造函数上,用于标注依赖关系。
  • 对于setter注入和构造器注入,可以使用@Autowired注解。
  • @Autowired注解会自动创建实体,并自动调用方法中的参数。

@Qualifier,用于解决@Autowired注入时的歧义问题。当Spring容器中有多个相同类型的Bean时,如果没有使用@Qualifier,则Spring会抛出异常,因为无法确定应该注入哪个Bean。使用@Qualifier可以消除这种歧义,让Spring明确注入指定的Bean。

@Qualifier要与@AutoWired一起使用

AOP的理解将在Spring中的IOC与AOP的理解(2)中阐述

相关推荐
P.H. Infinity12 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天16 分钟前
java的threadlocal为何内存泄漏
java
caridle27 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^32 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋336 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花41 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端43 分钟前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan1 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫