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)中阐述

相关推荐
一只叫煤球的猫5 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9656 小时前
tcp/ip 中的多路复用
后端
bobz9656 小时前
tls ingress 简单记录
后端
皮皮林5517 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友7 小时前
什么是OpenSSL
后端·安全·程序员
bobz9657 小时前
mcp 直接操作浏览器
后端
前端小张同学10 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook10 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康11 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在11 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net