Spring学习笔记(二)后处理器与AOP

1.后处理器

后处理器是对IOC容器功能的扩展。按我的理解,扩展的原理是在某动作结束后再次调用指定的方法进行扩展处理。有点类似于AOP。

后处理器分两种:Bean后处理器容器后处理器

1.1 Bean后处理器

Bean后处理器会在Bean实例化结束后(注意,该实例化应该是指Bean类的实例化,还没有进行Spring中的注入等操作,并不是Spring最终返回的Bean),对其进行近一步的增强处理,例如返回一个Bean的代理类。

Bean后处理器需要实现BeanPostProcessor接口,该接口包含的postProcessBeforeInitializationpostProcessAfterInitialization分别在Bean初始化之前和之后回调。

Spring学习笔记(二)后处理器与AOP_第1张图片

如上图,增强处理与 init-methodInitializingBeandestory-methodDisposableBean的执行顺序,增强处理永远在 最外围的

下面给出Bean后处理器的Demo:

  • 首先实现创建一个实现BeanPostProcessor的后处理器类
/**
 * Bean后处理器Demo类,该处理类会对容器里面的所有Bean进行后处理
 * @author wangsz
 */
public class BeanPostProc implements BeanPostProcessor{
    /**
     * 在Bean初始化后,对容器中的bean进行后处理,返回处理后的bean
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean后处理器在["+beanName+"]初始化后对其进行增强处理");
        if(bean instanceof Person){
            ((Person) bean).setName("akq");
        }
        //该bean可以与旧bean截然不同,如返回一个该Bean的代理类
        return bean;
    }
    /**
     * 在Bean初始化前,对容器中的bean进行后处理,返回处理后的bean
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean后处理器在["+beanName+"]初始化前对其进行增强处理");
        //该bean可以与旧bean截然不同
        return bean;
    }

}
  • 然后在Spring配置文件中加上这个Bean。这样,该后处理类就会对容器里面的所有Bean进行后处理。

    

1.2 容器后处理器

Bean后处理器是对Bean实例化后进行后处理的,而容器后处理器,顾名思义,就是对Spring容器进行后处理,通常用于Spring容器实例化Bean之前,读取配置文件元数据,对其进行修改。

容器后处理器需要实现BeanFactoryPostProcessor接口,重写该接口包含的postProcessBeanFactory方法。

Spring中已提供了几个常用的容器后处理器:

  • PropertyPlaceholderConfigurer:属性占位符配置器
  • PropertyOverrideConfigurer:重写占位符配置器
  • CustomAutowireConfigurer:自定义自动装配的配置器
  • CustomScopeConfigurer:自定义作用域的配置器

下面给出容器后处理器的Demo:

  • 首先实现创建一个实现BeanFactoryPostProcessor的容器后处理器类
/**
 * 容器后处理器Demo类,在容器实例化bean之前,读取配置文件的元数据,并修改
 * @author wangsz
 */
public class BeanFactoryProc implements BeanFactoryPostProcessor{

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("Spring的容器是:"+beanFactory);
        System.out.println("容器后处理器并没有对BeanFactory的初始化做修改");
    }

}
  • 然后在Spring配置文件中加上这个Bean。

    

2.AOP

Aspect Orient Pragramming:面向切面编程。

2.1 AOP的概念

这个术语不太好理解,下面我们用图来一步步阐述它演变的过程。

现在有三个方法,我要在里面添加同一段代码,比较low的方式,是将同一段代码复制粘贴三遍:


Spring学习笔记(二)后处理器与AOP_第2张图片

改进的方式是,我把这段代码抽离到一个方法中,然后在三个方法中手动调用这个抽离方法:


Spring学习笔记(二)后处理器与AOP_第3张图片

但是上面的方法仍然有些不方便。如果不是三个方法,是十个,二十个,那一个个的在里面写方法的调用很麻烦。而且,如果增加需求,例如再次为方法一、二、三增加日志打印,再次为方法一、二、三增加参数检验,那么每次都得加个抽离方法,然后在方法一二三里面加调用。

AOP就是针对这些不便的进一步优化。我们将方法一二三看成一个切面,然后在这个切面上进行增强处理。不需要方法一二三手动调用抽离方法,抽离方法“自动”进行了调用:


Spring学习笔记(二)后处理器与AOP_第4张图片

通过上面的图我们可以进行一个总结:AOP其实就是代理模式的一种体现,将程序运行过程看成一个切面,在切面上进行公共的处理。

2.2 AOP的应用

现在版本的Spring的AOP一般都是整合的AspectJ实现的。AspectJ框架是最早,功能比较强大的AOP实现之一,Spring中只是用到了它部分的功能,有兴趣的朋友可以百度了解一下。

值得注意的是,AspectJ和Spring的实现方式的不同,AspectJ是编译时对目标类进行增强(反编译目标类可发现多了内容),而Spring是生成一个代理类进行增强。

下面我们开始在Spring中配置AOP

  • 首先在Maven中增加AspectJ的支持jar包,注意版本要和jdk符合,我之前用的jar包过老,导致aop测试时莫名出现一系列找不到包的异常。

        
            org.aspectj
            aspectjrt
            1.8.9
        

        
            org.aspectj
            aspectjweaver
            1.8.9
        
  • 在Spring的配置文件中增加内容:


     
    
     
    
    
 

如果不采用Spring的XML Schema的方法,也可以去除对应配置,增加:


    
  • 然后我们创建一个Aspect的测试Demo类:
@Aspect //声明该类为切面类,在spring配置中加入该类的bean,ApplicationContext会自动加载,将该Bean作为切面处理
public class AspectDemo {
    /**
     * 在方法执行前进行调用,value指定切入点
     */
    @Before(value = "execution(* test.wsz.spring.bean..StoneAxe.useAxe(..))")
    public void beforeTest() {
        System.out.println("-----before stoneAxe.useAxe()-----");
    }

    /**
     * 在方法正常执行完成后进行调用,value指定切入点,也可用pointcut。returning指定返回形参
     */
    @AfterReturning(returning = "returnValue", pointcut = "execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public void afterReturnTest(Object returnValue) {
        System.out.println("-----after stoneAxe.useAxe()-----");
        System.out.println("返回值是:" + returnValue);
    }

    /**
     * 无论方法是否正常结束,只要完成后调用该方法
     */
    @After(value = "execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public void afterTest() {
        System.out.println("方法执行完成,无论是正常完成还是异常终止执行");
    }

    /**
     * 在方法异常后调用,但并不能像catch一样捕获异常,异常仍然会抛给上级进行处理
     * 
     * @param e
     *            方法中抛出的异常
     */
    @AfterThrowing(throwing = "e", pointcut = "execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public void afterThrowingTest(Throwable e) {
        System.out.println("方法抛出异常:" + e.getMessage());
    }

    /**
     * 功能较强的增强方法,类似before和afterReturning的集合
     * @param pjp 方法信息对象
     * @return
     * @throws Throwable
     */
    @Around("execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public Object aroundTest(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("-----around-------");
        System.out.println("方法执行前");
        Object object = pjp.proceed();
        System.out.println("方法执行后");
        return object;
    }
}

这个Demo类中演示了几种切面的注解方法。xml的配置方法就不贴出来了,可自行百度。

为了方便,我们还可设定一个切点,然后进行引用:

// 定义一个切入点,该切入点方法体中的代码无效
    @Pointcut("execution(* test.wsz.spring.bean..IronAxe.useAxe(..))") // 方法体中的代码无效
    public void mypointcut() {
        System.out.println("-----pointcout-----");
    }

    /**
     * 在方法执行前进行调用
     */
    @Before(value = "mypointcut()")
    public void before() {
        System.out.println("-----before-----");
    }

注意,几种切面方法的执行顺序如下:

最后补充下切面execution的规则
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
举一个例子说明:

Spring学习笔记(二)后处理器与AOP_第5张图片

你可能感兴趣的:(Spring学习笔记(二)后处理器与AOP)