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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://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>
新建测试类
@Test
public void test2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean10.xml");
Book book = ac.getBean("book", Book.class);
book.buy();
}
执行结果
配置的是前置通知,所以先输出before再输出buy