问题:
有时候有一组共享公共行为类。在OOP中,它们必须扩展相同的基类或者实现相同的接口。
此外,Java的单继承机制仅允许一个类最多扩展一个基类。所以,不能同时从多个实现类中继承行为。
解决方案:
引入是AOP中的一种特殊的通知。它允许为一个接口提供实现类,使对象动态的实现接口。就像对象在运行时扩展了实现类。而且,可以用多个实现类将多个接口同时引入对象。这可以实现与多重继承相同的效果。
实例:
假设有两个接口MaxCalculator与MinCalculator,其代码如下:
public interface MinCalculator {
double min(double a,double b);
}
public interface MaxCalculator {
double max(double a,double b);
}
其实现类代码如下:
public class MaxCalculatorImpl implements MaxCalculator {
@Override
public double max(double a, double b) {
double result = a<=b?b:a;
System.out.println("max("+a+","+b+")="+result);
return result;
}
}
public class MinCalculatorImpl implements MinCalculator {
@Override
public double min(double a, double b) {
double result = a<=b?a:b;
System.out.println("min("+a+","+b+")="+result);
return result;
}
}
而又另外一个类
ArithmeticCalculatorImpl希望也执行min()与max()计算。因为java语言仅支持单继承,让
ArithmeticCalculatorImpl同时扩展MaxCalculatorImpl与MinCalculatorImpl是不可能的。唯一的方法通过复制实现代码或者将处理委派给实际的实现类,扩展某个类(MaxCalculatorImpl)并实现另一个接口(MaxCalculator)。
ArithmeticCalculatorImpl代码:
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
public double add(double a, double b) {
double result = a + b;
System.out.println(a + " + " + b + " = " + result);
return result;
}
public double sub(double a, double b) {
double result = a - b;
System.out.println(a + " - " + b + " = " + result);
return result;
}
public double mul(double a, double b) {
double result = a * b;
System.out.println(a + " * " + b + " = " + result);
return result;
}
public double div(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero");
}
double result = a / b;
System.out.println(a + " / " + b + " = " + result);
return result;
}
}
使用引入,可以使用实现类MaxCalculatorImpl与MinCalculatorImpl动态的实现MaxCalculator与MinCalculator接口。这和从MaxCalculatorImpl与MinCalculatorImpl多重继承有相同的效果。引入可以不需要修改ArithmeticCalculatorImpl类引入新的方法。即,可在没有源代码的情况下将方法引入到现有的类中。
Spring AOP之所以能够使用引入,即使用了动态代理。引入就是向动态代理添加一个接口(MaxCalculator),当这个接口声明的方法在代理对象上调用时,代理将把调用委派给后端实现类(MaxCalculatorImpl)。
引入类代码:
@Aspect
public class CalculatorIntroduction {
@DeclareParents(value="com.apress.springrecipes.calculator.ArithmeticCalculatorImpl",
defaultImpl=com.apress.springrecipes.calculator.MaxCalculatorImpl.class)
public MaxCalculator maxCalculator;
@DeclareParents(value="com.apress.springrecipes.calculator.ArithmeticCalculatorImpl",
defaultImpl=MinCalculatorImpl.class)
public MinCalculator minCalculator;
}
spring配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<context:annotation-config/>
<aop:aspectj-autoproxy/>
<bean class="com.apress.springrecipes.calculator.CalculatorIntroduction"></bean>
<bean id="arithmeticCalculator"
class="com.apress.springrecipes.calculator.ArithmeticCalculatorImpl" />
<bean id="unitCalculator"
class="com.apress.springrecipes.calculator.UnitCalculatorImpl" />
</beans>
调用方式:
public class IntroductionTest {
private ApplicationContext context;
public ApplicationContext getContext(){
if(context==null){
context = new ClassPathXmlApplicationContext("xml/aop/introduction.xml");
}
return context;
}
public static void main(String[] args) {
IntroductionTest ct = new IntroductionTest();
ArithmeticCalculator ac = (ArithmeticCalculator)ct.getContext().getBean("arithmeticCalculator");
MinCalculator min = (MinCalculator)ac;
min.min(2.3,4.5);
MaxCalculator max = (MaxCalculator)ac;
max.max(3, 5);
}
}