Spring的基础知识

Spring的基础知识

前言

坊间有传言,java程序员实际上是spring程序员,spring是每个java开发者必须掌握的技术。由此可见,spring的影响之大,说spring是java技术的集大成者一点也不为过。从某种角度来说,java和spring已经是互相成就的关系了,java的迭代推动了spring强大,spring的广泛使用也推动java的迭代。

说了那么多,简言之,学java,那就学spring,spring的深度和广度都值得每一个java开发者去研究。

本人在学习和使用spring框架一段时间后,用本系列记录和总结自己的使用心得,通过知识点配合代码的方式来呈现,目的是加深理解,方便回忆,最好能让没接触过spring的小伙伴照着学习后,也能够快速入门。

spring是什么

spring一般说的是spring framework,它是spring家族强大生态下的一个项目,其他的还有诸如springboot,springcloud等等,本文用spring代指spring framework。作为系列的开篇,只展开介绍spring的核心部分,

即spring作为容器的部分。

spring作为容器

几乎每篇介绍spring的文章都会提到spring作为IOC容器的特性,可见,这必是spring的核心所在。把组件交给spring管理,spring把组件存起来,如同一个容器一般。这很好理解,但是,从源码角度直观的感受下:

类DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

   /**
    * Internal marker for a null singleton object:
    * used as marker value for concurrent Maps (which don't support null values).
    */
   protected static final Object NULL_OBJECT = new Object();


   /** Logger available to subclasses */
   protected final Log logger = LogFactory.getLog(getClass());

   /** Cache of singleton objects: bean name --> bean instance */
   private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

   /** Cache of singleton factories: bean name --> ObjectFactory */
   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

   /** Cache of early singleton objects: bean name --> bean instance */
   private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

   /** Set of registered singletons, containing the bean names in registration order */
   private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);

   /** Names of beans that are currently in creation */
   private final Set<String> singletonsCurrentlyInCreation =
         Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

   /** Names of beans currently excluded from in creation checks */
   private final Set<String> inCreationCheckExclusions =
         Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

   /** List of suppressed Exceptions, available for associating related causes */
   private Set<Exception> suppressedExceptions;

   /** Flag that indicates whether we're currently within destroySingletons */
   private boolean singletonsCurrentlyInDestruction = false;

   /** Disposable bean instances: bean name --> disposable instance */
   private final Map<String, Object> disposableBeans = new LinkedHashMap<String, Object>();

   /** Map between containing bean names: bean name --> Set of bean names that the bean contains */
   private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<String, Set<String>>(16);

   /** Map between dependent bean names: bean name --> Set of dependent bean names */
   private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<String, Set<String>>(64);

   /** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
   private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
 //.......................................
 //.......................................
}

由此可见,容器就是通过一个个map来保存组件的。

为什么是DefaultSingletonBeanRegistry这个类来保存组件的呢?

其中的设计思想我不理解,但是这里通过源码角度分析下其继承关系

一般而言,我们通过注解方式来使用spring,使用的上下文是AnnotationConfigApplicationContext这个类

这个类的继承关系如下:
在这里插入图片描述

当我们启动容器的时候,会经历刷新容器的阶段,其中有一个步骤是获bean factory:

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      // 关键在这一步获取容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      //.............................
       
      //...............................
}

获取bean factory实际就是获取父类GenericApplicationContext的成员变量bean factory,其所属类是DefaultListableBeanFactory

DefaultListableBeanFactory的继承关系如下

在这里插入图片描述

DefaultListableBeanFactoryDefaultSingletonBeanRegistry的一个子类。

由此可见,总结一下:

AnnotationConfigApplicationContext这个是实际使用的上下文,其有着成员变量DefaultListableBeanFactory,这个成员变量是DefaultSingletonBeanRegistry的子类。DefaultSingletonBeanRegistry有着许多的map来存放组件,所以最终得出AnnotationConfigApplicationContext也是通过这些map来存放组件的。

IOC容器使用实战

接下来是如何使用spring作为ioc容器,从以下几个方面记录下:

  • 作为IOC容器是怎么使用的
  • 怎么将类交给spring管理
  • spring管理的组件怎么赋值,怎么解决依赖问题

注意:本文使用注解方式使用spring,spring的版本为4.3.12

spring管理组件

为了演示这个功能,创建三个类:测试类,配置类,实体类

使用@Bean方式
//----------实体类-----------------
public class Person {

    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
//---------------配置类-----------------
@Configuration
public class MainConfig {
    @Bean
    public Person person() {
        return new Person();
    }
}
//--------------测试类------------------
public class Test {
    @org.junit.Test
    public void testIOC() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        System.out.println("容器种所有的组件名称如下:");
        List<String> names = Arrays.asList(definitionNames);
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("------------------------");
        System.out.println("获取person组件");
        Person person = applicationContext.getBean("person", Person.class);
        System.out.println("person = " + person);
    }
}

//-----------控制台输出---------------------
容器种所有的组件名称如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
person
------------------------
获取person组件
person = Person{name='null', age='null'}

从控制台输出可以看出,spring容器会自己注入一些组件

使用扫描注解@ComponenScan

在实体类上标上@Component

@Component
public class Person {
 //....................   
}

配置类

@Configuration
@ComponentScan(basePackages = {"com.hhdd.bean"})
public class MainConfig2 {
}

测试类

@org.junit.Test
public void test2(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    String[] definitionNames = applicationContext.getBeanDefinitionNames();
    System.out.println("容器种所有的组件名称如下:");
    List<String> names = Arrays.asList(definitionNames);
    for (String name : names) {
        System.out.println(name);
    }
    System.out.println("------------------------");
    System.out.println("获取person组件");
    Person person = applicationContext.getBean("person", Person.class);
    System.out.println("person = " + person);
}

//----------控制台输出----------------
容器种所有的组件名称如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
person
------------------------
获取person组件
person = Person{name='null', age='null'}
@import方式
直接引入类
@Configuration
@Import({Person.class})
public class MainConfig3 {

}

此时就不需要在Person类上标注@Component注解了

测试类

    @org.junit.Test
    public void test3() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        System.out.println("容器种所有的组件名称如下:");
        List<String> names = Arrays.asList(definitionNames);
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("------------------------");
        System.out.println("获取person组件");
        Person person = applicationContext.getBean(Person.class);
        System.out.println("person = " + person);
    }

}
//---------控制台输出------------
容器种所有的组件名称如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.hhdd.bean.Person
------------------------
获取person组件
person = Person{name='null', age='null'}

可见,通过这种方式,注入的组件名称是全路径类名com.hhdd.bean.Person

importSelector方式

实现这个接口,返回需要导入的组件的全类名

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.hhdd.bean.Person"};
    }
}

@org.junit.Test
public void test4() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
    String[] definitionNames = applicationContext.getBeanDefinitionNames();
    System.out.println("容器种所有的组件名称如下:");
    List<String> names = Arrays.asList(definitionNames);
    for (String name : names) {
        System.out.println(name);
    }
    System.out.println("------------------------");
    System.out.println("获取person组件");
    Person person = applicationContext.getBean(Person.class);
    System.out.println("person = " + person);
}
ImportBeanDefinitionRegistrar

实现这个接口,直接拿registrar给容器种注册对象

public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean res = registry.containsBeanDefinition("person");
        if (!res) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Person.class);
            registry.registerBeanDefinition("person", beanDefinition);
        }
    }
}
/**
 * 注册组件,@import方式之使用ImportBeanDefinitionRegistrar
 */
@org.junit.Test
public void test5() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);
    String[] definitionNames = applicationContext.getBeanDefinitionNames();
    System.out.println("容器种所有的组件名称如下:");
    List<String> names = Arrays.asList(definitionNames);
    for (String name : names) {
        System.out.println(name);
    }
    System.out.println("------------------------");
    System.out.println("获取person组件");
    Person person = applicationContext.getBean(Person.class);
    System.out.println("person = " + person);
}
总结

以上就是spring注入bean的最最基本也最常见的几种方式,更多信息请查阅spring的官方文档

给组件赋值

@Value

此注解可以给组件的基本成员变量赋值

@Component
public class Person2 {
    @Value("${person.name}")
    private String name;
    @Value("#{20-2}")
    private String age;
    @Value("male")
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}
@Configuration
@ComponentScan("com.hhdd.bean")
@PropertySource(value = {"classpath:/person.properties"})
public class DIConfig {

}

测试类

/**
 * 测试组件的属性赋值,基本类型赋值
 */
@org.junit.Test
public void test6(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DIConfig.class);
    System.out.println("--------获取Person2组件-----------");
    Person2 person = applicationContext.getBean(Person2.class);
    System.out.println("person = " + person);
}
@AutoWired

spring利用依赖注入DI,完成对ioc容器中各个组件依赖关系的赋值

@Autowired 注入的基本规则

  • 默认优先按照类型去容器中找对应的组件

  • 如果找到多个相同类型的组件,再将属性名称作为组件id去容器中找

  • @Qualifier(BeanId)明确指出要装配的bean

  • 自动装配默认必须要在容器中找到依赖,否则报错;可通过属性required = false取消这个特性

  • 这个注解可以在的位置:构造器,参数,方法,属性

示例,假设有实体类Student Money Dog Book

@Component
public class Student {
    //属性注入方式
    @Autowired
    private Book book;

    private Dog dog;

    private Money money;
    //构造器注入
    @Autowired
    public Student(Money money){
        this.money = money;
    }
    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    public Dog getDog() {
        return dog;
    }
    //setter注入
    @Autowired
    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Money getMoney() {
        return money;
    }

    public void setMoney(Money money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Student{" +
                "book=" + book +
                ", dog=" + dog +
                ", money=" + money +
                '}';
    }
}

测试类

/**
 * 测试属性赋值,autowired注入
 */
@org.junit.Test
public void test7(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DIConfig.class);
    System.out.println("-------获取student组件-----------");
    Student student = applicationContext.getBean(Student.class);
    System.out.println("student = " + student);
}
//--------------控制台输出------------------
-------获取student组件-----------
student = Student{book=com.hhdd.bean.Book@163e4e87, dog=com.hhdd.bean.Dog@56de5251, money=com.hhdd.bean.Money@419c5f1a}

最后

本文简单示例介绍了spring中管理组件和给组件赋值的功能,所有的示例代码已经放在github地址:spring-practice

有任何问题欢迎评论区留言,本人创建了一个java交流群:624017389,任何热爱技术的小伙伴都可以加入探讨技术。

后期,会继续更新spring系列的笔记总结,会更加深入,主要是原理方向的东西了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java Spring是一个广泛使用的轻量级开源框架,用于构建企业级Web应用程序。Spring框架提供了模块化的解决方案,简化了企业级应用的开发、管理以及依赖注入(Dependency Injection,DI)等功能。以下是一些Spring基础知识点的整理: 1. **IoC(Inversion of Control)和DI(Dependency Injection)**:Spring的核心思想就是IoC,它反转了传统的控制流动,使对象之间通过容器管理彼此的依赖关系,而不是硬编码。DI是IoC的一种具体实现方式,通过配置文件或注解自动为对象提供所需依赖。 2. **Bean的作用域和生命周期**:Spring中的Bean有多种作用域,如Singleton(单例)、Prototype(原型)、Request、Session等。每个Bean都有其生命周期,从创建、初始化到使用和销毁。 3. **Spring配置文件**:通常使用XML配置文件(如applicationContext.xml)或Java配置(@Configuration classes)来定义Spring应用的组件和依赖关系。 4. **AOP(Aspect Oriented Programming)**:Spring AOP支持面向切面编程,可以编写跨组件的行为,比如日志记录、事务管理等。 5. **Spring MVC**:Spring提供的web MVC架构,包括Controller处理HTTP请求,Model负责数据访问和业务逻辑,View负责渲染结果给用户。 6. **Spring Boot**:Spring Boot简化了Spring应用的初始搭建,自动配置了许多常用的功能,使得快速开发变得更容易。 7. **Spring Data**:提供了一套高级API,用于简化数据访问操作,如JPA、MongoDB等。 8. **Spring Security**:用于实现Web应用的安全管理,包括认证、授权、会话管理等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值