第二十八天 月出惊山鸟 —Spring的AOP

              6月13日,阴转细雨。“人闲桂花落。夜静春山空。

月出惊山鸟。时鸣春涧中。”  

             无论在面向过程还是在面向对象里,奇妙的“纯”字,似乎永远都充满了无限的可能性。除了函数之所调用、类之所封装,在程序猿文化里,对于“纯粹”的感知和定义。既起自于代码。又超越了代码。也就是说,可以真真切切地感觉到纯净的。不仅是我们的每个Bean和每个Class,还包含每个Coder的心。

              然而。客户的需求是千变万化和千奇百怪的,Spring在为Coder在应对和处理各自不同的要求时,提供了一种特殊的解决方案-AOP。在OOP和AOP里,最佳存在方式,并非让当中有某一个显得格外突出。而是OOP和AOP的调和以及平衡,不仅是Spring不断寻求的完美状态,也是每一个Coder所追求的理想境地。

编程语言终于极的目标就是能以更自然。更灵活的方式模拟世界,从原始机器语言到过程语言再到面向对象的语言,编程语言一步步地用更自然,更强大的方式描写叙述软件。

             AOPAspect Oriented Programing 的简称。即面向切面编程

尽管AOP作为一项编程技术已经有多年的历史,但一直长时间停顿在学术领域,直到近几年,AOP才作为一项真正的有用技术在应用领域开疆拓土。AOP是软件开发饲养发展到一定阶段的产物,但Aop的出现并非要全然取代OOP。而不过作为OOP的故意补充。须要指出的是AOP的应用场合是受限的,它一般只适合用于那些具有横切逻辑的应用场合:如性能监測,訪问控制,事物管理以及日志记录(尽管非常多解说日志记录的样例用于AOP的解说。但非常多人觉得非常难用AOP编写有用日志。)不过,这丝毫不影响AOP作为一种新的软件开发思想在软件开发领域所占的地位。

             以下通过一个简单的样例。一窥AOP的精妙。

        1、使用Log4j。我们能够在控制台查看日志信息,观察程序执行过程。

             (1)下载log4j.jar,下载链接http://logging.apache.org/log4j/2.x/

               (2) 在src以下。编写log4j.properties(网上找一个改一下)

log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
        2、编写一个业务接口文件WatchBall.java
package edu.eurasia.aop;

public interface WatchBall {
      public void watchfootball(String city);
      public void watchbasketball(String city); 
}

       当中一个方法是看足球,一个是看篮球。

     3、编写一个业务实现文件WatchBallImpl.java

package edu.eurasia.aop;

import org.apache.log4j.Logger;

public class WatchBallImpl implements WatchBall {
	Logger logger = Logger.getLogger(this.getClass());

	@Override
	public void watchfootball(String city) {
		logger.debug("去 " + city + " 看2014巴西世界杯");
	}

	@Override
	public void watchbasketball(String city) {
		logger.debug(" 去" + city + " 看2014NBA总决赛");
	}
}

      实现接口中的方法。在运行watchfootball()和watchbasketball()这些目标对象的方法前后。会织入一些其他业务方法。以下创建这些方法,在运行这些方法前要运行的方法  BeforeAdvice.java 。


       4、编写一个切面文件BeforeAdvice.java

package edu.eurasia.aop;

import java.lang.reflect.Method;
import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice {
	Logger logger = Logger.getLogger(this.getClass());

	@Override
	public void before(Method method, Object[] objects, Object o)
			throws Throwable {
		logger.debug(" 看球前,先去  " + objects[0] + "  咖啡馆喝杯巴西咖啡 ...");
	}
}

     在看球前先去咖啡馆喝杯巴西咖啡大笑...

     5、编写还有一个切面文件AfterAdvice.java

package edu.eurasia.aop;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object o, Method method, Object[] objects,
			Object o1) throws Throwable {
		Logger logger = Logger.getLogger(this.getClass());

		logger.debug("看完球去  " + objects[0] + "  麦当劳吃汉堡包!");
	}
}
        在看完球后去麦当劳吃汉堡包大笑...
     6、编写spring的配置文件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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 代理类.调用的时候则是调用这个代理类 --> <bean id="ball" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理接口(业务类接口) --> <property name="proxyInterfaces"> <value>edu.eurasia.aop.WatchBall</value> </property> <!-- interceptorNames 属性的list集合里面仅仅能放 通知/通知者 -被称为拦截者(拦截器)。--> <property name="interceptorNames"> <list> <value>beforeBallAdvisor</value> <value>afterBallAdvisor</value> </list> </property> <!-- 业务实现类 --> <property name="target"> <ref bean="ballTarget"></ref> </property> </bean> <!-- 通用正則表達式切入点 --> <bean id="beforeBallAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 须要对那些方法进行拦截 --> <property name="advice" ref="beforeBallAdvice"></property> <property name="pattern" value=".*.*foot.*"></property> </bean> <!-- 通用正則表達式切入点 --> <bean id="afterBallAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 须要对那些方法进行拦截 --> <property name="advice" ref="afterBallAdvice"></property> <property name="pattern" value=".*.*basket.*"></property> </bean> <!-- 业务实现 --> <bean id="ballTarget" class="edu.eurasia.aop.WatchBallImpl"></bean> <!-- 切面类 --> <bean id="beforeBallAdvice" class="edu.eurasia.aop.BeforeAdvice"></bean> <!-- 切面类 --> <bean id="afterBallAdvice" class="edu.eurasia.aop.AfterAdvice"></bean> </beans>

          这个配置文件是AOP的核心。重点讲解一下。

首先,建立了一个“ball”的bean。这是一个代理bean,由于spring AOP是基于代理的。在里面有代理接口属性。接口能够多个,能够用<list>来表示,本例仅仅有一个就是WatchBall。

interceptorNames属性,这是一个拦截器属性,就是訪问者,在运行目标对象方法时。进行拦截。目标(target)属性对象,就是运行这个对象的方法时,在其前、后再织入一些其他的业务方法。

      本例的訪问者是beforeBallAdvisor和afterBallAdvisor,訪问者定义了通知对象。以及通知何时和何地进行訪问target的问题。以beforeBallAdvisor为例。其定义的“通知”是advice,这个advice定义了在业务方法之前运行一些逻辑(何时)。pattern则通过正則表達式,能够知道在不论什么业务(.*.*)方法(何地)运行时,都触发advice。

      拦截器bean。先看beforeBallAdvisor,当中pattern是一个正則表達式,说明对WatchBall对象的方法中含有footkeyword则运行beforeBallAdvice这个对象的方法。

afterBallAdvisor是运行target目标方法之后运行的方法。通过正則表達式(<property name="pattern" value=".*.*basket.*">)能够看出,target对象的方法中含有basketkeyword。都要运行这种方法。

     7、编写測试类TestBall.java

package edu.eurasia.aop;

import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBall {
	static Logger logger = Logger.getLogger(TestBall.class);

	@Test
	public void testball() {

		logger.debug("test begin ");

		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		// 这里写的必须是代理类
		WatchBall watchball = (WatchBall) ctx.getBean("ball");

		watchball.watchfootball("里约热内卢");

		logger.debug("----------------------------");

		watchball.watchbasketball("德克萨斯州");
	}
}

         8、执行结果和环境配置

         执行结果例如以下:

         DEBUG [main] (BeforeAdvice.java:13) -  看球前,先去  里约热内卢  咖啡馆喝杯巴西咖啡 ...
         DEBUG [main] (WatchBallImpl.java:10) - 去 里约热内卢 看2014巴西世界杯
         DEBUG [main] (TestBall.java:34) - ----------------------------
         DEBUG [main] (WatchBallImpl.java:15) -  去德克萨斯州 看2014NBA总决赛
         DEBUG [main] (AfterAdvice.java:15) - 看完球去  德克萨斯州  麦当劳吃汉堡包。

          本例使用spring 2.5.6。仅仅需找出spring.jar。commons-logging-1.1.1.jar两个jar包,外加一个log4j.jar就可以。

         文件夹结构例如以下:



         9、改进一下

         上述样例的配置文件中面,代理类ProxyFactoryBean的配置显得啰嗦。Advisor已经把AOP所控制的东西都描写叙述了吗,advice描写叙述了“何时”触发target的对象(before、after、throw等)。pattern描写叙述了“何地”触发的问题(正則表達式)。为什么还要有ProxyFactoryBean呢?怎样解决?

         Spring提供了BeanPostProcessor的一个方便实现:DefaultAdvisorAutoProxyCreator。它会自己主动检查訪问者(advisor)的切点是否匹配Bean方法。而且用使用通知的代理来替换这个bean的定义。

      这样,将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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
              http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 自己主动应用于当前容器中的advisor,不须要定义指定目标Bean的名字字符串 -->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

        <!-- 通用正則表達式切入点 -->
	<bean id="beforeBallAdvisor"
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<!-- 须要对那些方法进行拦截 -->
		<property name="advice" ref="beforeBallAdvice"></property>
		<property name="pattern" value=".*.*foot.*"></property>
	</bean>
   
        <!-- 通用正則表達式切入点 -->
	<bean id="afterBallAdvisor"
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<!-- 须要对那些方法进行拦截 -->
		<property name="advice" ref="afterBallAdvice"></property>
		<property name="pattern" value=".*.*basket.*"></property>
	</bean>

	<!-- 业务实现 -->
	<!-- <bean id="ballTarget" class="edu.eurasia.aop.WatchBallImpl"></bean> -->
	<bean id="ball" class="edu.eurasia.aop.WatchBallImpl"></bean>
	<!-- 切面类 -->
	<bean id="beforeBallAdvice" class="edu.eurasia.aop.BeforeAdvice"></bean>
	<!-- 切面类 -->
	<bean id="afterBallAdvice" class="edu.eurasia.aop.AfterAdvice"></bean>
</beans>




原文地址:https://www.cnblogs.com/liguangsunls/p/6992089.html