AOP
1、什么是AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
我们从这个图就能看出来,比如我们在代理对象中想要添加一些功能,就是 目标对象的一些方法,我们不改变原来的源代码,而是通过横切的方式将其添加进去,比如在执行前置日志的功能之后会自动调用add()方法,这样遵循了开闭原则。
2、AOP在Spring中的作用
主要是提供声明式事务:允许用户自定义切面:
- 切面(aspect):横切关注点,被模块化的特殊对象。可以把他当成一个类,相当于上面的目标对象。
- 通知(advice):切面是必须要完成的任务。既是类中的方法,比如add之类的方法。
- 目标(target):被通知的对象,就是相当于上面的业务逻辑。
- 代理(proxy):向目标对象应用通知之后创建的对象。
- 切入点(pointCut):切面通知执行地方。上面的话就是在前置日志和后置日志中间的点。
- 连接点(joinPoint):与切入点匹配的执行点。
3、使用Spring实现AOP
使用AOP前需要导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
下面实现了一增删改查
方式一:使用spring接口
接口:
package com.yu.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
实现类
package com.yu.service;
public class UseServiceImpl 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 query() {
System.out.println("查询一个用户");
}
}
AOP前置增强
package com.yu.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
//这个的参数类型和InvocationHandle中的invake的一样
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
AOP后置增强(带返回)
package com.yu.log;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//和前面的MethodBeforeAdvice相比多了一个返回值,因为是执行后的
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
System.out.println("执行了"+target.getClass().getName()+"的"+method.getName()+"方法返回结果为:"+returnValue);
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns="http://www.springframework.org/schema/beans" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.yu.service.UseServiceImpl"/>
<bean id="log" class="com.yu.log.Log"/>
<bean id="afterLog" class="com.yu.log.AfterLog"/>
<!--配置aop-->
<!-- 方式一:使用原生Spring api接口-->
<!-- 配置-->
<aop:config>
<!--切入点 expression表达式execution(要执行的位置 修饰词 返回值 类名 方法名 参数)-->
<!-- 第一个*是返回类型
第二个参数是包下的类名的全部方法,(。。)表示任意参数-->
<aop:pointcut id="pointcut" expression="execution(* com.yu.service.UseServiceImpl.*(..))"/>
<!-- 执行环绕 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
excution表达式中,第一个:是返回类型 ,这里我们对类型没有限制,所以使用*,空格隔开
第二个值:是需要拦截的对象类中任意参数的方法。
方法二:自定义类(比前面的更加简单一点)
这个方法不使用aop的beforemethodadvise等接口,而是使用自己定义的类
切面:(我们使用的类)
package com.yu.diy;
public class DiyPointCut {
public void before(){
System.out.println("----------------------pointcut前面-----------------------");
}
public void after(){
System.out.println("-----------------------pointcut后面------------------------");
}
}
接口和实现类还是使用之前的
xml的的使用,和前面略有不同,使用到了aop:aspect标签
<bean id="diy" class="com.yu.diy.DiyPointCut"/>
<aop:config>
<!-- 自定义切入面-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.yu.service.UserService.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
方法三:使用注解实现aop
步骤
-
开启注解支持
<!--开启注解支持,JDK( proxy-target-class="false") cglib( proxy-target-class="true")--> <aop:aspectj-autoproxy proxy-target-class="false"/>
-
创建注解类
package com.yu.diy; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; //使用注解实现 @Aspect public class AnnotationPointcut { @Before("execution(* com.yu.service.UseServiceImpl.*(..))") public void before(){ System.out.println("---------方法执行前----------"); } @After("execution(* com.yu.service.UseServiceImpl.*(..))") public void after(){ System.out.println("---------方法执行后----------"); } // @Around("execution(* com.yu.service.UseServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); //执行方法,如果不写这个就会不执行目的方法 Object proceed = jp.proceed(); Signature signature = jp.getSignature();//获得签名 // System.out.println(proceed); System.out.println("环绕后"); } //顺序:环绕前,执行前,执行方法,环绕后,执行后
-
将自定义类交给spring托管
<bean id="annotationPointcut" class="com.yu.diy.AnnotationPointcut"/> <!--也可以使用注解的方式添加到bean里面-->
缺点:每次使用AOP注解时都要重新写execution表达式