AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足。在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面。切面能对关注点进行模块化,例如横切多个类型和对象的事务管理。
1. AOP的编程步骤
(1)定义接口
(2)编写对象(被代理对象=目标对象)
(3)编写通知
(4)配置beans.xml文件
1)配置被代理对象(目标对象)
2)配置通知
3)配置代理对象,是ProxyFactoryBean的对象实例
(代理接口集,把通知织入到代理对象,配置被代理对象)
2. 通知类型
名称 | 通知类型 | 接口 | 描述 |
---|---|---|---|
前置通知 | Before | org.springframework.aop.MethodBeforeAdvice | 在目标方法调用前调用 |
后置通知 | After | org.springframework.aop.AfterReturningAdvice | 在目标方法调用后调用 |
环绕通知 | Around | org.aopalliance.intercept.MethodInterceptor | 拦截对目标方法调用 |
异常通知 | Throws | org.springframework.aop.ThrowsAdvice | 当目标方法抛出异常时调用 |
3. AOP编程实例
(1)定义接口
package com.aop;
public interface TestServiceInterface {
public void sayHello();
}
package com.aop;
public interface TestServiceInterface2 {
public void sayBye();
}
(2)编写对象(被代理对象)
package com.aop;
public class Test1Service implements TestServiceInterface,TestServiceInterface2 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("hello "+name);
}
@Override
public void sayBye() {
System.out.println("byebye "+name);
}
}
(3)编写通知
- 前置通知
package com.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
/*
* method:被调用方法名字
* args:给method传递的参数
* target:目标对象
*
*/
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("记录日志..."+method.getName());
}
}
- 后置通知
package com.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[] args,
Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("关闭资源...");
System.out.println("***************************");
}
}
- 环绕通知
package com.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
// TODO Auto-generated method stub
System.out.println("调用方法前...");
Object obj=arg0.proceed();
System.out.println("调用方法后...");
return obj;
}
}
- 异常通知
package com.aop;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, Exception e) {
// Do something with all arguments
System.out.println("发生异常:"+e.getMessage());
}
}
(4)配置beans.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置被代理的对象 -->
<bean id="test1Service" class="com.aop.Test1Service">
<property name="name" value="Elana"></property>
</bean>
<bean id="test2Service" class="com.aop.Test2Service">
<property name="name" value="Damon"></property>
</bean>
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.aop.MyMethodBeforeAdvice">
</bean>
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.aop.MyAfterReturningAdvice"></bean>
<!-- 配置环绕通知 -->
<bean id="myMethodInterceptor" class="com.aop.MyMethodInterceptor"></bean>
<!-- 配置异常通知 -->
<bean id="myThrowsAdvice" class="com.aop.MyThrowsAdvice"></bean>
<!-- 定义前置通知的切入点 -->
<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice"></property>
<property name="mappedNames">
<list>
<value>say*</value><!-- 即只对以say开头的方法进行前置通知处理,支持正则表达式过滤 -->
</list>
</property>
</bean>
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.aop.TestServiceInterface</value>
<value>com.aop.TestServiceInterface2</value>
</list>
</property>
<!-- 把通知织入到代理对象 -->
<property name="interceptorNames">
<list>
<!-- 织入前置通知,相当于把myMethodBeforeAdvice前置通知和代理对象关联起来,也可以把通知看成拦截器,Struts2核心拦截器 -->
<value>myMethodBeforeAdvice</value>
<!-- 使用自定义切入点来控制前置通知 -->
<!-- <value>myMethodBeforeAdviceFilter</value>-->
<!-- 织入后置通知 -->
<value>myAfterReturningAdvice</value>
<!-- 织入环绕通知 -->
<value>myMethodInterceptor</value>
<!-- 织入异常通知 -->
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理对象(可以指定) -->
<property name="target" ref="test1Service">
</property>
</bean>
</beans>
(5)测试
package com.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ac=new ClassPathXmlApplicationContext("/com/aop/beans.xml");
//获取代理对象
//一个类实现多个接口,接口之间可以互相转换
TestServiceInterface ts=(TestServiceInterface)ac.getBean("proxyFactoryBean");
//System.out.println("ts的类型是:"+ts);//ts的类型是被代理对象(目标对象)的类型,即Test1Service,而不是代理对象的类型ProxyFactoryBean
ts.sayHello();
((TestServiceInterface2)ts).sayBye();
}
}
运行结果如下: