AOP(Aspect-OrientedProgramming):面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而是的业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高了开发的效率。
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,看到这其实应该明白了,AOP 其实就是代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:
public class test {
add()
select()
update()
delete()
}
通知类型
前置通知(@Before):在我们执行目标方法之前运行。
后置通知(@After ):在我们目标方法运行结束之后,不管有没有异常。(类似发finally)
返回通知(@AfterReturnning):在我们目标方法正常返回运行。
异常通知(@AfterThrowing) :在我们目标方法出现异常后运行。
环绕异常(@Around):动态代理,需要手动执行JoinPoint.procced() ,其实就是:执行我们的目标方法之前相当于前置通知, 执行之后就相当于我们后置通知。
Spring框架一般基于AspectJ,实现AOP
AspectJ不是Spring组成部分,而是独立的AOP框架,一般AapectJ和Spring框架一起使用,实现AOP操作。
切入点表达式:知道对哪个类里面的哪个方法进行通知(增强)
语法结构:execution([权限修饰符][返回类型][全限定类名][方法名称] ([参数列表]) )
举例:对com.simon.TestAop类中的add方法进行增强
execution(* com.simon.TestAop.add(..))
举例:对com.simon.TestAop类中的所有方法进行增强
execution(* com.simon.TestAop.*(..))
注意:默认 权限修饰符public 可以省略。参数列表可以使用 … 表示有无参数均可,有参数可以是任意类型。使用…来表示当前包及其子包。
导入依赖
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.6version>
<scope>runtimescope>
dependency>
创建增强的类
package simon.TestAop;
//增强的类
public class UserProxy {
//前置通知
public void before() {
System.out.println("before----");
}
}
创建被增强的类
package simon.TestAop;
//被增强的类
public class User {
public void add() {
System.out.println("User add----");
}
}
进行通知的配置
1.开启注解扫描
<context:component-scan base-package="simon.TestAop"/>
2.使用注解创建User 对象 ,可以被Spring扫描到。
package simon.TestAop;
import org.springframework.stereotype.Component;
//被增强的类
@Component
public class User {
public void add() {
System.out.println("User add----");
}
}
3.使用注解创建UserProxy。
@Component
可以Spring扫描到@Aspect
生成代理对象//增强的类
@Component
@Aspect
public class UserProxy {
//前置通知
public void before() {
System.out.println("before----");
}
}
4.开启生成代理对象
<aop:aspectj-autoproxy/>
5.在UserProxy代理类创建通知
package aopstudy.demo.TestAop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
@Before(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
public void before() {
System.out.println("before----");
}
//后置通知
@After(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
public void After() {
System.out.println("After----");
}
//返回通知
@AfterReturning(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
public void Return() {
System.out.println("Return----");
}
//异常通知
@AfterThrowing(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
public void AfterThrowing() {
System.out.println("AfterThrowing----");
}
//环绕异常
@Around(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around before----");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("Around after----");
}
}
使用junit测试
import aopstudy.demo.TestAop.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testAop {
@Test
public void testAop() {
ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
User user = context.getBean("user", User.class);
user.add();
}
}
**********运行记录************
Around before----
before----
User add----
Around after----
After----
Return----
由于没有异常,所以@AfterThrowing 没有执行。
接下来修改user,让他出现错误
package aopstudy.demo.TestAop;
import org.springframework.stereotype.Component;
//被增强的类
@Component
public class User {
public void add() {
System.out.println(10 / 0);
System.out.println("User add----");
}
}
继续执行junit
Around before----
before----
After----
AfterThrowing----
java.lang.ArithmeticException: / by zero
at aopstudy.demo.TestAop.User.add(User.java:9)
at aopstudy.demo.TestAop.User$$FastClassBySpringCGLIB$$2315042d.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~
观察发现,UserProxy代理类中切入点重复使用了多次,如进行修改的话不是很方便,因此我们优化代码如下:
package aopstudy.demo.TestAop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//相同切入点抽取
@Pointcut(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
private void point() {
}
//前置通知
@Before(value = "point()")
public void before() {
System.out.println("before----");
}
//后置通知
@After(value = "point()")
public void After() {
System.out.println("After----");
}
//返回通知
@AfterReturning(value = "point()")
public void Return() {
System.out.println("Return----");
}
//异常通知
@AfterThrowing(value = "point()")
public void AfterThrowing() {
System.out.println("AfterThrowing----");
}
//环绕异常
@Around(value = "point()")
public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around before----");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("Around after----");
}
}
若多个增强类对同一个方法进行增强。可以在增强类上用@Order(int )设置优先级,数字越小优先级越高