Spring 引入的运用
参考《Spring高级程序设计》
?
引入是Spring提供的AOP功能的重要组成部分。使用引入可以动态地在现有的对象中添加新的功能。当我们在现有的对象中添加的功能是一个横切关注点而用传统的面向对象方法难以实现时,我们就可以利用引入动态的添加该功能了。
?
Spring文档中列举了两个典型的引入用法:对象锁定和对象篡改检测。
我们主要对对象篡改检测进行分析:
现在我们构建一个统计信息收集框架。篡改检测的逻辑被封装在CallTracker接口中,他的一个实现连同自动篡改检测的拦截逻辑被一起引入到合适的对象中。
?
CallTracker接口
package com.spring.ch06.service;public interface CallTracker {void markNormal();void markFailing();int countNormalCalls();int countFailingCalls();String describe();}?
CallTracker接口的一个实现
package com.spring.ch06.service;public class DefaultCallTracker implements CallTracker {private int normalCalls;private int failingCalls;@Overridepublic int countFailingCalls() {return failingCalls;}@Overridepublic int countNormalCalls() {return normalCalls;}@Overridepublic String describe() {return toString();}@Overridepublic void markFailing() {this.failingCalls++;}@Overridepublic void markNormal() {this.normalCalls++;}public String toString(){final StringBuilder sb=new StringBuilder();sb.append("DefaultCallTracker");sb.append("{normalCalls=").append(this.normalCalls);sb.append(",failingCalls=").append(this.failingCalls);sb.append("}");return sb.toString();}}?
创建一个方面
?
package com.spring.ch06.service;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.DeclareParents;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class CallTrackerAspect {@Pointcut("execution(* com.spring.ch06.service.*.*(..))")private void serviceCall(){}/* * 声明我们将使用DefaultCallTracker实现把CallTracker接口引入到com.spring.ch06.service * 包下的所有的类中 */@DeclareParents(value="com.spring.ch06.service.*",defaultImpl=DefaultCallTracker.class)public static CallTracker mixin;/* * serviceCall()代表@Pointcut,而this(tracker)将匹配实现了CallTracker接口的对象上所有方法 * 执行。SpringAOP会将追踪器参数绑定到被通知的对象上。 */@AfterReturning(value="serviceCall()&&this(tracker)",argNames="tracker")public void normallCall(CallTracker tracker){tracker.markNormal();}@AfterThrowing(value="serviceCall()&&this(tracker)",throwing="t",argNames="tracker,t")public void failingCall(CallTracker tracker,Throwable t){tracker.markFailing();}}?
现有对象:
package com.spring.ch06.service;import com.spring.ch06.common.User;public interface UserService {public void login(String username);void setAdministratorUsername(String administratorUsername); User findById(long id);}?
package com.spring.ch06.service;import com.spring.ch06.common.SecurityContext;import com.spring.ch06.common.User;public class DefaultUserService implements UserService{ private String administratorUsername = "janm"; public void login(String username) { if (this.administratorUsername.equals(username)) { SecurityContext.setCurrentUser(username); } } public void setAdministratorUsername(String administratorUsername) { this.administratorUsername = administratorUsername; } public User findById(long id) { User user = new User(); user.setUsername(String.valueOf(id)); user.setPassword("Very secret password"); return user; }}?
package com.spring.ch06.service;import java.math.BigDecimal;import java.util.Date;public interface StockService {long getStockLevel(String sku); long getPredictedStockLevel(String sku); void applyDiscounts(Date cutoffDate, BigDecimal maximumDiscount);}?
package com.spring.ch06.service;import java.util.Date;import java.math.BigDecimal;/** * @author janm */public class DefaultStockService implements StockService { public long getStockLevel(String sku) { try { Thread.sleep(2000L); } catch (InterruptedException ignored) { } return getPredictedStockLevel(sku) / 2L;// return ((StockService)AopContext.currentProxy()).getPredictedStockLevel(sku) / 2L; } public long getPredictedStockLevel(String sku) { return 6L * sku.hashCode(); } public void applyDiscounts(Date cutoffDate, BigDecimal maximumDiscount) { // do some work }}?
测试:
package com.spring.ch06;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.math.BigDecimal;import java.util.*;import com.spring.ch06.service.CallTracker;import com.spring.ch06.service.StockService;import com.spring.ch06.service.UserService;public class IntroductionDemo {public static void main(String[] args) {//ApplicationContext ac=new ClassPathXmlApplicationContext("/introductiondemo1.xml");ApplicationContext ac=new ClassPathXmlApplicationContext("/introductiondemo2.xml");UserService userService=(UserService)ac.getBean("userService");describeTracker(userService);userService.login("janm");userService.setAdministratorUsername("x");describeTracker(userService);StockService stockService=(StockService)ac.getBean("stockService");describeTracker(stockService);try{stockService.getStockLevel(null);}catch(Exception ignored){}System.out.println(stockService.getStockLevel("ABC"));stockService.applyDiscounts(new Date(), new BigDecimal("10.0"));describeTracker(stockService);}public static void describeTracker(Object o){CallTracker t=(CallTracker)o;System.out.println(t.describe());}}?
配置文件:采用两种方式:注解和AOP命名空间配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "><aop:aspectj-autoproxy/><bean id="userService" name="code"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "><bean id="userService" ref="aspectBean"><aop:declare-parents types-matching="com.spring.ch06.service.*" implement-interface="com.spring.ch06.service.CallTracker"default-impl="com.spring.ch06.service.DefaultCallTracker"/><aop:after-returning method="normalCall"arg-names="tracker"pointcut="execution(* com.spring.ch06.service.*.*(..)) and this(tracker)"/><aop:after-throwing method="failingCall"arg-names="tracker"pointcut="execution(* com.spring.ch06.service.*.*(..)) and this(tracker)"/></aop:aspect></aop:config><!-- 使用CallTrackerAspect的userService bean类型是JdkDynamicAopProxy,它不是一个DefaultUserService的实例。这个代理双双实现了UserService接口和CallTracker混入体接口。它拦截对所有方法的调用并将UserService接口上的调用委托给DefaultUserService.这个代理也创建了一个DefaultCallTracker(在@DeclareParents注解中被定义)的实例并将CallTracker接口上所有的调用委托给DefaultCallTracker实例。 --> </beans>
?
package com.spring.ch06.service;import org.aspectj.lang.JoinPoint;import com.spring.ch06.common.User;public class AspectBean {public void normalCall(CallTracker tracker){tracker.markNormal();}public void failingCall(CallTracker tracker){tracker.markFailing();}}?