一、AOP概述
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。<
简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
二、AOP的作用及优势
- 作用:
在程序运行期间,不修改源码对已有方法进行增强
- 优势:
1、减少重复代码; 2、提高开发效率; 3、维护方便
三、AOP的实现方式(动态代理)
- 静态代理
- 动态代理(也叫JDK代理)
- Cglib代理
动态代理的特点
1:字节码随用随创建,随用随加载。
2:它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
3:装饰者模式就是静态代理的一种体现。动态代理常用的两种方式
1、基于接口的动态代理 —— 提供者:JDK官方的Proxy类。要求:被代理类最少实现一个接口。
2、基于子类的动态代理 —— 提供者:第三方的CGLib,如果报asmxxxx异常,需要导入asm.jar。要求:被代理类不能用final修饰的类(最终类)。<
四、AOP相关术语
1. Joinpoint(连接点)
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
2. Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。拦截的方法,连接点拦截后变成切入点
3. Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
4. Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
5. Target(目标对象):
代理的目标对象。
6. Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
7. Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。
8. Aspect(切面):
是切入点和通知(引介)的结合。
五、基于XML的AOP配置
第一步:准备客户的业务层和接口(需要增强的类)
客户的业务层接口:ICustomerService 与实现类 CustomerServiceImpl
/**
* 客户的业务层接口
*/
public interface ICustomerService {
/**
* 保存客户
*/
void saveCustomer();
/**
* 修改客户
* @param i
*/
void updateCustomer(int i);
}
/**
* 客户的业务层实现类
*/
public class CustomerServiceImpl implements ICustomerService {
@Override
public void saveCustomer() {
System.out.println("调用持久层,执行保存客户");
}
@Override
public void updateCustomer(int i) {
System.out.println("调用持久层,执行修改客户");
}
}
第二步:拷贝jar包
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理,要在Spring 中使用AOP,还需要加入这两个jar包
1、aopalliance.jar
2、aspectjweaver.jar
创建spring的配置文件并导入约束
<?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"
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.xsd">
</beans>
把客户的业务层配置到spring容器中
<!-- 把资源交给spring来管理 -->
<bean id="customerService" class="org.hodge.service.impl.CustomerServiceImpl"/>
第三步:制作通知(增强的class)
/**
* 一个记录日志的工具类
*/
public class Logger {
/**
* 期望:此方法在业务核心方法执行之前,就记录日志
*/
public void beforePrintLog(){
System.out.println("Logger类中的printLog方法开始记录日志了。。。。");
}
}
把通知类用bean标签配置起来
<!-- 把有公共代码的类也让spring来管理(把通知类也交给spring来管理) -->
<bean id="logger" class="org.hodge.util.Logger"></bean>
AOP配置
<!-- aop的配置 -->
<aop:config>
<!-- 配置通用切入点表达式 -->
<aop:pointcut expression="execution(* org.hodge.service.impl.*.*(..))" id="pt1"/>
<!-- 配置切面 :此标签要出现在aop:config内部
id:给切面提供一个唯一标识
ref:引用的是通知类的bean的id
-->
<aop:aspect id="logAdvice" ref="logger">
<!-- 用于配置前置通知:指定增强的方法在切入点方法之前执行
method:用于指定通知类中的增强方法名称
ponitcut-ref:用于指定切入点的表达式的引用
-->
<aop:before method="beforePrintLog" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
测试结果
六、基于@AspectJ注解的AOP实现
修改Spring的配置文件并导入约束
由于使用了注解的方式,需要导入xmlns:context约束来使用context:component-scan标签扫包
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring,在创建容器时要扫描的包 -->
<context:component-scan base-package="org.hodge"></context:component-scan>
<!-- 开启spring对注解AOP的支持 -->
<aop:aspectj-autoproxy/>
</beans>
在通知类上使用@Aspect注解声明为切面
/**
* 一个记录日志的工具类
*/
@Component("logger")
@Aspect//表明当前类是一个切面类
public class Logger {
/**
* 期望:此方法在业务核心方法执行之前,就记录日志
* 前置通知
*/
@Before("execution(* org.hodge.service.impl.*.*(..))")//表示前置通知
public void beforePrintLog(){
System.out.println("前置通知:Logger类中的printLog方法开始记录日志了");
}
}
七、不使用XML的配置方式
@Configuration
@ComponentScan(basePackages="org.hodge")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}