前言:
前面介绍了Spring的核心模块以及相关的依赖注入等概念。这篇讲解一下spring的另一个重点,AOP面向切面编程。
说道AOP不得不提到几个概念:
切面:也就是我们自己的一些业务方法。
通知:用于拦截时出发的操作。
切点:具体拦截的某个业务点。
这样说可能还是有点抽象,举个例子,下面是一个纸糊的多面体。
切面。
刺穿的时候会发出声响,这就是一种通知。
而具体从哪个面刺入,这就是一个切入点的选择了。
这样说,应该能稍微了解一点。
那么下面看一个简单的例子:
为了便于理清关系,先放上一张相关的类图:
首先定义个接口
1 public interface IService {
2 public void withAop();
3 public void withoutAop();
4 }
实现类:
1 public class TestAOP implements IService {
2 private String name;
3 public void withAop() {
4 System.out.println("with AOP name:"+name);
5 }
6 public void withoutAop() {
7 System.out.println("without AOP name:"+name);
8 }
9 public String getName() {
10 return name;
11 }
12 public void setName(String name) {
13 this.name = name;
14 }
15 }
这个实现类实现了接口定义的两个方法,下面我们定义几种拦截方式,这些拦截方式通过拦截的位置或者时机不同而不同。
方法前拦截,方法后拦截,以及异常拦截。通过在这些拦截中编写自己的业务处理,可以达到特定的需求。
方法前拦截,需要实现MethodBeforeAdvice接口,并填写before方法。这样,当拦截到某个方法时,就会在方法执行前执行这个before()方法。
1 public class BeforeAOPInterceptor implements MethodBeforeAdvice{
2 public void before(Method method, Object[] args, Object instance)
3 throws Throwable {
4 System.out.println("before()"+method.getName());
5 }
6 }
同理,方法后拦截,也是如此。需要实现AfterReturningAdvice接口。
1 public class AfterAOPInterceptor implements AfterReturningAdvice{
2 public void afterReturning(Object value, Method method, Object[] args,
3 Object instance) throws Throwable {
4 System.out.println("after()"+method.getName());
5 }
6 }
以及异常拦截。
1 public class ThrowsAOPInterceptor implements ThrowsAdvice{
2 public void afterThrowing(Method method,Object[] args,Object instance,AccountException ex) throws Throwable{
3 System.out.println("after()"+method.getName()+"throws exception:"+ex);
4 }
5 public void afterThrowing(NullPointerException ex) throws Throwable{
6 System.out.println("throws exception:"+ex);
7 }
8 }
spring的配置文件,把拦截器与切面方法关联起来。
参考上面的图,可以看到配置文件中的层次关系。
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns="http://www.springframework.org/schema/beans"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
6 <!-- 通过名字匹配 -->
7 <!--
8 <bean id="before" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
9 <property name="advice">
10 <bean class="com.test.pointcut.beforeAOP"></bean>
11 </property>
12 <property name="mappedName" value="withoutAop"></property>
13 </bean>
14 -->
15 <!-- 通过正则表达式 匹配 -->
16 <bean id="before" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
17 <property name="advice">
18 <bean class="com.test.pointcut.BeforeAOPInterceptor"></bean>
19 </property>
20 <property name="patterns">
21 <list>
22 <value>.*out.*</value>
23 </list>
24 </property>
25 </bean>
26 <bean id="after" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
27 <property name="advice">
28 <bean class="com.test.pointcut.AfterAOPInterceptor"></bean>
29 </property>
30 <property name="patterns">
31 <list>
32 <value>.*out.*</value>
33 </list>
34 </property>
35 </bean>
36 <bean id="exception" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
37 <property name="advice">
38 <bean class="com.test.pointcut.ThrowsAOPInterceptor"></bean>
39 </property>
40 <property name="patterns">
41 <list>
42 <value>.*out.*</value>
43 </list>
44 </property>
45 </bean>
46 <!-- -->
47 <bean id="aopService" class="org.springframework.aop.framework.ProxyFactoryBean">
48 <property name="interceptorNames">
49 <list>
50 <value>before</value>
51 <value>after</value>
52 <value>exception</value>
53 </list>
54 </property>
55 <property name="target">
56 <bean class="com.test.pointcut.TestAOP">
57 <property name="name" value="Hello"></property>
58 </bean>
59 </property>
60 </bean>
61 </beans>
ProxyFactoryBean下有两个属性,一个想要拦截的目标类,一个是拦截器。而拦截器又包括两种,主要是因为定位方法的不同而分类。分别是:
RegexpMethodPointcutAdvisor 通过正则表达式来定位业务方法。
NameMatchMethodPointcutAdvisor 通过名字来定位业务方法。
定位到了业务方法,还需要添加响应的拦截器,拦截器就是上面的三种。
最后看一下测试的方法:
public class TestMain {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContextAOP.xml"));
IService hello = (IService)factory.getBean("aopService");
hello.withAop();
hello.withoutAop();
}
}
我们上面通过正则表达式定位到所有包含out的方法,其实就是withoutAOP方法。这样当执行withoutAop方法时,会触发拦截器的操作。
执行结果:
2014-12-4 16:46:58 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContextAOP.xml]
with AOP name:Hello
before()withoutAop
without AOP name:Hello
after()withoutAop
总结:
包装或者监控。
举个例子,比如有个业务方法想要进行数据的查询,那么可以再这个查询前面获取JDBC连接池的连接,这样就对用户屏蔽掉了复杂的申请过程。而销毁就可以放在方法后拦截函数里。
再比如,想要监控某个业务方法呗执行了多少次,那么就可以通过这样一种拦截方式,进行信息的统计,计数或者计时!
妙处多多,还待完善!
《java web王者归来》《spring实战》《spring权威指南》