Spring AOP实现机制(二)--ProxyFactoryBean---将Spring AOP和Spring IoC容器相结合

<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 ?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值