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