AOP ( Aspect Oriented Programming 面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。
AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。
基本术语(一些名词):
(1)切面(Aspect)
切面泛指[*交叉业务逻辑*]。事务处理和日志处理可以理解为切面。常用的切面有通知(Advice)与顾问(Advisor)。实际就是对主业务逻辑的一种增强。
(2)织入(Weaving)
织入是指将切面代码插入到目标对象的过程。代理的invoke方法完成的工作,可以称为织入。
(3) 连接点(JoinPoint)
连接点是指可以被切面织入的方法。通常业务接口的方法均为连接点
(4)切入点(PointCut)
切入点指切面具体织入的方法
注意:被标记为final的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(5)目标对象(Target)
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(6)通知(Advice)
通知是切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间。
(7)顾问(Advisor)
顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。
AOP是一种思想,而非实现
AOP是基于OOP,而又远远高于OOP,主要是将主要核心业务和交叉业务分离,交叉业务就是切面。例如,记录日志和开启事务。
代码展示:
一。前置增强和后置增强
ISomeService.java
package cn.aop01; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction(); //日志 public String doLog(); }
SomeServiceImpl.java
package cn.aop01; public class SomeServiceImpl implements ISomeService { public void doTransaction() { System.out.println("开启事务"); } public String doLog() { System.out.println("书写日志"); return null; } }
MyMethodBeforeAdvice.java(前置增强)
package cn.aop01;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("==============before");
}
}
applicationContext.xml(Spring配置文件 前置增强)
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop01.SomeServiceImpl"></bean> <!-- 02.配置前置增强 --> <bean id="beforeAdvice" class="cn.aop01.MyMethodBeforeAdvice"></bean> <!-- 03.配置代理工厂bean --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象,对哪个对象的方法做增强 --> <property name="target" ref="someService"></property> <property name="interceptorNames" value="beforeAdvice"></property> </bean> </beans>
MyTest.java
package cn.aop01; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void myBeforeAdviceTest(){ ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop01/applicationContext.xml"); ISomeService service=(ISomeService)ctx.getBean("proxyService"); service.doLog(); service.doTransaction(); } }
效果展示:
ISomeService.java
package cn.aop01; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction(); //日志 public String doLog(); }
SomeServiceImpl.java
package cn.aop01; public class SomeServiceImpl implements ISomeService { public void doTransaction() { System.out.println("开启事务"); } public String doLog() { System.out.println("书写日志"); return null; } }
MyAfterReturningAdvice.java(后置增强)
package cn.aop02after; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice; public class MyAfterReturningAdvice implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行后置通知方法"); if (returnValue!=null) { returnValue=((String)returnValue).toUpperCase(); } } }
applicationContext.xml(Spring配置文件 后置增强)
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop02after.SomeServiceImpl"></bean> <!-- 02.配置前置增强 --> <bean id="afterAdvice" class="cn.aop02after.MyAfterReturningAdvice"></bean> <!-- 03.配置代理工厂bean --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象,对哪个对象的方法做增强 --> <property name="target" ref="someService"></property> <property name="interceptorNames" value="afterAdvice"></property> </bean> </beans>
MyTest.java
package cn.aop01; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void myBeforeAdviceTest(){ ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop01/applicationContext.xml"); ISomeService service=(ISomeService)ctx.getBean("proxyService"); service.doLog(); service.doTransaction(); } }
效果展示:
二:异常抛出增强和环绕增强
ISomeService.java
package cn.aop01; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction(); //日志 public String doLog(); }
SomeServiceImpl.java
package cn.aop01; public class SomeServiceImpl implements ISomeService { public void doTransaction() { System.out.println("开启事务"); } public String doLog() { System.out.println("书写日志"); return null; } }
MyMethodInterceptor.java(环绕增强)
package cn.aop03Interceptor;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("目标方法执行前");
Object result = invocation.proceed();
if (result!=null) {
result=((String)result).toUpperCase();
}
System.out.println("目标方法执行后");
return result;
}
}
applicationContext.xml(Spring配置文件 环绕增强)
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop03Interceptor.SomeServiceImpl"></bean> <!-- 02.配置前置增强 --> <bean id="methodAdvice" class="cn.aop03Interceptor.MyMethodInterceptor"></bean> <!-- 03.配置代理工厂bean --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象,对哪个对象的方法做增强 --> <property name="target" ref="someService"></property> <property name="interceptorNames" value="methodAdvice"></property> </bean> </beans>
MyTest.java
package cn.aop03Interceptor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void myBeforeAdviceTest(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop03Interceptor/applicationContext.xml");
ISomeService service=(ISomeService)ctx.getBean("proxyService");
service.doLog();
service.doTransaction();
}
}
效果展示:
ISomeService.java
package cn.aop01; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction(); //日志 public String doLog(); }
SomeServiceImpl.java
package cn.aop04throwsadvice;
public class SomeServiceImpl implements ISomeService {
//开始事务
public void doTransaction() throws Exception{
int result=5/0;
System.out.println("开启事务");
}
//日志
public String doLog() {
System.out.println("书写日志");
return null;
}
}
MyThrowsAdvice.java(异常抛出增强)
package cn.aop04throwsadvice;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){
System.out.println("亲,出错了啊!");
}
}
applicationContext.xml(Spring配置文件 异常抛出增强)
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop04throwsadvice.SomeServiceImpl"></bean> <!-- 02.配置前置增强 --> <bean id="throwsAdvice" class="cn.aop04throwsadvice.MyThrowsAdvice"></bean> <!-- 03.配置代理工厂bean --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象,对哪个对象的方法做增强 --> <property name="target" ref="someService"></property> <property name="interceptorNames" value="throwsAdvice"></property> </bean> </beans>
MyTest.java
package cn.aop04throwsadvice;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void myBeforeAdviceTest() throws Exception{
ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop04throwsadvice/applicationContext.xml");
ISomeService service=(ISomeService)ctx.getBean("proxyService");
service.doLog();
service.doTransaction();
}
}
效果展示:
三。顾问(Advisor)实现前置增强
通知Advice是Spring提供的一种切面(Aspect)。但其功能过于简单,只能
将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中。
顾问Advisor是Spring提供的另一种切面。其可以完成更为复杂的切面织入功能。PointcutAdvisor是顾问的一种,可以指定具体
的切入点。顾问将通知进行了包装,会根据不同的通知类型,在不同的时间点,将切面织入到不同的切入点。
PointcutAdvisor接口有两个较为常用的实现类:
*:NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问
*:RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问
<property name="pattern" value=".*do.*"></property> 表示方法全名(包名,接口名,方法名)
运算符 名称 意义
. 点号 表示任意单个字符
+ 加号 表示前一个字符出现一次或者多次
* 星号 表示前一个字符出现0次或者多次
=====BeanName自动代理生成器
BeanNameAutoProxyCreator
ISomeService.java
package service; //接口 public interface ISomeService { //待实现的方法 public void doFirst(); public void doSecond(); }
2.SomeServiceImpl.java
package service; //接口实现类 public class SomeServiceImpl implements ISomeService { //实现接口定义的方法 @Override public void doFirst() { System.out.println("方法A"); } @Override public void doSecond() { System.out.println("方法B"); } }
3.MyMethodBeforeAdvice.java
package cn.aop05aopadvisor; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("呵呵,喝~~~~~喝 呵呵呵"); } }
applicationContext.xml(Spring配置文件 )
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop05aopadvisor.SomeServiceImpl"></bean> <!-- 02. 通知 配置前置增强 --> <bean id="beforeAdvice" class="cn.aop05aopadvisor.MyMethodBeforeAdvice"></bean> <!-- 03.配置顾问 advisor 包装 advice--> <bean id="beforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="beforeAdvice"></property> <property name="mappedNames" value="do*"></property> </bean> <!-- 04.配置代理工厂bean --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象,对哪个对象的方法做增强 --> <property name="target" ref="someService"></property> <property name="interceptorNames" value="beforeAdvice"></property> </bean> </beans>
Test.java
package cn.aop05aopadvisor; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void myBeforeAdviceTest() throws Exception{ ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop05aopadvisor/applicationContext.xml"); ISomeService service=(ISomeService)ctx.getBean("proxyService"); service.doLog(); service.doTransaction(); } }
效果展示:
======使用正则自动代理生成器
ISomeService.java
package cn.aop05aopadvisor_regex; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction() throws Exception; //日志 public String doLog(); }
SomeServiceImpl.java
package cn.aop05aopadvisor_regex; public class SomeServiceImpl implements ISomeService { public void doTransaction() throws Exception{ System.out.println("开启事务"); } public String doLog() { System.out.println("书写日志"); return null; } }
MyMethodBeforeAdvice.java
package cn.aop05aopadvisor_regex; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("呵呵,喝~~~~~喝 呵呵呵"); } }
applicationContext.xml(Spring配置文件 )
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop05aopadvisor_regex.SomeServiceImpl"></bean> <!-- 02. 通知 配置前置增强 --> <bean id="beforeAdvice" class="cn.aop05aopadvisor_regex.MyMethodBeforeAdvice"></bean> <!-- 03.配置顾问 advisor 包装 advice--> <bean id="beforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="beforeAdvice"></property> <property name="pattern" value=".*do.*"></property> </bean> <!-- 04.配置代理工厂bean --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象,对哪个对象的方法做增强 --> <property name="target" ref="someService"></property> <property name="interceptorNames" value="beforeAdvice"></property> </bean> </beans>
Test.java
package cn.aop05aopadvisor_regex; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void myBeforeAdviceTest() throws Exception{ ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop05aopadvisor_regex/applicationContext.xml"); ISomeService service=(ISomeService)ctx.getBean("proxyService"); service.doLog(); service.doTransaction(); } }
效果展示:
=====默认Advisor自动代理生成器
DefaultAdvisorAutoProxyCreator
ISomeService.java
package cn.aop05aopadvisor_regex; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction() throws Exception; //日志 public String doLog(); }
SomeServiceImpl.java
package cn.aop05aopadvisor_regex; public class SomeServiceImpl implements ISomeService { public void doTransaction() throws Exception{ System.out.println("开启事务"); } public String doLog() { System.out.println("书写日志"); return null; } }
MyMethodBeforeAdvice.java
package cn.aop05aopadvisor_regex; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("呵呵,喝~~~~~喝 呵呵呵"); } }
applicationContext.xml(Spring配置文件 )
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop06aopauto_advisor.SomeServiceImpl"></bean> <!-- 02. 通知 配置前置增强 --> <bean id="beforeAdvice" class="cn.aop06aopauto_advisor.MyMethodBeforeAdvice"></bean> <!-- 03.配置顾问 advisor 包装 advice--> <bean id="beforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="beforeAdvice"></property> <property name="pattern" value=".*do.*"></property> </bean> <!-- 04.默认advisor 自动代理 切面只能是顾问 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> </beans>
MyTest.java
package cn.aop06aopauto_advisor; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void myBeforeAdviceTest() throws Exception{ ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop06aopauto_advisor/applicationContext.xml"); ISomeService service=(ISomeService)ctx.getBean("someService"); service.doLog(); service.doTransaction(); } }
效果展示:
=====BeanName自动代理生成器
BeanNameAutoProxyCreator
ISomeService.java
package cn.aop06aopauto_beannameautoproxy; /** * 接口 * @author Happy * */ public interface ISomeService { //开启事务 public void doTransaction() throws Exception; //日志 public String doLog(); //相信我,合乎 public void believeMePleaseOK$$$$$$(); }
SomeServiceImpl.java
package cn.aop06aopauto_beannameautoproxy; public class SomeServiceImpl implements ISomeService { public void doTransaction() throws Exception{ System.out.println("开启事务"); } public String doLog() { System.out.println("书写日志"); return null; } public void believeMePleaseOK$$$$$$() { System.out.println("you nx"); } }
MyMethodBeforeAdvice.java
package cn.aop06aopauto_beannameautoproxy; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("呵呵,喝~~~~~喝 呵呵呵"); } }
applicationContext.xml(Spring配置文件 )
<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"> <!-- 01.配置目标对象 --> <bean id="someService" class="cn.aop06aopauto_beannameautoproxy.SomeServiceImpl"></bean> <!-- 02. 通知 配置前置增强 --> <bean id="beforeAdvice" class="cn.aop06aopauto_beannameautoproxy.MyMethodBeforeAdvice"></bean> <!-- 03.配置顾问 advisor 包装 advice--> <bean id="beforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="beforeAdvice"></property> <property name="pattern" value=".*do.*"></property> </bean> <!-- 04.BeanNameAutoProxyCreator方式,切面 既可以 通知,也可以是顾问--> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- 目标对象 --> <property name="beanNames" value="someService"></property> <property name="interceptorNames" value="beforeAdvisor"></property> </bean> </beans>
MyTest.java
package cn.aop06aopauto_beannameautoproxy; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void myBeforeAdviceTest() throws Exception{ ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/aop06aopauto_beannameautoproxy/applicationContext.xml"); ISomeService service=(ISomeService)ctx.getBean("someService"); service.doLog(); service.doTransaction(); service.believeMePleaseOK$$$$$$(); } }
效果展示: