• Spring IoC和AOP的介绍


    基于Spring Framework 版本:5.0.2.RELEASE


    IoC

    概念:传统Java开发中,程序通过new主动创建对象实例,而Spring有专门的IoC容器来创建对象,具体来说就是在Spring容器中注册过的类,其创建、销毁等过程交由Spring来统一负责管理,所以这一过程也叫依赖注入(DI)。

     

    Spring的基础IoC容器包是org.springframework.beans和org.springframework.context。

    核心接口

    BeanFactory接口作为容器的根接口,提供Bean的一些基础定义和方法;

    ApplicationContext是BeanFactory的子接口,为应用程序提供了更丰富的功能。

    BeanFactory、ApplicationContext之间的继承关系如下图

    注入方式

    Spring的主要有两种注入方式:构造器注入、Setter方法注入。如何选择呢?

    • Spring推荐使用构造器注入,因为构造器注入时,component组件可以当作不可变对象,并且能确保其不为空,而且构造器注入的component返回时是完全初始化的状态。
    • 当一个类中需要注入太多的参数时,可能这个类负责了太多的功能,可以考虑适当的重构。
    • Setter方法注入可以作为备选方案,但是如果没有默认值时最好进行非空检查。
    • Setter注入的一个好处是类的对象能够在后来重新配置或重新注入。
    • 如果使用的第三方源码不提供Setter方法时,就只能选择构造器注入的方式了。
    • 构造器注入可能引入循环依赖问题。比如:A类构造器注入B,B类构造器注入A,此时Spring容器会抛出异常BeanCurrentlyInCreationException。此时需要考虑使用Setter方法注入的方式了。

    配置方法

    Spring常用XML和注解的方式来配置类,这里推荐使用注解来配置。需要注意的是注解是在XML之前执行注入的,因此后者的配置将覆盖注解的配置。

     

    Bean的作用域和生命周期

    Bean的作用域,其中request、Session、application、websocket仅在ApplicationContext上下文才有效。

    作用域

    描述

    singleton

    默认值,单例,整个IoC容器只有一个实例对象。

    prototype

    原型,每次调用都会实例化一个地响。

    request

    作用于HTTP请求的生命周期,每个HTTP请求都有一个自己的实例。仅在ApplicationContext上下文有效。

    session

    作用于HTTP Session的生命周期,仅在ApplicationContext上下文有效。

    application

    作用于ServletContext的生命周期,仅在ApplicationContext上下文有效。

    websocket

    作用于WebSocket的生命周期,仅在ApplicationContext上下文有效。

    Bean的生命周期

     

    过程描述

    1.Spring对bean进行实例化;

    2.Spring将值和bean的引用注入到bean对应的属性中;

    3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;

    4.如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;

    5.如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;

    6.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;

    7.如果bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()方法。类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用;

    8.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;

    9.此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;

    10.如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

     

    常用注解说明

    @Bean, @Configuration表示基于Java配置的类

    @Bean除了配置在@Configuration,也可以在@Component定义,此时没有特殊意义,只是普通的工厂方法。

    @Import 导入依赖的类,进行优先注入

    @ImportResource 导入依赖的Spring配置

    @Value("${jdbc.url}") 可以使用${}动态获取配置参数

    @PropertySource 可导入properties配置文件

    @Qualifier 指定属性名称

    @Autowired和 @Resource

    @Autowired通过类型选择Bean,@Resource通过名称选择Bean。

    @Resource 是JSR-250注解

    @Primary 注入的优先级

    @PostConstruct 在初始化时执行的方法

    @PreDestroy 在销毁时执行的方法

    @Bean(initMethod = "init") @Bean(destroyMethod = "cleanup") 调用初始化和销毁的方法

    @Component, @Repository, @Service, @Controller,都是component,所以都可以被scan,一般分别用于标注po、dao、service和controller

    Spring MVC 提供@RestController ,它是@Controller 和 @ResponseBody的组合形式。

    @ComponentScan (basePackages = "") 扫描component

    扫描过滤器使用:@ComponentScan(basePackages = "org.example",  includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),  excludeFilters = @Filter(Repository.class))

      基于XML的配置可以用ClassPathXmlApplicationContext来获取Bean,基于Java的配置可以使用AnnotationConfigApplicationContext,它提供register()注册配置类。

     

    Spring的国际化

    当加载一个ApplicationContext时,它会自动搜索上下文中定义的MessageSource bean。bean必须有名称messageSource。如果找到了这样的bean,那么所有对方法的调用都将被委托给消息源。如果没有找到消息源,ApplicationContext将尝试寻找包含同名bean的父类。

    Spring提供了一些国际化的实现类,如:ResourceBundleMessageSource

    Spring事件

     Spring的事件处理是通过ApplicationEvent类和ApplicationListener接口实现的。如果一个实现ApplicationListener接口的bean被部署到上下文中,那么每次应用程序事件被发布到ApplicationContext时,就会通知bean。实际上这就是观察者模式

    可以在容器中配置监听器以启动容器

    1 <context-param>
    2     <param-name>contextConfigLocation</param-name>
    3     <param-value> /WEB-INF/applicationContext.xml</param-value>
    4 </context-param>
    5 
    6 <listener>
    7     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    8 </listener>

     Resource

    Spring提供了更加高级的资源文件处理方法,具体使用可查看API文档

    校验、数据绑定、类型转换

    Spring实现Validator接口可以非侵入式地校验!另外可以使用这两个类MessageCodesResolver,DefaultMessageCodesResolver来获取国际化错误信息。

    Spring提供DataBinder类实现数据绑定的功能。DataBinder和Validator组合,实现Spring的校验包。

    Bean包装器BeanWrapper,用来包装Bean,给Bean设置属性。

       BeanWrapper company = new BeanWrapperImpl(new Company()); 

    Spring实现Converter接口、ConverterFactory接口来实现类型转换。

    常用的转换器:

    通用转换器GenericConverter

    条件转换器ConditionalGenericConverter

    门面模式转换器ConversionService

    Spring实现Formatter接口、AnnotationFormatterFactory注解格式化接口,实现参数的格式化

    举例如下:

    @NumberFormat(style=Style.CURRENCY)

    @DateTimeFormat(iso=ISO.DATE)

    Spring提供注解格式化规则注册类:FormatterRegistry ,FormatterRegistrar

    Spring MVC中的格式化类是FormattingConversionServiceFactoryBean

    自定义校验注解实现ConstraintValidator

     


    AOP

    Aspect-Oriented Programming (AOP)OOP的关键模块单元是class,而AOP的关键模块单元是切面。AOP主要可以应用在事务,日志,安全等方面。

     一些概念

    • 切面Aspect:由切面和切点来定义出一个切面;
    • 连接点Join point:执行程序时的一个点,例如执行方法、处理异常、修改字段,这些点可以用来插入切面代码。在Spring AOP中,连接点就是执行方法。
    • 通知Advice:在特定连接点上采取的行动。
    • 切点Pointcut:表达式匹配通知所要织入的一个或多个连接点,是AOP的核心,Spring在默认情况下使用AspectJ切点表达式语言。
    • 引入Introduction:引入允许我们向现有的类添加新方法或属性。Spring AOP允许向任何被通知的对象引入新的接口(以及相应的实现)。(在AspectJ社区中,介绍称为跨类型声明。)
    • 目标对象Target object:被通知的对象,由于Spring AOP是动态代理实现的,所以这个对象将永远是一个代理对象。
    • AOP代理AOP proxy:一个由AOP框架创建的对象,以实现切面约定(通知方法执行等等)。在Spring框架中,AOP代理将是一个JDK动态代理或CGLIB代理。
    • 织入Weaving:把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:编译期(AspectJ)、类加载期、运行期(Spring AOP)。

    通知类型

    • 前置通知Before advice: 在目标方法被调用之前调用通知功能;
    • 返回通知After returning advice: 在目标方法成功执行之后调用通知;
    • 异常通知After throwing advice: 在目标方法抛出异常后调用通知;
    • 后置通知After (finally) advice: 在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
    • 环绕通知Around advice: 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

    AOP代理,默认使用标准的JDK动态代理,如果业务对象没有实现接口,则默认使用CGLIB

    对于JDK代理,只有在代理上调用的公共接口方法才能被拦截。使用CGLIB,在代理的public和protected方法调用将被拦截,甚至在必要时也可以使用包可见default的方法。

     

    简单使用说明

    1、声明切面@Aspect

    首先要启用@AspectJ注释

    @Configuration

    @EnableAspectJAutoProxy

    public class AppConfig{ }

    2、声明切点@Pointcut 

    例如:@Pointcut("execution(* transfer(..))")匹配任何名为'transfer'的方法的执行

     

    Spring的PCD支持

    Spring不支持AspectJ中的call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode,如果使用了会抛出IllegalArgumentException

    Spring AOP支持的PCD(切点指示符)

    execution - 匹配执行方法的连接点,Spring是最主要的PCD;

    within - 用类型匹配连接点;

    this - 指定的Bean引用类型;

    target - 指定的对象实例;

    args - 指定的类型参数;

    @target - 执行对象的类有指定类型的注释;

    @args - 实际参数的运行时类型有指定类型的注释;

    @within - 在具有指定注释的类型中限制匹配的连接点;

    @annotation - 限制的连接点有指定的注释;

      Spring AOP还支持bean(idOrNameOfBean)指定Beanid或名字。

    PCD组合使用

    切入点表达式可以通过'&&'''''来组合。也可以通过名称引用切入点表达式。举例如下:

    @Pointcut("execution(public * *(..))")

    private void anyPublicOperation() {}

    @Pointcut("within(com.xyz.someapp.trading..*)")

    private void inTrading() {}

    @Pointcut("anyPublicOperation() && inTrading()")

    private void tradingOperation() {}

    3、声明通知

    @Before

    @AfterReturning

    @AfterThrowing

    @After //After (finally) advice

    @Around

    Spring代理机制简单说明

      • final方法不能通知;
      • Spring 3.2以后不需要再引用CGLIB包;
      • Spring 4.0以后代理对象的构造器不再被调用两次,因为CGLIB代理实例将通过Objenesis来创建,只有在JVM不允许构造器绕过的情况下才能看到两次调用

    强制使用CGLIB方法:@EnableAspectJAutoProxy(proxyTargetClass = true)

     

     参考资料

    Spring Framework官方文档:https://docs.spring.io/spring/docs/5.0.2.RELEASE/spring-framework-reference/

    《Spring In Action》

  • 相关阅读:
    为什么单个TCP连接很难占满带宽
    上传NUnit的单元测试结果和OpenCover的单元测试覆盖率到SonarQube服务中
    使用Visual Studio Code Coverage和nunit上传单元测试覆盖率和单元测试结果到SonarQube上
    java安装1.8的经验和Error: Registry key 'SoftwareJavaSoftJava Runtime Environment'CurrentVers问题处理
    NSubstitute.Analyzers检测NSubstitute用法冲突
    在TeamCity中执行gtest单元测试
    iOS OpenGL ES入门
    iOS 基础知识
    【内推】字节跳动-头条小说&番茄小说
    iOS开发小记(十四)
  • 原文地址:https://www.cnblogs.com/bigshark/p/7979324.html
Copyright © 2020-2023  润新知