Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

深入学习使用Spring

2022/02/27 Spring Web
Word count: 3,516 | Reading time: 14min

From : https://www.bilibili.com/video/BV1fr4y1Y7UH?p=6

创建bean的方式:

声明式

<bean></bean>

  • <bean></bean>: 通过xml来配置bean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <bean id="p1" class="top.nymrli.tryboot.po.User">
    <property name="name" value="张三"/>
    <property name="age" value="18"/>
    </bean>
    </beans>


    @Test
    public void testPerson()
    {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

    User user = annotationConfigApplicationContext.getBean("p1", User.class);
    System.out.println(user);
    }

@Bean

  • @Bean: 通过注解来标注SpringBean对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Config {
    @Bean("user")
    public User getUser(){
    return new User("name", 20);
    }
    }

    @Test
    public void testPerson()
    {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(Config.class);
    // 或者 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);

    ctx.refresh();

    User user = ctx.getBean("user", User.class);
    System.out.println(user);
    }

    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");一样,都是指定bean的配置路径后解析,获得bean的配置。

@Component

  • @Component: 通过标注来创建一个组件SpringBean对象,如下的User。 ==> 等价于ctx.registerBean(User.class),registerBean方法的实现最终也是调用了registerBeanDefinition

    而Config不是一个bean,他的作用跟applicationContext.xml一样,他是存放bean定义路径的一个配置文件,在这里是通过ComponentScan让Spring能够找到top.nymrli.tryboot.po下的所有Bean并解析成SpringBean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @ComponentScan("top.nymrli.tryboot.po")
    public class Config {
    }


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Component // ==> beanName为user, 类名小写
    public class User {
    private String name;
    private int age;
    }


    @Test
    public void testPerson()
    {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);

    User user = ctx.getBean(User.class);
    System.out.println(user);
    }

    会自动调用User的无参构造方式来创建JavaBean, 因此得到的User是User(name=null, age=0)

▲.上述三种创建方式本质上都是创建了BeanDefinition,只不过Spring进行了封装,因此使用起来比较简单直观

@Import

spring4.2之后@Import注解可以实例化普通类的bean实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
public class Role {
private Long id;
private String name;
}

@Import(Role.class) // @Import({Role.class, User.class})
@Configuration
public class MyConfig {
}

@RequestMapping("/")
@RestController
public class TestController {
@Autowired
private Role role;

@GetMapping("/test")
public String test() {
System.out.println(role);
return "test";
}
}

Spring在创建MyConfig的时候,会根据@Import将Role也实例化成一个Bean

编程式

BeanDefinition

通过创建并配置 BeanDefinition 来创建一个Bean

Q: 什么是BeanDefinition?

A: BeanDefinition表示Bean的定义信息, Spring会根据BeanDefinition来创建Bean对象。BeanDefinition有很多的属性用来描述Bean, BeanDefinition是Spring中非常核心的概念。

  • beanClass: 表示Bean对象的类类型, Spring在创建Bean的过程中根据此属性来判断实例化对象具体的类类型
  • scope:作用域,[singleton、prototype],单例或者原型bean
  • isLazy:是否需要懒加载,原型Bean的懒加载不奏效。懒加载的单例bean,会在第一次getBean的时候生成该bean,非懒加载的单例bean,则会在Spring.启动过程中直接生成好。
  • dependsOn:表示一个bean在创建之前所依颗的其他bean,在一个bean创建之前,它所依赖的这些bean得先全部创建好。
  • primary:表示一个bean是主bean,在Spring中一个类型可以有多个bean对象,在进行依赖注入时,如果根据类型找到了多个b©an,此时会判断这些bean中是否存在一个主bean,如果存在,则直接将这个bean注入给属性。
  • initMethodName: 表示一个bean的初始化方法,一个bean的生命周期过程中有一个步骤叫初始化,Spring会在这个步骤中去调用bean的初始化方法。初始化逻辑由程序员自己控制,使得程序员可以自定义逻辑对bean进行加工。

通过<bean></bean>@Bean@Component标注的类都会解析为BeanDefinition,供Spring创建(实例化)具体的Bean。

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testPerson()
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
bd.setBeanClass(User.class);
// 将BeanDefinition注册到上下文中
ctx.registerBeanDefinition("user", bd);
// 获得Bean对象
User user = (User)ctx.getBean("user");
System.out.println(user);
}

FactoryBean

FactoryBean是Spring.所提供的一种较灵活的创建Bean的方式,可以通过实现FactoryBean:接口中的getObject()方法来返回一个对象,这个对象就是最终的Bean对象。

FactoryBean接口中的方法

  • Object getObject():返回的是Bean对象
  • boolean isSingleton():返回的是否是单例Bean对象
  • Class getObjectType():返回的是Bean对象的类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component("zhouyu")
public class ZhouyuFactoryBean implements FactoryBean {
@Override
public boolean isSingleton() {
return true;
}

@Override
public Object getObject() {
return new User("cl", 18);
}

@Override
public Class<?> getObjectType() {
return User.class;
}
}

上述代码,实际上对应了两个Bean对象:
1、beanName为"zhouyu’,bean对象为getObject方法所返回的User对象。=>applicationcontext.getBean(name:"zhouyu",ZhouyuFactory.class);
2、beanName为"&zhouyu’,bean对象为ZhouyuFactoryBean类的实例对象。=>applicationcontext.getBean(name:"&zhouyu",ZhouyuFactoryBean.class);

与BeanFactory的区别:

  • FactoryBean对象本身也是一个Bean,同时它相当于一个小型工厂,可以生产出另外的Bean。==》指定类型的Bean对象
    • FactoryBean机制被广泛的应用在Spring内部和Spring-与第三方框架或组件的整合过程中。
  • BeanFactory是一个Spring容器,是一个大型工厂,它可以生产出各种各样的Beano

Supplier

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@Test
public void testPerson()
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.registerBean(User.class, new Supplier<User>() {
@Override
public User get() {
User userx = new User();
userx.setAge(222);
userx.setName("supplier");
return userx;
}
});

User user = (User)ctx.getBean("user");
System.out.println(user);
}

BeanFactory

BeanFactory是一种“Spring容器”, BeanFactory翻译过来就是Bean工厂,顾名思义,它可以用来创建Bean、获取Bean。

概念区分:

  • BeanFactory将利用BeanDefinition:来生成Bean对象
  • BeanDefinition相当于BeanFactory的原材料
  • Bean对象就相当于BeanFactory)所生产出来的产品

BeanFactory的核心子接口和实现类:

  • ListableBeanFactory
  • ConfigurableBeanFactory
  • AutowireCapableBeanFactory
  • AbstractBeanFactory
  • DefaultListableBeanFactory

···

Bean生命周期

Bean生命周期描述的是Spring中一个Bean创建过程和销毁过程中所经历的步骤,其中Bean创建过程是重点。程序员可以利用Bean生命周期机制对Bean进行自定义加工。

  • Bean定义:通过xml或者编写类的形式来创建BeanDefinition
  • 构造方法推断:从bean的多个构造方法中选出一个构造方法
  • 实例化:通过构造方法反射获得对象,在Spring中可以通过BeanPostProcessor机制对实例化进行干预
  • 属性填充(自动注入、依赖注入):给属性进行自动填充,比如@Value,@AutoWired
  • 初始化:在一个对象的属性填充之后,Spring提供了初始化机制,让程序员可以对其他属性进行赋值、校验等自定义加工(利用InitializingBean接口)
  • 初始化后:Aop、生成代理对象。常说的Aop机制就是在这个步骤中通过BeanPostProcessor机制实现的,这步之后获得的对象才是真正的Bean对象

创建过程:

bean创建过程

生命周期图:

bean生命周期-1

属性填充注解:@Autowired、@Resource、@Value

@Autowired.

表示某个属性是否需要进行依赖注入,可以写在属性和方法上。注解中的required属性默认为ture,表示如果没有对象可以注入给属性则抛异常。

  • @Autowired:加在某个属性上,Spring在进行Bean的生命周期过程中,在属性填充这一步会基于实例化出来的对象,对该对象中加了@Autowired的属性自动给属性赋值。具体实现为: Spring会先根据属性的类型去Spring容器中找出该类型所有的Bean对象,如果找出来多个,则再根据属性的名字从多个中再确定一个。如果required属性为true,并且根据属性信息找不到对象,则直接抛异常。

  • 当@Autowired注解写在某个方法上时,Spring在B ean生命周期的属性填充阶段,会根据方法的参数类型、参数名字从Spring容器找到对象当做方法入参,自动反射调用该方法。

  • @Autowired加在构造方法上时,Spring会在推断构造方法阶段选择该构造方法来进行实例化,在反射调用构造方法之前,会先根据构造方法参数类型、参数名从Spring容器中找到Bean对象,当做构造方法入参。

@Resource

@Resource注解与@Autowired类似,也是用来进行依赖注入的,@Resource是Java层面所提供的注解,@Autowired.是Spring所提供的注解,它们依赖注入的底层实现逻辑也不同。

@Resource:注解中有一个name属性,针对na me属性是否有值,@Resource的依赖注入底层流程是不同的。

  • @Reousrce:如果name属性有值,那么Spring会直接根据所指定的name值去Spring容器找Bean对象,如果找到了则成功,如果没有找到,则报错。
  • 如果@Resource中的name属性没有值,则:
    1. 先判断该属性名字在Spring容器中是否存在Bean对象。
    2. 如果存在,则成功找到Bean对象进行注入。
    3. 如果不存在,则根据属性类型去Spring容器找Bean对象,找到一个则进行注入。

@Value

@Value注解和@Resource、@Autowired类似,也是用来对属性进行依赖注入的,只不过@Value是用来从Properties.文件中来获取的,并且@Value可以解析SpEL(Spring表达式)。

  • @Value("yuzhou"): 直接将字符串“zhouyu”赋值给属性,如果属性类型不是String,或无法进行类型转化,则报错。

  • @Value(${"yuzhou"}): 将会把${}中的字符串当做key,从Properties文件中找出对应的value赋值给属性,如果没找到,则会把${zhouyu}当做普通字符串注入给属性。

  • @Value(#{"yuzhou"}):会将#{}中的字符串当做Spring表达式进行解析,Spring会把"zhouyu"当做beanName,并从Spring容器中找对应bean,如果找到则进行属性注入,没找到则报错。

ApplicationContext

ApplicationContext是比BeanFactory更加强大的Spring容器,它既可以创建bean、获取bean,还支持国际化、事件广播、获取资源等BeanFactor y不具备的功能。

Application Context,所继承的接口

  • EnvironmentCapable:拥有获取环境变量的能力:操作系统环境变量和Jvm环境变量
  • ListableBeanFactory:拥有了获取所有beanNames、判断某个bean Name是否存在peanDefinition对象、统计Be anDefinition个数、获取某个类型对应的所有beanNames等功能。
  • HierarchicalBeanFactory:拥有了获取父BeanFactory、。判断某个name是否存在bean对象的功能。
  • MessageSource:拥有了国际化功能,比如可以直接利用Messa geSource对象获取某个国际化资源(比如不同国家语言所对应的字符)
  • ApplicationEventPublisher: 拥有了事件发布功能,可以发布事件,这是Application Context相对于BeanFactory比较突出、常用的功能。
  • ResourcePatternResolver: 拥有了加载并获取资源的功能,这里的资源可以是文件,图片等某个URL资源都可以。

获得bean的方式:

getBean() API

  • 按名称获取Bean

    1
    2
    3
    Object lion = context.getBean("lion");

    assertEquals(Lion.class, lion.getClass());

    根据bean名称获取bean,如果在spring ico容器中存在和bean,则返回Object 类的实例。否则,抛出如下异常NoSuchBeanDefinitionException。

    ▲: 主要的缺点是,在获取bean之后,我们必须将它指定转换为所需的类型。如果返回的bean的类型与我们期望的不同,则可能会产生异常。

  • 通过名称和类型获取Bean

    1
    2
    3
    4
    5
    6
    7
    Lion lion = context.getBean("lion", Lion.class);
    // 与按名称获取Bean相比,此方法更安全,因为我们可以编译阶段就发现错误而不是在运行阶段。


    assertThrows(BeanNotOfRequiredTypeException.class, () ->
    context.getBean("lion", Tiger.class));
    }
  • 按类型获取Bean

1
2
3
4
5
6
7
// right
Lion lion = context.getBean(Lion.class);

// 注意: 在下面的情况下,需要特别注意可能存在的歧义:
assertThrows(NoUniqueBeanDefinitionException.class, () ->
context.getBean(Animal.class));
}

由于LionTiger都实现了Animal接口,因此仅指定类型不足以明确确定结果。因此,我们会得到一个*NoUniqueBeanDefinitionException*。即在同一个IOC 容器中,如果有相同类型的多个bean,则不能通过类型获取bean

  • 按名称和构造函数参数对Bean进行筛选
1
2
3
4
5
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");

assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());
  • 按类型和构造函数参数对Bean进行筛选

    ▲: 这个方法有点不同,因为它只适用于具有原型作用域的bean。

    1
    2
    Tiger tiger = context.getBean(Tiger.class, "Shere Khan");
    assertEquals("Shere Khan", tiger.getName());

BeanPostProcessor机制

BeanPostProcessor是Spring所提供的一种扩展机制,可以利用该机制对Bean进行定制化加工,在Spring底层源码实现中,也广泛的用到了该机制,BeanPostProcessor通常也叫做Bean后置处理器

BeanPostProcessor在Spring中是一个接口,我们定义一个后置处理器,就是提供一个类实现该接口,在Spring中还存在一些接口继承了BeanPostProcessor,这些子接口是在BeanPostProcessor的基础上增加了一些其他的功能。

BeanPostProcessori中的方法

  • postProcessBeforelnitialization():初始化前方法,表示可以利用这个方法来对Bean在初始化前进行自定义加工。
  • postProcessAfterInitialization():初始化后方法,表示可以利用这个方法来对Bean在初始化后进行自定义加工。

InstantiationAwareBeanPostProcessor

BeanPostProcessor的一个子接口

  • postProcessBeforelnstantiation():实例化前
  • postProcessAfterInstantiation():实例化后
  • postProcessProperties():属性注入后

AOP是什么?

AOP就是面向切面编程,是一种非常适合在无需修改业务代码的前提下,对某个或某些业务增加统一的功能(横向增加功能),比如日志记录、权限控制、事务管理等,能很好的使得代码解耦,提高开发效率。

Author: Mrli

Link: https://nymrli.top/2022/02/27/深入学习使用Spring/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
2022年3月19~20日-爬虫项目记录
NextPost >
研究生work——地图匹配
CATALOG
  1. 1. 创建bean的方式:
    1. 1.1. 声明式
      1. 1.1.1. <bean></bean>
      2. 1.1.2. @Bean
      3. 1.1.3. @Component
      4. 1.1.4. @Import
    2. 1.2. 编程式
      1. 1.2.1. BeanDefinition
      2. 1.2.2. FactoryBean
      3. 1.2.3. Supplier
  2. 2. BeanFactory
  3. 3. Bean生命周期
  4. 4. 属性填充注解:@Autowired、@Resource、@Value
    1. 4.1. @Autowired.
    2. 4.2. @Resource
    3. 4.3. @Value
  5. 5. ApplicationContext
    1. 5.1. 获得bean的方式:
  6. 6. BeanPostProcessor机制
    1. 6.1. InstantiationAwareBeanPostProcessor
  7. 7. AOP是什么?