<span style="font-size:18px;">Spring AOP的织入,在Spring AOP 中,使用类org.springframework.aop.framework.ProxyFactory作为织入器
为ProxyFactory提供必要的“生产原材料后”,ProxyFactory会返回那个织入完成的代理对象,使用ProxyFactory只需要制定如下两个最基本的东西
第一个是要对其进行织入的目标对象,可以通过ProxyFactory的构造方法直接传入,也可以在ProxyFactory构造完成之后,通过相应的setter方法进行设置
第二个是将要应用到目标对象的Aspect。在spring里面叫做Advisor,除了指定相应的Advisor之外,还可以直接指定各种类型的Advice() weaver.addAdvice(...);
下面是我自己写的一个小例子
public interface ISomeBean {
public void doSth();
}
public class ISomeBeanImpl implements ISomeBean{
@Override
public void doSth() {
System.out.println("do something");
}
}
有了要拦截的目标类,还得有织入到Joinpoint处的横切逻辑,也就是要用到某个Advice实现
public class PerformanceMethodInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("you must overcome the obstacle hiding in yor heart");
return invocation.proceed();
}
}
有了这些,我们来看一下使用ProxyFactory对实现了ISomeBean接口的目标类,以及没有实现任何接口的目标类如何进行处理
(1)基于借口的代理
ISomeBeanImpl somebean=new ISomeBeanImpl();
ProxyFactory proxy=new ProxyFactory(somebean);
proxy.setProxyTargetClass(true);//强制ProxyFactory采用基于类的代理
proxy.setInterfaces(new Class[]{ISomeBean.class});//告诉ProxyFactory要对ISomeBean接口进行代理
NameMatchMethodPointcutAdvisor advisor=new NameMatchMethodPointcutAdvisor();
advisor.setMappedName("doSth");
advisor.setAdvice(new PerformanceMethodInterceptor());
proxy.addAdvisor(advisor);
ISomeBean proxyObject=(ISomeBean) proxy.getProxy();
proxyObject.doSth();
System.out.println(proxyObject.getClass());
(2)基于类的代理
如果目标类没有实现任何接口,默认情况下,ProxyFactory会对目标类进行基于类的代理,即使用CGLIB,
假设我们现在有如下的类:
public class Executable {
public void execute(){
System.out.println("Executable without any Interfaces");
}
}
/**
* 如果使用Executable作为目标对象,那么ProxyFactory就会对其进行基于类的代理,下面的代码演示了使用ProxyFactory对Executable进行织入的过程.
*/
ProxyFactory proxy1=new ProxyFactory(new Executable());
NameMatchMethodPointcutAdvisor advisor1=new NameMatchMethodPointcutAdvisor();
advisor1.setMappedName("execute");
advisor1.setAdvice(new PerformanceMethodInterceptor());
proxy1.addAdvisor(advisor1);
Executable proxyObject1=(Executable) proxy1.getProxy();
proxyObject1.execute();
System.out.println(proxyObject1.getClass());
输出结果如下:
Executable without any Interfaces
class aop.spring.advice.Executable$$EnhancerByCGLIB$$decfdbd3
可以看出最终的代理对象是基于CGLIB的
即使目标对象至少实现了一个接口,我们也可以通过proxyTargetClass属性强制ProxyFactory采用基于类的代理
总的来说,如果满足下列三种情况中的任何一种,ProxyFactory将对目标对象进行基于类的代理
如果目标对象没有实现任何接口,不管proxyTargetClass的值是什么,都会采用基于类代理
如果ProxyFactory的proxyTargetClass属性值被设置为true,ProxyFactory会采用基于类的代理
如果ProxyFactory的optimize属性值被设置为true,ProxyFactory会采用基于类的代理
容器中的织入器----ProxyFactoryBean
虽然使用ProxyFactory,可以让我们能够独立于Spring的IoC容器之外来使用Spring的AOP支持,但是,将Spring AOP和Spring的IoC容器
支持相结合,才是发挥Spring AOP更大作用的最佳途径。通过Spring的IOC容器,我们可以在容器中对Pointcut和Advice等进行管理,即
使他们依赖于其他业务对象,也可以很容易注入其中
ProxyFactoryBean的本质是一个用来生产Proxy的FactoryBean.IoC容器中的FactoryBean,如果容器中的某个对象持有某个FactoryBean的
引用,它取得的不是FactoryBean本身,而是FactoryBean.getObject()方法所返回的对象
下面是我写的一个小程序
public interface ISomeBean {
public void doSth();
}
public class ISomeBeanImpl implements ISomeBean{
@Override
public void doSth() {
System.out.println("do something");
}
}
public interface IOtherBean {
public void doOther();
}
public class SomeBeanIntroductionInterceptor implements IOtherBean,IntroductionInterceptor{
/**
* 判断调用的方法是否为指定类中的方法
* 如果Method代表了一个方法 那么调用它的invoke就相当于执行了它代表的这个方法
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if(implementsInterface(invocation.getMethod().getDeclaringClass())){
System.out.println("zhangsi");
return invocation.getMethod().invoke(this, invocation.getArguments());
}
return invocation.proceed();
}
/**
* 判断clazz是否为给定接口IOtherBean的实现
*/
@Override
public boolean implementsInterface(Class clazz) {
return clazz.isAssignableFrom(IOtherBean.class);
}
@Override
public void doOther() {
System.out.println("do other thing");
}
}
配置文件如下,<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 目标对象 -->
<bean id="someBeanTarget" class="com.spring.targetImpl.ISomeBeanImpl" />
<!-- 通知 -->
<bean id="someBeanAdvice" class="aop.spring.advice.SomeBeanIntroductionInterceptor"></bean>
<!-- 通知者,只能以构造器方法注入 -->
<!-- DefaultIntroductionAdvisor(Advice advice, IntroductionInfo introductionInfo) -->
<!-- introductionInfo - the IntroductionInfo that describes the interface
to introduce (may be null) -->
<bean id="introductionAdvisor"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg ref="someBeanAdvice" />
<constructor-arg value="aop.spring.target.IOtherBean" />
</bean>
<!-- 代理,将我们的切面织入到目标对象 -->
<bean id="someBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 若目标对象实现了代理接口,则可以提供代理接口的配置 -->
<property name="proxyInterfaces" value="aop.spring.target.ISomeBean" />
<!-- 配置目标对象 -->
<property name="target" ref="someBeanTarget" />
<!-- 配置切面 -->
<property name="interceptorNames">
<list>
<value>introductionAdvisor</value>
</list>
</property>
</bean>
</beans>
测试代码:
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("/beans.xml");
/**
* 将代理对象强制转换为目标对象的具体类型
*/
ISomeBean weaver=(ISomeBean) ctx.getBean("someBeanProxy");
weaver.doSth();
System.out.println("=====");
((IOtherBean)weaver).doOther();
ProxyFactoryBean的改进-----加快织入的自动化进程
在IoC容器中使用ProxyFactoryBean进行横切逻辑织入固然不错,但是,我们都是针对每个目标对象,然后给出它们对应的ProxyFactoryBean的配置。如果
目标对象就那么几个,还应付的过来,但是系统中那么多的业务对象都可能是目标对象,这样实现起来就是有问难的。
Spring AOP中给出了自动代理机制,用以帮助我们解决使用ProxyFactoryBean配置工作量比较大的问题
比如常用的一个AutoProxyCreator类DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator注册到容器后,它会自动搜寻容器内的所有的Advisor,然后根据各个Advisor所提供动的拦截信息,
为符合条件的容器中的目标对象生成相应的代理对象。注意,DefaultAdvisorAutoProxyCreator只对Advisor有效,因为只有Advisor才既有Pointcut信息以捕捉
符合条件的目标对象,又有相应的Advice。使用DefaultAdvisorAutoProxyCreator对容器内所有的bean定义对应的对象进行自动代理后,我们从容器中取得的实例
就都是已经包含了织入横切逻辑的代理对象。
一个实现的小程序如下:
public interface ICounter {
void resetCounter();
int getCounter();
}
public class CounterImpl1 implements ICounter {
private int counter;
@Override
public void resetCounter() {
// TODO Auto-generated method stub
counter = 0;
}
@Override
public int getCounter() {
// TODO Auto-generated method stub
counter++;
return counter;
}
}
public class CounterImpl2 implements ICounter{
private int counter;
@Override
public void resetCounter() {
// TODO Auto-generated method stub
counter=10;
}
@Override
public int getCounter() {
// TODO Auto-generated method stub
counter--;
return counter;
}
}
public interface ITask {
void execute();
}
public class FakeTask implements ITask{
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println(" facktask executed");
}
}
public class MockTask implements ITask{
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("task executed");
}
}
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
<bean id="counterImpl1" class="aop.spring.deleIntroduction.CounterImpl1"></bean>
<bean id="counterImpl2" class="aop.spring.deleIntroduction.CounterImpl2"></bean>
<bean id="mockTask" class="aop.spring.deleIntroduction.MockTask"></bean>
<bean id="fakeTask" class="aop.spring.deleIntroduction.FakeTask"></bean>
<bean name="pointcut"
class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value="execute"></property>
</bean>
<!-- 有多个方法时 -->
<bean name="pointcut2" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>resetCounter</value>
<value>getCounter</value>
</list>
</property>
</bean>
<bean id="logAdvisor1" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="pointcut"></property>
<property name="advice">
<bean id="performaceInterceptor1"
class="aop.spring.deleIntroduction.PerformanceMethodInterceptor">
</bean>
</property>
</bean>
<bean id="logAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="pointcut2"></property>
<property name="advice">
<bean id="performaceInterceptor2" class="aop.spring.deleIntroduction.PerformanceMethodInterceptor"></bean>
</property>
</bean>
</beans>
测试代码如下:
public class AutoProxyCreaterTest {
@Test
public void test() {
ApplicationContext ctx=new ClassPathXmlApplicationContext("/beans1.xml");
ITask task1=(ITask) ctx.getBean("mockTask");
task1.execute();
System.out.println();
ITask task2=(ITask) ctx.getBean("fakeTask");
task2.execute();
System.out.println();
ICounter counter1=(ICounter) ctx.getBean("counterImpl1");
System.out.println(counter1.getCounter());
System.out.println();
ICounter counter2=(ICounter)ctx.getBean("counterImpl1");
System.out.println(counter2.getCounter());
System.out.println();
ICounter counter3=(ICounter)ctx.getBean("counterImpl2");
System.out.println(counter3.getCounter());
System.out.println();
}
}
输出结果如下:
start.....
task executed
stop....
start.....
facktask executed
stop....
start.....
stop....
1
start.....
stop....
2
start.....
stop....
-1
</span>
上面内容中都要区分Introduction类型的Advice ,我特别对Introduction进行了实现,有专门关于Introduction的博客。接下来时Spring AOP的第三种实现机制。。。。。。
What do you want ?