如下图,展示了Spring的大体框架。本文将详细介绍Spring框架中的AOP(面向切面编程)机制及其应用。
依赖注入就是给对象赋值,依赖注入是通过容器为对象动态赋值的一种设计模式
IOC(控制反转)将对象的创建与管理交由Spring框架处理,其底层机制依赖于反射技术。
AOP(面向切面编程)无需改动原有代码即可增强功能,提升系统的模块化和可维护性,其底层机制依赖于代理技术
AOP核心概念
AOP(面向切面编程)的核心概念包括以下几个关键术语:
1.Joinpoint(连接点):在类中,所有可以被增强的方法都称为连接点。这些方法是AOP能够拦截和增强的目标。
2.Pointcut(切入点):切入点是用于定义哪些连接点需要被拦截的规则。通过表达式或配置,指定具体哪些方法需要被增强。
3.Advice(通知/增强):通知是指在拦截到连接点后执行的具体操作。通知分为以下几种类型:
- 前置通知(Before):在目标方法执行之前执行。
- 后置通知(AfterReturning):在目标方法成功执行后执行。
- 异常通知(AfterThrowing):在目标方法抛出异常时执行。
- 最终通知(After):在目标方法执行完成后执行,无论是否抛出异常。
- 环绕通知(Around):在目标方法执行前后都执行,可以控制目标方法的执行过程。
4.Aspect(切面):切面是切入点和通知的结合体,定义了在何处(切入点)以及如何(通知)增强目标方法。切面是开发者编写和配置的核心部分,用于实现横切关注点的模块化。
下面介绍AOP的通知,有XML和注解两种实现方式
利用XML的方式
前置通知
前置通知(Before Advice)在目标方法执行之前执行,通常用于权限检查、日志记录等。
例子(希望在登录前做权限认证):
Spring.xml:
<?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"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"><bean id="login" class="com.qcby.Login"/><bean id="auth" class="com.qcby.Authorization"/><!--配置切面--><aop:config><!--配置切面 = 切入点 + 通知组成--><aop:aspect ref="auth"><!--前置通知:UserServiceImpl的save方法执行前,会增强--><!--pointcut:后边是切入点表达式,作用是知道对对面的那个方法进行增强--><aop:before method="authorization" pointcut="execution(public void com.qcby.Login.login())"/><!--pointcut有简写格式,不过一般不简写--></aop:aspect></aop:config></beans>
Login:
package com.qcby;public class Login {public void login(){System.out.println("登录成功");}
}
Authorization:
package com.qcby;public class Authorization {public void authorization(){System.out.println("进行权限认证");}
}
LoginTest:
public class LoginTest {@Testpublic void test(){ApplicationContext ac=new ClassPathXmlApplicationContext("Spring.xml");Login log=(Login) ac.getBean("login");log.login();}
}
结果:
最终通知和后置通知
最终通知(After (Finally) Advice),在目标方法执行结束后执行,无论是否抛出异常,通常用于资源清理、日志记录等。
后置通知(After Returning Advice)在目标方法成功执行并返回结果后执行,通常用于日志记录、结果处理等。
Login:
package com.qcby;public class Login {public void login(String name,String password){int n=10/0;//这样login运行出错System.out.println("登录成功");}
}
spring.xml:
<!--配置切面--><aop:config><!--配置切面 = 切入点 + 通知组成--><aop:aspect ref="auth"><!--最终通知--><!--pointcut:后边是切入点表达式,作用是知道对对面的那个方法进行增强--><aop:after method="authorization" pointcut="execution(public void com.qcby.Login.login(..))"/></aop:aspect></aop:config>
结果:
after是最终通知,after-returning是后置通知(切入点执行成功后才会执行),after-returning如果配置出错不会运行,而after在前面代码出错的情况下依然会进行通知
异常通知
只有在切入点异常的情况下才会通知
异常通知(After Throwing Advice)在目标方法抛出异常后执行,通常用于异常处理、日志记录等。
环绕通知
环绕通知(Around Advice)在目标方法执行前后都执行,可以控制目标方法的执行,通常用于事务管理、性能监控等。
Authorization:
package com.qcby;import org.aspectj.lang.ProceedingJoinPoint;public class Authorization {public void authorization(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("在方法执行之前进行权限认证");proceedingJoinPoint.proceed();System.out.println("在方法执行之后进行权限认证");}
}
结果:
利用注释的方式
Spring.xml:
<?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"><!--开启注解扫描--><context:component-scan base-package="com.qcby"/><!--开启Aspect生成代理对象--><aop:aspectj-autoproxy/>
</beans>
Authorization:
package com.qcby;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Aspect
public class Authorization {
// @Before(value = "execution(public void com.qcby.Login.login(..))")
// public void authorization(){
// System.out.println("进行权限认证");
// }
// @AfterReturning(value = "execution(public void com.qcby.Login.login(..))")
// public void authorization(){
// System.out.println("进行权限认证");
// }@Around(value = "execution(public void com.qcby.Login.login(..))")public void authorization(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("在方法执行之前进行权限认证");proceedingJoinPoint.proceed();System.out.println("在方法执行之后进行权限认证");}
}
Login:
package com.qcby;import org.springframework.stereotype.Component;@Component
public class Login {public void login(String name,String password){
// int n=10/0;//这样login运行出错System.out.println("登录成功");}
}