AOP的概念很多,初次接触,不好接受,我们一点点来理解。
1. 通知(Advice)
通知定义了想要的功能,在何时使用,以及会干什么事情,自定义通知可以实现Spring中的五种通知,这五种通知下面会有介绍。
2. 连接点(JoinPoint)
连接点是spring允许你使用通知的地方,基本每个方法的前、后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点。
3. 切入点(PointCut)
切入点是在上面说的连接点的基础上来定义的,在一个类里,有15个方法,那么就有几十个连接点,但并不是想在所有方法附近都使用通知(使用叫织入),只想让其中的几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
4. 切面(Aspect)
切面是通知和切入点的结合。没连接点什么事情,连接点是为了好理解切点,搞出来的。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
5. 引入(introduction)
引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。
6. 目标(Target)
即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事),比如人是目标,他是关注我要睡觉这件事,至于睡前和睡后可以不用关心。
7. 代理(proxy)
应用通知的对象。
8. 织入(Weaving)
把切面应用到目标对象来创建新的代理对象的过程。
PS:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理是使用了JDK的动态代理技术,Spring提供了4种实现AOP的方式:
8.1.经典的基于代理的AOP 8.2.@AspectJ注解驱动的切面 8.3.纯POJO切面 8.4.注入式AspectJ切面
9. demo实践
首先看经典的基于代理的AOP,Spring支持五种类型的通知:
前置通知(MethodBeforeAdvice) 后置通知(AfterReturningAdvice) 包围通知(MethodIntercepter) 抛出通知(ThrowAdvice) 引入(IntroductionIntercepter)
这东西怎么玩?大概步骤:
1.创建通知:实现这几个接口,把其中的方法实现了
2.定义切点和通知者:在Spring配置文件中配置这些信息
3.使用ProxyFactoryBean来生成代理
9.1 程序1:
/**
*
*/
package test.spring.aop;
/**
* 具有睡觉能力的生物都可以实现该接口(不光生物,包括关机选项里面的休眠)
*
* @author quyang.ybb
*
*/
public interface Sleepable {
void sleep();
}
/**
*
*/
package test.spring.aop.impl;
import test.spring.aop.Sleepable;
/**
* 这是纯粹的睡觉(业务逻辑),睡觉前后会有一些辅助工作,如脱衣服、吃安眠药等,这些与纯粹的睡觉是不相干的
*
* @author quyang.ybb
*
*/
public class Human implements Sleepable {
@Override
public void sleep() {
System.out.println("好累啊!终于可以睡觉了,谁也别吵我");
}
}
/**
*
*/
package test.spring.aop.help;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 里面包含了睡觉的辅助工作,用AOP术语来说它就应该是通知
*
* @author quyang.ybb
*
*/
public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("睡觉前,我要把衣服脱掉,脱掉!");
}
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("起床后,我要把衣服穿上,穿上!");
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://img.alipay.net/dtd/schema/service http://img.alipay.net/dtd/schema/service/sofa-service.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"
default-autowire="byName">
<bean id="human" class="test.spring.aop.impl.Human"></bean>
<!-- 睡觉帮助类,即通知 -->
<bean id="sleepHelper" class="test.spring.aop.help.SleepHelper"></bean>
<!-- pattern属性指定了正则表达式,它匹配所有的sleep方法 -->
<bean id="sleepPointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*sleep"></property>
</bean>
<!-- 切点仅仅是定义了故事发生的地点,还有故事发生的时间以及最重要的故事的内容,就是通知了,我们需要把通知跟切点结合起来,即切面 -->
<bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="sleepPointCut"></property>
<property name="advice" ref="sleepHelper"></property>
</bean>
<!-- 切入点和通知都配置完成,接下来该调用ProxyFactoryBean产生代理对象了 -->
<bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="human"></property>
<property name="interceptorNames" value="sleepHelperAdvisor"></property>
<property name="proxyInterfaces" value="test.spring.aop.Sleepable"></property>
</bean>
</beans>
package test.main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.spring.aop.Sleepable;
/**
* 测试程序1
* @author quyang.ybb
*
*/
public class MainTest {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test/main/context.xml");
Sleepable sleeper = (Sleepable) applicationContext.getBean("humanProxy");
sleeper.sleep();
}
}
运行结果1:
七月 25, 2015 10:11:32 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4586787c: startup date [Sat Jul 25 10:11:32 CST 2015]; root of context hierarchy
七月 25, 2015 10:11:32 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [test/main/context.xml]
七月 25, 2015 10:11:33 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@771a1d97: defining beans [human,sleepHelper,sleepPointCut,sleepHelperAdvisor,humanProxy]; root of factory hierarchy
睡觉前,我要把衣服脱掉,脱掉!
好累啊!终于可以睡觉了,谁也别吵我
起床后,我要把衣服穿上,穿上!
9.2 程序2:其余不变,配置文件变换为context2.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" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://img.alipay.net/dtd/schema/service http://img.alipay.net/dtd/schema/service/sofa-service.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"
default-autowire="byName">
<!-- context.xml文件中配置切点跟通知过程有点复杂,Spring提供一种自动代理的功能,能让切点和通知自动匹配 -->
<bean id="sleepHelper" class="test.spring.aop.help.SleepHelper"></bean>
<bean id="sleepAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="sleepHelper"></property>
<property name="pattern" value=".*sleep"></property>
</bean>
<bean id="human" class="test.spring.aop.impl.Human"></bean>
<bean id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
</beans>
package test.main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.spring.aop.Sleepable;
/**
* 测试程序2
* @author quyang.ybb
*
*/
public class MainTest2 {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test/main/context2.xml");
Sleepable sleeper = (Sleepable) applicationContext.getBean("human");
sleeper.sleep();
}
}
运行结果2:
七月 25, 2015 10:13:54 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7f50388c: startup date [Sat Jul 25 10:13:54 CST 2015]; root of context hierarchy
七月 25, 2015 10:13:54 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [test/main/context2.xml]
七月 25, 2015 10:13:55 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@462eab5b: defining beans [sleepHelper,sleepAdvisor,human,autoProxyCreator]; root of factory hierarchy
睡觉前,我要把衣服脱掉,脱掉!
好累啊!终于可以睡觉了,谁也别吵我
起床后,我要把衣服穿上,穿上!
9.3 程序3:配置文件变换为context3.xml,使用AspectJ提供的注解方式
/**
*
*/
package test.spring.aop.help;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* @author quyang.ybb
*
*/
// 用@Aspect的注解来标识切面,注意不要把它漏了,否则Spring创建代理的时候会找不到
@Aspect
public class SleepHelper3 {
public SleepHelper3() {
}
// @Pointcut注解指定了切点
@Pointcut("execution(* *.sleep())")
public void sleepPoint() {
}
// @Before指定了运行时的通知,注意的是要在注解中传入切点的名称
@Before("sleepPoint()")
public void beforeSleep() {
System.out.println("睡前我要看一会书");
}
//@AfterReturning指定了运行时的通知,注意的是要在注解中传入切点的名称
@AfterReturning("sleepPoint()")
public void afterSleep() {
System.out.println("睡觉起来我要洗漱,收拾东西");
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://img.alipay.net/dtd/schema/service http://img.alipay.net/dtd/schema/service/sofa-service.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<aop:aspectj-autoproxy />
<bean id="sleepHelper3" class="test.spring.aop.help.SleepHelper3"></bean>
<bean id="human" class="test.spring.aop.impl.Human"></bean>
</beans>
package test.main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.spring.aop.Sleepable;
/**
* 测试程序3
*
* @author quyang.ybb
*
*/
public class MainTest3 {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test/main/context3.xml");
Sleepable sleeper = (Sleepable) applicationContext.getBean("human");
sleeper.sleep();
}
}
运行结果3:
七月 25, 2015 10:16:28 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7f50388c: startup date [Sat Jul 25 10:16:28 CST 2015]; root of context hierarchy
七月 25, 2015 10:16:28 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [test/main/context3.xml]
七月 25, 2015 10:16:29 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5740aa8a: defining beans [org.springframework.aop.config.internalAutoProxyCreator,sleepHelper3,human]; root of factory hierarchy
睡前我要看一会书
好累啊!终于可以睡觉了,谁也别吵我
睡觉起来我要洗漱,收拾东西
最后我们来看最后一种常用的实现AOP的方式:使用Spring来定义纯粹的POJO切面
前面我们用到了<aop:aspectj-autoproxy/>标签,Spring在aop的命名空间里面还提供了其他的配置元素:
<aop:advisor> 定义一个AOP通知者
<aop:after> 后通知
<aop:after-returning> 返回后通知
<aop:after-throwing> 抛出后通知
<aop:around> 周围通知
<aop:aspect>定义一个切面
<aop:before>前通知
<aop:config>顶级配置元素,类似于<beans>这种东西
<aop:pointcut>定义一个切点
我们用AOP标签来实现睡觉这个过程:代码不变,只是修改配置文件,加入AOP配置即可:
<aop:config>
<aop:aspect ref="sleepHelper">
<aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))"/>
<aop:after method="afterSleep" pointcut="execution(* *.sleep(..))"/>
</aop:aspect>
</aop:config>
完!有需要补充的再继续在此版本上更改,程序附图: