什么是AOP?
AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。AOP和OOP互为补充,面向对象编程将程序分解成各个层次的对象,而面向切面编程将程序运行过程分解成各个切面。可以这样理解:面向对象编程是从静态角度考虑程序结构,而面向切面编程是从动态角度考虑运行过程
AOP的基本概念
- AOP从程序运行角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。
- AOP框架并不与特定的代码耦合,AOP框架能处理程序执行中特定的切入点(Pointcut),而不与某个具体类耦合。AOP具有如下两个特征:
- 各步骤之间的良好隔离性
- 源代码无关性
- 关于面向切面编程的一些术语
- 切面(Aspect):切面用于组织多个Advice,Advice放在切面中定义
- 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用
- 增强处理/通知(Advice):AOP框架在特定的切入点执行的增强处理,处理有“around”、“before”、“after”等类型
- 切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求是=时,该连接点将被添加增强处理,该连接点也就变成了切入点
如何使用表达式来定义切入点是AOP的核心,Spring默认使用AspectJ切入点语法。
- 引入:将方法或字段添加到被处理的类中。Spring允许将新的接口引入到任何被处理的对象中。例如,你可以使用一个引入,使任何对象实现IsModified接口,以此来简化缓存
- 目标对象:被AOP框架进行增强处理的对象,也被称为被增强的对象。如果AOP框架采用的是动态AOP实现,那么该对象就是一个被代理的对象
- AOP代理:AOP框架创建的对象,简单的说,代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是cglib代理。前者为实现接口的目标对象的代理,后者为不实现接口的目标对象的代理
- 织入(Weaving):将增强处理添加到目标对象中去,并创建一个被增强的对象(AOP代理对象)的过程就是织入。织入的两种实现方式--编译时增强(如AspectJ)和运行时增强(如Spring AOP)。Spring和其他纯Java AOP框架一样,在运行时完成织入。
关系图如下:

AOP的基本操作
-
为什么使用AOP
业务场景:
服务层:提供30个模块的服务。同时这些模块都依赖某个或某几个公共服务。(事务管理,日志管理。)
传统方式:使用继承的方式
将子类公有需要执行的方法抽取成一个父类,在使用到公共方法的地方去调用父类的方法
AOP目的:程序员能够安心做业务成的业务 - 使用AOP之最最最原始的步骤
- 编写服务层接口和实现类(目标类)
业务层接口:
public interface StudentService {
void addStudent();
void delStudent();
void updateStudent();
String findStudent();
}
业务层实现类:
public class StudentServiceImpl implements StudentService {
@Override
public void addStudent() {
System.out.println("addStudent");
}
@Override
public void delStudent() {
System.out.println("delStudent");
}
@Override
public void updateStudent() {
System.out.println("updateStudent");
}
@Override
public String findStudent() {
// TODO Auto-generated method stub
return "haha";
}
}
- 编写通知类
前置通知:在调用目标方法前执行
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class LogAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("前置通知,日志记录");
}
}
环绕通知:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕前");
//回调原来的目标方法
Object proceed = invocation.proceed();//上面的代码在调用目标方法之前执行,下面的代码在调用目标方法后执行
System.out.println("环绕后");
return proceed;
}
}
后置通知:在调用目标方法后执行
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class TestAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知返回值:"+returnValue);
}
}
- 使用Spring的代理类进行整合,在spring核心配置文件中整合
手动配置
<!-- 1.创建目标类对象 -->
<bean id="studentService" class="org.cx.service.StudentServiceImpl" ></bean>
<!-- 2.创建通知类对象 -->
<!-- <bean id="logAdvice" class="com.cx.advice.LogAdvice" ></bean>
<bean id="testAdvice" class="com.cx.advice.TestAdvice" ></bean> -->
<bean id="aroundAdvice" class="com.cx.advice.AroundAdvice" ></bean>
<!-- 3.创建Spring的代理类对象 -->
<bean id="studentServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean" >
<!-- aop编程整合 -->
<!-- 1.整合目标类 -->
<property name="target" ref="studentService"></property>
<!-- 2.整合通知类 -->
<property name="interceptorNames">
<list>
<!-- <value>logAdvice</value>
<value>testAdvice</value> -->
<value>aroundAdvice</value>
</list>
</property>
<!-- 3.整合接口关联 -->
<property name="proxyInterfaces" value="org.cx.service.StudentService"></property>
</bean>
自动配置
<!-- 1.创建目标类对象 -->
<bean id="studentService" class="com.cx.service.StudentServiceImpl" ></bean>
<!-- 2.创建通知类对象 -->
<!-- <bean id="logAdvice" class="com.cx.advice.LogAdvice" ></bean>
<bean id="testAdvice" class="com.cx.advice.TestAdvice" ></bean> -->
<bean id="aroundAdvice" class="com.cx.advice.AroundAdvice" ></bean>
<!-- 3.自动方式配置AOP -->
<aop:config>
<!-- 1.配置扫描的服务层
切入点表达式:扫描到具体的方法
* com.cx.service.*.*(..)
* 返回类型 任意类型
com.cx.service 服务层的包名
第一个.*包下面的所有类
第二个.*(..)类里面的所有方法-->
<aop:pointcut expression="execution(* com.cx.service.*.*(..))" id="mypoint"/>
<!-- 2.让服务层通知关联 -->
<aop:advisor advice-ref="aroundAdvice" pointcut-ref="mypoint"/>
</aop:config>
- 测试
使用手动配置的测试
public static void main( String[] args )
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService studentService = ac.getBean("studentServiceProxy",StudentService.class);
studentService.addStudent();
//studentService.findStudent();
}
使用自动配置的测试:
public static void main( String[] args )
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService studentService = ac.getBean("studentService",StudentService.class);
studentService.addStudent();
//studentService.findStudent();
}
使用AspectJ框架进行AOP编程
AspectJ的通知和原始通知不同:不需要单独去实现接口,所有通知方法写在一个类
- 编写通知
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
//aspectj包下的
//前置通知
public void myBefore(JoinPoint jp){
System.out.println("前置通知");
}
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前");
//手动执行目标方法
Object proceed = pjp.proceed();
System.out.println("环绕后");
return proceed;
}
public void myAfter(JoinPoint jp,Object res){
System.out.println("后置通知:"+res);
}
}
- 编写业务层接口和实现类
接口
public interface UserService {
void addUser();
void delUser();
void updateUser();
String findUser();
}
实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("addUser");
}
@Override
public void delUser() {
System.out.println("delUser");
}
@Override
public void updateUser() {
System.out.println("updateUser");
}
@Override
public String findUser() {
return "美好的一天从微笑开始";
}
}
- 使用AspectJ配置整合AOP
<!-- 1.创建目标类对象 -->
<bean id="userService" class="com.cx.service.UserServiceImpl" ></bean>
<!-- 2.创建通知类对象 -->
<!-- <bean id="logAdvice" class="com.cx.advice.LogAdvice" ></bean>
<bean id="testAdvice" class="com.cx.advice.TestAdvice" ></bean> -->
<bean id="myAdvice" class="com.cx.advice.MyAdvice" ></bean>
<!-- 3.AspectJ的AOP编程
a.有一个AspectJ单独的节点
b.通知也有单独的节点-->
<aop:config>
<aop:aspect ref="myAdvice">
<!-- 扫描方法:定义切入点表达式 -->
<aop:pointcut expression="execution(* com.cx.service.*.*(..))" id="mypoint"/>
<!-- 配置前置通知 -->
<!-- <aop:before method="myBefore" pointcut-ref="mypoint"/> -->
<!-- 配置环绕通知 -->
<!-- <aop:around method="myAround" pointcut-ref="mypoint"/> -->
<!-- 配置后置通知 -->
<aop:after-returning method="myAfter" pointcut-ref="mypoint" returning="res"/>
</aop:aspect>
</aop:config>
AspectJ使用注解的方式配置
- 编写业务接口和实现类
接口
public interface CarService {
void addCar();
String findCar();
}
实现类
import org.springframework.stereotype.Service;
@Service("carService")
public class CarServiceImpl implements CarService {
@Override
public void addCar() {
System.out.println("addCar");
}
@Override
public String findCar() {
// TODO Auto-generated method stub
return "风驰天下,大运摩托";
}
}
- 编写自定义通知类
@Component("myCarAspect")
@Aspect
public class MyCarAspect {
//定义切入点表达式
@Pointcut("execution(* com.cx.service.*.*(..))")
public void myPoint(){
}
// @Before("myPoint()")//前置通知
// public void myCarBefore(JoinPoint jp){
// System.out.println("前置通知:美好的一天从绿色开始");
// }
@Around("myPoint()")//环绕通知
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前");
//手动执行目标方法
Object proceed = pjp.proceed();
System.out.println("环绕后");
return proceed;
}
}
- 整合配置
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.cx"></context:component-scan>
<!-- 开启AspectJ的注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
网友评论