AOP的实现

2018-06-29 06:16:12来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

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 }

 

 



 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:RabbitMQ入门:远程过程调用(RPC)

下一篇:微服务下使用网关 Spring Cloud Gateway