AspectJ切入表达式

AspectJ切入表达式

  1.方法签名模式

      1.精确匹配和模糊匹配

     1. execution( public    *      liusheng.aspect.UserLogin.login(..)  )

     匹配的是 在 liusheng.aspect.UserLogin的名字为login,且参数为任意,返回值任意,方法修饰权限为public (如果为public则可以省略)

       

     2. execution(public   *    liusheng.aspect.Calc.sub(double,double))

     匹配的是 在 liusheng.aspect.Calc中方法名字为sub,且参数为两个double类型的,返回值任意

   

     3. execution(   *   Calc.sub(double,double))

     如果切面与目标类在同一个包下,那么包名可以省略

 

     4 execution(    public   *    liusheng.aspect.Calc.sub*(double,double)      )

     匹配的是 在 liusheng.aspect.Calc方法的名字的前缀为sub,且参数为两个double类型的(可以用两个点..来匹配所以参数列表),返回值任意

     

       4 execution(    public   *    liusheng.aspect..*sub(double,double)      )

     匹配的是 在 liusheng.aspect下及其子包下所有类中的sub(double,double)方法

     注意:上述都是匹配都是路径类似或者方法类似的切入点,当我们的切入点都没有共同点时,那该怎么注入了,当然你可以再写一个方法,再

     写入一个切入表达式即可,一个两个还行,但是方法很多的时候,我们就很麻烦了,而且以后当要还一个切面的时候,维护成本很高

     所以我们有必要把它们用表示统一一下。

    2.@annotation的使用

      我们可以自定义一个注解当作他们的统一标识符

      如下注解:

package liusheng.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLoginAnnotation {
    
}

   主配置文件如下:

  

<?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.xsd http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd"
> <aop:aspectj-autoproxy/> <context:component-scan base-package="liusheng"/> </beans>

    我的业务类:只需要在连接点上标注上@MyLoginAnnotation

    模拟学生数据访问层

package liusheng.aspect;

import org.springframework.stereotype.Component;

@Component
public class StudentDao {
    @MyLoginAnnotation
    public void  delete(Integer id){
        System.out.println("删除了一个学生");
    }
    @MyLoginAnnotation
    public void  delete2(Integer id,Integer id1){
        System.out.println("删除了两个学生");
    }
}

  模拟用户登陆层 

package liusheng.aspect;

import org.springframework.stereotype.Component;

@Component
public class UserLogin {
    @MyLoginAnnotation
    public void login(){
        System.out.println("登陆");
    }
}

  切面的类

package liusheng.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggerAspect {
    /**
     * 所以这个指向切入点,都是用
     * @MyLoginAnnotation标注了的
     */
    @Before("@annotation(liusheng.aspect.MyLoginAnnotation)")
    public void   logger(){
        System.out.println("日志打印");
    }
}

  测试类如下:

package liusheng.aspect;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Spring-Config.xml")
public class AspectTest {

    @Autowired
    public UserLogin login;
    @Autowired
    public StudentDao dao;

    @Test
    public void test(){
        login.login();
        dao.delete(1);
        dao.delete2(1,2);
    }
}

  结果如下:

  

  结论:这种模式可以匹配任何方法,灵活自如,在开发中主要是这种方式,要多家练习。

  

 2.类型签名模式

  我们的Dao层,的增删改,一般都需要事务,我们一个一个方法的匹配,就显得很麻烦,所以我们可以直接用切入表达式匹配的他们的类名

  dao类:

  

package liusheng.dao.aspect;

import org.springframework.stereotype.Component;

@Component
public class UserDao {
    public  void add(){
        System.out.println("add");
    }
    public  void update(){
        System.out.println("update");
    }
    public  void delet(){
        System.out.println("delete");
    }

}

  aspect类(事务)

  

package liusheng.dao.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(1)
public class Transaction {
    /**
     * 对UserDao的所以方法都关闭了事务
     */

    @Before("within(liusheng.dao.aspect.UserDao)")
    public void startTransaction(){
        System.out.println("开启事务");
    }
    @After("within(liusheng.dao.aspect.UserDao)")
    public void endTransaction(){
        System.out.println("关闭事务");
    }
    /**
     * 对UserDao的所以方法都开启了事务
     */

}

  注意:当类相同是,当他们的切点表达式是一样的时候,且注解一样,Spring按照方法的名字大小顺序切入,

  当类不同是,当他们的切点表达式是一样的时候,且注解一样,我们可以实现Ordered接口或者使用@Order在类上注解

  越小越优先

  

package liusheng.dao.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(1)
public class Transaction {
    /**
     * 对UserDao的所以方法都关闭了事务
     */

    @Before("within(liusheng.dao.aspect.UserDao)")
    public void startTransaction(){
        System.out.println("开启事务");
    }
    @After("within(liusheng.dao.aspect.UserDao)")
    public void endTransaction(){
        System.out.println("关闭事务");
    }
    /**
     * 对UserDao的所以方法都开启了事务
     */

}

  测试类 :

  

package liusheng.dao.aspect;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Spring-Config.xml")
public class ClassAspectTest {
    @Autowired
    public UserDao dao;
    @Test
    public  void test(){
        dao.add();
        dao.delet();
        dao.update();
    }
}

  结果:

  

  我们还可以写如下且点表达式

  1  within(liusheng.dao.aspect.*) //匹配liusheng.dao下所有的类  

  2 within(liusheng.dao.aspect..*) //匹配liusheng.dao及其子包下所有的类
3 如果切面与目标类在统一包下,那么包名可以省略
  4 within(liusheng.dao.aspect.UserDao+)//切入到所以实现了UserDao和继承UserDao的子类

  

3.Bean名字模式

  注意这个方式有个缺点:就是它的切点表达式的对象是Bean,而不是类,上面的都是类

  我们可以把上述切面写成如下样子

  

package liusheng.dao.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(1)
public class Transaction {
    
    @Before("bean(userDao)")
    public void startTransaction(){
        System.out.println("开启事务");
    }
    @After("bean(userDao)")
    public void endTransaction(){
        System.out.println("关闭事务");
    }
}

  结果与上面的结果一样,当然这种方式也支持,模糊匹配

  bean(*Dao) //匹配Bean的名字都以Dao结尾

  bean(User*)//匹配所有Bean的名字都是以User开头的Bean

4 总结论

  Spring为我们提供了如此之多的切入方式,在实际应用中我们应该多加练习,以便于在项目开发中实现灵活运用

  

 

  

  

 

  

  

 

  


    

    

    

 

posted on 2018-03-18 13:57  学习spring是我必须的  阅读(234)  评论(0编辑  收藏  举报