Java进阶知识23 Spring execution 切入点表达式
1、概述
切入点(execution ):可以对指定的方法进行拦截,从而给指定的类生成代理对象。(拦截谁,就是在谁那里切入指定的程序/方法)
格式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
参数解析:
modifiers-pattern?:指定方法的修饰符,支持通配符,该部分可以省略。(public/private/protected)
ret-type-pattern:指定方法的返回值类型,支持通配符,可以使用 * 来匹配所有的返回值类型。
declaring-type-pattern?:指定方法所属的类,支持通配符,该部分可以省略。(要切入的类:class)
name-pattern:指定要匹配的方法名,支持通配符,可以使用"*"通配符来匹配所有方法。(要切入的方法)
param-pattern:指定方法声明中的形参列表,支持两个通配符,即"*"和"..",其中“*”代表一个任意类型的参数,而“..”代表零个或多个任意类型的参数。例如,() 匹配一个不接受任何参数的方法,而(..) 匹配一个接受任意数量参数的方法,(*)匹配了一个接受一个任何类型的参数的方法,(*,String)匹配了一个接受两个参数的方法,其中第一个参数是任意类型,第二个参数必须是String类型。(要切入的方法的参数列表)
throws-pattern:指定方法声明抛出的异常,支持通配符,该部分可以省略。
切入点语法常用的几种:
1 <!-- 切入点表达式语法: --> 2 <!-- 【1、拦截所有public方法】 --> 3 <aop:pointcut expression="execution(public * *(..))" id="pt"/>
4 5 <!-- 【2、拦截所有save开头的方法】 --> 6 <aop:pointcut expression="execution(* save*(..))" id="pt"/>
7 8 <!-- 【3、拦截指定类的指定方法, 拦截时候一定要定位到方法】 --> 9 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..))" id="pt"/> 10 11 <!-- 【4、拦截指定类的所有方法】 --> 12 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.*(..))" id="pt"/>
13 14 <!-- 【5、拦截指定包,以及其自包下所有类的所有方法】 --> 15 <aop:pointcut expression="execution(* com..*.*(..))" id="pt"/>
16 17 <!-- 【6、多条件】 --> 18 <!-- 或:|| or --> 19 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) || execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />
20 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) or execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />
21 22 <!-- 且:&& and --> <!-- 语法虽然没错,但,没意义 --> 23 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) && execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" /> 24 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) and execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />
25 26 <!-- 【7、取非值:not ! 不拦截指定的规则,拦截除此之外的所有类的方法】 --> 27 <aop:pointcut expression="!execution(* com.shore.dao.impl.UserDao.save(..))" id="pt"/>
28 <!-- 注意not前必须有空格 --> 29 <aop:pointcut expression=" not execution(* com.shore.dao.impl.UserDao.save(..))" id="pt"/>
2、实例
用到的jar包:
相关代码:
1 //接口 2 public interface IUserDao { 3 public void save(); 4 } 5 6 //接口实现类 7 public class UserDao implements IUserDao { 8 9 @Override 10 public void save() { //3、执行业务方法 11 System.out.println("3、保存用户成功!"); 12 } 13 }
MessageDao 类(此类,没有实现任何接口)
1 public class MessageDao { 2 public void save() { //3、执行业务方法 3 System.out.println("3、保存信息成功!"); 4 } 5 }
Aop 类
1 package com.shore.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 5 /** 6 * @author DSHORE/2019-11-7 7 * 8 */ 9 public class Aop { 10 //around:环绕; ProceedingJoinPoint:必须添加(执行) 11 @SuppressWarnings("unused") 12 private void around(ProceedingJoinPoint pjp) throws Throwable { 13 System.out.println("2、环绕前...."); 14 pjp.proceed(); // 执行目标方法(业务代码) 15 System.out.println("5、环绕后...."); 16 } 17 18 public void begin() { 19 System.out.println("1、开启事务......"); 20 } 21 22 public void commit() { 23 System.out.println("6、提交事务......"); 24 } 25 26 public void afterReturning() { 27 System.out.println("4、afterReturning(),返回消息"); 28 } 29 30 //有异常,执行这个第四步;没有异常,则执行上面的第四步 31 public void afterThrowing(){ 32 System.out.println("4、afterThrowing(),返回异常消息"); 33 } 34 }
Spring 配置文件(beans.xml)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" 5 xsi:schemaLocation=" 6 http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/tx 9 http://www.springframework.org/schema/tx/spring-tx.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop.xsd"> 12 13 <!-- dao类 --> 14 <bean id="userDao" class="com.shore.dao.impl.UserDao"></bean> 15 <bean id="messageDao" class="com.shore.dao.impl.MessageDao"></bean> 16 17 <!-- 注入切面类 --> 18 <bean id="aop" class="com.shore.aop.Aop"></bean> 19 20 <aop:config> 21 <!-- 配置切入点 --> 22 <!-- 切入点表达式语法: --> 23 <!-- 【1、拦截所有public方法】 --> <!-- 【第一个*是返回类型,第二个*是方法名 --> 24 <!-- <aop:pointcut expression="execution(public * *(..))" id="pt" /> --> 25 26 <!-- 【2、拦截所有save开头的方法】 --> 27 <!-- <aop:pointcut expression="execution(* save(..))" id="pt" /> --> 28 29 <!-- 【3、拦截指定类的指定方法, 拦截时候一定要定位到方法】 --> 30 <!-- <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..))" id="pt" /> --> 31 32 <!-- 【4、拦截指定类的所有方法】 --> 33 <!-- <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.*(..))" id="pt" /> --> 34 35 <!-- 【5、拦截指定包,以及其自包下所有类的所有方法】 --> 36 <!-- <aop:pointcut expression="execution(* com..*.*(..))" id="pt" /> --> 37 <!-- <aop:pointcut expression="execution(* com..UserDao.*(..))" id="pt" /> --> 38 39 <!-- 6、多条件 --> 40 <!-- 【(1)或:or ||】 --> 41 <!-- <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) or execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" /> --> 42 <!-- 【(2)与:and &&】 --> 43 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) and execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" /> 44 45 <!-- 【7、否定:! not(not前后都需要空格)】 --> 46 <!-- <aop:pointcut expression="!execution(* com.shore.dao.impl.UserDao.*(..))" id="pt" /> --> 47 <aop:pointcut expression=" not execution(* com.shore.dao.impl.UserDao.*(..))" id="pt" /> 48 49 50 <!-- 配置切面(切入点形成的类,切入点就是重复的代码/方法) --> 51 <aop:aspect ref="aop"> 52 <!-- 1、开启事务。。。。。。 --> 53 <aop:before method="begin" pointcut-ref="pt" /> 54 <!-- 6、提交事务 --> 55 <aop:after method="commit" pointcut-ref="pt" /> 56 <!-- 环绕2/5 --> 57 <aop:around method="around" pointcut-ref="pt" /> 58 <!-- 4、afterReturning --> 59 <aop:after-returning method="afterReturning" 60 pointcut-ref="pt" /> 61 <!-- 4、异常(程序出现异常,执行这个第四步;没有异常,则执行上面的第四步) --> 62 <aop:after-throwing method="afterThrowing" 63 pointcut-ref="pt" /> 64 </aop:aspect> 65 </aop:config> 66 </beans>
测试类
1 package com.shore.test; 2 3 import org.junit.Test; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 import com.shore.dao.IUserDao; 8 import com.shore.dao.impl.MessageDao; 9 10 /** 11 * @author DSHORE/2019-11-7 12 * 13 */ 14 public class MyTest { 15 @Test 16 public void testUserDao() { 17 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 18 IUserDao userDao = (IUserDao) context.getBean("userDao"); 19 //$Proxy4 :class com.sun.proxy.$Proxy4 20 System.out.println("这个相当于动态代理$Proxy4:"+userDao);//返回值:com.shore.dao.impl.UserDao@73aecc3a 21 userDao.save(); 22 System.out.println("=============== 分割线 ==============="); 23 24 MessageDao messageDao = (MessageDao) context.getBean("messageDao"); 25 System.out.println("这个相当于Cglib子类代理:"+messageDao);//返回值:com.shore.dao.impl.MessageDao@15412e75 26 messageDao.save(); 27 } 28 }
上方实例,切入点出代码,详细解析:
1、拦截所有public方法
1 <!-- 【1、拦截所有public方法】 --> <!-- 第一个*是返回值类型,第二个*是方法名;两个..表示可以是两个或者是多个参数 --> 2 <aop:pointcut expression="execution(public * *(..))" id="pt" />
运行结果图:
解析:
从上面的项目截图以及相关代码,可以看出:只有三个public 方法,Aop类中的不算,因为他是切面类。故,上面红框中的结果,大家不难理解。没有红框的两处代码,是对IUserDao类进行拦截,输出两遍,是因为测试类,调用它两遍。
2、拦截所有save开头的方法
1 <!-- 【2、拦截所有save开头的方法】 --> <!-- 第一个*是返回类型,两个..表示可以是两个或者是多个参数 --> 2 <aop:pointcut expression="execution(* save(..))" id="pt" />
运行结果图:
解析省略(看结果图中的红色文字)
3、拦截指定类的指定方法, 拦截时候一定要 定位到方法
1 <!-- 【3、拦截指定类的指定方法, 拦截时候一定要定位到方法】 --> <!-- 第一个*是返回类型,两个..表示可以是两个或者是多个参数 --> 2 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..))" id="pt" />
运行结果图:
解析:
从上面的代码和结果图,可以看出,这里只对UserDao类中的save方法进行拦截。虽然MessageDao类中的save方法也运行了,但是,没有开启事务,也没提交事务,故不会生效。
4、拦截指定类的所有方法
1 <!-- 【4、拦截指定类的所有方法】 --> <!-- 第一个*是返回值类型,第二个*是方法名;两个..表示可以是两个或者是多个参数 --> 2 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.*(..))" id="pt" />
运行结果图:
解析省略(和上面第3点一样,UserDao类中,有多少个方法,就执行多少个,结果类似)
5、拦截指定包,以及其自包下所有类的所有方法
1 <!-- 【5、拦截指定包,以及其自包下所有类的所有方法】 --> 2 <aop:pointcut expression="execution(* com..*.*(..))" id="pt" /><!-- 这几个*分别是 返回类型、com包下的所有类、指定类下的所有方法 -->
3 <aop:pointcut expression="execution(* com..UserDao.*(..))" id="pt" />
运行结果图:
解析省略(和上面的测试结果类似)
6、多条件
1 <!-- 6、多条件 --> 2 <!-- 【(1)或:or ||】 --> 3 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) or execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" /> 4 <!-- 【(2)与:and &&】 --> 5 <aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) and execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />
运行结果图:
解析省略(看结果图中的红色文字)
7、否定: ! not(注意:not前后都需要空格)
1 <!-- 【7、否定: ! not(not前后都需要空格)】 --> 2 <aop:pointcut expression="!execution(* com.shore.dao.impl.UserDao.*(..))" id="pt" /> 3 <aop:pointcut expression=" not execution(* com.shore.dao.impl.UserDao.*(..))" id="pt" />
运行结果图:
解析省略(看结果图中的红色文字)
本文总结: 经测试结果显示,做修饰符(public/private/protected)和否定(! not)测试时,连接口类都一起拦截。除这两种以外,只要是指定了包名、类名、方法名的 等等,都不会再去拦截接口类。
原创作者:DSHORE 作者主页:http://www.cnblogs.com/dshore123/ 原文出自:https://www.cnblogs.com/dshore123/p/11823849.html 欢迎转载,转载务必说明出处。(如果本文对您有帮助,可以点击一下右下角的 推荐,或评论,谢谢!) |