sring aop的方式有两种:(1)xml文件配置方式(2)注解的方式实现,我们可以先通过一个demo认识spring aop的实现,然后再对其进行详细的解释。
一、基于注解的springAop配置。
环境准备阶段:
(1)pom.xml:
1 <dependencies> 2 <!-- 引入Spring-AOP等相关Jar --> 3 <dependency> 4 <groupId>org.springframework</groupId> 5 <artifactId>spring-core</artifactId> 6 <version>3.0.6.RELEASE</version> 7 </dependency> 8 <dependency> 9 <groupId>org.springframework</groupId> 10 <artifactId>spring-context</artifactId> 11 <version>3.0.6.RELEASE</version> 12 </dependency> 13 <dependency> 14 <groupId>org.springframework</groupId> 15 <artifactId>spring-aop</artifactId> 16 <version>3.0.6.RELEASE</version> 17 </dependency> 18 <dependency> 19 <groupId>org.springframework</groupId> 20 <artifactId>spring-orm</artifactId> 21 <version>3.0.6.RELEASE</version> 22 </dependency> 23 <dependency> 24 <groupId>org.aspectj</groupId> 25 <artifactId>aspectjrt</artifactId> 26 <version>1.6.1</version> 27 </dependency> 28 <dependency> 29 <groupId>aspectj</groupId> 30 <artifactId>aspectjweaver</artifactId> 31 <version>1.5.3</version> 32 </dependency> 33 <dependency> 34 <groupId>cglib</groupId> 35 <artifactId>cglib</artifactId> 36 <version>2.1_2</version> 37 </dependency> 38 39 <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> 40 <dependency> 41 <groupId>com.mchange</groupId> 42 <artifactId>c3p0</artifactId> 43 <version>0.9.5.2</version> 44 </dependency> 45 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> 46 <dependency> 47 <groupId>mysql</groupId> 48 <artifactId>mysql-connector-java</artifactId> 49 <version>5.1.37</version> 50 </dependency> 51 52 <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> 53 <dependency> 54 <groupId>dom4j</groupId> 55 <artifactId>dom4j</artifactId> 56 <version>1.6.1</version> 57 </dependency> 58 <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang --> 59 <dependency> 60 <groupId>commons-lang</groupId> 61 <artifactId>commons-lang</artifactId> 62 <version>2.6</version> 63 </dependency> 64 </dependencies>
(2)定义接口:
1 package cn.spring.aop.dao; 2 3 /** 4 * @author Simple 5 * @date 10:01 2019/8/20 6 * @description 7 */ 8 public interface UserService { 9 public void save(); 10 }
(3)接口实现类:
1 package cn.spring.aop.dao; 2 3 import org.springframework.stereotype.Service; 4 5 /** 6 * @author Simple 7 * @date 9:57 2019/8/20 8 * @description 9 */ 10 @Service 11 public class UserServiceImpl implements UserService { 12 13 @Override 14 public void save() { 15 System.out.println("保存成功....."); 16 } 17 }
(4)Aop类:
1 package cn.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.*; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * @author Simple 9 * @date 10:06 2019/8/20 10 * @description 11 */ 12 @Component 13 @Aspect 14 public class AopAspect { 15 /** 16 * 前置通知 17 */ 18 @Before("execution(* cn.spring.aop.dao.UserService.save(..))") 19 public void before(){ 20 System.out.println("前置通知...."); 21 } 22 23 /** 24 * 后置通知 25 * returnVal,切点方法执行后的返回值 26 */ 27 @AfterReturning(value="execution(* cn.spring.aop.dao.UserService.save(..))",returning = "returnVal") 28 public void AfterReturning(Object returnVal){ 29 System.out.println("后置通知...."+returnVal); 30 } 31 32 33 /** 34 * 环绕通知 35 * @param joinPoint 可用于执行切点的类 36 * @return 37 * @throws Throwable 38 */ 39 @Around("execution(* cn.spring.aop.dao.UserService.save(..))") 40 public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 41 System.out.println("环绕通知前...."); 42 Object obj= (Object) joinPoint.proceed(); 43 System.out.println("环绕通知后...."); 44 return obj; 45 } 46 47 /** 48 * 抛出通知 49 * @param e 50 */ 51 @AfterThrowing(value="execution(* cn.spring.aop.dao.UserService.save(..))",throwing = "e") 52 public void afterThrowable(Throwable e){ 53 System.out.println("出现异常:msg="+e.getMessage()); 54 } 55 56 /** 57 * 无论什么情况下都会执行的方法 58 */ 59 @After(value="execution(* cn.spring.aop.dao.UserService.save(..))") 60 public void after(){ 61 System.out.println("最终通知...."); 62 } 63 }
(5)spring.xml
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop 7 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 8 <!-- 开启注解扫描 --> 9 <context:component-scan base-package="cn.spring.aop"></context:component-scan> 10 <!-- 启动@aspectj的自动代理支持--> 11 <aop:aspectj-autoproxy /> 12 </beans>
(6)测试类:
1 package cn.spring.aop; 2 3 import cn.spring.aop.dao.UserService; 4 import cn.spring.aop.dao.UserServiceImpl; 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 8 /** 9 * @author Simple 10 * @date 10:13 2019/8/20 11 * @description 12 */ 13 public class TestDemo { 14 public static void main(String[] args) { 15 ApplicationContext ac =new ClassPathXmlApplicationContext("spring.xml"); 16 UserService userService = (UserService) ac.getBean("userServiceImpl"); 17 userService.save(); 18 } 19 }
(7)运行结果:
二、配置详解
(1)spring.xml中注解的作用
1.spring--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
这个是 开启事物注解权限,引入了三个jar包 aspectjweaver.jar aspectjrt.jar aspectj.jar aopalliance.jar。
2.Spring -- <context:component-scan>
在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean。
(2)Aop类中的注解
在aop类中,编写了5种注解类型的通知函数:
@Before 前置通知
@AfterReturning 后置通知
@Around 环绕通知
@AfterThrowing 异常通知
@After 最终通知
@pointcut 定义切点匹配表达式
(3)切点表达式
- execution
由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如下是execution表达式的语法:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下:
- modifiers-pattern方法的可见性,如public,protected;
- ret-type-pattern:方法的返回值类型,如int,void等;
- declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
- name-pattern:方法名类型,如buisinessService();
- param-pattern:方法的参数类型,如java.lang.String;
- throws-pattern:方法抛出的异常类型,如java.lang.Exception;
如下是一个使用execution表达式的例子:
execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))
上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。
通配符的类型,主要有两种:
- *通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。
如下示例表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法:execution(* com.spring.service.BusinessObject.*())
- ..通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。
如下示例表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数:execution(* com.spring.service..*.businessService())
这里需要说明的是,包路径service..*.businessService()中的..应该理解为延续前面的service路径,表示到service路径为止,或者继续延续service路径,从而包括其子包路径;后面的*.businessService(),这里的*表示匹配一个单词,因为是在方法名前,因而表示匹配任意的类。
如下示例是使用..表示任意个数的参数的示例,需要注意,表示参数的时候可以在括号中事先指定某些类型的参数,而其余的参数则由..进行匹配:
execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
三、基于xml的SpringAop配置
xml配置主要是将注解转换为xml这里我们在上述的情况下做下修改,主要修改两个地方:1,spring.xml,2 aop类
1.springaop.xml
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop 7 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 8 <!--基于配置需要我们手动进行配置--> 9 <!-- dao 实例 --> 10 <bean id="userService" class="cn.spring.aop.dao.UserServiceImpl"></bean> 11 <!-- 切面类 --> 12 <bean id="aop" class="cn.spring.aop.AopAspect2"></bean> 13 <!-- Aop配置 --> 14 <aop:config> 15 <!-- 定义一个切入点表达式: 拦截哪些方法 --> 16 <aop:pointcut expression="execution(* cn.spring.aop.dao.UserService.*(..))" id="pt"/> 17 <!-- 切面 --> 18 <aop:aspect ref="aop"> 19 <!-- 环绕通知 --> 20 <aop:around method="around" pointcut-ref="pt"/> 21 <!-- 前置通知: 在目标方法调用前执行 --> 22 <aop:before method="before" pointcut-ref="pt"/> 23 <!-- 后置通知: --> 24 <aop:after method="after" pointcut-ref="pt"/> 25 <!-- 返回后通知 --> 26 <aop:after-returning method="afterReturning" pointcut-ref="pt" /> 27 <!-- 异常通知 --> 28 <aop:after-throwing method="after" pointcut-ref="pt"/> 29 </aop:aspect> 30 </aop:config> 31 </beans>
2.aop类 AopAspect2
1 package cn.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.*; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * @author Simple 9 * @date 10:06 2019/8/20 10 * @description 11 */ 12 @Component 13 @Aspect 14 public class AopAspect2 { 15 /** 16 * 前置通知 17 */ 18 public void before(){ 19 System.out.println("前置通知...."); 20 } 21 22 /** 23 * 后置通知 24 * returnVal,切点方法执行后的返回值 25 */ 26 public void afterReturning(){ 27 System.out.println("后置通知...."); 28 } 29 30 31 /** 32 * 环绕通知 33 * @param joinPoint 可用于执行切点的类 34 * @return 35 * @throws Throwable 36 */ 37 public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 38 System.out.println("环绕通知前...."); 39 Object obj= (Object) joinPoint.proceed(); 40 System.out.println("环绕通知后...."); 41 return obj; 42 } 43 44 /** 45 * 抛出通知 46 * @param e 47 */ 48 public void afterThrowable(Throwable e){ 49 System.out.println("出现异常:msg="+e.getMessage()); 50 } 51 52 /** 53 * 无论什么情况下都会执行的方法 54 */ 55 public void after(){ 56 System.out.println("最终通知...."); 57 } 58 }
3.测试方法
1 package cn.spring.aop; 2 3 import cn.spring.aop.dao.UserService; 4 import cn.spring.aop.dao.UserServiceImpl; 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 8 /** 9 * @author Simple 10 * @date 10:13 2019/8/20 11 * @description 12 */ 13 public class TestDemo { 14 public static void main(String[] args) { 15 ApplicationContext ac =new ClassPathXmlApplicationContext("springaop.xml"); 16 UserService userService = (UserService) ac.getBean("userService"); 17 userService.save(); 18 } 19 }
4.运行结果
现在在开发中主要是使用注解进行开发,方便快捷,但是更多的是配置和注解一块使用,springaop在配置方面很多都是这种注解加配置,原因主要是方便管理维护。我们这里主要讲的是两种方式的使用和切点表达式。