Spring

IOC

Xml 配置 Bean

结构

xml配置方式.png

配置样例

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<bean id = "UserDao" 
name ="aaa,bbb"
class = "com.test.dao.impl.UserDaoImpl"
scope="singleton"
lazy-init="true"
init-method = "init"
destroy-method = "destroy"
autowire = "byType" or "byName"/>

<constructor-arg name="age" value="18"> </constructor-arg>
<property name = "userDao" ref = "userDao"> </property>

//注入数据结构,其余类似
<property name = "stringList">
<list> <value> aaa </value> </list>
</property>

</bean>

//静态工厂方法
<bean id = "UserDao1"
class = "com.test.factory.MyBeanFactory1"
factory-method = "UserDao">
</bean>

//实例工厂方法
<bean id ="MybeanFactory2"
class = "com.test.factory.MyBeanFactory2"
</bean>

<bean id = "UserDao2"
factory-bean = "MybeanFactory2">
factory-method = "UserDao">
</bean>

//Factorybeany延迟调用
<bean id ="MybeanFactory3"
class = "com.test.factory.MyBeanFactory2"
</bean>

public class MybeanFactory3 implements FactoryBean<UserDao>{

@override
public UserDao getObject() throws Expection{
return new UserDaoImpl();
}

@override
public class<?> getObejectType(){
return UserDao.class;
}
}



配置解析

  • 关键字
    • Id 为 bean 对象 name,name 为别名,可有多个
    • Ref 其他bean 对象
    • Singleton ,Bean 对象只有一个储存在缓存中,仅在每个 Spring容器中有效,不全局有效
    • Prototype,每次请求该 Bean 时返回一个 Bean 的新实例
    • Lazy-init,延迟加载,Spring 容器创建时不会立即创建 bean 示例,等待用时再创建并加载到单例池中
    • Init-method 与 Destroy-method,指定初始化方法与销毁方法
    • Constructor-arg 构造参数, name 参数名称,value 参数值 ref 引用注入另一个 bean 对象
    • ByType (自动装配 set 方法,依据类型,但不能多个相同 bean 类型) or byName (自动装配 set 方法,依据名称)
    • Profiles 属性区分开发环境,区分不同的 beans
    • Import 模块化,引入其他模块的配置 xml 文件
    • Alias 取别名
    • Parent 继承有配置的属性,在子Bean中必须也要存在,并且可以进行注入,否则会出现错误 当然,如果子类中某些属性比较特殊,也可以在继承的基础上单独配置
    • Abstract 如果我们只是希望某一个Bean仅作为一个配置模版供其他Bean继承使用,那么我们可以将其配置为abstract,这样,容器就不会创建这个Bean的对象了:
  • Bean 的注入方式
    • Bean 的Set 方法
    • Bean 的构造方法
  • InitializingBean 接口
    • 只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。Spring初始化bean的时候,如果该bean实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertieSet()方法,然后再调用init-method中指定的方法。
  • 静态工厂方法与实例工厂方法
    • 静态工厂方法不需要先定义 factorybean

Bean 的 get 方法

1
2
3
4
5
6
Object getBean(String beanName) 
//需要强制转换
UserService userService = (UserService)applicationContext.getBean(" userService")

T getBean(Class type)
T getBean(String beanName,Class type)

Spring 配置非自定义的 Bean

spring配置非自定义的bean.png

Bean 的实例化基本流程

bean实例化基本流程1.png
bean实例化基本流程2.png

Bean 工厂的后处理器

流程

bean的后处理器.png

代码

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
31
32
33
34
35
//github上 min-spring的实现过程,此时未使用applicationContext 
//接口
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

//实现
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("CustomBeanFactoryPostProcessor#postProcessBeanFactory");
BeanDefinition personBeanDefiniton = beanFactory.getBeanDefinition("person");
PropertyValues propertyValues = personBeanDefiniton.getPropertyValues();
//将person的name属性改为ivy
propertyValues.addPropertyValue(new PropertyValue("name", "ivy"));
}
}

//测试
public class BeanFactoryProcessorAndBeanPostProcessorTest {
public void testBeanFactoryPostProcessor() throws Exception {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");

//在所有BeanDefintion加载完成后,但在bean实例化之前,修改BeanDefinition的属性值
CustomBeanFactoryPostProcessor beanFactoryPostProcessor = new CustomBeanFactoryPostProcessor();
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);

Person person = (Person) beanFactory.getBean("person");
System.out.println(person);
//name属性在CustomBeanFactoryPostProcessor中被修改为ivy
assertThat(person.getName()).isEqualTo("ivy");
}

  • BeanDefinitionRegistryPostProcessor 继承自上面的 BeanFactoryPostProcessor,说明 BeanDefinitionRegistryPostProcessor对 BeanFactoryPostProcessor提供的方法进行了增强扩展。一句话总结其功能作用就是注册beanDefinition的.
  • ConfigurationClassPostProcessor 就是实现 BeanDefinitionRegistryPostProcessor 来完成配置类及其相关注解解析得到beanDefinition注册到Spring上下文中的。
    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
    31
    32
    33
    34
    35
    //使用applicationContext 
    public class Boo {
    private Long id;
    private String name;
    }


    public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    // 注入boo
    BeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClassName("com.shepherd.common.bean.Boo");
    registry.registerBeanDefinition("boo", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    System.out.println("BeanFactoryPostProcessor execute...");
    BeanDefinition beanDefinition = beanFactory.getBeanDefinition("boo");
    if (Objects.nonNull(beanDefinition)) {
    beanDefinition.setDescription("hello,world");
    }
    }
    }

    public class MyConfig {

    public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
    BeanDefinition beanDefinition = applicationContext.getBeanDefinition("boo");
    System.out.println(beanDefinition.getDescription());
    }
    }

  • 上述实现需要配置 xml 文件

自定义注解

元注解

  • Target、@Retention@Inherited@Documented 元注解的作用就是负责注解其他注解
  • @Target: 描述注解的使用范围
    • ElementType.TYPE 应用于类、接口(包括注解类型)、枚举
    • ElementType.FIELD 应用于属性(包括枚举中的常量)
    • ElementType.METHOD 应用于方法
    • ElementType.PARAMETER 应用于方法的形参
    • ElementType.CONSTRUCTOR 应用于构造函数
    • ElementType.LOCAL_VARIABLE 应用于局部变量
    • ElementType.ANNOTATION_TYPE 应用于注解类型
    • ElementType.PACKAGE 应用于包
  • @Retention:表明该注解的生命周期
    • RetentionPolicy.SOURCE 编译时被丢弃,不包含在类文件中
    • RetentionPolicy.CLASS JVM加载时被丢弃,包含在类文件中,默认值
    • RetentionPolicy.RUNTIME 由JVM 加载,包含在类文件中,在运行时可以被获取到
  • @Inherited:是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
  • @Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化

流程

自定义注解.png

代码

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
31
32
// 注解的作用 类似代替原有的
<bean id = "UserDao"
name ="aaa,bbb"
class = "com.itheima.dao.impl.UserDaoImpl" ></bean>


@Target(ElementType.TYPE)
@Retention(Retention.RUNTIME)
public @interface Mycomponent{
string value;
}

@Mycomponent("otherbena")
public class UserDaoImpl implements UserDao{
}

public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//通过扫描工具扫描指定包以及子包下的所有类,收集使用@Mycomponent注解的类
My<string,class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.test")
myComponentAnnotationMap.foreach((beanName,clazz))->(string beanClassName = clazz.getname);
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("beanClassName");
registry.registerBeanDefinition(beanName, beanDefinition);
}
//xml中配置MyComponentBeanFactoryPostProcessor让他生效

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory ){
}
}

Bean 后置处理器

流程

  • BeanPostProcessor
  • Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,BeanPostProcessor 就是这一阶段的对外扩展点,我们称之为Bean后置处理器。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

代码

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//xml中配置MyBeanPostProcessor 不使用注解的流程
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("beanPostProcessor的before()执行了...." + beanName);
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("beanPostProcessor的after()执行了...."+ beanName);
return bean;
}
}



public class Boo {
private Long id;
private String name;

public Boo() {
System.out.println("boo实例化构造方法执行了...");
}

@PostConstruct
// 该注解表示实例化之后执行该初始化方法
public void init() {
System.out.println("boo执行初始化init()方法了...");
}
}


public class MyConfig {


public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

}
//需要配置xml文件,结果如下
/**
boo实例化构造方法执行了...
beanPostProcessor的before()执行了....
boo boo执行初始化init()方法了...
beanPostProcessor的after()执行了....boo
**/

  • 说明BeanPostProcessor方法的执行时间点和顺序,BeanPostProcessor是在bean实例化之后,对bean的初始化init()方法前后进行回调扩展的,这时候你可能会想如果要对bean的实例化前后进行扩展怎么办?Spring肯定也想到这一点,提供了继承自 BeanPostProcessor 的扩展类后置处理器

扩展

  • InstantiationAwareBeanPostProcessor该接口继承了BeanPostProcess接口

  • BeanPostProcess接口只在bean的初始化阶段进行扩展 InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段

  • 该类主要的扩展点有以下5个方法,主要在bean生命周期的两大阶段:实例化阶段 和初始化阶段 

  • postProcessBeforeInstantiation:实例化bean之前,相当于new这个bean之前

  • postProcessAfterInstantiation:实例化bean之后,相当于new这个bean之后

  • postProcessPropertyValues:bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现

  • postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前

  • postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后

  • SmartInstantiationAwareBeanPostProcessor该扩展接口集成自上面InstantiationAwareBeanPostProcessor,有3个触发点方法:

  • predictBeanType:该触发点发生在postProcessBeforeInstantiation之前这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。(不太常用)

  • determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。

  • getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。

Bean 的生命周期

完整生命周期

bean的生命周期2.png
bean的生命周期1.png

初始化阶段

bean的生命周期初始化阶段.png

属性填充

bean生命周期属性填充.png

循环依赖

三级缓存

bean生命周期三级缓存.png

实例流程

bean生命周期三级缓存实例.png

Aware

概述

bean的生命周期Aware接口.png

解释

  • Aware 接口是一个具有标识作用的超级接口,指示 bean 是具有被 Spring 容器通知的能力,通知的方式是采用回调的方式。Aware 接口是一个空接口,具体的实现由各个子接口决定,且该接口通常只包含一个单个参数并且返回值为void的方法**。可以理解就是 set 方法。该方法的命名方式为 set + 去掉接口名中的 Aware 后缀,即 XxxAware 接口,则方法定义为 setXxx(),例如 BeanNameAware(setBeanName),ApplicationContextAware(setApplicationContext)。注意,仅实现Aware接口,不会提供任何默认功能,需要明确的指定实现哪个子接口。

  • 通俗的来说,Aware 翻译过来的意思是有感知的,察觉的,如果类上实现了该接口,表明对什么有感知,比如 BeanNameAware, 表示知道了自己的Bean Name。

  • Aware接口应用场景及实现原理!

  • 可以看看

注解配置

基本配置

bean注解基本配置.png

基本配置代码

1
2
3
4
5
6
7
8
9
10
11
//使用注解时xml文件中还必须有配置,让spring扫描注解来获得bean对象
<context:component-scan base-package ="com.test"/>
@Scope("singleton")
@Component("userService")
@Lazy(true)
public class UserServiceImpl implements UserService{
@PostConstruct
...
@PreDestory
...
}

基本注解衍生注解

基本注解衍生注解.png

依赖注入注解

bean依赖注入注解.png
  • Autowired 依据类型进行注入,如果同一类型的 bean 有多个,会根据名字二次匹配

非自定义 bean 注解

非自定义bean注解配置.png

Bean 配置类

1
2
3
4
5
6
7
8
9
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>


@Configuration
@ComponentScan("com.test")
@PropertySource("...")
@Import("OtherBean.classs")//导入外部配置类
public class MainConfiguration {
}

其他配置类

  • @Profile 切换环境
  • @Primary 标注 bean 的优先级会更高,在通过类型获取 bean 或者通过@Autowired 根据类型进行注入时优先级会更高

Xml 与注解方式

Xml与注解.png

AOP

AOP 概念

AOP概念.png
AOP概念解释.png

Xml 配置 AOP

流程

  • 导入 AOP 相关坐标
  • 准备目标类,增强类,并配置给 Spring 管理
  • 配置切点表达式(哪些方法被增强)
  • 配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)
Aop五类通知.png
Aop参数传递.png

代码

1
2
3
<?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"
//配置AOP命名空间
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class example { 
public void test(){
System.out.println("测试");
}
}

<bean class="com.example"/>

public class exampleAOP {
public void aftertest() {
System.out.println("测试之后");
}
}


<aop:config>
<aop:pointcut id="test" expression=""execution(* org.example.example.test())"/>
//相当 expression=""execution(public void com.example.test())
<aop:aspect ref="exampleAOP">
<aop:after method="aftertest" pointcut-ref="test"/>
</aop:aspect>
</aop:config>

配置解析

xml配置AOP解析1.png
Aop切点表达式例子.png
Xml配置AOP解析2.png

AOP 两种代理模式

  • JDK
    • 目标类有接口,是基于接口类生成实现类的代理对象
    • 有接口,默认实现
  • Cglib
    • 目标类无接口且不能用final 修饰,是基于被代理对象动态生成子对象为代理对象
    • 无接口,默认实现,但可强行使用 AOP两种代理模式.png

注解配置 AOP

流程

注解配置AOP.png

解析

注解开发AOP解析.png