Aspect-Oriented Programming(面向切面编程),以一种全新的方式思考程序的结构,这补充了Object-Oriented Programming(面向方法编程)范式的不足。OOP主要的模块单元是类,然而,AOP主要的模块单元是切面。切面允许关注诸如横跨不同类型和对象的业务管理这样的关注点。(这些关注点在AOP学术领域通常被称为横切)
(以上定义来自SpingAOP官方网站点击打开链接)
下面分别以纯AspectJ注解和XML配置两种配置方式实现一个简单的例子
一 AspectJ注解方式
1.先定义一个接口,用来获取一个简单对象
package spring; public interface PersonService { Person getPerson(String fooName, int age); }
package spring; public class DefaultPersonService implements PersonService{ public DefaultPersonService(){ } @Override public Person getPerson(String name, int age) { return new Person(name,age); } }3.其中Person类的定义如下
package spring; public class Person { private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString(){ //格式化输出 return "this is "+this.name+", age is"+this.age; } }4.为了统一管理项目的所有切点(Pointcut),最好将全部切点的定义统一放到同一个类文件里,如下所示
package spring; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class SimplePointcut { @Pointcut("execution(* spring.PersonService.getPerson(String,int))")//切点表达式 public void inServiceLayer() {//切点签名 //空方式实现即可 } }5. 对于同一个切点,允许同时为其配置多个通知(Advice),如下
package spring; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class SimpleAdvice { @Before("spring.SimplePointcut.inServiceLayer()")//指向指定的切点签名 public void beforeLog() { System.out.println("====================================="); System.out.println("Aop: do before in service layer"); System.out.println("====================================="); } @After("spring.SimplePointcut.inServiceLayer()") public void afterLog() { System.out.println("====================================="); System.out.println("Aop: do after in service layer"); System.out.println("====================================="); } }
package spring; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy public class Application { @Bean(name="personService") public DefaultPersonService defaultPersonService(){ return new DefaultPersonService(); } @Bean public SimplePointcut simplePointcut(){ return new SimplePointcut(); } @Bean public SimpleAdvice simpleAdvice(){ return new SimpleAdvice(); } public static void main(String[] args) throws Exception{ ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class); PersonService foo = (PersonService) ctx.getBean(PersonService.class); Person p = foo.getPerson("Lily", 12); Thread.sleep(100);//模拟其他业务逻辑 System.err.println(p); } }
二,XML配置方式
1. DefaultPersonService,Person,PersonService类的定义不变,通知类的定义改为以下方式
package spring; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.util.StopWatch; public class XMLAdvice { public void beforeLog(JoinPoint joinPoint) { System.out.println("====================================="); System.out.println("Aop: do before in service layer"); System.out.println("====================================="); } public void afterLog(JoinPoint joinPoint) { System.out.println("====================================="); System.out.println("Aop: do after in service layer"); System.out.println("====================================="); } //Around通知的参数为ProceedingJoinPoint,而不是JoinPoint public Object aroundLog(ProceedingJoinPoint call) throws Throwable { StopWatch clock = new StopWatch("aroundLog " ); try { clock.start(call.toShortString()); return call.proceed(); } finally { clock.stop(); System.out.println(clock.prettyPrint()); } } }2.xml的配置文件如下
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="personService" class="spring.DefaultPersonService" /> <bean id="adviceconfig" class="spring.XMLAdvice" /> <aop:config> <aop:aspect id="aspect" ref="adviceconfig"> <aop:pointcut id="theExecutionOfSomeFooServiceMethod" expression="execution(* spring.PersonService.getPerson(String,int))" /> <aop:before pointcut-ref="theExecutionOfSomeFooServiceMethod" method="beforeLog" /> <aop:after pointcut-ref="theExecutionOfSomeFooServiceMethod" method="afterLog" /> <!-- <aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod" method="aroundLog"/> --> </aop:aspect> </aop:config> </beans>3 . 示例代码如下
package spring; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) throws Exception{ BeanFactory ctx = new ClassPathXmlApplicationContext("daos.xml"); PersonService foo = (PersonService) ctx.getBean("personService"); Person p = foo.getPerson("Pengo", 12); Thread.sleep(100);//模拟其他业务逻辑 System.err.println(p); } }
xml优点:
切面、切点、通知等配置都写在一个或几个xml配置文件里,结构清晰,便于管理;
兼容旧项目
xml缺点:
配置比较麻烦(除非安装相应的配置插件)
AspectJ注解优点:
定义简单,只需要用相应的注解修饰;
自由组合切点;
能够同时被 Spring AOP 和 AspectJ理解,能够方便切换到AspectJ框架
AspectJ注解缺点:
必须使用Java5版本及以后版本;
切面、切点、通知的定义位置比较零散
最后,总结一下,这两种方式互相补充,一方的优点恰好是另一方的劣处。在实际项目中,可以同时采用两种方式混合配置。总之,
平衡才是王道!!