Spring之使用AOP缘由(3-SpringAOP实现-注解配置方式)
由于编写一个适用于企业级应用的AOP功能模块对普通程序员有一定的挑战以及重复开发此功能也会定义程度上造成浪费。因此,直接使用Spring框架提供的AOP功能可以做到事半功倍。
AOP 简介
AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程) 的补充。AOP 的主要编程对象是切面(aspect),而切面模块化横切关注点。在应用 AOP 编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。
AOP优点
每个事物逻辑位于一个位置,代码不分散,便于维护和升级。
业务模块更简洁,只包含核心业务代码。
AOP 术语
切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象。
通知(Advice):切面必须要完成的工作。
目标(Target):被通知的对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
Spring AOP
AspectJ:Java 社区里最完整最流行的 AOP 框架。
在 Spring2.0 以上版本中,可以使用基于 AspectJ 注解或基于 XML 配置的 AOP。
基于AOP分析动态代理实现
抽取切面模型
使用SpringAOP实现
1. AOP依赖包:
1.1 spring包:
核心包:
spring-framework-4.0.0.RELEASE-dist:
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
适配AspectJ包:
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
1.2 commons包:
commons-logging-1.1.1.jar
1.3 aspectj包:
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
2. SpringAOP配置步骤:
2.1 加入AOP依赖包;
2.2 在配置文件中加入aop的命名空间;
2.3 基于注解的方式;
2.3.1 在配置文件中加入如下配置:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.3.2 把横切关注点的代码抽象到切面的类中
a) 切面首先是一个IOC中的bean,即加入@Component注解
b) 切面还需要加入@Aspect注解
2.3.3 在类中声明各种通知:
a) 声明一个方法
b) 在方法上添加@Before、@After等注解
2.3.4 可以在通知方法中声明一个类型为JoinPoint的参数,然后就能方法连接细节,入方法的名称和入参。
3. 添加切面模型类并配置
package xyz.huning.spring4.aop.calculator.aopimpl.annotation; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * * 把这个类声明为一个切面: * 1. 使用注解“@Component”把该类放入到IOC容器中 * 2. 使用注解“@Aspect”把该类声明为一个切面 * ********切面的优先级****** * 3. 使用注解“@Order(number)”指定前面的优先级,值越小,优先级越高 * */ @Order(1) @Aspect @Component public class CalculatorValidateAspect { //声明该方法是一个前置通知:在目标方法开始之前执行 @Before("execution(public double xyz.huning.spring4.aop.calculator.PureCalculator.*(..))") public void validate(JoinPoint joinPoint) { System.out.println("validate parameter: " + Arrays.toString(joinPoint.getArgs())); } }
package xyz.huning.spring4.aop.calculator.aopimpl.annotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * * 把这个类声明为一个切面: * 1. 使用注解“@Component”把该类放入到IOC容器中 * 2. 使用注解“@Aspect”把该类声明为一个切面 * ********切面的优先级******** * 3. 使用注解“@Order(number)”指定前面的优先级,值越小,优先级越高 * */ @Order(2) @Aspect @Component public class CalculatorLogAspect { //定义切点表达式 @Pointcut("execution(public double xyz.huning.spring4.aop.calculator.PureCalculator.*(..))") public void declareJoinPointExpression(){} //声明该方法是一个前置通知:在目标方法开始之前执行 @Before("declareJoinPointExpression()") public void before(JoinPoint joinPoint) { System.out.println("before: " + "[x=" + joinPoint.getArgs()[0] + ", y=" + joinPoint.getArgs()[1] + "]"); } //声明该方法是一个后置通知:在目标方法开始之后执行(即使目标方法执行出现异常也会执行) @After("declareJoinPointExpression()") public void after(JoinPoint joinPoint) { System.out.println("after: " + "[x=" + joinPoint.getArgs()[0] + ", y=" + joinPoint.getArgs()[1] + "]"); } }
4. 添加配置文件
<?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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--自定义bean扫描范围--> <context:component-scan base-package="xyz.huning.spring4.aop.calculator"></context:component-scan> <!-- 是Aspectj注解启作用 此配置的作用是使得切面中的注解生效,即使得@Aspect标签标注的类里边使用@Before、@After等标注的方法起作用。 具体逻辑是这样,当Spring执行的方法与@Before、@After标签中execution中标注的方法签名匹配时, Spring会为这个类自动生成一个代理对象,并把@Before、@After匹配的方法放在目标方法之前之后执行。 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
5. 添加测试类
package xyz.huning.spring4.aop.calculator.aopimpl.annotation; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import xyz.huning.spring4.aop.calculator.ICalculator; public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("calculator-aop-annotation.xml"); ICalculator calc = ctx.getBean("pureCalculator", ICalculator.class); calc.add(10, 20); calc.sub(30, 10); calc.mul(10, 10); calc.div(20, 10); ((ClassPathXmlApplicationContext)ctx).close(); } }
6. 执行结果