Spring提供了结合了java自带的代理方式和Cglib的代理方式,提供了多种构造代理的入口。
1、ProxyFactoryBean
ProxyFactoryBean,可理解为产生代理的FactoryBean。具体使用方法:
声明一个Animal接口:
package org.antstudio;
public interface Animal {
public String say();
public String name();
}
声明一个Cat实现Animal接口:
package org.antstudio;
import org.springframework.stereotype.Component;
@Component
public class Cat implements Animal{
@Override
public String say() {
System.out.println("miao.");
return "Miao";
}
@Override
public String name() {
return "Cat";
}
}
在声明一个Dog,具有Animal的方法,但是不实现于Animal,用于测试没有接口的代理实现:
package org.antstudio;
import org.springframework.stereotype.Component;
/**
* 这里不实现Animal接口,以测试无接口时的代理
*/
@Component
public class Dog {
public String say(){
System.out.println("Wang.");
return "Wang";
}
public String name(){
return "Dog";
}
}
创建一个通知,用于代理拦截目标对象方法时执行:
package org.antstudio.org.antstudio.proxy;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class AnimalBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("Before execute "+ method.getName());
}
}
配置代理生成:
<bean id="personProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces">
<list>
<value>org.antstudio.Animal</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>animalBeforeAdvice</value>
</list>
</property>
<property name="target">
<ref bean="cat"></ref>
</property>
</bean>
对Dog类的代理配置:
<bean id="dogProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>animalBeforeAdvice</value>
</list>
</property>
<property name="target">
<ref bean="dog"></ref>
</property>
</bean>
创建测试类:
package org.antstudio;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-config.xml")
public class ProxyFactoryBeanTests {
@Autowired
private BeanFactory beanFactory;
@Test
public void testCatProxyFactory() {
Animal p = (Animal)beanFactory.getBean("catProxy") ;
System.out.println(p.getClass());//class com.sun.proxy.$Proxy11
Assert.assertEquals(p.say(), "Miao");
}
@Test
public void testDogProxyFactory() {
Dog d = (Dog)beanFactory.getBean("dogProxy") ;
System.out.println(d.getClass());//class org.antstudio.Dog$$EnhancerByCGLIB$$d23a9052
Assert.assertEquals(d.say(), "Wang");
}
}
2.BeanNameAutoProxyCreator
BeanNameAutoProxyCreator可以通过bean的名字自动进行代理,名字可以采用表达式匹配,可用于批量的代理生成。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>cat*</value>
<value>dog*</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>animalBeforeAdvice</value>
</list>
</property>
</bean>
这里我们采用cat*和dog*匹配cat开头和dog开头的beanname,在本示例中则匹配Cat和Dog两个类的bean示例。
创建测试类:
package org.antstudio;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
/**
* @author Gavin
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-config.xml")
public class BeanNameAutoProxyCreatoryTests {
@Resource
private BeanFactory beanFactory;
@Resource
private Dog dog;
@Test
public void testCat(){
Animal cat = (Animal) beanFactory.getBean("cat");
cat.say();
cat.name();
}
@Test
public void testDog(){
dog.say();
}
}
注意:上例中,如果对于Cat采用Dog的注入方式,即:
@Resource
private Cat cat;
会抛出类型转换错误,因为对于Cat(实现了Animal接口),Spring采用java自带的动态代理生成,因此生成的代理类型为接口Animal类型,而非具体实现类Cat。而Dog未实现接口,采用Cglib。Cglib使用继承Dog的形式创建Dog的代理。由此可知,如果我们将Dog声明为final(不可继承),那么将无法完成代理的生成。
3. DefaultAdvisorAutoProxyCreator
BeanNameAutoProxyCreator可以批量的通过bean的名字进行代理生成,其控制粒度为bean实例。然而有的时候只需要需要对bean中的部分方法进行代理,将控制粒度细化到方法级别。DefaultAdvisorAutoProxyCreator提供了基于通知的代理方式,在通知中可以控制相应的切入点,可精准到方法级别。
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns">
<list>
<value>.*say</value>
</list>
</property>
<property name="advice">
<ref bean="animalAfterAdvice"></ref>
</property>
</bean>
这里我们只对say方法进行代理,而不对其他方法,如name方法进行代理。此时调用相应的代理类时,则只会对切入点say方法进行代理。测试类,可参考 BeanNameAutoProxyCreator的测试类。
Spring提供了丰富的通知方式,前置后置环绕等等,也有很多的切入点方式。不是本文讨论的重点,就不一一列出。
源码:https://github.com/gavincook/springProxy