Spring学习笔记(四):AOP

学习资料:http://how2j.cn/k/spring/spring-aop/89.html

又出现了新名词AOP, Aspect Oriented Program 面向切面编程,其实也是Spring的两大核心之一。另一个就是之前提到的控制反转IOC
PS:其实我一直不懂这些翻译为什么要弄得这么高大上,好好说人话不好吗。。。比如控制反转改叫自动生成对象之类的。。。
 

面向切面AOP:把一个程序中的一些周边功能(如日志、性能统计)独立开,这些功能不会干涉别的功能运行,专心完成自己的业务逻辑,独立开发完成,再在需要的时候将它们与核心业务编制在一起,这样的编程方式便称为AOP
PS:我自己瞎总结的定义

新建一个业务类ProdudcService.java,假装它有在忙什么事情

package com.how2java.service;
 
public class ProductService {
    public void doSomeService(){   
        System.out.println("doSomeService");         
    }    
}

修改一下测试用例TestSpring

package com.how2java.test;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.how2java.service.ProductService;
 
public class TestSpring {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
        ProductService s = (ProductService) context.getBean("s");
        s.doSomeService();
    }
}

让Spring生成一个ProductService的对象,并且调用一下其中的方法
但这里how2java上漏掉了一个步骤,它并没有在xml中配置关键字为"s"的bean,也没有在ProductService.java中作注解,因此直接运行会报找不到该bean的错误
因为这里讲的是普通方式(非注解),因此先回到在xml中写bean的方法

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context     
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
  
    <bean name="c" class="com.how2java.pojo.Category">
        <property name="name" value="yyy" />
    </bean>
 
    <bean name="p" class="com.how2java.pojo.Product">
        <property name="name" value="product1" />
        <property name="category" ref="c" />
    </bean>
     
    <bean name="s" class="com.how2java.service.ProductService">
    </bean>   
  
</beans>

现在可以顺利运行TestSpring了

以上都不重要,就当复习了一下IOC的内容

====================================================================================================

假设ProductService就作为这次的核心功能,我们需要在核心功能的执行前后分别输出日志,那日志就作为周边功能,在AOP的思想中,称其为切面(aspect)

那么创建一个日志切面类LoggerAspect.java

扫描二维码关注公众号,回复: 5352832 查看本文章
package com.how2java.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
 
public class LoggerAspect {
 
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("start log:" + joinPoint.getSignature().getName());
        Object object = joinPoint.proceed();
        System.out.println("end log:" + joinPoint.getSignature().getName());
        return object;
    }
}

导入支持AOP的lib中名为ProceedingJoinPoint的类
这个后面经常出现的joinPoint,好记一点就称为joinPoint连接点
比如这其中的方法log(ProceedingJoinPoint joinPoint)传入了一个连接点作为参数,方法体内

Object object = joinPoint.proceed();

这一行代表核心功能的执行过程,于是在这一行的前后,分别输出日志

System.out.println("start/end log:" + joinPoint.getSignature().getName());

大概意思是在这个连接点根据标记输出其名称
日志功能写完之后,我们再来修改xml
首先在之前的<bean>下面,添加LoggerAspect.java的<bean>,关键词id设定为loggerAspect

<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>

在此之后添加

    <aop:config>
        <aop:pointcut id="loggerCutpoint"
            expression=
            "execution(* com.how2java.service.ProductService.*(..)) "/>
             
        <aop:aspect id="logAspect" ref="loggerAspect">
            <aop:around pointcut-ref="loggerCutpoint" method="log"/>
        </aop:aspect>
    </aop:config>  

这里出现一个词pointcut切入点(那个Cutpoint是自己起的id无所谓),把这一段代码由中间的空行分为上下两部分看:

①上面部分为切入点的声明
指定了这个切入点的id关键词loggerAspect和产生这个切入点的触发条件,即expression=后面的内容

execution(* com.how2java.service.ProductService.*(..)) 

其中 *  作为常见的通配符,这段中第一次出现表示方法返回的类型可以是任意的,第二次出现表示这个方法可以是ProductService中的任意方法
(..) 在这里就表示方法的参数的数量和类型也是任意的
总结起来就是,当ProductService中的任意方法被执行时,就设置一个名为loggerCutPoint的切入点

②下面的部分为切面的声明
指定了这个切面的id关键词logAspect和引用的<bean> loggerAspect

            <aop:around pointcut-ref="loggerCutpoint" method="log"/>

这一句声明了这个切面的通知类型为around(在下面AOP术语中作解释),触发切面的切入点关键词为loggerCutPoint,method后面不重要,似乎可以加上参数列表

这样,在不修改TestSpring的情况下,再次执行,就会发现核心功能执行的前后分别输出了日志。
结果为:
start log:doSomeService
doSomeService
end log:doSomeService

how2java上对这一部分的讲解到此结束

====================================================================================================

再具体说一说AOP

AOP的过程分为两步
1.在作为切面的类中设置好连接点
2.在配置中声明切入点和切面
实现核心功能的类完全不需要修改,达到了当核心功能的某个方法触发了切入点时,切面也就被执行了。就像切面功能在监视核心功能的执行一样。

AOP术语

(1)通知

AOP的通知类型有五种:

  • Before——在方法调用之前调用通知
  • After——在方法完成之后调用通知,无论方法执行成功与否
  • After-returning——在方法执行成功之后调用通知
  • After-throwing——在方法抛出异常后进行通知
  • Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

 前四个好理解
最后一个around 表示切面在被监视的函数运行前后都会执行
下面是切面要执行的方法 log,方法有一个形参 joinPoint ,中间一句代表的就是被监视的程序运行,在被监视的程序运行时,可以替换其形参,这个是 around特殊的地方。如果被监视的程序,运行的时候输入的是一个"haha"作为实参,但是经过log方法之后,这个参数就被替换为"abc"了

(2)连接点

在执行正常的功能时,能够插入切面的点、可以作为切入点的点、备选点。
连接点可以是调用方法时、抛出异常时、甚至修改字段时,可以在这些点去执行切面。

(3)切面

切面是通知切入点的集合,通知和切入点共同定义了切面的全部功能——它是什么,在何时何处完成其功能。
声明切面:
在Spring中,切面就是一个包含通知和切入点的对象,是一个Bean,Bean的字段和方法就是该切面的状态和行为,还要通过配置,来指定切入点和通知实现
在xml中,切面使用<aop:aspect>标签指定,ref属性用来引用切面支持Bean。这个bean里面就是用来执行要做的辅助功能的。

(4)切入点

如果通知定义了“什么”和“何时”。那么切入点就定义了“何处”。切入点会匹配通知所要织入的一个或者多个连接点。通常使用明确的类或者方法来指定这些切入点。
作用:定义通知被应用的位置(在哪些连接点)
切入点的声明:
切入点在Spring也是一个Bean。
切入点的声明有三种定义方式:
1)在<aop:config>标签下使用<aop:pointcut>声明一个切入点Bean,该切入点可以被多个切面使用,对于需要共享使用的切入点最好使用该方式,该切入点使用id属性指定Bean名字,在通知定义时使用pointcut-ref属性通过该id引用切入点,expression属性指定切入点表达式

<aop:config>  
   <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/>  
   <aop:aspect ref="aspectSupportBean">  
      <aop:before pointcut-ref="pointcut" method="before"/>  
   </aop:aspect>  
</aop:config>  

2)在<aop:aspect>标签下使用<aop:pointcut>声明一个切入点Bean,该切入点可以被多个切面使用,但一般该切入点只被该切面使用,当然也可以被其他切面使用,但最好不要那样使用,该切入点使用id属性指定Bean名字,在通知定义时使用pointcut-ref属性通过该id引用切入点,expression属性指定切入点表达式

<aop:config>  
 <aop:aspect ref="aspectSupportBean">  
    <aop:pointcut id=" pointcut" expression="execution(* cn.javass..*.*(..))"/>  
    <aop:before pointcut-ref="pointcut" method="before"/>  
 </aop:aspect>  
</aop:config>

3)匿名切入点Bean可以在声明通知时通过pointcut属性指定切入点表达式,该切入点是匿名切入点,只被该通知使用

<aop:config>  
 <aop:aspect ref="aspectSupportBean">  
     <aop:after pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/>  
 </aop:aspect>  
</aop:config> 

(5)引入

引入允许我们向现有的类中添加方法或属性

 (6)织入

织入是将切面应用到目标对象来创建的代理对象过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期中有多个点可以织入 
1)编译期——切面在目标类编译时期被织入,这种方式需要特殊编译器。AspectJ的织入编译器就是以这种方式织入切面。
2)类加载期——切面在类加载到
3)JVM ,这种方式需要特殊的类加载器,他可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的 LTW 就支持这种织入方式
4)运行期——切面在应用运行期间的某个时刻被织入。一般情况下,在织入切面时候,AOP 容器会为目标对象动态的创建代理对象。Spring AOP 就是以这种方式织入切面。

至此,对AOP的初步认识告一段落,边学习边总结成博客的方式帮助我比较清晰地梳理了一遍AOP的配置过程,最后的总结部分参考另一篇博文:《Spring之AOP在XML中的配置方法》,感谢其作者以及how2java的站长。

猜你喜欢

转载自blog.csdn.net/HiflyMaple/article/details/87068116
今日推荐