Spring AOP使用注解创建切面

Spring使用xml创建切面和使用AspectJ创建切面有所不同的。下面使用AspectJ来创建切面。

一、先举个栗子

所谓aop就是面向切面编程,使其他类共享相同的行为,比如日常开发中使用日志模块,总不能每次使用都new一个出来,这时候就要用到aop,我们先管他三七二十一,定义一个切面类,类中写方法,然后把方法直接搞到其他方法内部。先举个栗子,比如我们经常逛b站鬼畜区,什么Van,香蕉君啥的,哲学你们都懂得。那么,栗子来了,Van口头禅FQ,但是Banana君等等也想要说FQ,但是Banana没有自己不会说FQ,他只会跳舞和摔跤,Banana想要在跳舞和摔跤时候也说FQ,没法只能由Van来说FQ。

准备工作,先把pom写好


    4.0.0
    com.yoyiyi
    TestAOP
    1.0-SNAPSHOT
    jar
   TestAOP
    http://maven.apache.org
    
        UTF-8
        1.8
        4.1.5.RELEASE
    
    
        
            org.springframework
            spring-context
            ${spring-framework.version}
        
        
        
            org.springframework
            spring-aop
            ${spring-framework.version}
        
        
        
            org.aspectj
            aspectjrt
            1.8.6
        
        
            org.aspectj
            aspectjweaver
            1.8.5
        
        
        
            org.springframework
            spring-test
            ${spring-framework.version}
        
        
            junit
            junit
            4.11
        
        
            commons-io
            commons-io
            2.3
        
        
            javax.annotation
            jsr250-api
            1.0
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                2.3.2
                
                    ${java.version}
                    ${java.version}
                
            
        
    



二、定义切面

既然Banana不会说FQ,Van会,我们先把Van搞过来,就是定义一个切面。

@Aspect  //表明是一个切面
@Component   //表明是一个组件
public class Van {
    
    //定义公共切点,这样不用以后每一次都写一遍 切入到Banana摔跤方法 
    @Pointcut("execution(* com.yoyiyi.java.Banana.摔跤())")
    public void poincut() {
    }
    
    //前置通知     在Banan摔跤之前说FQ
    @Before("poincut()")
    public void 前置sayFQ() {
        System.out.println("前置FQ");
    }
    
    //后置通知    在Banana摔跤之后说FQ
    @After("poincut()")
    public void 后置sayFQ() {
        System.out.println("后置FQ");
    }
    
    //异常通知   Banana 摔跤发生异常说FQ 比如没体力
    @AfterThrowing("poincut()")
    public void 异常sayFQ() {
        System.out.println("异常FQ");
    }
    
    //返回后通知 Van想知道Banana跳舞之后在干啥 Van不知道,所以Object
     @AfterReturning(returning = "rvt",
            pointcut = "execution(* com.yoyiyi.java.Banana.跳舞())")
    public void 返回通知sayFQ(Object rvt) {
        System.out.println("返回通知FQ,发现蕉说" + rvt.toString());
    }

    //环绕通知   Van在Banana摔跤之前之后都说FQ
    @Around("poincut()")
    public void 环绕sayFQ(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("前面FQ");
            joinPoint.proceed();//执行方法
            System.out.println("后面FQ");
        } catch (Throwable throwable) {
            System.out.println("异常FQ");
            throwable.printStackTrace();
        }
    }

二、目标对象

这个目标对象就是Banana

@Component   //Banana组件
public class Banana {

    public void 摔跤() {
        //异常通知
        //int i = 1;
        //i=i/0;
        System.out.println("摔跤");
    }

    public String 跳舞() {
        return "我还要跳舞";
    }
}

三、装配

既然Van和Banana都有了,总有个地方要把他们弄到一起吧,b站通常在鬼畜区就可以了,也就是装配到Spring的bean中。

@Configuration            //相当于一个xml配置文件
@EnableAspectJAutoProxy  //表示开启AOP代理自动配置
@ComponentScan(basePackages = "com.yoyiyi.java")  //扫描com.yoyiyi.java 注解
public class GuiChuConfig { //配置

}

四、测试

我们写一个测试类看看效果

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = GuiChuConfig.class)  //找到鬼畜区配置类
public class TestBanana {
    //测试一个通知时候 需要把Van中其他通知注释掉
    @Autowired    自动装配
    Banana mBanana;
    @Test
    public void 前置() throws Exception {
        mBanana.摔跤();
        //控制台:
        //前置FQ
        //摔跤
    }

    @Test
    public void 后置() throws Exception {
        mBanana.摔跤();
        //控制台:
        //后置FQ
        //摔跤
    }

    @Test
    public void 环绕() throws Exception {
        mBanana.摔跤();
         //控制台:
         //前面FQ
         //摔跤
         //后面FQ
    }

    @Test
    public void 返回() throws Exception {
        mBanana.跳舞();
        //控制台:
        //返回通知FQ,发现蕉说我还要跳舞
    }

    @Test
    public void 异常() throws Exception {
        mBanana.摔跤();
        //控制台:
        //异常FQ
        //摔跤
    }

五、目标方法带有参数

举个栗子:

Banana要和Bili摔跤,所以要在摔跤方法中传入Bili大名,Van说FQ并且要知道Bili和Banana摔跤多少次。

在Banana中添加一个带有参数摔跤方法

 public void 摔跤(String name) {
        System.out.println("摔跤");
    }

在Van中添加一个前置通知

  @Before("execution(* com.yoyiyi.java.Banana.摔跤(String)) && args(name))")
  public void 带统计前置sayFQ(String name) {
        System.out.println("前置FQ");
        if ("bili".equals(name)) {
            System.out.println("统计" + 1000);
        }
    }

在测试类中添加测试方法

@Test
public void 统计() throws Exception {
    mBanana.摔跤("bili");
    //控制台:
    //前置FQ
   // 统计摔跤次数1000
    //摔跤
}

六、自定义注解

在现实开发中日志文件带有记录基本增删改的功能,但是表信息,权限等难记录就要用到自定义注解

举个栗子:

鬼畜视频是up制作,Van说FQ时候想知道这是哪个up主搞的视频,他想知道就是up主名字和年龄。

定义一个Up主注解类

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Up {
    /**
     * 姓名
     */
    String upName() default "";

    /**
     * 名字
     */
    int upAge() default 0;
}

在Banana里面摔跤方法添加注解

  @Up(upAge = 15, upName = "van")
  public void 摔跤() {
    System.out.println("摔跤");
  }

在Van里面添加

  @Before("poincut()")
    public void 前置带有up主名字和年龄sayFQ(JoinPoint joinPoint) {
        System.out.println("前置FQ");
        try {
            int upAge = getUpAge(joinPoint);
            String upName = getUpName(joinPoint);
            System.out.println("up姓名:" + upName);
            System.out.println("up年龄:" + upAge);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取Up名字
    private String getUpName(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String upName = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] types = method.getParameterTypes();
                if (types.length == arguments.length) {
                    upName = method.getAnnotation(Up.class).upName();
                    break;
                }
            }
        }
        return upName;
    }

    //获取Up年龄
    private int getUpAge(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        int upAge = 0;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] types = method.getParameterTypes();
                if (types.length == arguments.length) {
                    upAge = method.getAnnotation(Up.class).upAge();
                    break;
                }
            }

        }
        return upAge;
    }

在测试类添加

   @Test
    public void 前置带有up主名字和年龄() throws Exception {
        mBanana.摔跤();
        //控制台:
        //前置FQ
        //up姓名:van
        //up年龄:15
        //摔跤
    }

至此,Spring AOP使用注解创建切面骚操作差不过讲完了。

你可能感兴趣的:(Spring AOP使用注解创建切面)