spring中父类注入了一个接口,其子类会调用到该接口么

spring中父类注入了一个接口,其子类会调用到该接口么,第1张

注入的那个地方的class是写的实现类,并不是接口,你点的时候,点出来的有方法名啊,你可以选是哪一个,应该不冲突的,为了解耦和,最好是一个接口对应一个实现类,你可以写两个<bean>啊,就是两个bean的id不一样,你在service层的时候注入的接口类型的变量,都是一个接口类型,但是根据你注入的不通的实现类,他就不是相同的了

springmvc是基于方法的,也就是一个方法对应一个资源,你现在子类和父类都有这个方法,而且url是一样的,那我访问这个url时,走哪个方法好呢,必然报错。你根本就不需要去继承,如果这个类需要用其他controller的方法,你直接调用不就完了

所有事件将继承于 ApplicationEvent,并通过继承自EventObject 的成员变量 source 获取到事件源 (注册事件监听器和发布事件的地方) ;Spring中 事件源 一般是容器本身。

如:ApplicationContextEvent(也是一个抽象的事件)--容器事件

具体容器事件有:

ContextStartedEvent,ContextStopedEvent,ContextClosedEvent,ContextRefreshedEvent

PayloadApplicationEvent —Spring 42之后框架提供的 ApplicationEvent 的子类由于是泛型类,所以可以用来包装任何事件;在容器内部发送的任意类型的事件都会被包装成为 PayloadApplicationEvent 事件对象;包括前面介绍的四个具体 容器事件类

JDK 要求所有的监听器都要继承这个 EventListener

Spring 中 定义了 ApplicationListener 的两个自接口:SmartApplicationListener,

Bean 可以 通过上面的 ApplicationEventPublisherAware 获取到 ApplicationEventPublisher实例--其实也就是获取到容器这个实例,就可以发布自定义的事件,给容器里面自定义的 EventListener

    orgspringframeworkcontexti18nLocaleContext是一个接口,只有一个方法getLocale(),就是用来获取当前的Locale的,下面看下整体类图。

    从类图中,我们可以看到LocaleContext有三个子类:其中TimeZoneAwareLocaleContext是一个子接口,该接口提供了一个getTimeZone()方法来获取当前时区了;SimepleLocaleContext是对LocaleContext接口的一个简单实现;主要看下SimpleTimeZoneAwareLocaleContext类,这个类继承了SimpleLocaleContext,实现了TimeZoneAwareLocaleContext接口,这也就说该类可以同时获取Locale和TimeZone,这个类也是我们常用的,看下他的实现:

     整个LocaleContext的设计目的是为了保存了整个应用的Locale和TimeZone。看完下面的LocaleResolver,你就会明白LocaleContext的作用。

    首先看下orgspringframeworkwebservletLocaleResolver的整体类图:

    上面这个类图中有两个主要的接口:orgspringframeworkwebservletLocaleResolver和orgspringframeworkwebservletLocaleContextResolver,这两个接口的设计思想和上面的LocaleContext与TimeZoneLocaleContext是一致的。LocaleResolver接口提供了对Locale *** 作的两个方法:

    LocaleContextResolver提供了对LocaleContext的两个 *** 作方法:

    从上面的类图中,我们可以看到主要有四个实现类:

    1orgspringframeworkwebservleti18nAcceptHeaderLocaleResolver

        该实现类相当于LocaleResolver的默认实现,由于它只实现了LocaleResolver接口,因此只能解析Locale,不能设置Locale,该类在DispatcherServlet调用initLocaleResolver()方法的时候,会判断IOC容器中是由有一个叫localeResolver的Bean,如果这个Bean不存在,就会初始化该类作为默认的LocaleResolver。这个类是通过判断>

Converter用来将源数据类型转换目标数据类型,不过有时候一个数据类型会对应不同格式的字符串,如日期类型在不同国家显示的字符是不一样的,需要根据Locale进行转换,或者需要将日期类型转换为不同格式化的字符串,spring针对这种情况提供了Formatter接口来针对格式化进行处理。

格式化Formatter其实也是一种Converter,只是两个互转类型之间, 有一个固定是String类型 ,Formatter实现了不同格式的String类型与其他类型的类型互转

Formatter主要针对的是Number类型和日期类型。Spring对这两种类型的子类和字符串之间的转换提供了格式化实现,下面以日期类型的转换为例说明。

Formatter接口继承了Printer和Parser接口,一个用于将对象格式化为本地化的字符串,一个将字符串转换为对象。

AnnotationFormatterFactory集成了注解和Formatter。可以创建带有Annotation性质的Printer和Parser对象。

在上章节的继承图中可以看到FormattingConversionService继承了GenericConversionService。同时实现了FormatterRegistry和ConverterRegistry接口

FormatterRegistry扩展了ConverterRegistry接口,额外提供了注册Formatter、AnnotationFormatterFactory的功能

FormatterRegistrar接口用于批量注册Formatter的接口

spring提供了FormattingConversionService的默认实现DefaultFormattingConversionService,使用的时候一般都是直接使用这个类。

首先来看DefaultFormattingConversionService,在BeanFactory中只接收一个ConversionService变量,所以只能给spring容易配置一个ConversionService。那么到底应该用DefaultFormattingConversionService还是用DefaultConversionService?让我们来看DefaultFormattingConversionService中的实现

从源码可以看出,DefaultFormattingConversionService完全是对DefaultConversionService的扩展,在构造函数中调用了DefaultConversionService的addDefaultConverters完全拥有了DefaultConversionService所有的功能,所以只需要使用DefaultFormattingConversionService就可以

DefaultFormattingConversionService创建完成之后除了添加Converters,还注册了一些Formatters,这些Formaters主要是spring提供的用于针对 日期类型 Number类型 货币金额 进行格式化转换

接下来分析下Formater的注册功能。通过上述分析我们可以知道,Formater实质上是Class<>和String之间的互转,所以在注册的时候,只需要提供Class<>、Printer和Parser。来看FormattingConversionService类中的实现

可以看到的是注册Formater的过程,就是注册了一对Converter。注册Converter的过程已经在前文分析过,在这里我们来一起看下PrinterConverter和ParserConverter类。

可以看出PrinterConverter类型实现GenericConverter,用于实现传入类型到字符串的转换,具体的转换功能委托给参数Printer实现类对象实现。这里相当于是一个插件类保留。可以通过Printer实现任何类型到String类型的转换。

再来看ParserConverter

ParserConverter跟PrinterConverter实现了一个反方向的转换,至此注册通过一个Formater接口实现类,就可以完成Formater实现类中泛型到String类型之间的互转。

例如注册 LocalDateTime的转换器,实现LocalDateTime和String类型的互转

可以看到使用一个类TemporalAccessorPrinter和TemporalAccessorParser,并都传入了一个参数DateTimeFormatter,众所周知LocalDateTime和String格式化就是通过DateTimeFormatter来实现。那么我们猜测TemporalAccessorParser其实就是对DateTimeFormatter的封装调用

可以看到TemporalAccessorPrinter就是调用的DateTimeFormatter完成格式化的,不过这里使用到了ThreadLocal,可以先不管,TemporalAccessorParser中同理也会使用DateTimeFormatter完成String到日期的转换

Formatter的注册最终是注册了一对Converter,所以格式化转换完全就是Converter逻辑的实现,在前文已经分析过了,这里就不再赘述。

分析完了Formatter的注册和转换过程,一起来看下FormatConversionService提供了另外一种注册。前文提到了可以通过在对象字段上声明一个注解,在注解中指定格式化后字符串格式。这个功能就是通过AnnotationFormatterFactory来实现的。来看FormatConversionService的 addFormatterForFieldAnnotation() 方法

AnnotationFormatterFactory的注册,首先需要获取的就是AnnotationFormatterFactory泛型中的注解类型。然后通过 getFieldTypes() 获取所有声明的可以进行转换类型集合,然后循环注册了两个类型转换器AnnotationPrinterConverter和AnnotationParserConverter。那么来看一下AnnotationPrinterConverter到底如何完成可配置的类型转换的

可以看到的是,AnnotationPrinterConverter实现了ConditionalGenericConverter接口,在 matches() 方法中声明了该转换器只会作用于Class<>上有指定的注解的类型。在convert方法中最终创建了一个PrinterConverter对象,使用PrinterConverter完成格式化的功能,这个在上面已经分析过了。唯一的不同就是Printer的获取。使用annotationFormatterFactory获取printer,并将注解作为参数传递进去。所以可以我们可以实现AnnotationFormatterFactory的 getPrinter() 方法提供转换为字符串的功能即可,通过看以通过参数annotation获取用户配置的格式。实现格式的可配置。

同样在AnnotationParserConverter中实现String到Class<>的转换,调用AnnotationFormatterFactory获取Parser然后创建一个ParserConverter来实现类型转化。需要注意的是 注解只能添加在非String类型那一方上。

通过封装封装注册AnnotationPrinterConverter和AnnotationParserConverter,用户需要做的就只有是实现AnnotationFormatterFactory,在泛型中指定注解,然后实现了 getPrinter() 和 getParser() 来实现Class<>和String之间的转换,其他的调用直接走Converter流程。来看Date类型格式化的实现

在DateFormatterRegistrar中,在注册Formatters之前,先注册了日期类型的一些converter,这里先不去管这个。最主要的是 addFormatterForFieldAnnotation 方法,通过这个方法完成对Formater的注册

来看下DateTimeFormatAnnotationFormatterFactory实现了AnnotationFormatterFactory。并指定注解为DateTimeFormat。

可以看到的是在 getFieldType() 声明了支持转换的有Date、Calendar、Long。可以看到 getPrinter() 和 getFormatter() 方法返回了一个DateFormatter对象,并将用户配置的注解信息注入到这个DateFormatter对象中

接下来就是最终的格式化实现就是这个DateFormatter对象了。大家想一下,一般针对Date类型的格式化都会用什么呢?想必大家都猜到了,没错就是SimpleDateFormat。在DateFormatter中就是创建了一个SimpleDateFormat来实现类型和字符串的转换的

至此就实现了Formatter的注册和使用。那么在spring中是如何使用的呢?

没错就注册一个id为conversionService的FormattingConversionServiceFactoryBean对象。使用这个就可以了,不需要再注册ConversionServiceFactoryBean了。

首先先来理解spring中对JavaBean的管理机制。Spring容器所管理的bean对象都是由beanFacotry工厂来实例化,所以要使用pring容器中bean实例对象,必须该类也是由spring容器所管理,否则无法从bean工厂中得到bean实例对象。

Struts 最新版本中是由 我们先抛开struts2专门来看spring,那么我们就从spring标准配置文件来说起,如果你要向某个由spring管理的对象中注入一个对象,我们该如何去做呢举例说明:如果由两个对象A、B,现在需要在B对象中使用A对象,那么我们通过什么方式将A注入到B中去呢?首先A、B两个对象都必须是由spring容器来管理,那么我们就需要在配置文件定义property 属性来注入,现在A、B都有spring容器来管理的,那么我如何得到其中A对象呢?因为AB对象都是由spring容器来创建的,所以要想得到A对象,就首先得到BeanFactory--->GetA(),然后才能拿到A,这时A指向B对象的引用才有值,才能把A注入到B中。

1、 第一种不推荐的方式:

在spring配置文件中配置如下:Java代码

定义一个类变量(静态的static,这个类的所有实例共享的变量),启动服务器时,初始化spring时就创建了BeanFactory,在创建BeanFacotry时,就实例化了所有对象。也就说也就创建了一个roleAssignmentHandler03 对象,放到了它自己容器的那块内存中,这个对象拥有一个指向userService的引用,因为我在这里定义的是类变量。等下次在创建这个对象时,在这个类变量中同样是有值。但是这种方法并不推荐,这种方式有点浪费内存空间,spring已经给我们创建了对象,但是我们并没有去使用它。

Java代码publicclassRoleAssignmentHandler03implementsAssignmentHandler{ privatestaticUserServiceuserService; publicvoidsetUserService(UserService_userService){ userService=_userService; } ……public class RoleAssignmentHandler03 implements AssignmentHandler {private static UserService userService;public void setUserService(UserService _userService) {userService = _userService;}……

2、 第二种不推荐的方式:

我不能向往常做测试那样通过new ClassPathXmlApplicationContext("");来创建它,我们需要从当前环境中去拿,那么从什么地方去拿呢?那么就得明白它是如何创建的,由谁来创建并管理的?在服务器启动时,它就会初始化webxml中的linstener配置信息,创建一个Bean’Factroy对象,并把这个对象放到了ServletContext中,我们可以利用spring中的一个工具类来得到ServletContext中的BeanFactory对象,但是并推荐,因为ServletActionContext是Struts中的,在业务逻辑中过分依赖于展现层的内容,JBPM是不依赖于展现层内容的,也不推荐于依赖关系的存在。

Java代码BeanFactoryfactory=WebApplicationContextUtilsgetRequiredWebApplicationContext(ServletActionContextgetServletContext()); UserServiceuserService=factorygetBean("userService");BeanFactory factory = WebApplicationContextUtilsgetRequiredWebApplicationContext(ServletActionContextgetServletContext());UserService userService = factorygetBean("userService");

3、 第三种并不推荐的方式

在项目开发中采用SSH与JBPM集成时,用到了一个第三方的集成包,我们可以从这里得到解决方案,(采用jbpm31所以其第三方集成包为spring-modules-jbpm31jar)在其文档(jbpm31html)中可以了解到,采用如下配置:

流程定义文件配置Java代码 roleAssignmentHandler03 roleAssignmentHandler03

在spring配置文件中可以这样定义如下Java代码

4、 第四种方式:

回过头来,我们接着继续思考从BeanFactory中去获得,流程定义文件并不去修改它,这样就不会破坏它的灵活性,那么接下来要分析的问题就是如何来获得BeanFactory呢?那就得深入了解一下spring,现在来看一个问题:在一个单例中如何获得一个非单例的对象呢?也就是将B注入到A中 A是单例,但是B不是单例,直接采用Resource或在配置文件利用property注入的话,是无法实现的,只能将B变成一个单例对象来使用。那么我们在单例对象中如何动态获取依赖对象呢?有一种方式就从它的BeanFactory中去获得其对象,如何在由spring管理的对象中拿到BeanFactory呢?也就是说在容器实例对象中访问容器本身,在spring中有这样一个BeanFactoryAware接口,在这个接口有个setBeanFactory(BeanFactory f)方法,我们可以实现该接口,重写这个方法,来得到我们BeanFactory,从中得到动态的得到BeanFactory中实例对象,但是放到jbpm中handler类中来使用,还是无法实现的,因为在这里它不是有spring来管理的,所以即使实现BeanFactory接口,重写其方法,但是无法将BeanFactory设置上值的,因为它是由spring来管理的,所以我无法从一个不是spring所管理的bean中去注入spring所提供的bean对象。但是我们可以通过另一种途径来解决这个问题:创建一个辅助类BeanFactroyHelper类,让它来实现刚才这个BeanFactoryAware接口,具体如下:Java代码publicclassBeanFactoryHelperimplementsBeanFactoryAware{ privatestaticBeanFactoryfactory; publicvoidsetBeanFactory(BeanFactoryf)throwsBeansException{ factory=f; } publicstaticBeanFactorygetFactory(){ returnfactory; } }public class BeanFactoryHelper implements BeanFactoryAware {private static BeanFactory factory;public void setBeanFactory(BeanFactory f) throws BeansException {factory = f;}public static BeanFactory getFactory(){return factory;}}

然后,让上面这个类纳入spring的管理,在spring中配置如下:Java代码

最后在我们handler类中,直接使用就可以了Java代码BeanFactoryfactory=BeanFactoryHelpergetFactory();BeanFactory factory = BeanFactoryHelpergetFactory();

这种方式可以,但是我并采用这种方式,因为第四种方式已经有人给我做了。所以我们就不需要在重复做这些类似的工作了。所以这种方式我也不会采用。

5、 第五种方式:

通过上面所说的方式,在spring-modulesjar这个第三方集成包开发人中已经采用类似的方式实现了。那么他们是如何实现的呢?这就需要来了解一下jbpmHandlerProxy的基本原理,在JbpmHandlerProxy类中实现了ActionHandler, AssignmentHandler, DecisionHandler, TaskControllerHandler等接口,在实现不同的接口会调用不同它的不同的方法execute、assign、decide…

ServiceLocator,在spring中也提供了一个ServiceLocator这种设计模式的一个接口BeanFactoryLocator、 BeanFactoryReference接口,要想详细了解其内部机制需要我们进一步花费精力去解析其源码了。(在这里就不详细去分析了)这里我只是了解jbpmFactoryPorxy类中实现机制的大体思想,所以下面我就针对在代理类中的具体实现思想大体说一下:在JbpmHandlerProxy类中得到BeanFactory对象,主要是通过JbpmFactoryLocator来得到BeanFactoryReference,通过BeanFactoryReference来得到Factory。那么在JbpmHandlerProxy类中的JbpmFactoryLocator是从哪里得到的呢?查看源码可以得知在其JbpmFactoryLocator类下有个setBeanFactory()方法,那么这个方法是有谁调用的呢?通过查看得知是LocalJbpmConfigurationFactoryBean类来调用的。主要分析图如下:

以上就jbpmFactoryProxy中内部机制。

下面我就来说一下具体解决方案是:

首先通过利用jbpmFactoryProxy的实现类中retrieveBeanFactory方法的具体实现方式,来得到jbpm中已经注入的BeanFactory对象,下面我将进一步进行封装来实现自动注入的功能。

先贴出在jbpm的handler类中的具体使用,和在spring中使用Bean对象一样使用。

Java代码/ 是由BaseAutowire自动注入 /@ResourceprivateUserServiceuserServce;/ 是由BaseAutowire自动注入/@Resourceprivate UserService userServce;下面一段代码将是所抽取出来的基类:Java代码/ 自动注入BeanFactory基类 @authorlzh /publicclassBaseAutowire{ / 在调用子类的时会自动调用 在构造方法中调用retrieveBeanFactory()方法得到BeanFactory对象 利用它的实现接口设置将该自身设置为自动注入到子类中 /publicBaseAutowire(){ ((AutowireCapableBeanFactory)retrieveBeanFactory()) autowireBeanProperties(this,AutowireCapableBeanFactoryAUTOWIRE_BY_TYPE,true); } protectedBeanFactoryretrieveBeanFactory(){ BeanFactoryLocatorfactoryLocator=newJbpmFactoryLocator(); BeanFactoryReferencefactory=factoryLocatoruseBeanFactory(null); if(factory==null) thrownewIllegalArgumentException("nobeanFactoryfoundunderkey="+null); try{ returnfactorygetFactory(); } finally{ factoryrelease(); } } }/ 自动注入BeanFactory基类 @author lzh/public class BaseAutowire {/ 在调用子类的时会自动调用 在构造方法中调用retrieveBeanFactory()方法得到BeanFactory对象 利用它的实现接口设置将该自身设置为自动注入到子类中/public BaseAutowire() {((AutowireCapableBeanFactory)retrieveBeanFactory())autowireBeanProperties(this, AutowireCapableBeanFactoryAUTOWIRE_BY_TYPE, true);}protected BeanFactory retrieveBeanFactory() {BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();BeanFactoryReference factory = factoryLocatoruseBeanFactory(null);if (factory == null)throw new IllegalArgumentException("no beanFactory found under key=" + null);try {return factorygetFactory();}finally {factoryrelease();}}}

以上就是关于spring中父类注入了一个接口,其子类会调用到该接口么全部的内容,包括:spring中父类注入了一个接口,其子类会调用到该接口么、如何使用继承spring AbstractController 类来控制每个实现子类方法顺序 父类contoller方法也需要被执行、Spring的事件驱动模型等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/9423850.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-28
下一篇 2023-04-28

发表评论

登录后才能评论

评论列表(0条)

保存