AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
举个现实中的例子:
做活动的时候,一般对每一个接口都会做活动的有效性校验(是否开始、是否结束等等)、以及这个接口是不是需要用户登录。
按照正常的逻辑,我们可以这么做:
如果是这样的话,有多少个接口,我们就需要复制多少次红框里的代码。
下面,我们的解决方法是把公共的方法抽象出来,每个接口都调用这个公共的方法。
虽然不需要重复的复制代码,但是每次需要的时候我们都需要去调用这个方法,如果在我们需要的时候(切面)这个方法都能 直接注入到需要的地方(切点)就好了。
AOP的实现方式:
方法一:通过springAPI实现
接口(被增强对象/目标对象Target):
public interface UserService {
//四个连接点Joint point
public void add();
public void delete();
public void update();
public void find();
}
实现类:
public class UserServiceImpl 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 find() {
System.out.println("查询一个用户");
}
}
增强类:
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了...");
}
}
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+o);
}
}
配置spring配置文件
<!--aop的配置-->
<aop:config>
<!--切入点expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut"
expression="execution(* com.jason.service.UserServiceImpl.*(..))"/>
<!--执行环绕,advice-ref执行方法,pointcut-ref切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试类:
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationImpl.xml");
UserService userService = context.getBean("userService", UserService.class);
//切入点Pointcut
userService.add();
}
}
方法二:自定义类实现AOP
自定义切入类:
public class DiyPointCut {
public void before(){
System.out.println("========执行方法前=========");
}
public void after(){
System.out.println("========执行方法后=========");
}
}
配置配置文件:
<bean id="diy" class="com.jason.diy.DiyPointCut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="point" expression="execution(* com.jason.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方法三:使用注解
编写增强类:
//标注这个面是个切面
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.jason.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("+++方法执行前+++");
}
@After("execution(* com.jason.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("+++方法执行后+++");
}
@Around("execution(* com.jason.service.UserServiceImpl.*(..))")
public void adround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:" + joinPoint.getSignature());
Object proceed = joinPoint.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
配置配置文件:
<bean id="annotationPointCut" class="com.jason.diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>
AOP中的相关概念:
Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
在AOP实现方法一例子中,add()、delete()、update()、find()方法为连接点,即可以被增强或拦截到的点。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
在AOP实现方法一例子中,在实际开发的测试类中,是对add()方法进行增强,也就是真正被增强或拦截的点,因此,add()方法称为切点。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
在AOP实现方法一例子中,增强类即为增强,对add()方法进行before和after的增强 。
Target(目标对象):织入 Advice 的目标对象.。
在AOP实现方法一例子中,UserService是被增强的对象,因此称为目标对象也叫倍增强对象。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程