一、Spring AOP
1、Spring AOP的两种实现方式
1.基于xml的Spring AOP开发
2.基于注解的Spring AOP开发
2.引入的jar包
spring-aop-4.3.3.RELEASE.jar,
aspectjweaver-1.8.5.jar
aspectjrt-1.8.5.jar
二、基于xml的Spring AOP开发
(1)创建applicationContext-xml-aop.xml,引入aop命名空间和xsd文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>
(2)在xml文件中将业务bean注入
<bean id="bankService" class="springboot.aop.xml.BankServiceImpl">
</bean>
<bean id="loggerAspect" class="springboot.aop.xml.LoggerAspect">
</bean>
(3)使用aop:aspect标签配置代理信息
<aop:config>
<!--execution(* springboot.aop.xml.*.*(..))解释 第一个*代表匹配所有参数,第二个代表匹配springboot.aop.xml包下的所有类,第三个表示匹配到的类中的所有方法,(..)表示匹配所匹配到方法的所有参数-->
<aop:pointcut expression="execution(* springboot.aop.xml.*.*(..))" id="loggerPointCut"/>
<aop:aspect ref="loggerAspect">
<!-- 前置通知,在被代理方法执行前执行LoggerAspect中的logerBefore()方法-->
<aop:before method="logerBefore" pointcut-ref="loggerPointCut"/>
<!-- 后置通知-->
<aop:after method="logerAfter" pointcut-ref="loggerPointCut"/>
<!-- 后置返回通知,属性returning的值必须和方法logerAfterReturning返回值的参数名保持一致-->
<aop:after-returning method="logerAfterReturning" pointcut-ref="loggerPointCut" returning="returnValue"/>
<!-- 环绕通知-->
<aop:around method="logerAround" pointcut-ref="loggerPointCut"/>
<!-- 后置异常通知通知,抛出异常的时候执行-->
<aop:after-throwing method="loggerAfterThrowing" pointcut-ref="loggerPointCut" throwing="exception"/>
</aop:aspect>
</aop:config>
(4)LoggerAspect中的各种方法
public void logerBefore(JoinPoint jp) {
String methodName = jp.getSignature().getName();//可以获取当前执行的方法名
System.out.println("前置通知: " + methodName + "将要被执行!");
Object[] args = jp.getArgs();//可以获取参数
for(Object arg : args) {
System.out.println("参数为:" + arg);
}
}
public void logerAfter(JoinPoint jp) {
String methodName = jp.getSignature().getName();
System.out.println("后置通知: " + methodName + "已经被执行!");
Object[] args = jp.getArgs();
for(Object arg : args) {
System.out.println("参数为:" + arg);
}
}
public void logerAfterReturning(JoinPoint jp, Object returnValue) {
String methodName = jp.getSignature().getName();
System.out.println("后置返回通知 " + methodName + "已经被执行!");
System.out.println("后置返回通知 返回值:" + returnValue);
}
/**
* 环绕通知 需要配置返回值,否則目标方法所有的返回值都为null
* @param pjp
* @return
*/
public Object logerAround(ProceedingJoinPoint pjp) {
String methodName = pjp.getSignature().getName();
System.out.println("环绕通知: " + methodName );
Object[] args = pjp.getArgs();數
args[0] = "改变参数";
try {
Object returnValue = pjp.proceed(args);
System.out.println("环绕通知: " + methodName +“返回值:” + returnValue);
return new BigDecimal("999999999999999");//可以改变参数的返回值
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public void loggerAfterThrowing(JoinPoint jp, Exception exception) {
System.out.println("后置异常通知:" + exception);
}
(4)被代理类的代码
public class BankServiceImpl implements BankService{
public BigDecimal transfer( String source, String target,BigDecimal money) {
System.out.println(source + "向" + target + "转账:" + money);
// throw new RuntimeException("故意出的异常");
return new BigDecimal("12345678");
}
}
(5)Main类中方法调用
public class Main {
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-xml-aop.xml");
BankService bs = ioc.getBean("bankService", BankService.class);
bs.transfer("你", "我", new BigDecimal("100000000"))
}
}
(6)分析
调用main方法时,首先创建了一个ioc容器,后面的参数(“applicationContext-xml-aop.xml”)代表的是该容器所对应的文件,然后通过ioc容器调用了getBean方法生成了一个bs对象,然后bs对象调用transfer方法。
执行过程中,因为xml文件中注册了LoggerAspect,所以transfer方法会被截住,执行一系列通知方法。其中后置返回通知方法能够改变方法的参数以及返回值
三、基于注解的Spring AOP开发
1.开发过程
(1)引入aop命名空间和xsd文件(同xml)
(2)在xml中进行配置
<!-- 自动扫描注入springboot.aop.anno包下的内容-->
<context:component-scan base-package="springboot.aop.anno"></context:component-scan>
<!-- 开启Spring AOP 自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(3)业务类BankServiceImpl加注解@Service(“bankService”),将其注入到IOC容器中
(4)在通知类LoggerAspect中编写代码
//@Aspect:作用是把当前类标识为一个切面供容器读取
//@Order(0):当有多个通知类时决定优先级,数字越小优先级越高
//@Component:将该类注入到ioc容器
@Aspect
@Order(0)
@Component
public class TrancationAspect {
@Pointcut("execution(* springboot.aop.anno.*.*(..))")
public void logPointCut() {
}
//PointCut封装,封装之后在注解中execution(* springboot.aop.anno.*.*(..))等价于logPointCut()
//@Before("execution(* springboot.aop.anno.*.*(..))")因为PointCut已经被封装,所以本行跟下一行效果相同
@Before("logPointCut()")
public void logerBefore(JoinPoint jp) {
//AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象
String methodName = jp.getSignature().getName();
System.out.println("TrancationAspect 事务前置通知:method: " + methodName + "将要被执行!");
Object[] args = jp.getArgs();
for(Object arg : args) {
System.out.println("=============参数:>" + arg);
}
}
}
(5)Main类中调用,代码如下
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-anno-aop.xml");
BankService bs = ioc.getBean("bankService", BankService.class);
bs.transfer("你", "我", new BigDecimal("100"));
}
2、分析
通过注解的方式进行Spring AOP开发,主要分三步
(1)将业务类以及通知类注入到IOC容器
(2)编写业务类业务代码,在通知类中编写出发通知时的方法
(3)编写主函数出发通知类的通知
(4)执行逻辑跟xml方式一样,只不过是实现方式不同