欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AOP的实现

程序员文章站 2022-07-05 09:48:01
AOP基于xml配置方式实现 Spring基于xml开发AOP 定义目标类(接口及实现类) 定义切面类 配置spring核心配置文件 测试 AOP的切入点表达式execution 表达式的写法:修饰关键词 返回值类型 包名.类名.方法名(..) 修饰关键词:protected , public , ......

AOP基于xml配置方式实现

Spring基于xml开发AOP

 

  •     定义目标类(接口及实现类)
 1 /**
 2  * 目标类
 3  */
 4 public interface UserService {
 5     //业务方法
 6     public void getById();    
 7     public void add();    
 8     public void delete();    
 9     public String update();
10     public void batch();    
11 }

 

 1 public class UserServiceImpl implements UserService {
 2     @Override
 3     public void getById() {
 4         System.out.println("UserServiceImpl类的getById方法被调用...");
 5         //抛出异常
 6         System.out.println(1/0);
 7     }
 8     @Override
 9     public void add() {
10         System.out.println("UserServiceImpl类的add方法被调用...");
11     }
12     @Override
13     public void delete() {
14         System.out.println("UserServiceImpl类的delete方法被调用...");
15 //        System.out.println(1/0);
16     }
17     @Override
18     public String update() {
19         System.out.println("UserServiceImpl类的update方法被调用...");
20 //        System.out.println(1/0);
21         return "update的返回值";
22     }
23     @Override
24     public void batch() {
25         System.out.println("UserServiceImpl类的batch方法被调用...");
26     }
27 }

 

  •     定义切面类

 

 1 /**
 2  * 切面
 3  */
 4 public class UserAspect {
 5     //前置通知
 6     public void beforeAdvice(JoinPoint jp){
 7         //获取切入点的方法的名称
 8         String name = jp.getSignature().getName();
 9         System.out.println("前置通知切入点的方法的名称:"+name);                
10         System.out.println("前置通知");
11     }
12     
13     //后置通知
14     public void afterAdvice(JoinPoint jp){
15         //获取切入点的方法的名称
16         String name = jp.getSignature().getName();
17         System.out.println("后置通知切入点的方法的名称:"+name);                
18         System.out.println("后置通知");
19     }
20         
21     //返回通知
22     //可以获取object类型的返回值
23     //注意:result名称必须和配置文件中的returning的名称保持一致
24     public void afterReturningAdvice(JoinPoint jp,Object result){
25         //获取切入点的方法的名称
26         String name = jp.getSignature().getName();
27         System.out.println("返回通知切入点的方法的名称:"+name);        
28         //打印返回值
29         System.out.println(result);
30         System.out.println("返回通知");
31     }
32         
33     //异常通知
34     public void exceptionAdvice(JoinPoint jp,Exception ex){
35         //获取切入点的方法的名称
36         String name = jp.getSignature().getName();
37         System.out.println("异常通知切入点的方法的名称:"+name);    
38         //打印异常信息
39         System.out.println(ex.getMessage());
40         System.out.println("异常通知");
41     }
42         
43     //环绕通知
44     //必须有一个参数:ProceedingJoinPoint
45     //有返回值:Object
46     public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
47         System.out.println("环绕通知前");
48         //执行业务逻辑方法
49         Object result = pjp.proceed();        
50         //获取切入点的方法的名称
51         String name = pjp.getSignature().getName();
52         System.out.println("环绕通知切入点的方法的名称:"+name);        
53         System.out.println("环绕通知后");
54         return result;
55     }
56 }

 

 

  •     配置spring核心配置文件

 

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">    
    <!-- 把切面和业务对象放到容器中 -->
    <bean id="userAspect" class="spring.aop.xml.UserAspect"></bean>
    <bean id="userServiceImpl" class="spring.aop.xml.UserServiceImpl"></bean>
        
    <!-- 配置aop的实现 -->
    <aop:config>
        <!-- 
            切入点的配置
            expression(切入点表达式):指定切哪个方法
         -->
         <aop:pointcut expression="execution(* *.add*(..))" id="pointCut01"/>         
         <aop:pointcut expression="execution(* *.delete*(..))" id="pointCut02"/>
         <aop:pointcut expression="execution(* *.update*(..))" id="pointCut03"/>
         <aop:pointcut expression="execution(* *.getById*(..))" id="pointCut04"/>
         <aop:pointcut expression="execution(* *.batch*(..))" id="pointCut05"/>
                 
         <!-- 切面的配置 -->
         <aop:aspect ref="userAspect">
             <!-- 
                 前置通知的配置:在pointCut01切入点的位置添加beforeAdvice的通知
              -->
              <aop:before method="beforeAdvice" pointcut-ref="pointCut01"/>              
              
             <!-- 
                 后置通知的配置:在pointCut02切入点的位置添加afterAdvice的通知
              -->
              <aop:after method="afterAdvice" pointcut-ref="pointCut02"/>
                            
             <!-- 
                 返回通知的配置:在pointCut03切入点的位置添加afterReturningAdvice的通知
                 returning:返回值的参数名称
              -->
              <aop:after-returning method="afterReturningAdvice" pointcut-ref="pointCut03" returning="result"/>
                            
             <!-- 
                 异常通知的配置:在pointCut04切入点的位置添加exceptionAdvice的通知
                 throwing:抛出异常的名称,必须和通知上的形参名称一致
              -->
              <aop:after-throwing method="exceptionAdvice" pointcut-ref="pointCut04" throwing="ex"/>
                            
             <!-- 
                 环绕通知的配置:在pointCut05切入点的位置添加aroundAdvice的通知
              -->
              <aop:around method="aroundAdvice" pointcut-ref="pointCut05"/>
         </aop:aspect>    
    </aop:config>    
</beans>

 

 

  •     测试
 1 //使用xml方式配置AOP
 2 @RunWith(SpringJUnit4ClassRunner.class)
 3 @ContextConfiguration("classpath:applicationContext-aop-xml.xml")
 4 public class XmlAopTest {
 5     //注入业务对象
 6     @Autowired
 7     private UserService userService;
 8     
 9     //测试AOP,对切入点进行增强
10     @Test
11     public void test(){
12         //前置通知的测试
13 //        userService.add();
14                 
15         //后置通知的测试(无论有没有异常都会执行)
16 //        userService.delete();
17                 
18         //返回通知的测试(只有业务方法正常执行时,才会执行的通知)
19 //        userService.update();
20                 
21         //异常通知的测试
22 //        userService.getById();
23                 
24         //环绕通知的测试
25         userService.batch();
26     }
27 }

 


 

    

AOP的切入点表达式execution

    表达式的写法:修饰关键词   返回值类型   包名.类名.方法名(..)

修饰关键词:protected ,  public  ,  private ….一般都省略不写

返回值类型:一般返回值类型用 * 号代替,表示任意的返回值都可以

方法名中的两个点:表示所带的参数的个数。

 

        举例如下:

  任意以public修饰的方法都可以

execution(public * *(..))

 

  任何以set开头的方法都可以

execution(* set*(..))

 

  任何在AccountService 这个接口下面的方法都可以

execution(* com.xyz.service.AccountService.*(..))

 

  任何在com.xyz.service 这个包下面的所有类的所有方法都可以

execution(* com.xyz.service.*.*(..))

 

  任何在com.xyz.service 这个包以及整个包下面的所有子包的所有类的所有方法都可以

execution(* com.xyz.service..*.*(..))

  ..代表这个包及这个包下面的所有子包

 

 

 

Spring中的5种通知类型及参数

 

      •   前置通知:在目标方法开始之前进行执行的通知

            参数:JoinPoint

      •   后置通知:在目标方法执行之后,无论是否发生异常,都进行执行的通知

            参数:JoinPoint

      •   返回通知:在目标方法正常结束时,才执行的通知

            参数:JoinPoint,Object

      •   异常通知:在目标方法出现异常时才会进行执行的通知

            参数:JoinPoint,Exception

      •   环绕通知:在目标方法执行之前、 之后都会执行的通知

            参数:ProceedingJoinPoint(必须有)

 

 

 

 


 


 

 

AOP基于注解方式的实现

 

xml配置中开启spring注解扫描及开启aop注解的自动代理

    

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
    <!-- 开启spring的注解驱动 -->
    <context:component-scan base-package="spring.aop.annotation"></context:component-scan>
    
    <!-- 开启aop的自动代理:使用注解实现AOP,这个必须配置 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

 

 

    定义接口及接口实现类

    

 此处省略,同xml配置方式相同

 

 

    通过注解的方式定义切面类

 

 1 /**
 2  * 切面
 3  */
 4 @Component
 5 @Aspect
 6 public class UserAspect {
 7     //前置通知
 8     @Before(value = "execution(* *.add*(..))")
 9     public void beforeAdvice(JoinPoint jp){
10         String name = jp.getSignature().getName();
11         System.out.println("前置通知的切入点的方法名:" + name);        
12         System.out.println("前置通知...");
13     }
14         
15     //后置通知
16     @After(value = "execution(* *.delete*(..))")
17     public void afterAdvice(JoinPoint jp){
18         String name = jp.getSignature().getName();
19         System.out.println("后置通知的切入点的方法名:" + name);        
20         System.out.println("后置通知...");
21     }
22         
23     //返回通知
24     @AfterReturning(value = "execution(* *.update*(..))",returning="result")
25     public void afterReturningAdvice(JoinPoint jp,Object result){
26         String name = jp.getSignature().getName();
27         System.out.println("返回通知的切入点的方法名:" + name);        
28         System.out.println(result);        
29         System.out.println("返回通知...");
30     }
31         
32     //异常通知
33     @AfterThrowing(value = "execution(* *.getById*(..))",throwing="ex")
34     public void exceptionAdvice(JoinPoint jp,Exception ex){
35         String name = jp.getSignature().getName();
36         System.out.println("异常通知的切入点的方法名:" + name);        
37         System.out.println(ex.getMessage());
38         System.out.println("异常通知...");
39     }
40         
41     //环绕通知
42     @Around(value = "execution(* *.batch*(..))")
43     public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
44         System.out.println("环绕通知前...");        
45         Object result = pjp.proceed();
46         String name = pjp.getSignature().getName();
47         System.out.println("环绕通知的切入点的方法名:" + name);        
48         System.out.println("环绕通知后...");        
49         return result;
50     }
51 }

 

    测试

 

 1 /**
 2  * 注解形式的AOP的单元测试类
 3  */
 4 @RunWith(SpringJUnit4ClassRunner.class)
 5 @ContextConfiguration("classpath:applicationContext-aop-annotation.xml")
 6 public class AnnotationAopTest {
 7     @Autowired
 8     private UserService userService;
 9     
10     @Test
11     public void test(){
12         //注解:前置通知的测试
13 //        userService.add();
14         
15         //后置通知的测试
16 //        userService.delete();
17         
18         //返回通知
19 //        userService.update();
20         
21         //异常通知
22 //        userService.getById();
23         
24         //环绕通知
25         userService.batch();
26     }
27 }