AOP(面向切面编程)的理解与实现
1.什么是面向切面编程
要理解面向切面编程,必须要理解切面的含义,切面是指在程序的许多地方都会应用到的功能,例如对于日志系统来说,记录日志就可以被当做一个切面,在一个应用程序的许多地方,日志都是不可或缺的,但在这些地方,我们主要的关注点却不一定记录日志上,比如当我们在开启一个数据库连接时,我们主要关注的点在于开启连接,而不是记录日志,举个现实中的例子就是,我们日常生活中会使用到电脑,电视机,电灯等多种电器,产生的电力消耗都由电表来记录,但我们主要的关注点只在使用电器上,电表就可以理解为一个切面,面向切面编程可以将切面与应用程序解耦合
2.一些术语
- Advice(通知):
切面所需要的工作以及开始工作的时间,例如在使用电脑时,电表增加用电量,使用电脑之后,电表统计总的用电量,这些都是通知,通知分为以下几种
1.前置通知(Before):目标方法被调用之前调用通知
2.后置通知(After):目标方法被调用之后调用通知
3.返回通知(After-Returning):目标方法成功返回之后调用通知
4.异常通知(After-Throwing):目标方法抛出异常后调用通知
5.环绕通知(Around):通知被包裹被通知的方法,在通知方法调用前和调用后执行自定义的行为 - 连接点(join point):
插入切面的一个点,例如在调用某方法之前将切面插入 - 切点(poincut):
在那个地方调用通知,例如在建立数据库连接的函数上 - 切面(Aspect):
完成工作所需要的一切,比如一个电表 - 引入(Introduction):
向现有类添加属性和新方法 - 织入(Waving):
把切面应用到目标对象创建新的代理的过程
3.例子
spring框架中实现AOP与实现DI一样不只有一种方式,这里以一场演出为例(观众为一个切面)
- 1.通过注解实现
//表演接口
package aoptest;
public interface Performance {
public void perform(String name);
}
//表演类,注解为一个组件,用于注入单元测试类中
package aoptest;
import org.springframework.stereotype.Component;
@Component("testPerformance")
public class TestPerformance implements Performance{
public void perform(String name){
System.out.println("表演开始...");
System.out.println("正在表演剧目"+name);
System.out.println("表演结束...");
}
}
//spring配置类,开启自动扫描和开启AOP
package aoptest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "aoptest")
@EnableAspectJAutoProxy
public class ConcertConfig {
}
//定义环绕通知
package aoptest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class Audience {
//定义一个切点
@Pointcut("execution(* aoptest.Performance.perform(String)) && args(name)")
public void performance(String name){};
/*
@Before("performance(name)") //前置通知
public void closePhone(String name){
System.out.println("表演马上开始,关闭手机,表演的剧目 是"+name);
}
@AfterReturning("performance(name)") //返回通知
public void sayGood(String name){
System.out.println(name+"演的好");
}
}
*/
@Around("performance(name)") //环绕通知
public void watchPerformance(ProceedingJoinPoint joinPoint,String name){
try{
System.out.println("关闭手机,观众就坐,今天的剧目是"+name);
joinPoint.proceed();//连接点
System.out.println(name+"演的不错");
}catch(Throwable e){
System.out.println(name+"演的什么玩意!");
}
}
}
//测试类
package aoptest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConcertConfig.class)
public class TestMain {
Performance performance;
@Resource(name="testPerformance")
public void setPerformance(Performance performance){
this.performance = performance;
}
@Test
public void main(){
performance.perform("花木兰");
performance.perform("倩女幽魂");
}
}
结果如下
关闭手机,观众就坐,今天的剧目是花木兰
表演开始...
正在表演剧目花木兰
表演结束...
花木兰演的不错
关闭手机,观众就坐,今天的剧目是倩女幽魂
表演开始...
正在表演剧目倩女幽魂
表演结束...
倩女幽魂演的不错
Process finished with exit code 0
- 通过配置文件实现
不使用注解声明切面的方式如下
<bean id="audience" class="aoptest.audience">
</bean>
<aop:config>
<aop:aspect ref="auidence">
<aop:before
pointcut="execution(* aoptest.Performance.perform(..))"
method="closePhone"/>
<aop:after
pointcut="execution(* aoptest.Performance.perform(..))"
method="sayGood"/>
</aop:aspect>
</aop:config>
声明切点并且使用切点的方式如下
<bean id="auidence" class="aoptest.auidence">
</bean>
<aop:config>
<aop:aspect ref="auidence">
<!--没有参数的切点
<aop:poincut
id="performance"
expression="execution(* aoptest.Performance.perform(..))"/>
-->
<aop:poincut
id="performance"
expression="execution(* aoptest.Performance.perform(String)) and args(name)"/>
<aop:before
poincut-ref="performance"
method="closePhone"/>
<aop:after-returning
poincut-ref="performance"
method="sayGood"/>
<aop:around
pointcut-ref="performance"
method="watchPerformance"/>
</aop:aspect>
</aop:config>
以上就是spring两种实现AOP的方式