Spring AOP 初探

1 何为AOP
AOP是Aspect Orinted Programming,翻译为面向切面编程。AOP能干什么呢,比如系统中有3个业务类,AService,BService和CService,如果现在要在3个Service的方法中,进入方法开始的时System.out.println("hello");离开方法时System.out.println("byebye")。
那么我们可以怎么做呢?
1)分别修改AService,BService,CService所有的方法之前和之后拷贝粘贴这两个Sysetm.out.println()。如果给byebye加一个"!",那么得修改所有的方法,那叫一个累啊。系统中首先要复用,有句话说的好:"复制粘贴设计之谬",显然这种方式不好。
2)通过委托模式实现。将System.out.println("hello")和System.out.println("bye")封装成公共的方法,ASerivce,BService,CService都委托给这个公共方法实现。这个比第1种方法好多了,但是依然得重复调用委托对象,如果此时AService不想hello和byebye了,需要修改AService删掉相关代码。客户的需求就是这么多变,唯有不变的就是变化,我们要拥抱变化不是嘛,把变化想象成美女吧。另外这种方式还有个弊端,AService更应该关注它自身的业务逻辑,对于这种事情,他不关心,这种代码不应该影响到他的核心业务逻辑代码。

AOP的出现优雅的解决了上述问题,他可以将上面的System.out.println("hello")和System.out.println("bye")定义成切面,通过AOP将这两段代码织入到AService,BService和CService中,而各Service本身并不需要修改代码。所以AOP是OOP的补充,他解决了这种交叉业务问题,比如常见的有:日志、事务、性能监控、安全处理、权限校验等等。
如下图所示:

[img]http://dl2.iteye.com/upload/attachment/0104/7192/1c3e12b5-85b1-3016-9701-1889d63ca9f5.jpg[/img]

图中安全、事务就是切面,它可以插入到CourseService等业务模块中。

2 AOP核心概念
 切面(Aspect) : 切面就是你要实现的交叉功能,例如权限切面,事务切面。
 连接点(JoinPoint) :连接点是应用程序执行过程中插入切面的地点。可以是方法调用、属性或者异常抛出等。Spring不提供属性连接点。
 通知(Advice):通知是切面的具体实现。例如:向日志文件中写入日志。
 切入点(Pointcut) : 切入点定义了通知应该应用在哪些连接点。通常通过类名和方法名来指定切入点。应用的什么地方使用通知。
 引入(Introduction):引入允许你为已存在的类添加新方法和属性。
 目标对象(Target):目标对象就是被通知对象。
 代理(Proxy):代理是将通知应用到目标对象后创建的对象。 Spring有2种代理创建方式,如果目标对象实现了接口那么就通过JDK动态代理实现,如果没有接口,那么就通过CGLIB生成目标对象的子类来实现,当然final的方法无法被通知。
 织入(Weaving):织入是将切面应用到目标对象从而创建一个新的代理对象的过程。
织入可以在编译器类装载期或者运行期发生。aspectJ发生在编译期,Spring AOP发生在运行期,通过动态生成目标对象的代理实现。

概念好多,而且貌似不太容易懂,不过没关系,下面结合实际代码分别对应一下就柳暗花明了。
3 Spring AOP之hello world
例子实现的是统计一下方法执行的时间,这个可以实际应用到性能测试中。
翠花上代码:
HelloWorldService:
public interface HelloWorldService {
public void sayHello();
}

HelloWorldImpl:
public class HelloWorldImpl implements HelloWorldService {

public void sayHello() {
System.out.println("hello frank1234,begin aop");
try{
System.out.println("老夫累了,休息下先");
Thread.currentThread().sleep(new Random().nextInt(1000));
}catch (Exception e){
e.printStackTrace();
}
}
}


PerformanceInterceptor:主要是这个类
public class PerformanceInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable{
long beginTime = System.currentTimeMillis();
Object rtnObj = invocation.proceed();
System.out.println("执行花费了:"+(System.currentTimeMillis() - beginTime));
return rtnObj;
}
}


helloworld.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="helloTarget" class="org.frank1234.spring.aop.HelloWorldImpl"/>

<bean id="performanceAdvice" class="org.frank1234.spring.aop.PerformanceInterceptor"/>

<bean id="hello" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.frank1234.spring.aop.HelloWorldService</value>
</property>
<property name="interceptorNames">
<list>
<value>performanceAdvice</value>
</list>
</property>
<property name="target">
<ref bean="helloTarget"/>
</property>
</bean>
</beans>


测试类:
public class HelloWorldAopMain {
public static void main(String[] args) throws Exception{
ApplicationContext factory = new ClassPathXmlApplicationContext("helloworld.xml");
HelloWorldService service = (HelloWorldService)factory.getBean("hello");
service.sayHello();
}
}

输出:
hello frank1234,begin aop
老夫累了,休息下先
执行花费了:645

从输出可见统计了sayHello()方法执行的时间。

代码同概念对应关系:
 切面(Aspect) : 无对应代码,就是性能统计这个交叉功能。
 连接点(JoinPoint) : 此处的连接点是方法调用,见PerformanceInterceptor类的invocation.proceed(),这个方法会调用HelloWorldImpl的sayHello()方法。
 通知(Advice): PerformanceInterceptor类就是通知。
 切入点(Pointcut) : 配置文件中的<property name="interceptorNames">
<list>
<value>performanceAdvice</value>
</list>
</property>定义了切入点,这个还可以通过正则表达式来定义。
 引入(Introduction):示例未涉及。
 目标对象(Target):HelloWorldImpl就是目标对象。
 代理(Proxy):ApplicationContext载入Bean时,Spring会创建一个JDK动态代理对象,由代理对象实际执行,并且代理对象调用目标对象(HelloWorldImpl)的执行方法(sayHello),JDK动态代理生成的代理类的名称为:$Proxy0.class。
 织入(Weaving):将性能统计这个切面应用到HelloWorldImpl,并创建动态代理的过程就叫织入。


4 Spring AOP
4.1 Spring的通知类型
1)前置通知
public interface MethodBeforeAdvice{
void before(Method method,Object[] args,Object target) throws Throwable;
}
调用时机:目标方法调用之前调用。
2)后置通知
public interface AfterReturningAdvice{
void afterReturning(Object returnValue,Method method,Object[] args,Object target) throws Throwable;
}
调用时机:目标方法调用之后调用。
3)环绕通知
public interface MethodInterceptor extends Interceptor{
Object invoke(MethodInvocation invocation) throws Throwable;
}
拦截对目标方法的调用。
这是一个AOP联盟的接口,它和其他的通知类型主要有2个不同,1:可以控制目标方法是否真正被调用。2:可以返回不同的对象。之前helloworld的例子就是基于这个通知类型。
4)异常通知
public interface ThrowsAdvice{
void afterThrowing(Throwable throwable);
void afterThrowing(Method method,Object[] args,Object target,Throwable throwable);
}
调用时机:目标方法抛出异常时调用。
4.2 通过正则表达式定义切入点
示例片段:
<bean id="xxx" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>xxxdService</value>
</property>
<property name="interceptorNames">
<list>
<value>regPointcutAdvisor</value>
</list>
</property>
<property name="target">
<ref bean="xxxTarget"/>
</property>
</bean>
<bean id="regPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern">
<value>.*get.+By.+</value>
</property>
<property name="advice">
<ref bean="xxxAdvice"/>
</property>
</bean>


<value>.*get.+By.+</value>中的符号解释:
*代表匹配字符0次到多次
+代表匹配字符1次到多次,至少包含1个
4.3 ProxyFactoryBean配置
关键属性解释:
target: 代理的目标对象
proxyInterfaces: 代理应该实现的接口列表
interceptorNames: 需要应用到目标对象上的通知Bean的名字,可以是拦截器、Advisor或者其他通知类型的名字。

示例可参见 hello world 配置文件。


《spring in action》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值