Spring 之扩展点

353 阅读7分钟

Spring 它提供了很多实用的功能和方便的编程方式。 Spring 框架的强大之处在于它的可扩展性,通过扩展 Spring 框架,可以使其满足更多的业务需求。

Spring 框架的扩展可以分为两种类型,一种是基于 AOP 的扩展,另一种是基于 IoC 的扩展。下面将分别介绍这两种扩展方式,并结合源码分析说明具体实现方法。

基于AOP的扩展

AOP(Aspect Oriented Programming)是一种编程范式,它将横切关注点(Cross-Cutting Concerns)从主逻辑中分离出来,在运行时动态地将这些关注点织入到程序中。 Spring 框架的AOP模块提供了一种灵活的方式来实现AOP编程,在 Spring 中,AOP 实现的关键在于切面(Aspect)和通知(Advice)。

切面(Aspect)

切面是一种横切关注点的模块化实现,它是AOP编程的核心概念。在 Spring 中,切面通常是一个Java类,其中包含了一些通知(Advice)和切点(Pointcut)定义。

通知是切面中的方法,它定义了在程序执行过程中需要执行的额外逻辑。

切点是一组连接点的集合,它定义了哪些连接点将被拦截。在 Spring 中,切点通常使用AspectJ切点表达式来定义。AspectJ切点表达式可以匹配方法调用、字段访问等连接点。

通知(Advice)

通知是切面中的方法,它定义了在程序执行过程中需要执行的额外逻辑。通知可以分为以下几种类型:

前置通知(Before Advice):在目标方法执行之前执行。

后置通知(After Returning Advice):在目标方法执行之后执行,如果目标方法抛出异常,则不会执行。

后置异常通知(After Throwing Advice):在目标方法抛出异常之后执行。

环绕通知(Around Advice):在目标方法执行之前和之后执行,可以控制目标方法的执行过程。

Spring 框架的 AOP 模块提供了很多通知的实现,如 MethodBeforeAdviceAfterReturningAdviceThrowsAdvice等。如果需要实现自定义的通知,可以通过实现Advice接口来实现。

下面以一个简单的例子来说明如何使用AOP扩展 Spring 框架。

首先定义一个切面类,该切面用于统计方法的执行时间:

@Aspect
public class PerformanceAspect {

    @Around("execution(\* com.example.service.*.*(..))")
    public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        System.out.println(joinPoint.getSignature().getName() + " takes " + (end - start) + "ms");
        return result;
    }
}

在该切面中,使用@Aspect注解标识该类为切面,并使用@Around注解定义一个环绕通知。该通知拦截com.example.service包中所有方法的执行,并在方法执行前后打印方法的执行时间。

然后在 Spring 配置文件中定义该切面:

<bean id="performanceAspect" class="com.example.aspect.PerformanceAspect"/>
<aop:config>
    <aop:aspect ref="performanceAspect">
        <aop:pointcut expression="execution(* com.example.service.*.*(..))" id="serviceMethod"/>
        <aop:around method="measureTime" pointcut-ref="serviceMethod"/>
    </aop:aspect>
</aop:config>

在该配置文件中,首先定义了一个名为 performanceAspect的bean,该bean是PerformanceAspect类的实例。

然后使用aop:config和aop:aspect标签定义一个切面,使用ref属性引用performanceAspect bean。

在切面中定义了一个名为serviceMethod的切点,用于匹配com.example.service包中的所有方法。然后在切面中使用aop:around标签定义一个环绕通知,使用method属性引用PerformanceAspect类中的measureTime方法,并使用pointcut-ref属性引用serviceMethod切点。

最后,在需要使用该切面的类中,只需在类上添加@AspectJ注解即可:

@Service
@AspectJ
public class UserServiceImpl implements UserService {
// ...
}

在该类中,使用@Service注解标识该类为 Spring 的服务类,使用@AspectJ注解引用PerformanceAspect切面。

基于IoC的扩展

IoC(Inversion of Control)是一种编程思想,它将对象的创建和依赖关系的管理交给容器来完成。 Spring 框架的IoC容器是其核心部分,它管理着应用程序中的所有对象,并负责将它们组装成一个完整的应用程序。

Spring 框架的IoC容器提供了很多扩展点,通过扩展这些扩展点,可以实现很多自定义的功能。

BeanFactoryPostProcessor

BeanFactoryPostProcessor 是 Spring 框架中的一个扩展点,它在 Spring IoC容器实例化所有的bean之后执行。通过实现 BeanFactoryPostProcessor 接口,可以在bean实例化之前修改bean的属性或者添加新的bean定义。

下面以一个简单的例子来说明如何使用 BeanFactoryPostProcessor 扩展 Spring 框架。

首先定义一个 BeanFactoryPostProcessor 实现类,该类用于将所有的bean的scope属性设置为prototype:

public class PrototypeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String\[] beanNames = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            if (beanDefinition.getScope().equalsIgnoreCase("singleton")) {
                beanDefinition.setScope("prototype");
            }
        }
    }
}

在该类中,实现了BeanFactoryPostProcessor接口,并重写了 postProcessBeanFactory 方法。在该方法中,遍历所有 的bean 定义,将 scope 属性为singleton 的 bean 的 scope 属性设置为 prototype。

然后在 Spring 配置文件中定义该BeanFactoryPostProcessor:

<bean class="com.example.postprocessor.PrototypeBeanFactoryPostProcessor"/>

在该配置文件中,定义了一个 PrototypeBeanFactoryPostProcessor 类的实例。

最后,在需要使用该bean的类中,只需将该bean定义为singleton即可:

<bean id="myService" class="com.example.service.MyServiceImpl" scope="singleton"/>

在该bean定义中,将该bean的scope属性设置为singleton。

BeanPostProcessor

BeanPostProcessor 是 Spring 框架中的另一个扩展点,它在每个bean实例化之后执行。通过实现BeanPostProcessor 接口,可以在bean实例化之后对bean进行一些额外的处理。

下面以一个简单的例子来说明如何使用 BeanPostProcessor 扩展 Spring 框架。

首先定义一个BeanPostProcessor实现类,该类用于将所有的bean的name属性转换为大写:

public class UpperCaseBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        BeanWrapper beanWrapper = new BeanWrapperImpl(bean);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            Object value = beanWrapper.getPropertyValue(propertyDescriptor.getName());
            if (value instanceof String) {
                String strValue = (String) value;
                beanWrapper.setPropertyValue(propertyDescriptor.getName(), strValue.toUpperCase());
            }
        }
        return beanWrapper.getWrappedInstance();
    }

}

在该类中,实现了 BeanPostProcessor 接口,并重写了 postProcessAfterInitialization 方法。在该方法中,获取bean的所有属性,并将字符串类型的属性值转换为大写。

然后在 Spring 配置文件中定义该BeanPostProcessor:

<bean class="com.example.postprocessor.UpperCaseBeanPostProcessor"/>

在该配置文件中,定义了一个UpperCaseBeanPostProcessor类的实例。

最后,在需要使用该bean的类中,只需定义一个带有字符串类型属性的bean即可:

<bean id="myService" class="com.example.service.MyServiceImpl">
    <property name="name" value="test"/>
</bean>

在该bean定义中,定义了一个name属性,该属性的值为test。

源码解析

AOP关键类

Spring 框架的AOP模块的核心是 ProxyFactoryBean 类,该类是一个 FactoryBean,用于创建AOP代理对象。在创建AOP代理对象时,ProxyFactoryBean 会根据配置文件中的信息,将切面和目标对象组装成一个代理对象。

在ProxyFactoryBean中,有两个重要的成员变量:TargetSource和AdvisedSupport

TargetSource 是目标对象的封装类,它用于获取目标对象。在ProxyFactoryBean中,可以通过setTargetSource方法设置TargetSource。

AdvisedSupport 是AOP代理的配置类,它包含了AOP代理的所有配置信息。在ProxyFactoryBean中,可以通过setAdvisedSupport方法设置AdvisedSupport。

在创建AOP代理对象时,ProxyFactoryBean会根据AdvisedSupport中的信息,创建一个AopProxy对象,并通过AopProxy对象获取代理对象。

IoC关键类

Spring 框架的IoC容器的核心是 BeanFactory 接口,它定义了IoC容器的基本功能。在 Spring 中,有很多实现了BeanFactory接口的容器,如XmlBeanFactory、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等。

在BeanFactory接口中,有两个重要的方法:getBean和containsBean。getBean方法用于获取指定名称的bean实例,containsBean方法用于判断指定名称的bean是否存在。

在 Spring 的IoC容器中,bean的生命周期包括:实例化、属性注入、初始化和销毁。在bean实例化时,IoC容器会根据bean的定义信息,使用Java反射机制创建bean的实例。在属性注入时,IoC容器会将bean的属性值注入到bean实例中。在初始化时,IoC容器会调用bean的初始化方法。在销毁时,IoC容器会调用bean的销毁方法。

在 Spring 中,可以通过实现BeanPostProcessor接口,来扩展bean的生命周期。BeanPostProcessor接口包含了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别在bean的初始化前和初始化后执行。

Spring 框架的扩展性非常强大,通过AOP和IoC的扩展,可以实现很多自定义的功能。在实现扩展时,需要了解 Spring 框架的核心功能和扩展点,以便更好地实现自定义功能。