AOP概念
AOP是Aspect Oriented Programming的缩写,即『面向切面编程』。它和我们平时接触到的OOP都是编程的不同思想,OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理,当然,我们在实际编程过程中,不可能单纯的安装AOP或者OOP的思想来编程,很多时候,可能会混合多种编程思想,大家也不必要纠结该使用哪种思想,取百家之长,才是正道
AspectJ
AspectJ实际上是对AOP编程思想的一个实践,当然,除了AspectJ以外,还有很多其它的AOP实现,例如ASMDex,但目前最好、最方便的,依然是AspectJ。
Advice(通知)
注入到class文件中的代码。典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。 除了在方法中注入代码,也可能会对代码做其他修改,比如在一个class中增加字段或者接口。
Joint point(连接点)
程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。
Pointcut(切入点)
告诉代码注入工具,在何处注入一段特定代码的表达式。例如,在哪些 joint points 应用一个特定的 Advice。切入点可以选择唯一一个,比如执行某一个方法,也可以有多个选择,比如,标记了一个定义成@DebguTrace 的自定义注解的所有方法。
AspectJ提供不同类型的通知,分别为:
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing异常抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
1:引入aspectj依赖
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency>
2:新建一个User类
public class User {public void add(){System.out.println("add方法");}
}
3:新建一个UserProxy类,被增强的一个类,
Before方法上加上@Before注解,配置切入点表达式,
*表示所有修饰符类型,后面加一个空格
接着后面上全路径和方法名,最后..表示参数
@Component
@Aspect //表示生成一个代理对象
public class UserProxy {//前置通知//@Before注解表示作为前置通知@Before("execution(* com.example.wzy.demo.aop.User.add(..))")public void before(){System.out.println("before方法");}
}
4:新建一个bean9.xml
首先加上context和aop的名称空间
接着配置开启组件扫描
<context:component-scan base-package="com.example.wzy.demo.aop"></context:component-scan>
com.example.wzy.demo.aop是全路径,表示扫描这个包
还有添加
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
表示开启Aspect生成代理对象,就是去类上找有没有@Aspect注解,有的话就把这个对象生成一个代理对象
5:最后添加一个测试类
6:输出结果,这就是前置通知,在方法执行之前执行
@Component
@Aspect //表示生成一个代理对象
public class UserProxy {//前置通知//@Before注解表示作为前置通知@Before("execution(* com.example.wzy.demo.aop.User.add(..))")public void before() {System.out.println("before方法");}//后置通知,最终通知,方法执行之后执行,有异常也会执行@After("execution(* com.example.wzy.demo.aop.User.add(..))")public void after() {System.out.println("after方法");}//后置通知(返回通知)方法返回结果之后执行,有异常不执行@AfterReturning("execution(* com.example.wzy.demo.aop.User.add(..))")public void afterReturning() {System.out.println("AfterReturning");}//异常通知@AfterThrowing("execution(* com.example.wzy.demo.aop.User.add(..))")public void afterThrowing() {System.out.println("AfterThrowing");}//环绕通知@Around("execution(* com.example.wzy.demo.aop.User.add(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕通知之前");proceedingJoinPoint.proceed();//被增强的方法执行System.out.println("环绕通知之后");}
}
优化:
相同的切入点抽取,下面的都是重复的,可以提取出来,如下图
execution(* com.example.wzy.demo.aop.User.add(..))
比如对User的add方法需要两个增强类,那么使用
@Order()注解可以,里面的数字越小表示先执行
看下执行结果
使用下xml配置文件配置
新建Book类和BookProxy类
public class Book {public void buy(){System.out.println("buy*********");}
}
public class BookProxy {public void before(){System.out.println("before**********");}
}
2:新建bean.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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--名称空间--><!--创建对象--><bean id="book" class="com.example.wzy.demo.aopxml.Book"></bean><bean id="bookProxy" class="com.example.wzy.demo.aopxml.BookProxy"></bean><!--配置aop增强--><aop:config><!--切入点--><aop:pointcut id="p" expression="execution(* com.example.wzy.demo.aopxml.Book.buy(..))"/><!--配置切面--><aop:aspect ref="bookProxy"><!--增强作用在具体的方法上--><aop:before method="before" pointcut-ref="p"/></aop:aspect></aop:config>
</beans>
新建测试类
@Testpublic void test2(){ApplicationContext ac = new ClassPathXmlApplicationContext("bean10.xml");Book book = ac.getBean("book", Book.class);book.buy();}
执行结果
配置的是前置通知,所以先输出before再输出buy