Spring AOP
AOP概念
AOP意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
有关AOP的名词
横切关注点、切面、通知、目标、代理、切入点、连接点
Spring中的五种通知(Advice)
- 前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
- 正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
- 异常返回通知[After throwing advice]:在连接点抛出异常后执行。
- 返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
- 环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join
point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
Spring实现AOP步骤
方法一:通过Spring API 实现
- 导入AOP所需要的jar包
- 编写业务接口和实现类
public interface xxService{
public void a();
public void b();
}
public class xxServiceImpl implements xxService{
public void a(){
System.out.println("a");
}
public void b(){
System.out.println("b");
}
}
- 编写前置增强类(不同的通知需要实现不同的接口)
public class BeforeLog implements MethodBeforeAdvice{
//method:要执行目标对象的方法
//objects:被调用的方法的参数
//Object:目标对象
public void before(Method method,Object[]objects,Object o)throws Throwable{
System.out.println("前置增强");
}
}
- 在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">
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.cn.xxServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="BeforeLog" pointcut-ref="pointcut"/>
</aop:config>
<!--注册bean-->
<bean id="xxService" class="com.cn.xxServiceImpl"/>
<bean id="BeforeLog" class="com.cn.log.BeforeLog"/>
</beans>
- 测试
Spring中可以用自定义类实现Aop
//自定义切入类
public class Cut(){
public void before(){
System.out.println("方法执行前");
}
}
<!--配置文件-->
<?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">
<!--aop的配置-->
<aop:config>
<!--注意:不同点,自定义切入类多加了一个配置-->
<aop:aspect ref="cut">
<aop:pointcut id="pointcut" expression="execution(* com.cn.xxServiceImpl.*(..))"/>
<!--不同点-->
<aop:before pointcut-ref="pointcut" method="before">
</aop:aspect ref="cut">
</aop:config>
<!--注册bean-->
<bean id="cut" class="com.cn.Cut"/>
</beans>
方法二:使用注解实现
- 编写一个增强类(注解实现)
@Aspect
@Component
public class Cut(){
@Before("execution(* com.cn.xxServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前");
}
}
- 增加支持注解的配置
<aop:aspectj-autoproxy/>
<context:component-scan base-packag="com.cn"/>
- 测试
aspectj-autoproxy说明:
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。