定义
AOP(Aspect Oriented Programming),面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
用处
举个景点管理的例子。
pminitor 是一个监控类,在日志中写入时间戳和调用信息,用来监控业务逻辑的执行情况和效率
transManager 是事务类,用于写数据库时生成一个事务。
import com.smart.dao.ViewPointDao;
import com.smart.dao.ViewSpaceDao;
import com.smart.domain.ViewSpace;
/**
* 景区管理的服务类
*/
public class ViewSpaceService {
private TransactionManager transManager;
private PerformanceMonitor pmonitor;
private ViewSpaceDao viewSpaceDao;
private ViewPointDao viewPointDao;
/**
* 新增一个旅游景区
*
* @param viewSpace
*/
public void addViewSpace(ViewSpace viewSpace) {
pmonitor.start();
transManager.beginTransaction();
viewSpaceDao.addViewSpace(viewSpace);
transManager.endTransaction();
pmonitor.end();
}
/**
* 删除某个景点
*
* @param pointId
*/
public void deleteViewPoint(int pointId) {
pmonitor.start();
transManager.beginTransaction();
viewPointDao.deleteViewPoint(pointId);
transManager.endTransaction();
pmonitor.end();
}
}
在上面的例子中,增加,删除一个景点的业务逻辑,重复编码了监控和事物,这些非业务代码充斥在业务代码中,使得在编写业务代码时需要考虑各种非业务逻辑,比如写日志,,性能监控,事物管理。这使得我们在编写业务逻辑时变得困难重重,并且在后期维需要监控业务逻辑时不得不修改源码,并且出现了大量重复的编码。
AOP就是将依附在业务逻辑中的非业务代码抽离开来, 作为一个独立的类单独管理,避免了重复的编码,同时在编写业务逻辑时不需要考虑非业务逻辑。
例子
引用一个猴子偷桃,守护者守护果园抓住猴子的小情节。
猴子偷桃类(普通类)
package com.samter.common;
/**
* 猴子
* @author Administrator
*
*/
public class Monkey {
public void stealPeaches(String name){
System.out.println("【猴子】"+name+"正在偷桃...");
}
}
守护者类(声明为Aspect)
package com.samter.aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 桃园守护者
* @author Administrator
*
*/
@Aspect
public class Guardian {
@Pointcut("execution(* com.samter.common.Monkey.stealPeaches(..))")
public void foundMonkey(){}
@Before(value="foundMonkey()")
public void foundBefore(){
System.out.println("【守护者】发现猴子正在进入果园...");
}
@AfterReturning("foundMonkey() && args(name,..)")
public void foundAfter(String name){
System.out.println("【守护者】抓住了猴子,守护者审问出了猴子的名字叫“"+name+"”...");
}
}
XML配置文件
<?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-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>
<!-- 定义Aspect -->
<bean id="guardian" class="com.samter.aspect.Guardian" />
<!-- 定义Common -->
<bean id="monkey" class="com.samter.common.Monkey" />
<!-- 启动AspectJ支持 -->
<aop:aspectj-autoproxy />
</beans>
测试类
package com.samter.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
Monkey monkey = (Monkey) context.getBean("monkey");
try {
monkey.stealPeaches("孙大圣的大徒弟");
}
catch(Exception e) {}
}
}
输出
【守护者】发现猴子正在进入果园...
【猴子】孙大圣的大徒弟正在偷桃...
【守护者】抓住了猴子,守护者审问出了猴子的名字叫“孙大圣的大徒弟”...
分析
- 写了一个猴子正在偷桃的方法。
- 写了一个标志为@Aspect的类,它是守护者。它会在猴子偷桃之前发现猴子,并在猴子偷桃之后抓住猴子。
- @Aspect的声明表示这是一个切面类。
- @Pointcut使用这个方法可以将com.samter.common.Monkey.stealPeaches(..)方法声明为poincut即切入点。作用,在stealPeaches方法被调用的时候执行2的foundMonkey方法。其中execution是匹配方法执行的切入点,也就是spring最常用的切入点定义方式。
- @Before(value=”foundMonkey()”):@Before声明为在切入点方法执行之前执行,而后面没有直接声明切入点,而是value=”foundMonkey()”,是因为如果@afterReturning等都有所改动的时候都必须全部改动,所以统一用Pointcut的foundMonkey代替,这样子有改动的时候仅需改动一个地方。其他@AfterReturning类同。 - 是xml配置文件,里面有具体的注释。
特别说明:Guardian类里面的@Pointcut(“execution(* com.samter.common.Monkey.stealPeaches(..))”),如果stealPeaches有参数则..表示所有参数,@AfterReturning(“foundMonkey() && args(name,..)”)的&& args(name,..)可以获取切入点方法stealPeaches的参数。
- 总结:这里列举了一个简单的例子,但是不难引申到应用中,当你写一个登陆系统的时候,你或许要记录谁成功登陆了系统,谁登陆系统密码错误等等的信息,这样子你用切面是再合适不过的了,总之当你的事务逻辑都设计到日志、安全检查、事务管理等等共同的内容的时候,用切面是要比你没有一个事务逻辑类都有相关代码或者相关引用好得多。
实现
AOP可以用动态代理实现