Spring AOP:自动创建代理


 在 《Spring AOP 切面开发详解》中演示了Spring中通过切面如何进行横切逻辑的抽取。但是所有的例子,都是通过ProxyFactoryBean创建织入切面的代理,每一个需要被代理的Bean都需要使用一个ProxyFactoryBean进行配置,虽然可以使用父子进行改造,但还是很麻烦。

 幸运的是,Spring提供了自动代理机制,让容器自动生成代理,把用户从烦琐的配置工作中解放出来。在内部,Spring使用BeanPostProcessor自动完成这项工作。

1.原理介绍:

 这些基于BeanPostProcessor的自动代理创建器的实现类,将根据一些规则自动在容器实例化Bean时为匹配的Bean生成代理实例。这些代理创建器可以分为以下3类

在这里插入图片描述

注:虚线箭头表示实现,实线箭头表示继承

  • 第一类:基于Bean配置名规则的自动代理创建器。

    允许为一组特定配置名的Bean自动创建代理实例。实现类是:BeanNameAutoProxyCreator

  • 第二类:基于Advisor匹配机制的自动代理创建器。

    它会扫描容器中的所有Advisor,自动将这些切面匹配到Bean中。

    实现类是:DefaultAdvisorAutoProxyCreator 。之所以能将Advisor自动匹配到Bean,是因为切面Advisor本身就包含了增强和切点信息,能够过滤出Bean.

  • 第三类:基于Bean中AspectJ注解标签的自动代理创建器。

    实现类是:AnnotationAwareAspectJAutoProxyCreator

    但凡可以通过配置实现,注解往往也可以,而且目前SpringBoot中大量使用注解,所以此种方式是最常用的。

 从上面的类图,我们可以清楚地看到所有的自动代理创建器类,都实现了BeanPostProcessor,在容器实例化Bean时,BeanPostProcessor将对它进行加工处理,因此,自动代理创建器有机会对满足匹配规则的Bean自动创建代理对象。

第三类代理创建器是最常用的,我会在下一篇通过一整篇文章详细介绍。而BeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreator 的使用可以说是AspectJ的基础,原理是一致的,只是后者采用了注解的形式。

2.BeanNameAutoProxyCreator:

 我们通过BeanNameAutoProxyCreator 改造上一篇中的切面例子。如果没有看上篇文章,建议先看一下,当然,直接往下看也是没有任何问题的。我们采用了上篇普通方法名匹配切面的例子:

这里为了直观作对比,我先贴出不采用自动代理创建器的配置文件:

	<!-- 目标对象配置 -->
	<bean id="waiterTarget" class="com.smart.advisor.Waiter" />
	<bean id="sellerTarget" class="com.smart.advisor.Seller" />
	<!--增强配置-->
	<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
	<!--切面配置-->
	<bean id="greetingAdvisor" class="com.smart.advisor.GreetingAdvisor"
		p:advice-ref="greetingAdvice" />

	<!--代理类配置-->
	<bean id="parent" abstract="true"
		class="org.springframework.aop.framework.ProxyFactoryBean"
		p:interceptorNames="greetingAdvisor" p:proxyTargetClass="true" />
	<bean id="waiter" parent="parent" p:target-ref="waiterTarget" />
	<bean id="seller" parent="parent" p:target-ref="sellerTarget" />

这里有两个类将会使用到切面,所以代理类的配置已经不少,而且是通过Parent节点简化后的配置。

下面通过BeanNameAutoProxyCreator以更优雅、更便捷的方式完成相同的功能。

<bean id="waiter" class="com.smart.advisor.Waiter" />
<bean id="seller" class="com.smart.advisor.Seller" />
<!--增强配置-->
<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
<!--切面配置-->
<bean id="greetingAdvisor" class="com.smart.advisor.GreetingAdvisor"
		p:advice-ref="greetingAdvice" />

<!-- 通过Bean名称自动创建代理 -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
		p:beanNames="*er" p:interceptorNames="greetingAdvisor"
		p:optimize="true"/>

说明:

BeanNameAutoProxyCreator有一个beanNames属性,它允许用户指定一组需要自动代理的Bean名称,Bean名称可以使用*通配符。

当然使用通配符会带来一定的风险,在这个例子中,假设一个其他的Bean名称也以“er”结尾,则自动代理创建器也会为该Bean创建代理。所以为了保险起见,用户可以使用下面的方式配置beanNames属性:value=“waiter,seller”。即:

	<bean
		class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
		p:beanNames="waiter,seller"
		p:interceptorNames="greetingAdvisor"
		p:optimize="true">
	</bean>

optimise为true表明强制使用CGLib动态代理技术。

使用到的切面类:

public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {

    public boolean matches(Method method, Class clazz) {
        return "greetTo".equals(method.getName());
    }

    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            public boolean matches(Class clazz) {
                return Waiter.class.isAssignableFrom(clazz);
            }
        };
    }
}

使用到的增强类:

public class GreetingBeforeAdvice implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object obj) throws Throwable {
        String clientName = (String) args[0];
        System.out.println("How are you!Mr." + clientName + ".");
    }
}

测试代码不变:

public class AutoProxyAdvisorTest {
    private String configPath ="";
    private ApplicationContext ctx =null;

    @BeforeClass
    public void init(){
         configPath = "com/smart/autoproxy/beans.xml";
         ctx = new ClassPathXmlApplicationContext(configPath);
    }

    @Test
	public  void staticMethod() {
		Waiter waiter = (Waiter)ctx.getBean("waiter");
		Seller seller = (Seller)ctx.getBean("seller");
		waiter.greetTo("John");
		waiter.serveTo("John");
		seller.greetTo("John");	
	}
}

打印结果:和预期一致,只有Waiter的greetTo方法被织入增强。

How are you!Mr.John.
waiter greet to John...
waiter serving John...
seller greet to John...

3.DefaultAdvisorAutoProxyCreator:

 DefaultAdvisorAutoProxyCreator能够扫描容器中的Advisor,并将Advisor自动织入匹配的目标Bean中,即为匹配的目标Bean自动创建代理。

 我们知道切面Advisor是切点和增强的复合体,Advisor本身已经包含了足够的信息:横切逻辑(要织入什么)以及连接点(织入哪里),这是该种方式的底层逻辑。

下面我们通过示例看它如何使用:实际上只需要将配置文件改动一下,其他都不需要变动

	<bean id="waiter" class="com.smart.advisor.Waiter" />
	<bean id="seller" class="com.smart.advisor.Seller" />
	<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
	<bean id="greetingAdvisor" class="com.smart.advisor.GreetingAdvisor"
		  p:advice-ref="greetingAdvice" />
	<!-- 通过Bean名称自动创建代理 -->
		<!--<bean-->
		<!--class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"-->
		<!--p:beanNames="waiter,seller"-->
		<!--p:interceptorNames="greetingAdvisor"-->
		<!--p:optimize="true">-->
		<!--</bean>-->


<!--通过Advisor自动创建代理-->	
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

只需要加入一个org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator

就可以扫描容器中的切面,然后给匹配的Bean生成代理。

思考:如果将上面通过名称自动创建代理的代码也放开,会出现什么状况?是同时给符合的Bean创建多个代理吗? 答案是,Spring容器中不允许重复代理,所以放开代码,运行会报错。所以,一般只会使用一种自动生成代理策略。

总结:

Spring AOP可以说是Spring核心中的核心,它贯穿着Spring的整个生态系统,包括目前的SpringBoot等很多地方依然是采用AOP思想来实现。AOP的基本逻辑就是:在使用目标对象时,不是直接使用,而是通过代理间接使用,这是因为,通过代理,让Spring容器有机会在代理中根据配置织入横切逻辑,而这也是AOP的精髓所在。代理类的创建,可以通过配置的方式实现,但是不够优雅,所以我们介绍了自动生成代理器。包括通过配置Bean名称来匹配进行自动创建代理类和通过Advisor自动匹配进行创建两种方式。第三种更为流行的注解方式,下篇文章将详细讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值