AOP应用 - 1

拦截的原理参考瑾华同学文章:

场景:让一个事务内的调用的第一个方法正常执行,第二个方法抛出异常, 数据都进行回滚,数据库里面没有数据

1、业务类:UserManager.java
package com.alibaba.spring;

public interface UserManager {

  public void addUser(String username, String password);
  
  public void deleteUser(int id);
  
  public void modifyUser(int id, String username, String password);
  
  public String findUserById(int id);
}
其实现类:UserManagerImpl.java
package com.alibaba.spring;

public class UserManagerImpl implements UserManager {

  public void addUser(String username, String password) {
    System.out.println("-------UserManagerImpl.addUser()----------");
  }

  public void deleteUser(int id) {
    System.out.println("-------UserManagerImpl.deleteUser()----------");
  }

  public String findUserById(int id) {
    System.out.println("-------UserManagerImpl.findUserById()----------");
    return null;
  }

  public void modifyUser(int id, String username, String password) {
    System.out.println("-------UserManagerImpl.modifyUser()----------");
  }
}
 
2、增加切入点SecurityHandler.java
package com.alibaba.spring;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
* 定义Aspect
* @author Administrator
*
*/

@Aspect
public class SecurityHandler {
  
  /**
    * 定义Pointcut,Pointcut的名称就是allAddMethod,此方法不能有返回值和参数,该方法只是一个
    * 标识
    *
    * Pointcut的内容是一个表达式,描述那些对象的那些方法(订阅Joinpoint)
    */

  @Pointcut("execution(* add*(..))")
  private void allAddMethod(){};
  
  @Pointcut("execution(* del*(..))")
  private void allAddMethod1(){};
  
  
  /**
    * 定义Advice,标识在那个切入点何处织入此方法
    */

  @Before("allAddMethod()")
  private void checkSecurity() {
    System.out.println("----------add()---------------");
  }
  
  @Before("allAddMethod1()")
  private void checkSecurity1() {
    Integer.parseInt("abc");
    System.out.println("----------delete()---------------");
  }
}
如果多个地方使用的织入点语法都是一样的(例如:都是 @Pointcut("execution(* add*(..))")),则可以将pointcut独立出来,使用上面的方式即可。
或者直接在Advice上面添加织入点,而不是单独的定义@Pointcut,如下:
  1. @Aspect 
  2. public class SecurityHandler { 
  3.  
  4.     /** 
  5.      * 定义Pointcut,Pointcut的名称就是allAddMethod,此方法不能有返回值和参数,该方法只是一个 标识 
  6.      *  
  7.      * Pointcut的内容是一个表达式,描述那些对象的那些方法(订阅Joinpoint) 
  8.      */ 
  9.     /*@Pointcut("execution(* add*(..))") 
  10.     private void allAddMethod() { 
  11.     }; 
  12.  
  13.     @Pointcut("execution(* del*(..))") 
  14.     private void allAddMethod1() { 
  15.     };*/ 
  16.  
  17.     /** 
  18.      * 定义Advice,标识在那个切入点何处织入此方法 
  19.      */ 
  20.     //@Before("allAddMethod()") 
  21.     @Before("execution(* com.alibaba.normandy.service.UserManager.add*(..))"
  22.     private void checkSecurity() { 
  23.         System.out.println("----------add()---------------"); 
  24.     } 
  25.  
  26.     //@Before("allAddMethod1()") 
  27.     @Before("execution(* del*(..))"
  28.     private void checkSecurity1() { 
  29.         System.out.println("----------delete()---------------"); 
  30.         Integer.parseInt("abc"); 
  31.     } 
 
3、打开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:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
                     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

  <aop:aspectj-autoproxy/>
  <bean id="securityHandler" class="com.alibaba.spring.SecurityHandler"/>                    
  <bean id="userManager" class="com.alibaba.spring.UserManagerImpl"/>
</beans>
4、pom.xml文件中的依赖
<dependency>
      <groupId>com.alibaba.external</groupId>
      <artifactId>test.junit</artifactId>
      <version>4.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.surefire</groupId>
      <artifactId>surefire-junit47</artifactId>
      <version>2.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.alibaba.external</groupId>
      <artifactId>sourceforge.spring.modules.aop</artifactId>
      <version>2.5.6</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba.external</groupId>
      <artifactId>sourceforge.spring.modules.aspects</artifactId>
      <version>2.5.6</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba.external</groupId>
      <artifactId>sourceforge.spring.modules.core</artifactId>
      <version>2.5.6</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba.external</groupId>
      <artifactId>sourceforge.spring.modules.context</artifactId>
      <version>2.5.6</version>
    </dependency>
5、执行结果: 调用第一个方法是正确执行,执行第二个方法的时候抛出异常
 
客户端调用:
package com.alibaba.spring;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

  public static void main(String[] args) {
    BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    UserManager userManager = (UserManager)factory.getBean("userManager");
    
    userManager.addUser("张三", "123");
    userManager.deleteUser(1);
  }
}
 
执行结果,抛出异常
----------add()---------------
-------UserManagerImpl.addUser()----------
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
  at java.lang.Integer.parseInt(Integer.java:447)
  at java.lang.Integer.parseInt(Integer.java:497)
 
 
【注意】
1、通知的种类,具体参考spring手册http://ajava.org/online/spring2.5/html/
6.2. @AspectJ支持
6.2.4.3. 抛出后通知(After throwing advice):抛出后通知在一个方法抛出异常后执行。使用 @AfterThrowing 注解来声明。http://ajava.org/online/spring2.5/html/aop-api.html#aop-api-pointcuts-aspectj
6.2.4.5. 环绕通知(Around Advice)
  1. @Around("com.xyz.myapp.SystemArchitecture.businessService()"
  2.  public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { 
  3. // start stopwatch 
  4. Object retVal = pjp.proceed(); 
  5. // stop stopwatch 
  6. return retVal; 
  7.  } 
 
2、CGLIB代理
如果使用实现了接口的类,则会自动使用jdk的动态代理来完成的;但是当用到一些类没有实现接口(比如某些服务类),则使用二进制操作的cglib来完成的,需要加入cglib的jar包。