深入理解Spring AOP: Proxy的生成机制

需积分: 10 2 下载量 146 浏览量 更新于2024-09-18 收藏 80KB DOC 举报
“Spring源代码解析(五):Spring_AOP获取Proxy.doc” Spring AOP(面向切面编程)是Spring框架的重要组成部分,它允许开发者在不修改原有代码的情况下,通过添加额外的行为(通知,Advice)来增强功能。本文将探讨Spring AOP如何获取代理对象(Proxy),以及涉及到的相关概念。 首先,我们要理解什么是通知(Advice)。在Spring AOP中,通知定义了在特定连接点(Join Point)执行的动作。有五种不同的通知类型: 1. Before Advice:在目标方法执行前执行的代码。 2. After Returning Advice:在目标方法正常返回后执行的代码。 3. After Throwing Advice:在目标方法抛出异常后执行的代码。 4. Method Before Advice:与Before Advice类似,但在方法执行前应用。 5. 这些通知都是Spring AOP定义的接口,需要用户自定义实现来提供具体的行为。 接着,我们讨论切点(Pointcut)。切点是定义通知应用于哪些连接点的规则。它可以帮助我们定位到需要插入额外逻辑的方法集合。Spring提供了多种切点实现,例如基于正则表达式的`JdkRegexpMethodPointcut`。下面是一个简化版的`JdkRegexpMethodPointcut`中的`matches`方法,用于检查方法名是否符合预设的正则表达式模式: ```java public final boolean matches(Method method, Class<?> targetClass) { String pattern = method.getDeclaringClass().getName() + "." + method.getName(); for (int i = 0; i < this.patterns.length; i++) { boolean matched = matches(pattern, i); if (matched) { for (int j = 0; j < this.excludedPatterns.length; j++) { boolean excluded = matchesExclusion(pattern, j); if (excluded) { return false; } } return true; } } return false; } ``` 这段代码遍历预设的正则表达式模式,检查方法全名(包括类名和方法名)是否匹配,并排除任何不符合排除模式的方法。 了解了这些基础概念后,我们转向代理(Proxy)的获取。在Spring AOP中,代理对象是实际业务对象的包装,它会在方法调用前后插入通知。Spring支持两种类型的代理:JDK动态代理和CGLIB代理。 1. JDK动态代理:当目标对象实现了至少一个接口时,Spring会创建一个与该接口相同的动态代理类。代理类通过反射调用目标方法,并在调用前后插入通知。 2. CGLIB代理:如果目标对象没有实现接口,Spring会使用CGLIB库生成一个目标对象的子类,并在子类的方法上插入通知。CGLIB代理效率较高,但需要目标类支持子类化。 Spring通过`ProxyFactoryBean`或`AopProxy`接口来创建代理对象。`ProxyFactoryBean`是一个Spring Bean工厂,可以创建具有AOP功能的代理对象。而`AopProxy`接口是所有Spring AOP代理的基类,它包含一个`getProxy()`方法,用于创建代理实例。 Spring AOP通过定义通知和切点,能够灵活地在运行时增强对象的行为,而无需直接修改原始代码。通过代理对象,我们可以透明地插入这些增强,使得代码更加模块化和易于维护。理解Spring AOP的原理和实现,对于深入掌握Spring框架和实现高效的面向切面编程至关重要。
2012-10-07 上传
package cn.javass.spring.chapter4; import java.io.File; import java.io.IOException; import junit.framework.Assert; import org.jboss.vfs.VFS; import org.jboss.vfs.VirtualFile; import org.jboss.vfs.spi.RealFileSystem; import org.junit.Test; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; @SuppressWarnings("all") public class ResourcePatternTest { @Test public void testClasspathPrefix() throws IOException { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); //只加载一个绝对匹配Resource,且通过ResourceLoader.getResource进行加载 Resource[] resources = resolver.getResources("classpath:META-INF/INDEX.LIST"); Assert.assertEquals(1, resources.length); //只加载一个匹配的Resource,且通过ResourceLoader.getResource进行加载 resources = resolver.getResources("classpath:META-INF/*.LIST"); Assert.assertTrue(resources.length == 1); //只加载一个绝对匹配Resource,且通过ResourceLoader.getResource进行加载 resources = resolver.getResources("classpath:META-INF/MANIFEST.MF"); Assert.assertEquals(1, resources.length); } @Test public void testClasspathAsteriskPrefix() throws IOException { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); //将加载多个绝对匹配的所有Resource //将首先通过ClassLoader.getResources("META-INF")加载非模式路径部分 //然后进行遍历模式匹配 Resource[] resources = resolver.getResources("classpath*:META-INF/INDEX.LIST"); Assert.assertTrue(resources.length > 1); //将加载多个模式匹配的Resource resources = resolver.getResources("classpath*:META-INF/*.LIST"); Assert.assertTrue(resources.length > 1); } @Test public void testClasspathAsteriskPrefixLimit() throws IOException { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); //将首先通过ClassLoader.getResources("")加载目录, //将只返回文件系统的类路径不返回jar的跟路径 //然后进行遍历模式匹配 Resource[] resources = resolver.getResources("classpath*:asm-*.txt"); Assert.assertTrue(resources.length == 0); //将通过ClassLoader.getResources("asm-license.txt")加载 //asm-license.txt存在于com.springsource.net.sf.cglib-2.2.0.jar resources = resolver.getResources("classpath*:asm-license.txt"); Assert.assertTrue(resources.length > 0); //将只加载文件系统类路径匹配的Resource resources = resolver.getResources("classpath*:LICENS*"); Assert.assertTrue(resources.length == 1); } @Test public void testFilekPrefix() throws IOException { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources("file:D:/*.txt"); Assert.assertTrue(resources.length > 0); } @Test public void testVfsPrefix() throws IOException { //1.创建一个虚拟的文件目录 VirtualFile home = VFS.getChild("/home"); //2.将虚拟目录映射到物理的目录 VFS.mount(home, new RealFileSystem(new File("d:"))); //3.通过虚拟目录获取文件资源 VirtualFile testFile = home.getChild("test.txt"); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources("/home/test.txt"); Assert.assertTrue(resources.length > 0); System.out.println(resources[0].getClass()); } }