什么是AOP
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的(关于动态代理的内容我们已经在MyBatis(三)动态代理中介绍过了)如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。
AOP中的基本概念
Joinpoint(连接点):指那些被拦截的点,在spring中,这些点指的是方法,因为spring只支持方法的连接点,类中的方法可以被增强,这些方法称之为连接点。
Pointcut(切入点):指我们要对哪些Joinpoint进行拦截的定义,在类中可以有很多方法被增强,实际增强的方法称之为切入点。
Advice(通知/增强):指拦截到Joinpoint之后要做的事情就是通知,通知分为:前置通知、后置通知、异常通知、最终通知、环绕通知:
- 前置通知:在方法调用前执行
- 后置通知:在方法调用后执行
- 异常通知:在方法调用出现异常时调用
- 最终通知:在方法调用后且后置通知之后执行
- 环绕通知:在方法调用前后执行
Aspect(切面):把增强应用到切入点的过程。
Introduction(引介):引介是一种特殊的通知在不修改代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field。
Traget(目标对象):代理的目标对象(要增强的类)。
Weaving(织入):是把增强应用到目标的过程,把advice应用到target的过程。
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
AOP实现
AOP的实现也有两种方式:XML配置和注解,我们先介绍XML配置的方式:
1、XML实现AOP
(1) AOP依赖的jar包(在对应的pom文件中添加相关依赖)
<!--AOP相关jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
(2)增加AOP相关的约束
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
(3)创建student类和增强类
package com.tulun;
public class Student1 {
//add方法
public void add(){
System.out.println("Student的add方法");
}
}
public class AddAdvice {
public void addBefore(){
System.out.println("addBefore");
}
(4)配置XML文件(SpringAOP.xml)
此处介绍如何用表达式来配置切入点
在通知中通过value属性定义切入点,通过execution函数,可以定义切入点的方法切入
1、切入点:实际增强的方法
2、常用的表达式
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
(1)execution(* com.tulun.bean.Student1.show(..)) 表类里面的某一个方法
(2)execution(* com.tulun.bean.Student1.*(..)) 表类某个包里类所有方法
(3)execution(* *.*(..)) 表示所有
例:
-匹配所有类public方法 execution(public *.*(..))
-匹配指定包下所有类方法 execution(* com.tulun.bean.*(..)) (不包含子包)
- execution(* com.tulun.bean..*(..)) (包含包、子包下所有类)
-匹配指定类所有方法 execution(* com.tulun.bean.Book.*(..))
-匹配实现特定接口所有类方法 execution(* com.tulun.bean.Book+.*(..))
-匹配所有com开头的方法 execution(* com*(..))
<!--配置对象-->
<bean id="student" class="com.tulun.bean.Student1"/>
<bean id="log" class="com.tulun.bean.AddAdvice"/>
<!--配置AOP操作-->
<aop:config>
<!--配置切入点:通过表达式实现-->
<aop:pointcut id="a" expression="execution(* com.tulun.bean.Student1.add(..))"/>
<!--配置切面:把增强用到方法上-->
<aop:aspect ref="AddAdvice" >
<!--配置增强类:使用的前置增强-->
<aop:before method="addBefore" pointcut-ref="a"/>
</aop:aspect>
</aop:config>
(5)进行测试调用:
ApplicationContext context = new ClassPathXmlApplicationContext("SpringAOP.xml");
Student1 student = (Student1)context.getBean("student");
student.add();
2、注解实现AOP
(1)、在XML配置创建对象
<!--配置对象-->
<bean id="student" class="com.tulun.bean.Student1"/>
<bean id="log" class="com.tulun.bean.AddAdvice"/>
(2)、开启AOP操作
<!--开启AOP操作:注解方式使用-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(3)、在增强类上添加注解,在增强类方法上添加通知的注解
/**
* 增强类
*/
@Aspect
//@Aspect 配置在类上,配置在增强类上
public class AddAdvice {
//前置增强
//Before前置通知标签,需要配置在方法, value属性值是通过execution表达式来配置切入点
@Before(value = "execution(* com.tulun.bean.Student1.add(..))")
public void beforeAdvice(){
System.out.println("前置增强");
}
}
以上关于AOP的两种实现方式,都是一些很简单的过程,如果需要深入还是应该多看看源码,基于老式的xml文件的实现,不推荐我们这样做,为什么?当系统大了之后,那系统中将会有很多的bean
、xml
文件,这不容易维护和管理,我建议采用基于注解的方式进行AOP编程会更好。