想要彻底搞懂spring源码?首先你得先搞懂Spring IOC,安排

 2023-02-09
原文作者:A一只程序猿 原文地址:https://juejin.cn/post/7095696968659238942

1.Spring概述

Spring是Java应用轻量级开源框架,所谓轻量级指的是:API简单,运行时占用资源少。 Spring的核心是 IOC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)。 Spring一个应用框架, 提供了表现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多应用技术。 Spring还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

2.IOC(Inverse Of Control)思想

IOC(控制反转)相当于一个容器,这个容器里面放你想放的对象或者要用到的对象。之前我们在创建对象直接nw创建。现在有了spring提供的IOC容器我们可以直接从容器中拿对象。转化了之前创建对象的方式,将对象的控制权从程序员手中转换到了spring IOC容器中。

3.重要API

两个接口 BeanFactory 这是SpringIOC容器的顶级接口,它定义了SpringIOC的最基础的功能,但是其功能比较简单, 一般面向Spring自身使用。 BeanFactroy在第一次使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

202301012021278521.png

javaApplicationContext 这是在BeanFactory基础上衍生出的接口,它扩展了BeanFactory的功能,一般面向程序员使用。 ApplicationContext是在容器启动时,一次性创建并加载了所有的Bean。

三个实现类

ClassPathXmlApplicationContext 读取类路径下的xml作为配置文件 javaFileSystemXmlApplicationContext 读取本地目录下的xml作为配置文件 javaAnnotationConfigApplicationContext 读取一个Java类作为配置文件

202301012021286162.png

一个方法

getBean() 用于从Spring容器中获取Bean对象,参数可以使用三种情况:

    getBean("id")                 使用bean的id从容器中查找对象
    getBean(Bean.class)           使用bean的class类型从容器中查找对象
    getBean("id", Bean.class)     使用bean的id 和 class类型从容器中查找对象
    复制代码

一般用第二种,当接口有多个实现类的时候一般用第三种这样可以指定具体应用哪个实现类,当然一个接口有多个实现类的情况非常少。

4.Bean的配置(xml)

4.1Bean的创建方式

new的方式创建 这里拿创建一个UserDao对象举例 这种方式会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。 id为该对象的名字 class为该对象的实现类

    <bean id="userDao" class="com.demo.dao.impl.UserDaoImpl"/>
    复制代码

静态工厂创建

工厂类代码

    /**
     * @Author WS Hu
     * @Date 2020/7/22 18:00
     * @Version 1.0
     */
    public class FactoryBean {
        //静态工厂
        public static UserDao createUserDao(){
            return new UserDaoImpl();
        }
    
    }
    复制代码

在配置文件中添加静态工厂创建对象配置 id为该对象的名字 class为静态工厂类 factory-method为工厂中的静态方法

    <!--使用静态工厂创建对象-->
        <bean id="userDao" class="com.demo.factory.FactoryBean" factory-method="createUserDao"/>
    
    复制代码

实例化工厂创建

工厂类代码

    /**
     * @Author WS Hu
     * @Date 2020/7/22 18:00
     * @Version 1.0
     */
    public class FactoryBean {
            //实例工厂
        public UserDao createUserDaoSimple(){
            return new UserDaoImpl();
        }
    }
    复制代码

在配置文件中添加实例化工厂创建对象配置 第一个bean标签中: id为该工厂类的名字 class为实例化工厂类

第二个bean标签中: id为该对象的名字 class为实例化工厂类 factory-bean为工厂实例 factory-method为工厂中的实例化方法

     <!--使用实例工厂创建对象-->
        <bean id="factoryBean" class="com.demo.factory.FactoryBean"/>
        <bean id="userDao" factory-bean="factoryBean" factory-method="createUserDaoSimple"/>
    复制代码

4.2bean的作用范围

在Spring中,对于bean支持五种作用范围:

    singleton(默认) 单例模式,即对象只创建一次, 然后一直存在
    prototype 多例模式,即每次获取bean的时候,IOC 都给我们创建一个新对象request  web项目中,Spring创建一个Bean的对象,将对象存入到request域中(  同一个request获取到 的是同一个对象)
    session web 项目中,Spring创建一个Bean 的对象,将对象存入到session域中
    globalSession 用于分布式web开发中,创建的实例绑定全局session对象
    复制代码

依赖注入(Dependency Injection,DI)

给对象中的属性赋值 依赖注入有两种方式,分别是使用构造函数和set方法

构造函数

满参构造

    //满参构造
        public User(String id, String name, Date date) {
            this.id = id;
            this.name = name;
            this.date = date;
        }
    复制代码

在配置文件中配置相关的值

    <!--
            构造函数的依赖注入
            constructor-arg 指的是参数
            name    用于指定构造函数的形参名称
            index   用于指定构造函数的形参索引位置  一般省略
            type    用于指定构造函数的形参类型  一般省略   底层可以通过反射技术  自动获取
            value   用于给简单类型(基本类型  基本类型的包装类型  String)的变量赋值
            ref=""  用于指定一个当前容器中已经存在的bean的id
        -->
        <bean id="user" class="com.demo.domain.User">
            <constructor-arg name="id" value="1"/>
            <constructor-arg name="name" value="WSH"/>
            <constructor-arg name="date" ref="date"/>
        </bean>
        <bean id="date" class="java.util.Date"/>
    复制代码

set方法

JavaBean中的set方法

    public void setId(String id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setDate(Date date) {
            this.date = date;
        }
    复制代码

在配置文件中配置相关的值

    <!--
        	set方法的依赖注入
        	property 指的是属性,需要注意的是属相指的是set方法名字抛去set剩下的字段
            name    用于指定构造函数的形参名称
            value   用于给简单类型(基本类型  基本类型的包装类型  String)的变量赋值
            ref=""  用于指定一个当前容器中已经存在的bean的id
        -->
    <bean id="user" class="com.demo.domain.User">
            <property name="id" value="1"/>
            <property name="name" value="WSH"/>
            <property name="date" ref="date"/>
        </bean>
        <bean id="date" class="java.util.Date"/>
    复制代码

IOC注解版

在IOC注解版中我们利用一些注解实现和配置文件相同的功能,省去了繁琐的配置问题。这时候配置文件中就不需要写某些配置信息了,在纯注解版中任何配置信息都不需要在配置文件中写。 首先要用注解我们要在配置文件里加一个爱声明

    <!--
            组件扫描
            它要求我们制定一个基础包, Spring会去扫描这个包及其子包下的类中的注解只有被Spring扫描到的注解才会发挥作用
        -->
        <context:component-scan base-package="com.demo" />
    复制代码

下面总结一些注解和应用场景 需要说明的是在web层,service层,dao层中推荐使用@Controller,@Service,@Repository这三个注解将对象存到容器中 但是在util包domain包等不属于三层的包中想要存对象一般用@Component

存对象注解

@Component@Controller @Service @Repository

    	@Component
    	用于实例化对象,相当于配置文件中的< bean id="" class=""/>
    	它支持一个属性value,相当于xml中bean的id。如果不写,默认值为类名的首字母小写
    	@Controller  @Service  @Repository
    	这三个注解的功能跟@Component类似,他们分别标注在不同的层上。
    	@Controller  标注在表现层的类上
    	@Service     标注在业务层的类上
    	@Repository  标注在持久层的类上
    	推荐使用这三个,当一个类实在不好归属在这三个层上时,再使用@Component
    复制代码

例如 将Account对象存入IOC容器中(Account在domain包下)

    @Component
    public class Account {
        private Integer aid;
        private String name;
        private Float balance;
    复制代码

将AccountDaoImpl对象存入IOC容器中(dao包下)

202301012021291523.png

将AccountServiceImpl对象存入IOC容器中(service包下)

202301012021299624.png

@Scope

@Scope用于指定bean的作用范围(单例和多例),相当于配置文件中的< bean scope="">

    @Scope用于指定bean的作用范围(单例和多例),相当于配置文件中的< bean scope=""> 
    @Scope("prototype") //多例
    @Scope("singleton") //单例
    复制代码

@PostConstruct @PreDestroy

    @PostConstruct @PreDestroy 这两个注解标注方法分别在对象的创建之后和销毁之前执行。
    相当于< bean init-method="init" destroy-method="destory" /> 
    复制代码

取对象

@Autowired @Resource

    @Autowired
    放在属性上:
    表示先按照类型给属性注入值【by type】
    如果IOC容器中存在多个与属性同类型的对象,则会按照属性名注入值【by name】
    也可以配合@Qualifier("IOC容器中对象id")注解直接按照名称注入值
    放在方法上:
    表示自动执行当前方法,如果方法有参数,会自动从IOC容器中寻找同类型的对象给参数传值
    也可以在参数上添加@Qualifier("IOC容器中对象id")注解按照名称寻找对象给参数传值
    
    @Resource
    只能放在属性上,表示先按照属性名匹配IOC容器中对象id给属性注入值【by name】
    若没有成功,会继续根据当前属性的类型匹配IOC容器中同类型对象来注入值 【by type】
    若指定了name属性@Resource(name = "对象id"),则只能按照对象id注入值
    复制代码

@Value

    @Value
    用于简单数据类型的注入,相当于< property name="" value="" >,但通常不这么使用
    此注解一般用于解析其它properties配置文件中的key值俩获取对应的value,写法如下:
    @Value("${key}")
    复制代码

202301012021307325.png

@Configuration

    @Configuration
    用于指定当前类是一个 spring配置类,当创建容器时会从该类上加载注解。
    复制代码

202301012021315366.png

@Bean

@Bean

    @Bean
    该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。它支持一个name属性,用于
    给生成的bean取一个id。
    复制代码

202301012021323707.png

@ComponentScan

    @ComponentScan
    组件扫描注解。 相当于xml配置文件中的< context:component-scan base-package=""/
    复制代码

202301012021334718.png

@PropertySource

    @PropertySource
    用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties 配置
    文件中,就可以使用此注解指定 properties 配置文件的位置。
    复制代码

202301012021342689.png

@Import

    @Import
    在一个配置类中,引入其他配置,具体写法:@Import(DaoConfig.class)
    效果等于之前xml配置中的
    复制代码

最后

感谢你看到这里,说的都是自己的一些看法和见解,如有不对,请指正!觉得文章对你有帮助的话不妨给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!