Spring事件机制,spring容器及bean加载机制,Spring启动过程

Spring事件机制 - 简书 (jianshu.com)

1. 使用Spring 事件

首先spring事件分为事件发布者(EventPublisher)、事件监听者(EventListener),还包括一个事件广播者(这个是spring实现相关,这一节不讨论)。使用spring事件机制,需要自定义事件发布者和监听者。
以一个简单的例子开始,事件发布者发送一个string类型的消息,接受者将接收到的消息打印出来。

  1. 事件发布者
@Component
public class SaySomethingPublisher implements ApplicationEventPublisherAware{
    private ApplicationEventPublisher applicationEventPublisher;

    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void saySomething(String msg){
        applicationEventPublisher.publishEvent(msg);
    }
}

需要实现ApplicationEventPublisherAware这个Aware接口,广播事件需要利用applicationEventPublisher

关于ApplicationEventPublisherAware如下:

public interface ApplicationEventPublisher {
    void publishEvent(ApplicationEvent event);
    void publishEvent(Object event);
}

用户发布的事件类型可以是:
1. 用户可以继承ApplicationEvent从而自定义Event类型
2. 也可以使用任意Object类型,但是如果event真实类型不是ApplicationEvent的话,那么event会被封装成PayloadApplicationEvent
  1. 事件监听者

//事件监听者需要实现ApplicationListener接口
@Component
public class ListenerA implements ApplicationListener {
    // 由于监听的是String类型的事件会被封装成PayloadApplicationEvent,所以此处类型是PayloadApplicationEvent
    public void onApplicationEvent(PayloadApplicationEvent event) {
        // getSource返回真实的事件
        Object msg = event.getSource();
        System.out.println("ListenerA receive:" + msg);
    }
}

关于发布出去的事件,那些监听者会监听到?

  1. 发布的事件类型是ApplicationEvent的实现类A
    那么所有监听者的onApplicationEvent的参数类型是A或者A的子类都会收到事件。
  2. 发布的事件类型是不是ApplicationEvent类型,类型是B
    这种情况下,最终事件会被包装成PayloadApplicationEvent, 那么所有监听者方法onApplicationEvent的参数是PayloadApplicationEvent的监听者会收到, 假设有C是B的父类,且有一个监听者X监听PayloadApplicationEvent,那X是收不到PayloadApplicationEvent类型的事件的

2. Spring事件原理

Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角色负责把事件转发给监听者,工作流程如下:

 
Spring事件机制

也就是说上面代码中发布者调用applicationEventPublisher.publishEvent(msg); 是会将事件发送给了EventMultiCaster, 而后由EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。

2.1 EventMultiCaster

ApplicationContext完成bean的装配和初始化后(非lazy-init的singleton bean会加载后就初始化),会尝试创建一个eventMultiCaster,创建代码如下:

protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //判断有没有一个name是“applicationEventMulticaster”且实现了“ ApplicationEventMulticaster”的bean,有的话那它就是eventMultiCaster
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {
            // 没有这样一个bean,那就会创建一个默认的
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                        APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                        "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
}
  1. 默认SimpleApplicationEventMulticaster
    直接看一下SimpleApplicationEventMulticaster用来广播event的代码:
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
                // 这个是用来根据event的类型找到合适的listener的
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                        
            Executor executor = getTaskExecutor();
            // executor不是空的时候会在executor中激活listener
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            // 否则就直接在当前调用线程中激活listener 
            else {
                invokeListener(listener, event);
            }
        }
}

在创建SimpleApplicationEventMulticaster时,executor是null,所以默认情况下所有的listener 的onApplicationEvent是直接在当前线程(事件发布者所在线程)中调用,所以
如果onApplicationEvent有阻塞操作也会导致事件发布者被阻塞,后续的其他listener也会被阻塞无法调用。

  1. 自定义multicaster
    「2.1」中介绍spring会加载一个叫applicationEventMulticaster且实现了ApplicationEventMulticaster接口的multicaster,自定义multicaster需要实现了该接口然后将bean的名字设为applicationEventMulticaster即可。
    下面的例子为默认的SimpleApplicationEventMulticaster添加了executor,以使事件发布者和监听者不用在同一个线程中调用:
//使用线程池运行listener

 


       
       

3. 附

  1. Spring启动完成之后(已经完成bean解析,non-lazy-init的singleton实例化和初始化,完成listener的注册),默认会发布一个ContextRefreshedEvent事件,该事件包装的消息是一个ApplicationContext对象。

spring容器及bean加载机制源码解读_songyang19871115的博客-CSDN博客_spring中bean加载机制

前言:这是本人第一个博客,早就想记录些总结和理解,可一直不知道从哪开始,最近正好在解决一个spring的问题,正好这个问题涉及到了spring的一些相关基础,整理一下就从这部分开始了。
欢迎所有阅读者和爱好者批评从各个方面(特别是文档和技术方面)批评,指正。互相交流学习。
原想这部分单写一个文章可无奈文笔有限,先放在这吧。

spring的容器加载机制分为提前加载和运行时动态加载,本文通过源码解读容器加载机制和bean加载机制。
1、spring 启动的时候加载
这里写图片描述
(图1.1)
obtainFreshBeanFactory方法中实现了beanFactory的初始化和对配置文件、注解加载。
registerBeanPostProcessors(beanFactory); 方法实现了注册bean自定义初始化(不是类对象实例化)前后的处理器的注册信息。
finishBeanFactoryInitialization 方法是处理提前加载的哪些实例
1.1 spring的配置是怎么样读取的都干了什么
由于篇幅较大这部分使用单独篇幅:http://blog.csdn.net/songyang19871115/article/details/54346444

1.2 spring都提前加载了哪些自带和自定义的bean
这里写图片描述
(图1.2.1)
这里写图片描述
(图1.2.2)

如以上代码(图1.2.1,图1.2.2):应用上下文在启动的时候会自动调用这段代码,这段代码会初始化所有(非懒加载的、单例的、不是抽象的 bean或者SmartFactoryBean)
这里使用了beanDefinitionNames这个集合,这个集合是从配置文件和注解中获取出来的,所有bean的名称。来实现bean的提前加载。

至此 与spring一般的初始化bean,bean工厂,bean 的初始化前置执行器都已经执行完了。

2、spring 动态加载的实现
spring的一切都是为了bean不论是自定义的还是spring自带的,而bean是怎么来的,是什么样的bean都是从getBean开始的,下面揭开他的神秘面纱:
2.1doGetBean的实现:
这里写图片描述
(图2.1.1)
这里写图片描述
(图2.1.2)
这里写图片描述
(图2.1.3)
(图2.1.1,2.1.2,2.1.3为doGetBean的代码实现)
1、如果是单例看看是否在当前容器中。如果单例缓存中没有则查看父容器有没有同名的bean,如果有则返回。说明如果子容器没有这个bean,会从父容器中获取,直到祖先容器都没有。
getObjectForBeanInstance 只是处理如果当前bean是工厂bean的情况
2、获取bean的定义描述信息,如果存在依赖按个递归调用getBean获取依赖的bean,并且注册到当前的bean上
3、如果当前容器和祖先容器都没有则开始创建bean 实例
4、处理bean的转换问题,spring自带conversionService
2.2 createBean的源码实现:
这里写图片描述
(图2.2.1)
这里写图片描述
(图2.2.2)
这里写图片描述
(图2.2.3)
从源码得知,只有bean在创建以前都做了什么,先是查看这个容器中是否含有beanPostProcessors()(这个是由refresh方法调用registerBeanPostProcessors()时候注册进去的)
并且是InstantiationAwareBeanPostProcessor 接口的实现类,如果bean提前实现了,调用beanPostProcessors 的 postProcessAfterInitialization 将用不在调用doCreateBean方法(具体实现如图2.2.2,2.2.3).这块也体现了spring的灵活,连bean的创建都可以定制。

下面说下doCreateBean的创建原理和执行内幕:
这里写图片描述
(图2.2.4)
这里写图片描述
(图2.2.5)
图2.2.4和2.2.5是doCreateBean的实现细节
createBeanInstance中实现了bean的实例的过程,其中包含了构造方法,工厂方法的实例并且放置在beanWrapperImpl中。
populateBean中实现了bean的属性的注入,其中区分了属性的强校验注入等,而且递归调用getBean方法。
registerDisposableBeanIfNecessary 为当前bean的销毁提供策略和实现
initializeBean中的实现下面会详细阐述。

下面详细说下initializeBean方法
这里写图片描述
(图2.2.6)
这个方法并不是对象实例的方法,而是对象实例后的方法。
invokeAwareMethod方法是如果当前的bean是BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 创建bean的时候,会帮你把你需要的数据放进去,但前提一定是三个接口的实现类。
applyBeanPostProcessorsBeforeInitialization 会调用当前容器中所有的BeanPostProcessor 并且执行他们的postProcessBeforeInitialization 方法
invokeInitMethods 是先调用bean的afterPropertiesSet 前提bean是InitializingBean的实现类,然后调用bean 的 initMethod
applyBeanPostProcessorsAfterInitialization 会调用当前容器中所有的BeanPostProcessor 并且执行他们的postProcessAfterInitialization 方法 (这个方法为以后理解代理的相关原理及实现方式埋下伏笔)

至此 springIOC 最核心的提前加载和运行时获取并加载的主要功能及源码解析说完了。 其他的功能请按这个顺序也可不按看源码找到答案,但spring 代码非常庞大并且复杂设计模式用的非常广泛,最好看的抓住核心才能避免事倍功半。
个人理解,虽然有时原理或者实现机制看样子并不能解决碰到的非常复杂问题,但是会为以后解决各种问题及二次开发,打好基本功。

Spring启动过程——源码分析 - 掘金 (juejin.cn)

前言

Spring是什么?
Spring发展至今,已经不是简单的Spring Framework,它包括Spring Data、Spring Boot、Spring Cloud等等组成
不过这里我们仅讨论Spring Framework,重点讨论Spring的启动过程及拓展应用

Spring启动

监听器启动

web.xml配置

 

Spring通过监听器启动是最常见的方式(后续文章会补上其它启动方式,如用SpringBoot启动等)
ContextLoaderListener实现了ServletContextListener,会在web容器启动的时候调用

 

 

进入initWebApplicationContext()

这里只挑重点方法讲 this.context = createWebApplicationContext(servletContext);默认会创建一个XmlWebApplicationContext
而后进入configureAndRefreshWebApplicationContext(cwac, servletContext)

 

 

获取所有需要初始化的配置文件sc.getInitParameter(CONFIG_LOCATION_PARAM) 也就是上面一开始配置的context-param标签 最后执行refresh

 

Spring容器创建过程

refresh()
删减了一些无关紧要的代码

        @Override
	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);
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory)
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				// Initialize message source for this context.
				initMessageSource();
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				// Check for listener beans and register them.
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				finishRefresh();
			}
		}
	}
复制代码

prepareRefresh()

容器refresh预准备工作 AbstractApplicationContext

 

 

  • initPropertySources() AbstractApplicationContext子类自定义属性设置的方法

  • getEnvironment().validateRequiredProperties() 校验设置属性

  • this.earlyApplicationEvents = new LinkedHashSet<>() 保存容器早期的事件

obtainFreshBeanFactory()

获取工厂

 

 

  • refreshBeanFactory()

 

重点讲下 loadBeanDefinitions(beanFactory);
BeanDefinition是工厂中非常重要的属性,它将所有需要spring管理的bean信息都存储起来,会在后边一个一个去实例化
最终进入

 

 

reader.loadBeanDefinitions(configLocation)方法很深,我看了下,代码很深,大致的内容:
根据件用xmlReader去读取一个个配置文件 最终调用
this.beanDefinitionMap.put(beanName, beanDefinition)

 

refreshBeanFactory执行完进入getBeanFactory()
直接返回改beanFactory 方法结束

prepareBeanFactory(beanFactory)

 

从上往下四个框框作用分别是

 

  • 在工厂设置类加载器、表达式解析器

  • 给工厂添加ApplicationContextAwareProcessor(processor先不解释有什么意思 后续会说明)

  • 设置需要忽略的自动装配的接口(EnvironmentAware、ApplicationContextAware等Aware)

  • 给工厂添加 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this))

接着上面方法

最终调用

 

 

给容器注册一些组件

 

  • environment(ConfigurableEnvironment)
  • systemProperties(Map<String, Object>)
  • systemEnvironment(Map<String, Object>)

postProcessBeanFactory(beanFactory)

BeanFactory准备工作完成后 后置处理
空方法 子类自定义属性设置的方法 这里web环境 用的工厂是AbstractRefreshableWebApplicationContext
记住这里只是web端拓展才有的实现

 

这里先添加了一个ServletContextAwareProcessor和设置需要忽略的自动装配的接口
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext)
添加了几个scope

 

spring本身只有singleton和prototype两个,web环境加上了request、session、globalSession

invokeBeanFactoryPostProcessors(beanFactory)

这里没仔细看

执行BeanFactory后置处理器 执行这两种处理器
BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
他们的关系:BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor

 

 

 

 

总的来说

 

  1. 先获取所有的BeanDefinitionRegistryPostProcessor

  2. 依次执行实现了PriorityOrdered、Ordered接口和没有实现它们的 processor postProcessor.postProcessBeanDefinitionRegistry(registry)
    在这里我debug发现 spring内部有一个ConfigurationClassPostProcessor
    它的作用是负责解析处理所有@Configuration标签类,并将Bean定义(包括其它processor)注册到BeanFactory中
    这里注册完之后 再次获取所有BeanDefinitionRegistryPostProcessor,按顺序执行 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)
    重点:其它的BeanDefinitionRegistryPostProcessor就是Spring提供给我们注册bean的通道

  3. 再执行postProcessor.postProcessBeanFactory(beanFactory)

     

  4. 获取所有的BeanFactoryPostProcessor

  5. 分类执行 执行过的不再执行

 

6. 依次执行实现了PriorityOrdered、Ordered接口和没有实现它们的 processor postProcessor.postProcessBeanFactory(beanFactory)

 

实践出真知 这里是用SpringBoot简单搭起的一个demo,跟上面不一样,不过不影响测试结果

 

 

 

 

执行结果:

 

 

结果在意料之中 也就是说

 

  • 先执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry
  • 再执行BeanDefinitionRegistryPostProcessor的postProcessBeanFactory
  • 最后执行BeanFactoryPostProcessor的postProcessBeanFactory

registerBeanPostProcessors(beanFactory)

注册Bean的后置处理器(跟上面的FactoryPostProcessor不一样)

Bean的生命周期: Bean创建--初始化--销毁

不同BeanPostProcessor功能都不一样
BeanPostProcessor的实现子接口:
DestructionAwareBeanPostProcessor InstantiationAwareBeanPostProcessor MergedBeanDefinitionPostProcessor SmartInstantiationAwareBeanPostProcessor
BeanPostProcessor

 

 

 

红色部分:
获取所有BeanPostProcessor 依次注册了PriorityOrdered、Ordered接口、没有实现它们的、MergedBeanDefinitionPostProcessor
注册调用的是

黄色部分:
注册ApplicationListenerDetector :在bean初始化后 检查如果是listener的话 执行applicationContext.addApplicationListener

 

initMessageSource()

初始化MessageSource组件(做国际化、消息绑定、消息解析)

 

 

  1. 获取BeanFactory
  2. 查看容器是否有messageSource的组件
  • 有->this.applicationEventMulticaster beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class)

  • 没有->new DelegatingMessageSource() 注册一个MessageSource组件
    beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource)

initApplicationEventMulticaster()

初始化事件派发器

 

和上面实现差不多

 

  1. 获取BeanFactory
  2. 查看容器是否有applicationEventMulticaster的组件
  • 有->this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class)

  • 没有->new SimpleApplicationEventMulticaster() beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster)

这个派发器在后续很有用

onRefresh()

子类自定义的方法 web环境下直接进入一个空方法
还不确定什么环境下会有执行 欢迎联系交流~

registerListeners()

 

 

  1. 获取所有ApplicationListener
  2. getApplicationEventMulticaster().addApplicationListenerBean
    这里拿到的派发器就是刚才注册的

finishBeanFactoryInitialization(beanFactory)

较为复杂,单独作为一篇

finishRefresh()

 

 

  1. 初始化和生命周期有关的后置处理器 initLifecycleProcessor()
  • LifecycleProcessor:
  • void onRefresh();
    调用生命周期处理器的onRefresh方法,这个方法会找出Spring容器中实现了SmartLifecycle接口的类并进行start方法的调用
  1. 发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
    publishEvent(new ContextRefreshedEvent(this))
  2. 调用LiveBeansView的registerApplicationContext方法:如果设置了JMX相关的属性,则就调用该方法 LiveBeansView.registerApplicationContext(this)

AOP如何实现及实现原理 - 掘金 (juejin.cn)

概述:

最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章中用到的编程语言为kotlin,需要的可以在IDEA中直接转为java。 这篇文章将会按照如下目录展开:

  • AOP简介
  • 代码中实现举例
  • AOP实现原理
  • 部分源码解析

1. AOP简介

相信大家或多或少的了解过AOP,都知道它是面向切面编程,在网上搜索可以找到很多的解释。这里我用一句话来总结:AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。

我们在来看一下AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。

接下来介绍一下提到AOP就必须要了解的知识点:

  • 切面:拦截器类,其中会定义切点以及通知
  • 切点:具体拦截的某个业务点。
  • 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:
    1. 前置通知:@Before 在目标业务方法执行之前执行
    2. 后置通知:@After 在目标业务方法执行之后执行
    3. 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
    4. 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
    5. 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行

2. 代码中实现举例

上面已经大概的介绍了AOP中需要了解的基本知识,也知道了AOP的好处,那怎么在代码中实现呢?给大家举个例子:我们现在有个学校管理系统,已经实现了对老师和学生的增删改,又新来个需求,说是对老师和学生的每次增删改做一个记录,到时候校长可以查看记录的列表。那么问题来了,怎么样处理是最好的解决办法呢?这里我罗列了三种解决办法,我们来看下他的优缺点。

 

-最简单的就是第一种方法,我们直接在每次的增删改的函数当中直接实现这个记录的方法,这样代码的重复度太高,耦合性太强,不建议使用。

 

-其次就是我们最长使用的,将记录这个方法抽离出来,其他的增删改调用这个记录函数即可,显然代码重复度降低,但是这样的调用还是没有降低耦合性。

-这个时候我们想一下AOP的定义,再想想我们的场景,其实我们就是要在不改变原来增删改的方法,给这个系统增加记录的方法,而且作用的也是一个层面的方法。这个时候我们就可以采用AOP来实现了。

我们来看下代码的具体实现:

  1. 首先我定义了一个自定义注解作为切点
@Target(AnnotationTarget.FUNCTION)  //注解作用的范围,这里声明为函数
@Order(Ordered.HIGHEST_PRECEDENCE)  //声明注解的优先级为最高,假设有多个注解,先执行这个
annotation class Hanler(val handler: HandlerType)  //自定义注解类,HandlerType是一个枚举类型,里面定义的就是学生和老师的增删改操作,在这里就不展示具体内容了
复制代码
  1. 接下来就是要定义切面类了
@Aspect   //该注解声明这个类为一个切面类
@Component
class HandlerAspect{

 @Autowired
 private lateinit var handlerService: HandlerService

@AfterReturning("@annotation(handler)")   //当有函数注释了注解,将会在函数正常返回后在执行我们定义的方法
fun hanler(hanler: Hanler) {
    handlerService.add(handler.operate.value)   //这里是真正执行记录的方法
}
}
复制代码
  1. 最后就是我们本来的业务方法了
/**
* 删除学生方法
*/
@Handler(operate= Handler.STUDENT_DELETE)   //当执行到删除学生方法时,切面类就会起作用了,当学生正常删除后就会执行记录方法,我们就可以看到记录方法生成的数据
fun delete(id:String) {
   studentService.delete(id)
}
复制代码

3. AOP实现原理

我们现在了解了代码中如何实现,那么AOP实现的原理是什么呢?之前看了一个博客说到,提到AOP大家都知道他的实现原理是动态代理,显然我之前就是不知道的,哈哈,但是相信阅读文章的你们一定是知道的。

讲到动态代理就不得不说代理模式了, 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式包含如下角色:subject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口; RealSubject:真实主题角色,是实现抽象主题接口的类; Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。如下图所示:

 

那么代理又分为静态代理和动态代理,这里写两个小的demo,动态代理采用的就是JDK代理。举个例子就是现在一个班上的学生需要交作业,现在由班长代理交作业,那么班长就是代理,学生就是被代理的对象。

3.1 静态代理

首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有交作业的行为。这样,学生交作业就可以让班长来代理执行。

/**
 * Created by Mapei on 2018/11/7
 * 创建person接口
 */
public interface Person {
    //交作业
    void giveTask();
}
复制代码

Student类实现Person接口,Student可以具体实施交作业这个行为。

/**
 * Created by Mapei on 2018/11/7
 */
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }

    public void giveTask() {
        System.out.println(name + "交语文作业");
    }
}
复制代码

StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,那么他可以代理学生类对象执行交作业的行为。

/**
 * Created by Mapei on 2018/11/7
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样就可以代理学生产生行为
 */
public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;

    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }

    //代理交作业,调用被代理学生的交作业的行为
    public void giveTask() {
        stu.giveTask();
    }
}
复制代码

下面测试一下,看代理模式如何使用:

/**
 * Created by Mapei on 2018/11/7
 */
public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的学生林浅,他的作业上交有代理对象monitor完成
        Person linqian = new Student("林浅");

        //生成代理对象,并将林浅传给代理对象
        Person monitor = new StudentsProxy(linqian);

        //班长代理交作业
        monitor.giveTask();
    }
}
复制代码

运行结果:

 

 

这里并没有直接通过林浅(被代理对象)来执行交作业的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。代理模式就是在访问实际对象时引入一定程度的间接性,这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。比如班长在帮林浅交作业的时候想告诉老师最近林浅的进步很大,就可以轻松的通过代理模式办到。在代理类的交作业之前加入方法即可。这个优点就可以运用在spring中的AOP,我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

3.2 动态代理

动态代理和静态代理的区别是,静态代理的的代理类是我们自己定义好的,在程序运行之前就已经变异完成,但是动态代理的代理类是在程序运行时创建的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。比如我们想在每个代理方法之前都加一个处理方法,我们上面的例子中只有一个代理方法,如果还有很多的代理方法,就太麻烦了,我们来看下动态代理是怎么去实现的。

首先还是定义一个Person接口:

/**
 * Created by Mapei on 2018/11/7
 * 创建person接口
 */
public interface Person {
    //交作业
    void giveTask();
}
复制代码

接下来是创建需要被代理的实际类,也就是学生类:

/**
 * Created by Mapei on 2018/11/7
 */
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }

    public void giveTask() {
        System.out.println(name + "交语文作业");
    }
}
复制代码

创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。

/**
 * Created by Mapei on 2018/11/7
 */
public class StuInvocationHandler implements InvocationHandler {
    //invocationHandler持有的被代理对象
    T target;

    public StuInvocationHandler(T target) {
        this.target = target;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");
        Object result = method.invoke(target, args);
        return result;
    }
}
复制代码

那么接下来我们就可以具体的创建代理对象了。

/**
 * Created by Mapei on 2018/11/7
 * 代理类
 */
public class ProxyTest {
    public static void main(String[] args) {

        //创建一个实例对象,这个对象是被代理的对象
        Person linqian = new Student("林浅");

        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler(linqian);

        //创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

        //代理执行交作业的方法
        stuProxy.giveTask();
    }
}
复制代码

我们执行代理测试类,首先我们创建了一个需要被代理的学生林浅,将林浅传入stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数,那么所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。所以在看到下面的运行结果也就理所当然了。

 

 

那么到这里问题就来了,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行,带着这个问题,我们需要看一下动态代理的源码,对他进行简单的分析。

上面我们使用Proxy类的newProxyInstance方法创建了一个动态代理对象,看一下他的源码:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
  }
复制代码

然后,我们需要重点关注Class<?> cl = getProxyClass0(loader, intfs)这句代码,这里产生了代理类,这个类就是动态代理的关键,由于是动态生成的类文件,我们将这个类文件打印到文件中。

        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
        String path = "/Users/mapei/Desktop/okay/65707.class";

        try{
            FileOutputStream fos = new FileOutputStream(path);
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        }catch (Exception e) {
            System.out.println("写文件错误");
        }
复制代码

对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,就可以去调用真正的对象实例。
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  //这个静态块本来是在最后的,我把它拿到前面来,方便描述
   static
  {
    try
    {
      //看看这儿静态块儿里面的住giveTask通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveTask", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
 
  /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);我们可以对将InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
  */
  public final void giveTask()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

}
复制代码

看完了动态代理的源码,我们接下来就要看一下Spring中AOP实现的源码是怎样的?

4. 部分源码解析

aop创建代理的源码分析

  1. 看一下bean如何被包装为proxy
       	protected Object createProxy(
   		Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
   		
   	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
   		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   	}

       // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
   	ProxyFactory proxyFactory = new ProxyFactory();
   	proxyFactory.copyFrom(this);

   	if (!proxyFactory.isProxyTargetClass()) {
   		if (shouldProxyTargetClass(beanClass, beanName)) {
   			proxyFactory.setProxyTargetClass(true);
   		}
   		else {
   			evaluateProxyInterfaces(beanClass, proxyFactory);
   		}
   	}

       // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
   	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   	for (Advisor advisor : advisors) {
   		proxyFactory.addAdvisor(advisor);
   	}

   	proxyFactory.setTargetSource(targetSource);
   	customizeProxyFactory(proxyFactory);

   	proxyFactory.setFrozen(this.freezeProxy);
   	if (advisorsPreFiltered()) {
   		proxyFactory.setPreFiltered(true);
   	}

       // 3.调用getProxy获取bean对应的proxy
   	return proxyFactory.getProxy(getProxyClassLoader());
   }
复制代码
  1. 创建何种类型的Proxy?JDKProxy还是CGLIBProxy?
	public Object getProxy(ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
	}
    // createAopProxy()方法就是决定究竟创建何种类型的proxy
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
        // 关键方法createAopProxy()
		return getAopProxyFactory().createAopProxy(this);
	}
	
    // createAopProxy()
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
        // config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
        // hasNoUserSuppliedProxyInterfaces()是否存在代理接口
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
            
            // 2.如果目标类是接口类(目标对象实现了接口),则直接使用JDKproxy
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
            
            // 3.其他情况则使用CGLIBproxy
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
复制代码
  1. getProxy()方法
   final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用
   // getProxy()
   public Object getProxy(ClassLoader classLoader) {
   	if (logger.isDebugEnabled()) {
   		logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
   	}
   	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
       
       // JDK proxy 动态代理的标准用法
   	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
   }
复制代码
  1. invoke()方法法
    //使用了JDK动态代理模式,真正的方法执行在invoke()方法里,看到这里在想一下上面动态代理的例子,是不是就完全明白Spring源码实现动态代理的原理了。
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;
 
		TargetSource targetSource = this.advised.targetSource;
		Class<?> targetClass = null;
		Object target = null;
 
		try {
            // 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}
 
			Object retVal;
 
			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}
 
			// May be null. Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}
			// 2.获取当前bean被拦截方法链表
			List

那么到了这里,我要讲的内容就差不多结束了,如果有什么不对的,或者有什么疑惑,欢迎大家指点!

Spring中bean后置处理器BeanPostProcessor - 简书 (jianshu.com)

上一篇我们知道了Spring中bean工厂后置处理器的作用和触发时间。

作用:允许我们在工厂里所有的bean被加载进来后但是还没初始化前,对所有bean的属性进行修改也可以add属性值。
触发时间:先执行工厂后置处理器,再执行构造方法,最后init-method。

本篇我们学习BeanPostProcessor,和BeanFactoryPostProcessor仅差一个Factory,可以推断后者是针对工厂,前者是针对bean元素。
我们来看一下BeanPostProcessor的两个方法

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

public interface BeanPostProcessor {

    //实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务  
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    //实例化、依赖注入、初始化完毕时执行 
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

注意:

BeanFactory和ApplicationContext两个容器对待bean的后置处理器稍微有些不同。ApplicationContext容器会自动检测Spring配置文件中那些bean所对应的Java类实现了BeanPostProcessor
接口,并自动把它们注册为后置处理器。在创建bean过程中调用它们,所以部署一个后置处理器跟普通的bean没有什么太大区别。

接下来我们通过案例来分析它的用法:

beans.xml



 
 
 

这里我们注册了一个普通类User和一个BeanPostProcessor类

User类

package xz.quartz.analysis;

public class User {
    String name;
    String age;
    
    void init(){
        System.out.println("init");
        name = "zx-init";
        age = "zx-init";
        System.out.println("name:"+name+",age:"+age);
    }
    
    public User() {
        System.out.println("construtor");
        name = "zx-construtor";
        age = "zx-construtor";
        System.out.println("name:"+name+",age:"+age);
    }
    
    public void go(){
        System.out.println("最终age的值:"+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;
    }
}

user类里包含了init-method和construtor

BeanPostProcessor实现类

package xz.quartz.analysis;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class beanpostpro implements BeanPostProcessor{

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before"+beanName);
        return bean;
    }

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

}

main函数

public class xz {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
        System.out.println("获取到User实例 ");
        ((User)applicationContext.getBean("user")).go();
    }
}

运行结果

construtor
name:zx-construtor,age:zx-construtor
beforeuser
init
name:zx-init,age:zx-init
afteruser
获取到User实例 
最终age的值:zx-init

通过这个例子我们可以得出结论

执行顺序:构造方法—>BeanPostProcessor的before—>init-method—>BeanPostProcessor的after。

我们把上篇提到的BeanFactoryPostProcessor也加进来看一下效果

package xz.quartz.analysis;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class beanfactorypostpro implements BeanFactoryPostProcessor{

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("******调用BeanFactoryPostProcessor开始");
        String[] beanStr = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanStr) {
            System.out.println("bean name:"+beanName);
            if ("user".equals(beanName)) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
                System.out.println("修改user的age值");
                beanDefinition.getPropertyValues().add("age", "20");             
            }
        }
        System.out.println("******调用BeanFactoryPostProcessor结束");
    }       
}

运行结果

******调用BeanFactoryPostProcessor开始
bean name:user
修改user的age值
bean name:beanfactorypostpro
bean name:beanpostpro
******调用BeanFactoryPostProcessor结束
construtor
name:zx-construtor,age:zx-construtor
beforeuser
init
name:zx-init,age:zx-init
afteruser
获取到User实例 
最终age的值:zx-init

到这里我们可以得出结论

1.执行顺序:BeanFactoryPostProcessor—>构造方法—>BeanPostProcessor的before—>init-method—>BeanPostProcessor的after.
2.实际运用中,可以配置多个BeanFactoryPostProcessor和BeanPostProcessor

3.应用场景

1.解析bean的注解,将注解中的字段转化为属性
2、统一将属性在执行前,注入bean中,如数据库访问的sqlMap,如严重服务,这样不需要每个bean都配置属性
3、打印日志,记录时间等。

Spring学习(十五)Spring Bean 的5种作用域介绍_wangxin1248的博客-CSDN博客

Spring Bean 中所说的作用域,在配置文件中即是“scope
在面向对象程序设计中作用域一般指对象或变量之间的可见范围。
而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围
 
在Spring 容器当中,一共提供了5种作用域类型,在配置文件中,通过属性scope来设置bean的作用域范围。
1.    singleton:
当Bean的作用域为singleton的时候,Spring容器中只会存在一个共享的Bean实例,所有对Bean的请求只要id与bean的定义相匹配,则只会返回bean的同一实例。单一实例会被存储在单例缓存中,为Spring的缺省作用域。
2.    prototype:
每次对该Bean请求的时候,Spring IoC都会创建一个新的作用域。
对于有状态的Bean应该使用prototype,对于无状态的Bean则使用singleton
3.    request:
Request作用域针对的是每次的Http请求,Spring容器会根据相关的Bean的
定义来创建一个全新的Bean实例。而且该Bean只在当前request内是有效的。
4.    session:
针对http session起作用,Spring容器会根据该Bean的定义来创建一个全新的Bean的实例。而且该Bean只在当前http session内是有效的。
5.    global session:
类似标准的http session作用域,不过仅仅在基于portlet的web应用当中才有意义。Portlet规范定义了全局的Session的概念。他被所有构成某个portlet外部应用中的各种不同的portlet所共享。在global session作用域中所定义的bean被限定于全局的portlet session的生命周期范围之内。
 
解析来我们详细的介绍每种作用域的功能:
1)singleton作用域
是指在Spring IoC容器中仅存在一个Bean的示例,Bean以单实例的方式存在,单实例模式是重要的设计模式之一,在Spring中对此实现了超越,可以对那些非线程安全的对象采用单实例模式。
接下来看一个示例:
  1. 1. <bean id="car" class="cn.lovepi.Car" scope="singleton">bean>
  2. 2. <bean id="boss1" class="cn.lovepi .Boss” p:car-ref=“car">bean>
  3. 3. <bean id="boss2" class="cn.lovepi .Boss” p:car-ref=“car">bean>
  4. 4. <bean id="boss3" class="cn.lovepi .Boss” p:car-ref=“car">bean>
在1中car这个Bean生命周期声明为了singleton模式,其他的bean如2,3,4引用了这个Bean。在容器中boss1、boss2、boss3的属性都指向同一个bean car。如下图所示:

不仅在容器中通过对象引入配置注入引用相同的car bean,通过容器的getBean()方法返回的实例也指向同一个bean。
在默认的情况下Spring的ApplicationContext容器在启动的时候,自动实例化所有的singleton的bean并缓存于容器当中。
 
虽然启动时会花费一定的时间,但是他却带来了两个好处:
  1. 对bean提前的实例化操作,会及早发现一些潜在的配置的问题。
  2. Bean以缓存的方式运行,当运行到需要使用该bean的时候,就不需要再去实例化了。加快了运行效率。
 
2)prototype作用域
是指每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new Bean()的操作。在默认情况下,Spring容器在启动时不实例化prototype的Bean。
接下来看一个示例:
  1. 1. <bean id=“car" class="cn.lovepi.Car" scope=“prototype">bean>
  2. 2. <bean id=“boss1" class="cn.lovepi.Boss” p:car-ref=“car">bean>
  3. 3. <bean id=“boss2" class="cn.lovepi.Boss” p:car-ref=“car">bean>
  4. 4. <bean id=“boss3" class="cn.lovepi.Boss” p:car-ref=“car">bean>
通过以上的配置,Boss1、boss2、boss3所引用的都是一个新的car的实例,每次通过容器getBean()方法返回的也是一个新的car实例。如下图所示:
在默认情况下,Spring容器在启动时不实例化prototype这种bean。此外,Spring容器将prototype的实例交给调用者之后便不在管理他的生命周期了。
 
3)当用户使用Spring的WebApplicationContext时,还可以使用另外3种Bean的作用域,即request,sessionglobalSession
 
在使用Web应用环境相关的Bean作用域时,必须在Web容器中进行一些额外的配置:
 
对于低版本Web容器配置:
用户可以使用http过滤器来进行配置并在url-pattern中对所有的页面进行过滤。
  1. <filter>
  2. <filter-name>requestContextFilterfilter-name>
  3. <filter-class>org.springframework.web.filter.RequestContextFilterfilterclass>
  4. filter>
  5. <filter-mapping>
  6. <filter-name>requestContextFilterfilter-name>
  7. <url-pattern>/*url-pattern>
  8. filter-mapping>
对于高版本Web容器配置:
使用http请求监听器来进行配置。
  1. <listener> <listener-class>
  2. org.springframework.web.context.request.RequestContextListener
  3. listener-class>listener>
ServletContextListener只负责监听web容器启动和关闭的事件,
RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听http请求事件。
Web服务器接收到的每一次请求都会通知该监听器。
 
Spring容器的启动和关闭事件由web容器的启动和关闭事件来触发。
但如果Spring容器中的bean需要requestsessionglobalSession作用域的支持,Spring容器本身就必须获得web容器的http请求事件。以http请求事件来驱动bean的作用域的控制逻辑,也就是说通过配置RequestContextListener接口,Spring和web容器的结合就更紧密了。
 
那么接下来简单的介绍下这三种作用域。
1.request作用域
       对应一个http请求和生命周期,当http请求调用作用域为request的bean的时候,Spring便会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。
2.session作用域
       Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。
3.globalSession作用域
       与session大体相同,但仅在portlet应用中使用。
Portlet规范定义了全局session的概念。请求的bean被组成所有portlet的自portlet所共享。
如果不是在portlet这种应用下,globalSession则等价于session作用域
 
接下来使用一个实例来演示下bean作用域的概念:
首先我们创建scope包,然后在包中创建两个Java Bean:老板(Boss.java)以及轿车(Car.java)
  1. public class Boss {
  2. private String name;
  3. private Car car;
  4.  
  5. public Boss() {
  6. }
  7.  
  8. public Car getCar() {
  9. return car;
  10. }
  11.  
  12. public void setCar(Car car) {
  13. this.car = car;
  14. }
  15.  
  16. @Override
  17. public String toString() {
  18. return "Boss{" +
  19. "name='" + name + '\'' +
  20. ", car=" + car +
  21. '}';
  22. }
  23. }
  24.  
  25. public class Car {
  26. private String color;
  27. private String brand;
  28. private double price;
  29.  
  30. public String getColor() {
  31. return color;
  32. }
  33.  
  34. public void setColor(String color) {
  35. this.color = color;
  36. }
  37.  
  38. public String getBrand() {
  39. return brand;
  40. }
  41.  
  42. public void setBrand(String brand) {
  43. this.brand = brand;
  44. }
  45.  
  46. public double getPrice() {
  47. return price;
  48. }
  49.  
  50. public void setPrice(double price) {
  51. this.price = price;
  52. }
  53.  
  54. }
接着我们创建对应的创建配置文件,在conf包中创建一个conf-scope.xml文件:
  1.  
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  7.  
  8. <bean id="car" class="cn.lovepi.chapter03.scope.Car" scope="singleton"/>
  9. <bean id="boss1" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />
  10. <bean id="boss2" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />
  11. <bean id="boss3" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />
  12. beans>
最后我们创建测试类进行测试
  1. public class Main {
  2. public static void main(String[] args){
  3. //加载配置文件,启动IoC容器
  4. BeanFactory factory=
  5. new FileSystemXmlApplicationContext("src/conf/conf-scope.xml");
  6. //获得bean实例
  7. Boss boss1=factory.getBean("boss1",Boss.class);
  8. Boss boss2=factory.getBean("boss2",Boss.class);
  9. Boss boss3=factory.getBean("boss3",Boss.class);
  10. System.out.println(boss1.getCar());
  11. System.out.println(boss2.getCar());
  12. System.out.println(boss3.getCar());
  13. }
  14. }
然后将作用域设置成singleton和 prototype分别执行程序观察结果。
 
接下来再来简单的学习下在Spring当中如何自定义作用域
在Spring 2.0中,Spring的Bean作用域机制是可以扩展的,这意味着,你不仅可以使用Spring提供的预定义Bean作用域,还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)
 
1.首先需要实现自定义Scope类:
      org.springframework.beans.factory.config.Scope
      首先我们得实现config.scope这个接口,要将自定义scope集成到Spring容器当中就必须要实现这个接口。接口中只有两个方法,分别用于底层存储机制获取和删除这个对象。
 
2.在实现一个或多个自定义Scope并测试通过之后,接下来便是如何让Spring容器来识别新的作用域。我们需要注册自定义Scope类,使用registerScope方法
     ConfigurableBeanFactory.registerScope(String scopeName, Scope scope)
其中:第一个参数是与作用域相关的全局唯一的名称,第二个参数是准备实现的作用域的实例。
 
3.在实现和注册自定义的scope类之后我们便来使用自定义的Scope:
  1. Scope customScope = new ThreadScope();
  2. beanFactory.registerScope(“thread”, customeScope);
  3. <bean id=“***” class=“***” scope=“scopeName” />
     

 

 

posted @ 2021-12-13 00:10  CharyGao  阅读(207)  评论(0编辑  收藏  举报