简介
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
作用:AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。
可以将其分为两类:
静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。
AOP 领域中的特性术语:
通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
切点(PointCut): 可以插入增强处理的连接点。
切面(Aspect): 切面是通知和切点的结合。
引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
使用
我们需要先在maven中添加这样一个包 简单理解,支持切入点表达式等等
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
- 第一中方式 使用spring中原生接口API
写一个接口、实现类。
package aw.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
package aw.service;
public class UserServiceImp implements UserService{
@Override
public void add() {
System.out.println("添加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void select() {
System.out.println("查看了一个用户");
}
}
上面是我们创建好的实体类,现在我们需要在他执行的时候添加一些日志的输出情况。
方法执行前的日志,这里重点是我们要让这个类继承MethodBeforeAdvice类,然后覆盖这类里面多的before方法,Method method, Object[] objects, Object o这个三个参数添加切点类的方法、参数数组、切点类
package aw.service.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LogBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法");
}
}
执行后的日志,这里他继承AfterReturningAdvice类, 这个覆盖方法是afterReturning他的参数比before多一个,Object returnValue这个是返回值。
package aw.service.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class LogAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果是"+returnValue);
}
}
现在开始写配置文件
首先是在spring中注册类
<bean id="logBefore" class="aw.service.log.LogBefore"/>
<bean id="logAfter" class="aw.service.log.LogAfter"/>
<bean id="userServiceImp" class="aw.service.UserServiceImp"/>
写aop的配置,这时我们需要先添加约束,我们可以先添加<aop:然后进行导入。
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
这里重点讲解一下配置文件中的标签,
<aop:pointcut id="" expression="execution()"很明显这是一个切点类,id自己进行设置,execution表达式他是一种格式,下面是他的使用方式
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
<aop:advisor advice-ref="" pointcut-ref="" 定义通知其(通知器跟切面一样,也包括通知和切点)。advice-ref是引用我们之前定义好的切面类,pointcut-ref是定义的切点类。
<aop:config>
<aop:pointcut id="poi" expression="execution(* aw.service.UserServiceImp.*(..))"/>
<aop:advisor advice-ref="logBefore" pointcut-ref="poi"/>
<aop:advisor advice-ref="logAfter" pointcut-ref="poi"/>
</aop:config>
测试类,我们在使用这种API接口形式,再接受对象的时候必须使用接口接受。
import aw.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class T1 {
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理必须是接口
UserService userService = (UserService) context.getBean("userServiceImp");
userService.add();
}
}
2.使用切面类
使用切面类我们就单独定义一个类,在里面写好我们需要的切面。
package aw.diy;
public class Diy {
public void before(){
System.out.println("执行前——————————");
}
public void after(){
System.out.println("执行后------------");
}
}
配置文件,同样是先注册切面类,<aop:config在写配置,<aop:aspect ref="diy"我们现在使用的这种就是以切面类来定义, <aop:pointcut同样是写切点类, <aop:before这个标签是执行之前的标签 方法名就是我们切面里的方法名,切点是定义的切点。
<bean id="diy" class="aw.diy.Diy"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="poi" expression="execution(* aw.service.UserServiceImp.*(..))"/>
<aop:before method="before" pointcut-ref="poi"/>
<aop:after method="after" pointcut-ref="poi"/>
</aop:aspect>
</aop:config>
测试类同样
import aw.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class T1 {
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理必须是接口
UserService userService = (UserService) context.getBean("userServiceImp");
userService.add();
}
}
3.注解开发
同样我们先写这个注解类,首先是在这个的上面添加@Aspect注解这就说明这个类是一个切面类。在我们写方法上面添加好相对应的注解名,注解的参数需要添加execution表达式。
@Around注解是环绕的意思、ProceedingJoinPoint 这个类对切面类进行一些操作。
package aw.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
public class AnnotationPointCut {
@Before("execution(* aw.service.UserServiceImp.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution(* aw.service.UserServiceImp.*(..))")
public void after(){
System.out.println("方法执行后");
}
@Around("execution(* aw.service.UserServiceImp.*(..))")
public void around(ProceedingJoinPoint pj) throws Throwable {
System.out.println("方法环绕前");
Signature signature = pj.getSignature();//获得签名 就是那个类下的那个方法
System.out.println("signature:"+signature);
//执行方法
Object proceed = pj.proceed();
System.out.println(proceed);
System.out.println("方法环绕后");
}
}
配置文件,添加注册类,开启注解支持
<bean id="an" class="aw.service.AnnotationPointCut"/>
<!-- 开启注解支持-->
<aop:aspectj-autoproxy/>
测试类
import aw.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class T1 {
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理必须是接口
UserService userService = (UserService) context.getBean("userServiceImp");
userService.add();
}
}
他的输出顺序:
方法环绕前
signature:void aw.service.UserService.add()
方法执行前
添加了一个用户
方法执行后
null
方法环绕后