你好,我是郭屹。今天我们继续手写MiniSpring。
从这节课开始,我们就要进入AOP环节了。在学习之前,我们先来了解一下是AOP怎么回事。
AOP,就是面向切面编程(Aspect Orient Programming),这是一种思想,也是对OOP面向对象编程的一种补充。你可能会想:既然已经存在OOP面向对象编程了,为什么还需要AOP面向切面编程呢?
这是因为在许多场景下,一个类的方法中,除了业务逻辑,通常还会包括其他比较重要但又不算主业务逻辑的例行性逻辑代码,比如常见的日志功能,它不影响我们的主业务逻辑,但又能在必要时定位问题,几乎每一个业务方法中都需要。又比如权限检查、事务处理,还有性能监控等等,都是这种情况。
显而易见,日志这类例行性逻辑,在任何一个业务方法实现中都是需要的。如果简单地将这些代码写在业务方法中,会出现两个后果,第一,我们就会将日志之类的代码重复地编写多次;第二,一个业务方法中会包含很多行例行代码,去看源代码会发现方法中多数语句不是在做业务处理。
有专业进取心的程序员就会思考一个问题, 有没有办法将这些例行性逻辑单独抽取出来,然后在程序运行的时候动态插入到业务逻辑中呢? 正是因为这个疑问,AOP应运而生了。这个问题听起来似乎无解,程序在运行时改变程序本身,似乎有点不可思议。我们研究一下Java,就会惊奇地发现,Java里面早就给我们提供了一个手段: 动态代理。我们可以利用它来开展我们的工作。
我们一步步来,先从代理讲起。
看图,我们知道真正干活儿的类是RealSubject,具体则是由DoAction()执行任务。Proxy作为代理提供一个同样的DoAction(),然后调用RealSubject的DoAction()。它们都实现Subject接口,而Client应用程序操作的是Subject 接口。
简单说来,就是在Client应用程序与真正的服务程序RealSubject之间增加了一个Proxy。
我们举例说明,先定义一个服务类接口。
public interface Subject {
String doAction(String name);
}
再定义具体的服务类。
public class RealSubject implements Subject {
public String doAction(String name) {
System.out.println("real subject do action "+name);
return "SUCCESS";
}
}
最后再定义一个代理类。
public class ProxySubject implements Subject {
Subject realsubject;
public ProxySubject() {
this.realsubject = new RealSubject();
}
public String doAction(String name) {
System.out.println("proxy control");
String rtnValue = realsubject.doAction(name);
return "SUCCESS";
}
}
通过代码我们看到,代理类内部包含了一个真正的服务类,而代理类给外部程序提供了和真正的服务类同样的接口。当外部应用程序调用代理类的方法时,代理类内部实际上会转头调用真正的服务类的相应方法,然后把真正的服务类的返回值直接返回给外部程序。这样做就隐藏了真正的服务类的实现细节。
同时,在调用真正的服务方法之前和之后,我们还可以在代理类里面做一点手脚,加上额外的逻辑,比如上面程序中的 System.out.println("proxy control");
,这些额外的代码,大部分都是一些例行性的逻辑,如权限和日志等。
最后我们提供一个客户程序使用这个代理类。
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.doAction("Test");
}
}
总结一下,代理模式能够让我们在业务处理之外添加例行性逻辑。但是这个经典的模式在我们这里不能直接照搬,因为这个代理是静态的,要事先准备好。而我们需要的是在相关的业务逻辑执行的时候,动态插入例行性逻辑,不需要事先手工静态地准备这些代理类。解决方案就是 Java中的动态代理技术。
Java提供的动态代理可以对接口进行代理,在代理的过程中主要做三件事。
这里我们还是举例说明。首先定义一个IAction接口。
package com.test.service;
public interface IAction {
void doAction();
}
提供一个具体实现类。
package com.test.service;
public class Action1 implements IAction {
@Override
public void doAction() {
System.out.println("really do action");
}
}
我们定义了一个DynamicProxy类,用来充当代理对象的类。
package com.test.service;
public class DynamicProxy {
private Object subject = null;
public DynamicProxy(Object subject) {
this.subject = subject;
}
public Object getProxy() {
return Proxy.newProxyInstance(DynamicProxy.class
.getClassLoader(), subject.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("doAction")) {
System.out.println("before call real object........");
return method.invoke(subject, args);
}
return null;
}
});
}
}
通过这个类的实现代码可以看出,我们使用了Proxy类,调用newProxyInstance方法构建IAction接口的代理对象,而且重写了InvocationHandler接口中的invoke方法。在重写的方法中我们判断方法名称是否与接口中的doAction方法保持一致,随后加上例行性逻辑(print语句),最后通过反射调用接口IAction中的doAction方法。
通过这个操作,例行性逻辑就在业务程序运行的时候,动态地添加上去了。
我们编写一个简单的测试程序,就能直观感受到代理的效果了。
package com.test.controller;
public class HelloWorldBean {
@Autowired
IAction action;
@RequestMapping("/testaop")
public void doTestAop(HttpServletRequest request, HttpServletResponse response) {
DynamicProxy proxy = new DynamicProxy(action);
IAction p = (IAction)proxy.getProxy();
p.doAction();
String str = "test aop, hello world!";
try {
response.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行这个程序,返回内容是“test aop,hello world!”。这个时候查看命令行里的内容,你就会发现还有两行输出。
before call real object........
really do action
第一行是代理对象中的输出,第二行是Action1中doAction方法的实现。
根据这个输出顺序我们发现,这个代理对象达到了代理的效果,在调用IAction的具体实现类之前进行了额外的操作,从而增强了代理类。而这个代理是我们动态增加的,而不是事先静态地手工编写一个代理类。
但是读代码,这种方式显然是不美观的,需要在业务逻辑程序中写上,对代码的侵入性太强了。
DynamicProxy proxy = new DynamicProxy(action);
IAction p = (IAction)proxy.getProxy();
这个写法跟我们手工写一个代理类实际上相差不多。这种侵入式的做法不是我们推崇的,所以我们要继续前进。
我们的目标是 非侵入式编程, 也就是应用程序在编程的时候,它不应该手工去创建一个代理,而是使用本来的业务接口,真正的实现类配置在外部,代理类也是配置在外部。
@Autowired
IAction action;
@RequestMapping("/testaop")
public void doTestAop(HttpServletRequest request, HttpServletResponse response) {
action.doAction();
}
配置如下:
<bean id="realaction" class="com.test.service.Action1" />
<bean id="action" class="com.minis.aop.ProxyFactoryBean" >
<property type="java.lang.Object" name="target" ref="realaction"/>
</bean>
业务类中自动注入的是一个action,也就是上面代码里的ProxyFactoryBean类,这个类内部包含了真正干活儿的类realaction。
这里就有一个初看起来非常奇怪的需求:注册的action bean是ProxyFactoryBean类,而业务程序使用getBean(“action”)的时候,期待返回的又不是这个Bean本身,而是内部那个target。因为只有这样才能让业务程序实际调用target中的方法,外面的这个ProxyFactoryBean对我们来讲是一个入口,而不是目标。这也就要求,当业务程序使用getBean(“action”)方法的时候,这个ProxyFactoryBean应该在内部进行进一步地处理,根据target再动态生成一个代理返回,达到侵入式编程中下面这两句话的效果。
DynamicProxy proxy = new DynamicProxy(action);
IAction p = (IAction)proxy.getProxy();
上面的方案,看起来奇怪,但是确实能解决动态代理的问题。
好,现在我们就按照这个思路动手去实现。首先我们参考Spring框架,定义FactoryBean接口。
相关代码参考:
package com.minis.beans.factory;
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
主要的方法就是getObject(),从Factory Bean中获取内部包含的对象。
接着定义FactoryBeanRegistrySupport,提供一部分通用的方法。
package com.minis.beans.factory.support;
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry{
protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
return factoryBean.getObjectType();
}
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName) {
Object object = doGetObjectFromFactoryBean(factory, beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (BeansException e) {
e.printStackTrace();
}
return object;
}
//从factory bean中获取内部包含的对象
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) {
Object object = null;
try {
object = factory.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
}
最重要的是这个方法:doGetObjectFromFactoryBean(),从一个Factory Bean里面获取内部包含的那个target对象。
因为FactoryBeanRegistrySupport继承了DefaultSingletonBeanRegistry,所以我们接下来可以改写AbstractBeanFactory,由原本继承DefaultSingletonBeanRegistry改成继承FactoryBeanRegistrySupport,保留原有功能的同时增加了功能扩展。
我们重点要修改核心的getBean()方法。
package com.minis.beans.factory.support;
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory,BeanDefinitionRegistry{
public Object getBean(String beanName) throws BeansException{
Object singleton = this.getSingleton(beanName);
if (singleton == null) {
singleton = this.earlySingletonObjects.get(beanName);
if (singleton == null) {
System.out.println("get bean null -------------- " + beanName);
BeanDefinition bd = beanDefinitionMap.get(beanName);
if (bd != null) {
singleton=createBean(bd);
this.registerBean(beanName, singleton);
//beanpostprocessor
//step 1 : postProcessBeforeInitialization
applyBeanPostProcessorsBeforeInitialization(singleton, beanName);
//step 2 : init-method
if (bd.getInitMethodName() != null && !bd.getInitMethodName().equals("")) {
invokeInitMethod(bd, singleton);
}
//step 3 : postProcessAfterInitialization
applyBeanPostProcessorsAfterInitialization(singleton, beanName);
}
else {
return null;
}
}
}
else {
}
//处理factorybean
if (singleton instanceof FactoryBean) {
return this.getObjectForBeanInstance(singleton, beanName);
}
else {
}
return singleton;
}
我们看到在getBean()这一核心方法中,原有的逻辑处理完毕后,我们新增下面这一段。
//process Factory Bean
if (singleton instanceof FactoryBean) {
return this.getObjectForBeanInstance(singleton, beanName);
}
根据代码实现可以看出,这里增加了一个判断,如果Bean对象是FactoryBean类型时,则调用getObjectForBeanInstance方法。
protected Object getObjectForBeanInstance(Object beanInstance, String beanName) {
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
object = getObjectFromFactoryBean(factory, beanName);
return object;
}
代码显示,getObjectForBeanInstance又会调用doGetObjectFromFactoryBean方法。
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) {
Object object = null;
try {
object = factory.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
最后落实到了factory.getObject()里。由此可以看出,我们通过AbstractBeanFactory获取Bean的时候,对FactoryBean进行了特殊处理,获取到的已经不是FactoryBean本身了,而是它内部包含的那一个对象。而这个对象,也不是真正底层对应的Bean。它仍然只是一个代理的对象,我们继续往下看。
我们这个getObject()只是FactoryBean里的一个接口,接下来我们提供一下它的接口实现——ProxyFactoryBean。
package com.minis.aop;
public class ProxyFactoryBean implements FactoryBean<Object> {
private AopProxyFactory aopProxyFactory;
private String[] interceptorNames;
private String targetName;
private Object target;
private ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
private Object singletonInstance;
public ProxyFactoryBean() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
public void setAopProxyFactory(AopProxyFactory aopProxyFactory) {
this.aopProxyFactory = aopProxyFactory;
}
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
protected AopProxy createAopProxy() {
return getAopProxyFactory().createAopProxy(target);
}
public void setInterceptorNames(String... interceptorNames) {
this.interceptorNames = interceptorNames;
}
public void setTargetName(String targetName) {
this.targetName = targetName;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object getObject() throws Exception {//获取内部对象
return getSingletonInstance();
}
private synchronized Object getSingletonInstance() {//获取代理
if (this.singletonInstance == null) {
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
protected Object getProxy(AopProxy aopProxy) {//生成代理对象
return aopProxy.getProxy();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
这段代码的核心在于,ProxyFactoryBean在getObject()方法中生成了一个代理getProxy(createAopProxy()),同样也是通过这种方式,拿到了要代理的目标对象。这里的工作就是 创建动态代理。
Spring作为一个雄心勃勃的框架,自然不会把自己局限于JDK提供的动态代理一个技术上,所以,它再次进行了包装,提供了AopProxy的概念,JDK只是其中的一种实现。
package com.minis.aop;
public interface AopProxy {
Object getProxy();
}
还定义了factory。
package com.minis.aop;
public interface AopProxyFactory {
AopProxy createAopProxy(Object target);
}
然后给出了基于JDK的实现。
package com.minis.aop;
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
Object target;
public JdkDynamicAopProxy(Object target) {
this.target = target;
}
@Override
public Object getProxy() {
Object obj = Proxy.newProxyInstance(JdkDynamicAopProxy.class.getClassLoader(), target.getClass().getInterfaces(), this);
return obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("doAction")) {
System.out.println("-----before call real object, dynamic proxy........");
return method.invoke(target, args);
}
return null;
}
}
package com.minis.aop;
public class DefaultAopProxyFactory implements AopProxyFactory{
@Override
public AopProxy createAopProxy(Object target) {
return new JdkDynamicAopProxy(target);
}
}
在这个实现里,我们终于看到了我们曾经熟悉的Proxy.newProxyInstance()和invoke()。利用Java的动态代理技术代理了目标对象,而这也是ProxyFactoryBean里真正要返回的Object。
这就是Spring AOP的实现原理。
有了上面的工具,我们的测试程序就不需要再手动构建代理对象了,而是交给框架本身处理。而注入的对象,则通过配置文件注入属性值。
applicationContext.xml配置中新增一段内容。
<bean id="realaction" class="com.test.service.Action1" />
<bean id="action" class="com.minis.aop.ProxyFactoryBean" >
<property type="java.lang.Object" name="target" ref="realaction"/>
</bean>
通过配置,我们在HelloWorldBean里注入的IAction对象就纳入了容器管理之中,因此后续测试的时候,直接使用action.doAction(),就能实现手动初始化JDK代理对象的效果。
package com.test.controller;
public class HelloWorldBean {
@Autowired
IAction action;
@RequestMapping("/testaop")
public void doTestAop(HttpServletRequest request, HttpServletResponse response) {
action.doAction();
String str = "test aop, hello world!";
try {
response.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们终于看到了动态代理的结果。
这节课我们 利用JDK动态代理技术实现了AOP这个概念。
我们介绍了代理模式实现的静态代理,然后使用了JDK的动态代理技术。在使用动态代理技术的程序代码中,我们发现它是侵入式的,不理想,所以我们就想办法把代理配置在XML文件里了。但是如果按照原有的Bean的定义,这个配置在外部文件里的代理Bean本身不能代理业务类,我们真正需要的是通过这个代理Bean来创建一个动态代理,于是引入了FactoryBean的概念,不是直接获取这个Bean本身,而是通过里面的getObject()获取到Factory Bean里面包含的对象。
这样将IoC容器里的Bean分成了两类:一是普通的Bean,二是Factory Bean。在getObject()的实现中,我们使用JDK的动态代理技术创建了一个代理。这样就实现了AOP。
另外,Spring中的代理支持JDK代理与Cglib代理两种,目前MiniSpring定义的DefaultAopProxyFactory只支持JDK代理。另一种方式我留作思考题,你可以先想一想要怎么实现。
AOP还有别的实现方案,比如AspectJ,也比较常用,在实际工程实践中,一般采用的就是AspectJ,而不是Spring AOP,因为AspectJ更加高效,功能更强。比如,AspectJ是编译时创建的代理,性能高十倍以上,而且切入点不仅仅在方法上,而是可以在类的任何部分。所以AspectJ才是完整的AOP解决方案,Spring AOP不是成功的工业级方案。之所以保留Spring AOP,一个原因是原理简单、利于理解,另一个是Rod Johnson不忍抛弃自己的心血。
完整源代码参见 https://github.com/YaleGuo/minis
学完这节课,我也给你留一道思考题。如果MiniSpring想扩展到支持Cglib,程序应该从哪里下手改造?欢迎你在留言区与我交流讨论,也欢迎你把这节课分享给需要的朋友。我们下节课见!