Spring(二)——AOP
1.概念
面向切面编程 即 新加的代码不会影响原来的,像切入一样。
2.AspectJ导入包
com.springsoure.net.sf.cglib
com.springsoure.org.aopalliance
com.springsoure.org.aspectj.weaver
spring-aop
spring-aspects
3.五种通知
- before:前置通知,在一个方法执行前被调用,@Before
- after: 在方法执行之后调用的通知,无论方法执行是否成功,即最终通知,@After
- after-returning: 仅当方法成功完成后执行的通知,@AfterReturning
- after-throwing: 在方法抛出异常退出时执行的通知,@AfterThrowing
- around: 在方法执行之前和之后调用的通知,@Around
实现条件,用@Aspect修饰通知(切面)类,被通知类和通知类 都用@Component修饰,在XML文件里用扫描包全部扫一下就好了
使用@Aspect注解需要添加aop命名空间,然后用<aop:aspectj-autoproxy /> 开启自动代理功能。
对于要织入的通知需要用上面5种注解之一来织入,对于同一个方法可以有多个通知,注解的属性value 需要指明 修饰符-返回值-哪个包下的哪个类的哪个方法名-参数类型。解析切入点,例如
@Before(value = "execution(public void com.atguigu.spring.aop.Dog.say(int))")
@before修饰的是切面类里要织入的方法,而不是被通知的方法。
这类切面编程需要加载配置文件,通过IOC来实现,不可以直接new对象。
看一下代码:
package com.atguigu.spring.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class MyAOP { //将方法指定为前置通知,需要加这个属性 @Before(value = "execution(public void com.atguigu.spring.aop.Dog.say(int))") public void before() { System.out.println("前置通知"); } }
切面类MyAOP,用@Aspect修饰,表明切面编程,@Componet修饰表示用了IOC技术,在配置文件里能加载。
package com.atguigu.spring.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component public class Dog { public void say(int x) { System.out.println("狗叫"+x+"次"); } }
被通知类Dog,用@Componet修饰表示用了IOC技术,在配置文件里能加载。
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" 4 5 xmlns:p="http://www.springframework.org/schema/p" 6 xmlns:util="http://www.springframework.org/schema/util" 7 xmlns:context="http://www.springframework.org/schema/context" 8 xmlns:aop="http://www.springframework.org/schema/aop" 9 xsi:schemaLocation=" 10 http://www.springframework.org/schema/beans 11 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 12 13 http://www.springframework.org/schema/util 14 http://www.springframework.org/schema/util/spring-util.xsd 15 16 http://www.springframework.org/schema/context 17 http://www.springframework.org/schema/context/spring-context-4.3.xsd 18 19 http://www.springframework.org/schema/aop 20 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd 21 22 "> 23 24 <context:component-scan base-package="com.atguigu.spring.aop" ></context:component-scan> 25 <!-- 开启自动代理功能,即启动注解声明功能--> 26 <aop:aspectj-autoproxy /> 27 28 29 30 </beans>
配置文件aop.xml中第8,19,20行是aop命名空间需要配置的东西。24行是扫描包,26行是启动注解声明功能。
package com.atguigu.spring.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { Dog dog=new Dog(); dog.say(11); ApplicationContext ac=new ClassPathXmlApplicationContext("aop.xml"); Dog dog2=ac.getBean("dog",Dog.class); dog2.say(110); } } /*通知 狗叫11次 前置通知 狗叫110次 */
测试类Test的main方法用new和IOC的方法执行 类 被前置通知的方法,效果不同,其他通知类似如此。
4.切入点表达式
一个通知方法作用于很多个类或者多个方法,可以用*匹配。
例如@Before(value = "execution(* com.atguigu.spring.aop.*.*(..))");
修饰符和返回值合并成1个*,不可以用2个,所有类可以用一个*,所有方法可以用一个*,所有参数类型用[..]匹配。
可重用的切入点表达式是 在切面类里 使用一个返回值为void,方法体为空的方法例如myPointCut()来命名切入点,被通知方法需要加上@Before("myPointCut()")这样的注解,可以通过加JoinPoint类型的参数来查看被通知的类是哪个,被通知的方法是哪个。
示例如下:
package com.atguigu.spring.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class MyAOP { @Pointcut(value = "execution(* com.atguigu.spring.aop.*.*(..))") private void myPointCut() { } @Before(value = "myPointCut()") public void before() { System.out.println("这是前置通知"); } @AfterThrowing(value="myPointCut()",throwing="e") public void afterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("被通知的类是:"+joinPoint.getTarget()); System.out.println("被通知的方法是:"+joinPoint.getSignature().getName()); System.out.println("异常了,这是异常通知"+e.getMessage()); } }
获取参数名:Object[] args=joinPoint.getArgs();
对于异常通知,参数类型也可以是Exception或者具体异常类型,但是要匹配。
对于环绕通知,参数类型必须是ProceedingJoinPoint,它是JoinPoint的子类。
5.切面优先级
对于同一连接点应用不止一个切面类时,除非明确确定,否则他们的优先级是不确定的。可以通过@Order(x)注解来指定,对于x越小的,优先级越高。
6.通过XML配置文件声明切面类和通知方法
即不同在Java文件里写@Aspect,@Before之类的注解,全都在XML里写,例如
<aop:config> <aop:aspect ref="MyAOP"> <aop:pointcut expression="execution(* com.atguigu.spring.aop.*.*(..))" id="myPointCut"/> <aop:before method="myBefore" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config>
看着都麻烦,还是用注解吧。
学习资源:B站尚硅谷Spring视频。