面向切面编程
什么是AOP
AOP即面向切面编程
Aspect Oriented Program
什么是切面
假设我们需要在一个方法的开头和结尾进行日志的输出,通常的做法是直接在方法里写入日志输出的代码。
但如果我们需要对多个甚至n多个方法都实现日志输出,这太麻烦了,而且代码复用率不高。
那我们进行一个假设:
有三个方法,每个方法的头尾都要输出日志。当然,一个个输出太麻烦了。
那么,假若我对这三个方法切一刀。
我们将得到三个统一的刀口,将三个刀口当作一整个切面。
接着,把这个切面抽象成一个可以进行编程的对象!
然后,我们对该对象写入所需要的功能,例如输出日志。
至此——我们就完成了要求这三个方法的头尾都要输出日志的操作。
这样我们就能对 AOP是什么 理解个大概了。
注:通过上面的例子,由于我们把切面当作了一个对象,那么有对象就会有IOC,所以AOP是必定基于IOC的。AOP和IOC/DI只是一种软件设计思想 ,不单是Java语言,其他语言都有这种设计思想概念
代码部分
AOP分为核心业务功能和辅助功能(aspect),两种功能可以分别独立。
- 核心业务功能:
比如登陆功能,即便是没有性能统计和日志输出,也可以正常运行。- 辅助功能(也叫切面aspect):
打印日志。
事务处理(commit,close)
权限处理
上面的例子其实已经把AOP的概念讲出来了:能够选择性地,低耦合地将核心业务功能(三个方法)和辅助功能(输出日志)结合在一起的编程思想就叫做面向切面编程即AOP。
准备业务类 ProductService.java
package com.how2java.service;
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
}
准备日志切面 LoggerAspect.java
该日志切面的功能是 在调用核心功能之前和之后分别打印日志,切面就是原理图中讲的那些辅助功能。
package com.how2java.aspect; import org.aspectj.lang.ProceedingJoinPoint; public class LoggerAspect { public Object log(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("start log:" + joinPoint.getSignature().getName()); Object object = joinPoint.proceed(); System.out.println("end log:" + joinPoint.getSignature().getName()); return object; } }
applicationContext.xml
声明业务对象
<bean name="s" class="com.how2java.service.ProductService"> </bean>
声明日志切面
<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
指定核心业务功能
<aop:pointcut id="loggerCutpoint" expression= "execution(* com.how2java.service.ProductService.*(..)) "/>
这一句是声明切入点。
- 切入点的 id 叫 loggerCutPoint ,用来标记这个切入点,
- 这个expression表示:满足expression中的方法调用之后,就会去进行切面操作,类似于触发了切面:
– 第一个 * 代表返回任意类型 com.how2java.service.ProductService.* 表示包名以
– com.how2java.service.ProductService 开头的类的任意方法(第二个*表示任意方法)
– (…) 表示方法的参数是任意数量和类型
简单说就是,只要com.how2java.service这个包中的ProductService类的任意一个函数被调用,不管你的返回值是什么,都会触发开关,我就会去执行切面,也就是辅助功能,但是辅助功能是什么呢,就是下面两句:
指定辅助功能
<aop:aspect id="logAspect" ref="loggerAspect"> <aop:after pointcut-ref="loggerCutpoint" method="log"/> </aop:aspect>
这两句是定义了一个切面,上面说只要触发开关,就会去执行切面
id代表这个切面的名字,ref就是指的方法所在的类,method代表的就是方法的名字,
pointcut-ref=“loggerCutpoint” 这个就是表示我这个切面是和上面的切点关联起来的(一个切点是可以关联多个切面的,一个切面只能关联一个方法),只要上面的切点被触发,我就会到这里来执行一些辅助功能
after表示在切入点触发之后来执行我这个中断,当然也有before,一共有五个before,after,After-returning,After-throwing,Around。
在 method 参数后面,还可以加上参数列表。
tips:
如果还是差点感觉的话推荐看一下这个帖子(实在写的详细,优秀):
然后通过aop:config把业务对象与辅助功能编织在一起。
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="c" class="com.how2java.pojo.Category">
<property name="name" value="yyy" />
</bean>
<bean name="p" class="com.how2java.pojo.Product">
<property name="name" value="product1" />
<property name="category" ref="c" />
</bean>
<bean name="s" class="com.how2java.service.ProductService">
</bean>
<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
<aop:config>
<aop:pointcut id="loggerCutpoint"
expression=
"execution(* com.how2java.service.ProductService.*(..)) "/>
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
</beans>
TestSpring.java
package com.how2java.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.how2java.service.ProductService;
public class TestSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "applicationContext.xml" });
ProductService s = (ProductService) context.getBean("s");
s.doSomeService();
}
}