Spring AOP (四)
Spring AOP (四)
前面我们学习了AOP的代理模式,匹配连接点,以及创建,配置,匹配Proxy bean,今天我们学习使用IOC来配置AOP的框架类。
所有的Spring的代理工厂都继承了
org.springframework.aop.ProxyConfig
,一个这样的配置可以生成多个AOP代理,而所有的AOP代理都包含一个队配置对象的引用。事实上,ProxyConfig的子类通常也可以作为代理工厂。ProxyConfig下面有一些重要的类:
- AdvisedSupport : extends ProxyConfig,同时实现了Advised接口,在一个单独的TargetSource对象中保留了对象的配置信息。
Spring源代码声明:
//org.springframework.aop.framework.AopProxy
public class AdvisedSupport extends ProxyConfig implements Advised {...}
- ProxyFactoryBean : extends ProxyCreatorSupport 用于在Spring中以声明编程方式定义代理,是一个工厂Bean。
Spring源代码声明:
public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {...}
ProxyFactoryBean实现了FactoryBean,BeanClassLoaderAware,BeanFactoryAware接口,用于创建工厂以及处理。并开放了开发者连接接口。
- ProxyFactory : 功能同ProxyFactoryBean,用于编程方式创建代理。
public class ProxyFactory extends ProxyCreatorSupport {...}
ProxyFactory 继承了ProxyCreatorSupport,用于创建代理支持。实际中我们使用IOC创建代理的方式应当是ProxyFactoryBean,ProxyCreatorSupport继承了AdvisedSupport和AdvisedSupport的属性,而且还包含有自己的属性。
使用ProxyFactoryBean
配置一个ProxyFactoryBean的步骤:
- 定义Advisor或独立的Pointcut与Advice
- 定义interceptorNames
具体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 -->
<bean id="advice" class="boy.aop.advice.BeforeMethod">
</bean>
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="advice"></property>
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" >
<value>.*get.*</value>
</property>
</bean>
</property>
</bean>
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 我们不希望其他Bean单独方法,所以这里的target使用内部bean -->
<property name="target">
<bean class="boy.aop.pointcut.PointcutMethod">
<property name="age"><value>21</value></property>
<property name="name"><value>Boy</value></property>
<property name="title"><value>Winner</value></property>
</bean>
</property>
<!-- 我们使用interceptorNames拦截器来连接通知/通知者 -->
<!-- 属性的list集合里面只能放 通知/通知者。-->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
</beans>
简单测试类
package boy.aop.pointcut;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* Created by Boy on 2016/12/14 21:46.
*/
public class ProxyFactoryBeanTest {
public static void main(String[] args){
ApplicationContext ac = new FileSystemXmlApplicationContext("/src/main/resources/SpringProxy.xml");
PointcutMethod pm = (PointcutMethod) ac.getBean("person");
pm.getAge();
pm.getName();
pm.getTitle();
}
}
运行结果
BeforeMethod : now we start before!
我们处理 getAge 方法
BeforeMethod : now we start before!
我们处理 getName 方法
BeforeMethod : now we start before!
我们处理 getName 方法
Process finished with exit code 0
可以看到,我们成功执行,同前面的例子我们的代码简洁了好多。现在我们再考虑一下,假如我们需要代理另外一个或者多个target,它的Advisor与上面的完全相同,只是target不同,我们是否要写成下面的样子?
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 我们不希望其他Bean单独方法,所以这里的target使用内部bean -->
<property name="target">
<bean class="boy.aop.pointcut.PointcutMethod">
<property name="age"><value>21</value></property>
<property name="name"><value>Boy</value></property>
<property name="title"><value>ZMM Love ZKK</value></property>
</bean>
</property>
<!-- 我们使用interceptorNames拦截器来连接通知/通知者 -->
<!-- 属性的list集合里面只能放 通知/通知者。-->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
<bean id="person2" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 我们不希望其他Bean单独方法,所以这里的target使用内部bean -->
<property name="target">
<bean class="boy.aop.pointcut.PointcutMethod">
<property name="age"><value>19</value></property>
<property name="name"><value>ZMM</value></property>
<property name="title"><value>ZKK Love ZMM</value></property>
</bean>
</property>
<!-- 我们使用interceptorNames拦截器来连接通知/通知者 -->
<!-- 属性的list集合里面只能放 通知/通知者。-->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
可以看到代码的冗余量就体现了,假如有100个,我们的配置文件几乎不能看了,如何避免呢?答案就是使用Bean定义的abstract和parent。通过使用abstract属性可以将ProxyFactoryBean的共有部分作为一个抽象Bean,而需要使用公用属性的子Bean只需要继承这个抽象Bean极客。我们修改下上面的配置文件:
<bean id="proxyCommon" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
<bean id="person" parent="proxyCommon">
<!-- 使用内部Bean -->
<property name="target">
<bean class="boy.aop.pointcut.PointcutMethod">
<property name="age" value="21"></property>
<property name="name" value="Boy"></property>
<property name="title" value="ZKK Love ZMM"></property>
</bean>
</property>
</bean>
<bean id="person1" parent="proxyCommon">
<!-- 使用内部Bean -->
<property name="target">
<bean class="boy.aop.pointcut.PointcutMethod">
<property name="age" value="19"></property>
<property name="name" value="ZMM"></property>
<property name="title" value="ZMM Love ZKK"></property>
</bean>
</property>
</bean>
测试类
ApplicationContext ac = new FileSystemXmlApplicationContext("/src/main/resources/SpringProxy.xml");
PointcutMethod pm = (PointcutMethod) ac.getBean("person");
pm.getAge();
pm.getName();
pm.getTitle();
PointcutMethod pm1 = (PointcutMethod) ac.getBean("person1");
pm1.getAge();
pm1.getName();
pm1.getTitle();
运行结果
23:06:38.062 [main] DEBUG org.springframework.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isInterfaceProxied(java.lang.Class)
23:06:38.062 [main] DEBUG org.springframework.aop.framework.CglibAopProxy - Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass()
BeforeMethod : now we start before!
我们处理 getAge 方法
BeforeMethod : now we start before!
我们处理 getName 方法
BeforeMethod : now we start before!
我们处理 getName 方法
23:06:38.120 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'person1'
23:06:38.120 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'advisor'
23:06:38.121 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; recaching singleton instance
23:06:38.121 [main] DEBUG org.springframework.aop.framework.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [boy.aop.pointcut.PointcutMethod@3c87521]
BeforeMethod : now we start before!
我们处理 getAge 方法
BeforeMethod : now we start before!
我们处理 getName 方法
BeforeMethod : now we start before!
我们处理 getName 方法
Process finished with exit code 0
我们对配置文件进行了优化,成功实现了,减少了冗余,但是我们还有继续优化的可能性,明天再写吧。今天就先写到这里,这两天也要考试了,还是需要应付应付的~~