Spring学习笔记 —— AOP(面向切面编程) 之使用ProxyFactoryBean实现AOP
2016-10-19 23:21
1171 查看
引言
AOPAspect Oriented Programming简介
Spring AOP实例
Spring AOP 实现分析
小结
参考文章
而Java的反射,则对应到了Spring中的AOP(面向切面编程),当然,AOP有着比反射更强大的功能以及更方便的配置。关于反射,我在前面已经有系列文章分析过了,有兴趣的可以从这里开始再阅读一下。
Spring中的AOP分为两种,一种是由Spring框架实现,基于
本文主要分成三部分,AOP概念的介绍,Spring AOP实例以及Spring AOP具体实现的分析。
我们每家每户都会有电脑,电脑会耗电,而每家每户都会有一个电表来记录用电量,每个月会有人来查电表,这样电力公司就知道该收取多少电费了。
但是,如果没有电表,也没有人来查看用电量,而是由户主来联系电力公司并报告自己的用电量。虽然可能会有一些户主会很详细地记录所有的用电量,但是肯定大多数人并不会这么做。因为每个人每天都有很多事要处理,而监控电力使用情况会浪费他们大量时间,不交电费对他们只有好处而没有坏处。
软件系统的某些功能就像我们家里的电表,这些功能需要应用到系统的多个地方,但却不适合在应用到的地方被显式调用。
监控电量是一个很重要的功能,但却并不是大多数家庭重点关注的问题。监控电量更像是一个被动事件。
在软件中,有些行为对于大多数应用都是通用的。日志、安全和事务管理的确很重要。但它们是否应用对象主动参与的行为呢?如果让应用对象只关注于自己所针对的业务领域问题,而其他方面的问题由其他应用对象来处理,会不会更好呢?
在软件开发中,分布于应用中多处的功能被称为横切关注点(cross-cutting concerns)。通常,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往直接嵌入到应用的业务逻辑之中)。将这些横切关注点与业务逻辑相分离正式面向切面编程(AOP)所要解决的。
而在AOP中,会涉及到一些新的概念,现在先对这些概念进行描述。
在Spring AOP中暂时不会用到切面的概念,但是在AspectJ AOP中则会用到。下面我们就实现一个Spring AOP吧。
再声明一个连接点。
还有
最后是Advice
配置文件
Maven 依赖
主函数
得到以上结果,就证明已经成功的完成了。我们在不修改业务逻辑代码的情况下,成功地在方法执行前加上了自己想要执行的代码。
但是,具体的生成过程又是怎样的呢? 我们下面先通过一个时序图来进行分析。
因为最后生成的对象仍旧是一个Bean对象,所以前面仍然是调用
因为必须要实现
首先,会根据我们在property中声明的
其次,就是根据
而对应的类图,则如下图所示。
这里看大图
而对于使用JDK原生代理的,也不难想象代理的原理是通过配置的属性,生成需要代理的接口以及设置对应的代理方法。
在下一篇文章,我们会对AspectJ中的代理进行分析。
《深入分析Java Web技术内幕》
AOPAspect Oriented Programming简介
Spring AOP实例
Spring AOP 实现分析
小结
参考文章
引言
到上一篇文章Spring学习笔记 —— Spring Context为止,我们已经基本了解Spring中的基本对象——Bean——的创建、相关属性的注入以及获取。其实在这不难发现,Spring的容器设计与Java的对象设计之间是有相似的地方的,比如BeanDefinition和Class对象,Bean和Object对象。而Java的反射,则对应到了Spring中的AOP(面向切面编程),当然,AOP有着比反射更强大的功能以及更方便的配置。关于反射,我在前面已经有系列文章分析过了,有兴趣的可以从这里开始再阅读一下。
Spring中的AOP分为两种,一种是由Spring框架实现,基于
ProxyFactoryBean的AOP,而另外一种则是基于
AspectJ的AOP,我们首先介绍前者,在后续的文章也会介绍后者。
本文主要分成三部分,AOP概念的介绍,Spring AOP实例以及Spring AOP具体实现的分析。
AOP(Aspect Oriented Programming)简介
关于什么是AOP,我想通过一个《Spring in Action》中的一个例子来解释。我们每家每户都会有电脑,电脑会耗电,而每家每户都会有一个电表来记录用电量,每个月会有人来查电表,这样电力公司就知道该收取多少电费了。
但是,如果没有电表,也没有人来查看用电量,而是由户主来联系电力公司并报告自己的用电量。虽然可能会有一些户主会很详细地记录所有的用电量,但是肯定大多数人并不会这么做。因为每个人每天都有很多事要处理,而监控电力使用情况会浪费他们大量时间,不交电费对他们只有好处而没有坏处。
软件系统的某些功能就像我们家里的电表,这些功能需要应用到系统的多个地方,但却不适合在应用到的地方被显式调用。
监控电量是一个很重要的功能,但却并不是大多数家庭重点关注的问题。监控电量更像是一个被动事件。
在软件中,有些行为对于大多数应用都是通用的。日志、安全和事务管理的确很重要。但它们是否应用对象主动参与的行为呢?如果让应用对象只关注于自己所针对的业务领域问题,而其他方面的问题由其他应用对象来处理,会不会更好呢?
在软件开发中,分布于应用中多处的功能被称为横切关注点(cross-cutting concerns)。通常,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往直接嵌入到应用的业务逻辑之中)。将这些横切关注点与业务逻辑相分离正式面向切面编程(AOP)所要解决的。
而在AOP中,会涉及到一些新的概念,现在先对这些概念进行描述。
名称 | 含义 |
---|---|
切面(Aspect) | 作为提供业务代码之外的功能对象,如例子中提到的电表,记录了每家每户的用电量。程序中有可能会是日志,输出特定的日志结果。 |
目标对象(target object) | 执行业务逻辑的对象,如例子中每个独立的户主。 |
织入(Weaving) | 就是将切面中的对象与目标对象糅合在一起的过程。 |
连结点(JoinPoint) | 需要插入切面的地方,通常指业务代码中具体某个对象的某个方法。而在例子中,就是需要装电表的家庭(有些地方可能不需要安装电表)。 |
通知(Advice) | 通知是指在连接点上发生的事情,比如说电表会记录用电量。 |
切点(pointCut) | 是指一系列连结点的组合 |
Spring AOP实例
首先是TargetObject,TargetObjectSample.java
public class TargetObjectSample { public void getMessage() { System.out.println("getMsg!"); } public void getPointCutMessage() { System.out.println("get point cut Msg!"); } }
再声明一个连接点。
PointCutSample.java
public class PointCutSample implements Pointcut{ @Override public ClassFilter getClassFilter() { return ClassFilter.TRUE; } @Override public MethodMatcher getMethodMatcher() { return new MethodMatcher(){ @Override public boolean matches(Method method, Class<?> targetClass) { if(method.getName().contains("PointCut")){ return true; } return false; } @Override public boolean isRuntime() { return true; } @Override public boolean matches(Method method, Class<?> targetClass, Object... args) { if(method.getName().contains("PointCut")){ return true; } return false; }}; } }
还有
AdvisorSample.java
public class AdvisorSample implements PointcutAdvisor{ @Override public Advice getAdvice() { return new BeforeAdviceSample(); } @Override public boolean isPerInstance() { return false; } @Override public Pointcut getPointcut() { return new PointCutSample(); } }
最后是Advice
BeforeAdviceSample.java
public class BeforeAdviceSample implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("before method execution!"); } }
配置文件
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="advisorSample" class="com.study.Spring.AdvisorSample"></bean> <bean id="targetSample" class="com.study.Spring.TargetObjectSample"></bean> <bean id="aopSample" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="targetName"> <value>targetSample</value> </property> <property name="interceptorNames"> <list> <value>advisorSample</value> </list> </property> </bean> </beans>
Maven 依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.6.RELEASE</version> </dependency>
主函数
App.java
public class App { public static void main( String[] args ) { ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml"); TargetObjectSample obj = (TargetObjectSample)app.getBean("aopSample"); obj.getMessage(); //getMsg! obj.getPointCutMessage(); //before method execution! //get point cut Msg! } }
得到以上结果,就证明已经成功的完成了。我们在不修改业务逻辑代码的情况下,成功地在方法执行前加上了自己想要执行的代码。
Spring AOP 实现分析
在引言中提到过,AOP其实是Java代理的一种增强,所以我们也能够想到,最后会生成一个代理的对象。但是,具体的生成过程又是怎样的呢? 我们下面先通过一个时序图来进行分析。
因为最后生成的对象仍旧是一个Bean对象,所以前面仍然是调用
getBean方法获取的。真正产生差异的地方在于,我们定义的bean是
ProxyFactoryBean,它实现了
FactoryBean这个接口。
public interface FactoryBean<T> { //返回Bean实例 T getObject() throws Exception; //返回Bean的具体类型 Class<?> getObjectType(); //这个bean是否为单例 boolean isSingleton(); }
因为必须要实现
getBoject方法,所以我们就能够自定义生成代理对象的时候需要经历哪些步骤了。
首先,会根据我们在property中声明的
interceptorNames来初始化
advisors(如果代理对象不是单例,初始化的advisors也不是真正的bean对象,
advisors会在
newPrototypeInstance时再进行初始化)。
其次,就是根据
advisors生成cglib的代理对象。在Spring里面会根据类的定义/bean的定义,决定要生成jdk原生的代理还是cglib的代理。因为我们示例中的类没有实现任何接口,使用的是cglib的代理实现。
CglibAopProxy.getProxy
public Object getProxy(ClassLoader classLoader) { //省略debug代码 try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } validateClassIfNecessary(proxySuperClass, classLoader); // 对cglib enhancer进行配置 Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); //根据advisors来生成callback对象 Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // 根据advisors生成callbackFilter,确定哪个方法将会被代理。 enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // 创建代理对象 return createProxyClassAndInstance(enhancer, callbacks); } //异常处理 }
而对应的类图,则如下图所示。
这里看大图
小结
使用ProxyFactoryBean实现的AOP结构较为简单,原理就是通过我们配置的属性(interceptors),生成对应的callback以及callbackfilter,最后通过cglib生成动态代理类,实现代理的功能。而对于使用JDK原生代理的,也不难想象代理的原理是通过配置的属性,生成需要代理的接口以及设置对应的代理方法。
在下一篇文章,我们会对AspectJ中的代理进行分析。
参考文章
《Spring in Action 第三版》《深入分析Java Web技术内幕》
相关文章推荐
- Spring源码阅读-使用ProxyFactoryBean实现AOP
- spring 直接使用ProxyFactoryBean 实现AOP 流程小结
- spring学习笔记7--使用spring进行面向切面的(AOP)编程(1)注解方式实现
- ITCAST视频-Spring学习笔记(使用JDK中的Proxy技术实现AOP功能)
- spring aop(五)--ProxyFactoryBean创建代理的实现
- Spring学习之旅(二) AOP(面向切面编程)的使用
- Spring学习笔记:使用代理实现AOP
- 使用java的Proxy实现AOP(面向切面编程)
- ITCAST视频-Spring学习笔记(使用CGLIB实现AOP功能与AOP概念解释)
- Spring4第四讲学习笔记,AOP面向切面编程
- Spring学习笔记(14)----使用Spring的注解方式实现AOP
- Spring AOP实现机制(二)--ProxyFactoryBean---将Spring AOP和Spring IoC容器相结合
- Spring学习笔记 使用XML配置实现Bean的auto-wiring (自动绑定)
- Spring ProxyFactoryBean 是如何实现所有的AOP proxy都有可能转为Advised接口的?
- ITCAST视频-Spring学习笔记(使用Spring配置文件实现AOP)
- ITCAST视频-Spring学习笔记(使用Spring的注解方式实现AOP入门)
- Spring学习笔记(AOP面向切面编程)
- 使用spring的ProxyFactoryBean来实现权限控制
- 【spring源码学习】spring的AOP面向切面编程的实现解析
- Spring源码分析之ProxyFactoryBean方式实现Aop功能的分析