Spring AOP

软件开发的时候,提倡“高内聚,低耦合”,如何减少模块之间的耦合程度是一件麻烦的事情。比如,有时候我们的业务需要在某些操作的时候加上一些记录日志、加上一些验证、加上保存缓存等等。这些操作都是夹杂在整个业务之中的,如果把这些业务代码也写在一起,就变成了“大杂烩”,模块之间的耦合程度非常的高,使得开发不能专注于某一个方面,出了问题也不好调试。

Spring AOP(Aspect Oriented Programming) 面向切面编程,正是解决上述问题的法宝。Spring AOP位于Spring Core之上,但不是不可或缺的,如果你的程序不需要切面编程,完全可以不用AOP模块。

这样,当使用Spring AOP的时候,我们的程序可以大体上分成两个方面,主题业务逻辑与切面逻辑。先来了解一下AOP的一些术语:

方面(Aspect: 一个关注点的模块化,这个关注点实现可能 另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。

连接点(Joinpoint:   程序执行过程中明确的点,如方法的调 用或特定的异常被抛出。

通知(Advice:   在特定的连接点,AOP框架执行的动作。各种类 型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架 包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

切入点(Pointcut:   指定一个通知将被引发的一系列连接点 的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。

引入(Introduction:   添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。

目标对象(Target Object:   包含连接点的对象。也被称作 被通知或被代理对象。

AOP代理(AOP Proxy:   AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving:   组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。

 

这些词可能有些抽象,或者相近。下面以一个例子来说明AOP。

就以买东西为例,暂且抽象这类人群为买手Buyer,可能有男买手ManBuyer和女买手

WomenBuyer,所以抽象出来一个接口Buyer

package test.spring.aop;

public interface Buyer {

	public void buy();
	
}

两个实现类WomenBuyer和ManBuyer

package test.spring.aop;

public class WomenBuyer implements Buyer {

	public void buy() {
		System.out.println("A women is buying something...");
	}
}

package test.spring.aop;

public class ManBuyer implements Buyer {

	public void buy() {
		System.out.println("A man is buying something...");
	}
	
}

但在实际买东西的过程中,这些职业买手们往往会在买东西之前货比三家,所以我们在买东西的之前加上比价的过程,所以,上述两个实现类就变成了这样

package test.spring.aop;

public class WomenBuyer implements Buyer {

	public void buy() {
		System.out.println("-------购买前比价--------");
		System.out.println("A women is buying something...");
	}
}

package test.spring.aop;

public class ManBuyer implements Buyer {

	public void buy() {
		System.out.println("-------购买前比价--------");
		System.out.println("A man is buying something...");
	}
	
}

试想一下,如果这个Buyer接口有几十个实现类,这样的比价过程要重复几十遍,当这个比较过程很复杂的时候,为此付出的代价非常大,并且把比价行为和购买行为合在一起,不符合“高内聚,低耦合”的标准。于是乎,这时候就可以使用AOP,把比价过程作为一个切面独立出去,这样我们可以专注于方面编程。

我们将比价行为抽出,放到切面Aspect中

package test.spring.aop;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class CompareAspect implements MethodBeforeAdvice {

	public void before(Method arg0, Object[] arg1, Object arg2)
			throws Throwable {
		System.out.println("-------购物前比价----------");
	}
	
}

这个切面Aspect实现了MethodBeforeAdvice接口,用于在主体业务执行之前,执行切面逻辑(这里也就是我们的比价行为)。

将WomenBuyer和ManBuyer中的比较语句去掉。那么现在我们可以在切面中专注比价行为的逻辑,可以中实现类的buy( )方法中专注主题的业务逻辑。

现在的问题是怎么把切面与主题业务逻辑联系在一起,这就是“织入”,怎么把切面中的代码织入进主题业务中,然后程序就可以像我们第一次写的代码一样,顺序执行切面和主程序。织入的方式有以下3种:

  • 在编译器织入,通过编译器在编译的时候就把切面织入进去,AspectJ就是通过编译器在编译器织入的。
  • 在类加载的时候,通过ClassLoader加载切面的字节码进行织入。
  • 在运行期的某个时刻,通过JDK的动态代理进行切面的织入,SpringAOP就是通过这种动态代理的方式进行织入的。

回到上述买东西的问题上,在applicationContext.xml我们定义这种关系

applicationContext.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:aop="http://www.springframework.org/schema/aop"  
         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.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" >

	<bean id="womenBuyer" class="test.spring.aop.WomenBuyer"></bean>
	<bean id="compareAspect" class="test.spring.aop.CompareAspect"></bean>


	<!--通知者,整合通知Advice和切入点Point-->
	<bean id="buyCompareAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice" ref="compareAspect"></property>
		<property name="pattern" value=".*buy"></property>
	</bean>
	
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
    
</beans> 

在写一个测试类:

package test.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		Buyer buyer = (Buyer)ac.getBean("womenBuyer");
		buyer.buy();
	}
	
}

这样就可以按照我们设计的逻辑进行输出了。这里我们利用容器管理我们的对象,然后直接在容器中获取,容器会帮助我们管理对象间的依赖关系(IoC),并且会在特定的切入点引入切面的逻辑。

除了利用<bean>的配置方案之外,还可以利用spring提供的aop命名空间进行配置。

applicationContext.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:aop="http://www.springframework.org/schema/aop"  
         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.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" >

	<bean id="womenBuyer" class="test.spring.aspectj.aop.WomenBuyer"></bean>
	<bean id="compareAspect" class="test.spring.aspectj.aop.CompareAspect"></bean>
	
	<aop:config>
		<aop:aspect ref="compareAspect">
			<aop:before method="beforeBuy" pointcut="execution(* buy(..))"/>
			<aop:after method="afterBuy" pointcut="execution(* buy(..))"/>
		</aop:aspect>
	</aop:config>
    
</beans>  

上面是通过xml进行AOP配置的,下面使用AspectJ注解驱动AOP,当然你要加入相关的jar包

切面:

package test.spring.aspectj.aop;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class CompareAspect {

	public CompareAspect(){}
	
	@Pointcut("execution(* buy())")
	public void buypoint() {
		System.out.println("This is buypoint");
	}
	
	@Before("buypoint()")
	public void beforeBuy() {
		System.out.println("-------购买前比价----------");
	}
	
	@AfterReturning("buypoint()")
	public void afterBuy() {
		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:aop="http://www.springframework.org/schema/aop"  
         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.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" >

	<bean id="womenBuyer" class="test.spring.aspectj.aop.WomenBuyer"></bean>
	<bean id="compareAspect" class="test.spring.aspectj.aop.CompareAspect"></bean>
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
</beans>  

其他不变,程序输出:

-------购买前比价----------
A women is buying something...
--------购买后使用----------------

最后,附上整个项目所用到的jar包




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值