Spring中AOP编程的本质是:无须修改源码增加系统功能。所谓的系统功能即为AOP中的A,方面,体现在代码上称为Advice(增强):即日志、审核、权限、计时等每个系统均可能要使用到的功能。在感受AOP让我们见证神奇的背后,我们可以通过以下方式不同程度地实现:
1. JavaSE 动态代理;
从JDK1.3开始,便加入了可协助动态生成代理类的API。在这些API中,一个主要接口是
java.lang.reflect.InvocationHandler以及import java.lang.reflect.Proxy。在动态代理中,我们主要编写该接口的实现类型。
2. 动态字节码生成;
针对指定的类动态生成一个子类,并且覆盖其中的方法,从而增加系统功能。其中最流行的工具是:
CGLIB(Code Generation Library).
缺点:无法为 final 方法或类 提供代码。
3. Java代码生成;
生成新的Java源码,在其中提供横切性的代码,例如EJB的代码生成策略;
由于动态代理和动态字节码生成技术的出现,这种做法正在逐渐退出使用;
4. 使用定制的类加载器;
自定义类加载器,在一个类被加载时自动对其进行横切入系统功能,改变new操作符的行为。
JBoss和AspectWerkz采用这种做法。
缺点:偏离Java标准,在某些应用服务器中可能出现问题,因为J2EE服务器需要控制整个类加载的层级体系。
5. 语言扩展;
增加Java语言功能,使其在OOP编程基础上从语言级别上支持AOP。这一点类似于C++扩展C,引入OO概念一样。
AspectJ就对Java进行了扩展。
缺点:复杂,且引用新的语言。
OOP编程中一些模式也已经解决了AOP希望解决的部分问题:
. Decorator(装饰器模式): 类似于静态代理;
. CORBA在处理远程调用时提供了拦截器的概念;
. Servlet2.3规范引入的servlet拦截器会在处理Web请求的前后被调用;
这些解决方案的主要问题是:它们将拦截器的代码与特定的API或上下文环境(例如:处理远程调用)绑在一起,不够通用。
Spring中AOP构建在动态代码和字节码生成概念基础上, 提供更优雅、更通用的解决方案。Spring中将要增加的系统功能体现为Advice接口的实现类型。根据系统功能加入目标对象的时刻又分为:
1. 前增强(before,pre):在连接点调用之前,首先调用增强;
2. 后增强(after,post):在连接点调用之后,首先调用增强;
a. 返回后增强(after returning advice):在调用成功(没抛异常)之后;
b. 抛出增强(throws advice):在抛出某种特定类型的异常之后;
c. 后增强(after advice):在连接点调用后,不管是否抛异常;
3. 环绕增强(around):完全控制流程,除完成本身工作,还要负责主动调用连接点;
不同增强授受的参数不同;例如后增强获得实际方法的返回值,但抛出增强则不能;
我们应从可用性角度出发,尽量使用功能最弱,目的最明确的增强,这样可以使程序更加清晰。
Spring中为了对系统功能加入到目标对象的方法进行进一步精细控制,提供PointCut接口。然后将系统功能结合到目标对象的工作交由配置文件完成。Advice编写是简单的,PointCut接口的实现类是现成的,配置的代码是可以参考的,但配置文件中一个名为ProxyFactoryBean的工作原理是很重要的。为了能近距离感受AOP神奇背后的普通,我们可以自行编写ProxyFactoryBean。在看具体的源码之前,对ProxyFactory所实现的接口稍做说明:
1. BeanFactoryAware: 实现该接口的Bean对象在由Spring框架实例化完后,会由Spring框架自动注入BeanFactory对象;
2. FactoryBean: 实现该接口,通过该Bean对象所配置的Bean的名称获得的不是该Bean对象,而是该Bean对象方法getObject()返回的对象;
3. InvocationHandler: 实现该接口,动态地改变所执行原方法的功能,实现在原方法前、后或前后加入增强;
package common; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.FactoryBean; public class MyProxyFactoryBean implements BeanFactoryAware, FactoryBean, InvocationHandler{ private Object target; private Class[] proxyInterfaces; private List interceptorNames=new ArrayList(); private List realInterceptorNames=null; private BeanFactory factory; @Override public Object getObject() throws Exception { return Proxy.newProxyInstance( target.getClass().getClassLoader(), proxyInterfaces, this ); } @Override public Class getObjectType() { return target.getClass(); } @Override public boolean isSingleton() { return true; } public void setTarget(Object target) { this.target=target; } public void setProxyInterfaces(String proxyInterfaces) { try { this.proxyInterfaces=new Class[]{Class.forName(proxyInterfaces)}; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(realInterceptorNames==null) { realInterceptorNames=new ArrayList(); for(Object o: interceptorNames) { realInterceptorNames.add(factory.getBean(o.toString())); } } for(Object o: realInterceptorNames) { if(o instanceof MethodBeforeAdvice) ((MethodBeforeAdvice)o).before(method, args, target); } Object result=method.invoke(target,args); return result; } public void setInterceptorNames( List interceptorNames) { this.interceptorNames=interceptorNames; } @Override public void setBeanFactory(BeanFactory arg0) throws BeansException { this.factory=arg0; } }
两种代理模式详述
代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理类的创建时期,代理类可分为两种。
静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在。
动态代理类:在程序运行时,运用反射机制动态创建而成。
1、静态代理类
A:先定义一个接口类
java 代码
package ttitfly.proxy;
public interface HelloWorld {
public void print(); // public void say();
}
B: 定义一个该接口的实现类
java 代码
package ttitfly.proxy;
public class HelloWorldImpl implements HelloWorld{
public void print(){
System.out.println("HelloWorld");
} // public void say(){ // System.out.println("Say Hello!"); // }
}
C:定义一个静态代理类
java 代码
package ttitfly.proxy;
public class StaticProxy implements HelloWorld{
public HelloWorld helloWorld ;
public StaticProxy(HelloWorld helloWorld){
this.helloWorld = helloWorld;
}
public void print(){
System.out.println("Welcome");
//相当于回调
helloWorld.print();
}
// public void say(){ // //相当于回调 // helloWorld.say(); // }
}
D: 一个测试类:
java 代码
package ttitfly.proxy;
public class TestStaticProxy {
public static void main(String[] args){
HelloWorld helloWorld = new HelloWorldImpl();
StaticProxy staticProxy = new StaticProxy(helloWorld);
staticProxy.print();
// staticProxy.say();
}
}
可以看出静态代理类有一个很不爽的缺点:当如果接口加一个方法(把上面所有的代码的注释给去掉),所有的实现类和代理类里都需要做个实现。这就增加了代码的复杂度。动态代理就可以避免这个缺点。
2、动态代理
动态代理与普通的代理相比较,最大的好处是接口中声明的所有方法都被转移到一个集中的方法中处理(invoke),这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
动态代理类只能代理接口,代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
Proxy类提供了创建动态代理类及其实例的静态方法。
(1)getProxyClass()静态方法负责创建动态代理类,它的完整定义如下:
public static Class getProxyClass(ClassLoader loader, Class[] interfaces) throws IllegalArgumentException
参数loader 指定动态代理类的类加载器,参数interfaces 指定动态代理类需要实现的所有接口。
(2)newProxyInstance()静态方法负责创建动态代理类的实例,它的完整定义如下:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler) throws
IllegalArgumentException
参数loader 指定动态代理类的类加载器,参数interfaces 指定动态代理类需要实现的所有接口,参数handler 指定与动态代理类关联的 InvocationHandler 对象。
由Proxy类的静态方法创建的动态代理类具有以下特点:
动态代理类是public、final和非抽象类型的;
动态代理类继承了java.lang.reflect.Proxy类;
动态代理类的名字以“$Proxy”开头;
动态代理类实现getProxyClass()和newProxyInstance()方法中参数interfaces指定的所有接口;
Proxy 类的isProxyClass(Class cl)静态方法可用来判断参数指定的类是否为动态代理类。只有通过Proxy类创建的类才是动态代理类;
动态代理类都具有一个public 类型的构造方法,该构造方法有一个InvocationHandler 类型的参数。
由Proxy类的静态方法创建的动态代理类的实例具有以下特点:
1. 假定变量foo 是一个动态代理类的实例,并且这个动态代理类实现了Foo 接口,那么“foo instanceof Foo”的值为true。把变量foo强制转换为Foo类型是合法的:
(Foo) foo //合法
2.每个动态代理类实例都和一个InvocationHandler 实例关联。Proxy 类的getInvocationHandler(Object proxy)静态方法返回与参数proxy指定的代理类实例所关联的InvocationHandler 对象。
3.假定Foo接口有一个amethod()方法,那么当程序调用动态代理类实例foo的amethod()方法时,该方法会调用与它关联的InvocationHandler 对象的invoke()方法。
InvocationHandler 接口为方法调用接口,它声明了负责调用任意一个方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable
参数proxy指定动态代理类实例,参数method指定被调用的方法,参数args 指定向被调用方法传递的参数,invoke()方法的返回值表示被调用方法的返回值。