Spring AOP 简单理解AOP技术即(面向切面编程)技术是在面向对象编程基础上的发展,AOP技术是对所有对象或一类对象编程。核心是在不增加代码的基础上,还增加了新的功能。AOP编程在开发框架本身用的比较多,而实际项目中,用的比较少。它是将分散在各个业务逻辑代码中的相同代码抽取出来形成一个独立的模块。
1、定义AOP术语
(1)切面(aspect):要实现的交叉功能,是系统模块化的一个切面或领域。
(2)通知(advice):切面的具体实现,包含五类通知。
(3)连接点(jointpoint):应用程序执行过程中插入切面的地点。
(4)切点(cutpoint):定义通知应该应用哪些连接点。
(5)引入(introduction):为类添加新方法和属性。
(6)目标对象(target):通知逻辑的织入目标类。
(7)代理(proxy):将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变
(8)织入(weaving):将切面应用到目标对象从而创建一个新代理对象的过程。
2、AOP原理和实例
(1)基础接口和类的实现:
package com.jasson.aop;
public interface TestServiceInter1 {
public void sayHello();
}
package com.jasson.aop;
public interface TestServiceInter2 {
public void sayBye();
public void sayHi();
}
(2)实现类如下:
package com.jasson.aop;
public class TestService implements TestServiceInter1,TestServiceInter2 {
public void sayHello() {
System.out.println("sayHello() method ");
}
public void sayBye() {
System.out.println("sayBye() method");
}
public void sayHi() {
System.out.println("sayHi() method");
}
}
(1)前置通知:要求在每个方法调用前进行日志记录,则用的前置通知,定义如下:
package com.jasson.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice
implements MethodBeforeAdvice {
/**
* method: 方法名
* args: 输入参数
* target: 目标对象
*/ public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置通知调用 记录日志
"+method.getName());
}
}
配置文件如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的对象,即目标对象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<!-- 相当于包MyMethodBeforeAdvice前置通知和代理对象关联,我们也
可以把通知看出拦截器,struts2核心拦截器 -->
<list>
<value>myMethodBeforeAdvice</value>
</list>
</property>
<!-- 配置被代理对象,即目标对象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
package com.jasson.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ac=new ClassPathXmlApplicationContext("com/jasson/aop/beans.xml");
TestServiceInter1 ts=(TestServiceInter1) ac.getBean("proxyFactoryBean");
ts.sayHello();
System.out.println("*******************************************");
((TestServiceInter2)ts).sayBye();
System.out.println("*******************************************");
((TestServiceInter2)ts).sayHi();
}
}
执行结果如下:
31-May-2012 18:19:53 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c8f6f8: defining beans [testService,myMethodBeforeAdvice,
myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,proxyFactoryBean]; root of factory hierarchy
前置通知调用 记录日志
sayHello
sayHello() method
*******************************************
前置通知调用 记录日志
sayBye
sayBye() method
*******************************************
前置通知调用 记录日志
sayHi
sayHi() method
(2)后置通知:要求在调用每个方法后执行的功能,例如在调用每个方法后关闭资源
package com.jasson.aop;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterReturningAdvice
implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] arg,
Object target)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("后置通知调用,关闭资源
"+method.getName());
}
}
bean 配置如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的对象,即目标对象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<!-- 相当于包MyMethodBeforeAdvice前置通知和代理对象关联,我们也
可以把通知看出拦截器,struts2核心拦截器 -->
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturningAdvice</value>
</list>
</property>
<!-- 配置被代理对象,即目标对象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
执行结果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@122cdb6: defining beans [testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,proxyFactoryBean]; root of factory hierarchy
前置通知调用 记录日志
sayHello
sayHello() method
后置通知调用,关闭资源
sayHello
*******************************************
前置通知调用 记录日志
sayBye
sayBye() method
后置通知调用,关闭资源
sayBye
*******************************************
前置通知调用 记录日志
sayHi
sayHi() method
后置通知调用,关闭资源sayHi
(3)环绕通知:指在某个具体的方法中,添加相应的操作
package com.jasson.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg) throws Throwable {
// TODO Auto-generated method stub
System.out.println("环绕通知调用方法前");
Object obj = arg.proceed();
System.out.println("环绕通知调用方法后");
return obj;
}
}
配置文件如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的对象,即目标对象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置环绕通知 -->
<bean id="myMethodInterceptor" class="com.jasson.aop.MyMethodInterceptor" />
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<!-- 相当于包MyMethodBeforeAdvice前置通知和代理对象关联,我们也
可以把通知看出拦截器,struts2核心拦截器 -->
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturningAdvice</value>
<value>myMethodInterceptor</value>
</list>
</property>
<!-- 配置被代理对象,即目标对象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
执行结果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ce2dd4: defining beans [testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,proxyFactoryBean]; root of factory hierarchy
前置通知调用 记录日志
sayHello
环绕通知调用方法前
sayHello() method
环绕通知调用方法后
后置通知调用,关闭资源
sayHello
*******************************************
前置通知调用 记录日志
sayBye
环绕通知调用方法前
sayBye() method
环绕通知调用方法后
后置通知调用,关闭资源
sayBye
*******************************************
前置通知调用 记录日志
sayHi
环绕通知调用方法前
sayHi() method
环绕通知调用方法后
后置通知调用,关闭资源sayHi
(4)异常通知:当发生异常时,要执行的通知
package com.jasson.aop;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] os, Object target,
Exception exception) {
System.out.println("异常通知产生异常,进行处理" + exception.getMessage());
}
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的对象,即目标对象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置环绕通知 -->
<bean id="myMethodInterceptor" class="com.jasson.aop.MyMethodInterceptor" />
<!-- 配置异常通知 -->
<bean id="myThrowsAdvice" class="com.jasson.aop.MyThrowsAdvice" />
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<!-- 相当于包MyMethodBeforeAdvice前置通知和代理对象关联,我们也
可以把通知看出拦截器,struts2核心拦截器 -->
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturningAdvice</value>
<value>myMethodInterceptor</value>
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理对象,即目标对象 -->
<property name="target" ref="testService"/>
</bean>
package com.jasson.aop;
public class TestService implements TestServiceInter1,TestServiceInter2 {
public void sayHello() {
System.out.println("sayHello() method ");
}
public void sayBye() {
System.out.println("sayBye() method");
}
public void sayHi() {
int a =10/0;
System.out.println("sayHi() method");
}
}
执行结果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ce2dd4: defining beans [testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,proxyFactoryBean]; root of factory hierarchy
前置通知调用 记录日志
sayHello
环绕通知调用方法前
sayHello() method
环绕通知调用方法后
后置通知调用,关闭资源
sayHello
*******************************************
前置通知调用 记录日志
sayBye
环绕通知调用方法前
sayBye() method
环绕通知调用方法后
后置通知调用,关闭资源
sayBye
*******************************************
前置通知调用 记录日志
sayHi
环绕通知调用方法前
异常通知产生异常,进行处理/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
(5)上面的通知都是针对每个方法的,如果只是对单个或者一类的方法进行相应处理的时,可采用名字或者正则表达式的方式进行处理
配置如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的对象,即目标对象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置环绕通知 -->
<bean id="myMethodInterceptor" class="com.jasson.aop.MyMethodInterceptor" />
<!-- 配置异常通知 -->
<bean id="myThrowsAdvice" class="com.jasson.aop.MyThrowsAdvice" />
<!-- 通知与正则表达式切入点一起配置 -->
<!-- Advisor等于切入点加通知,所有say开头的方法添加前置通知 -->
<bean id="regexpPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice"/>
<property name="patterns">
<list>
<value>.*say.*</value>
</list>
</property>
</bean>
<!-- 方法名匹配切入点配置器:只对 sayHello方法添加环绕通知-->
<bean id="namePointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodInterceptor"/>
<property name="mappedNames">
<list>
<value>sayHello</value>
</list>
</property>
</bean>
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<!-- 相当于包MyMethodBeforeAdvice前置通知和代理对象关联,我们也
可以把通知看出拦截器,struts2核心拦截器 -->
<list>
<value>namePointcutAdvisor</value>
<value>myAfterReturningAdvice</value>
<value>regexpPointcutAdvisor</value>
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理对象,即目标对象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
执行结果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ef9157: defining beans
[testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,regexpPointcutAdvisor,namePointcutAdvisor,proxyFactoryBean];
root of factory hierarchy
环绕通知调用方法前
前置通知调用 记录日志
sayHello
sayHello() method
后置通知调用,关闭资源
sayHello
环绕通知调用方法后
*******************************************
前置通知调用 记录日志
sayBye
sayBye() method
后置通知调用,关闭资源
sayBye
*******************************************
前置通知调用 记录日志
sayHi
异常通知产生异常,进行处理/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
-----------------------------------------------------
Silence, the way to avoid many problems;
Smile, the way to solve many problems;