创建日期:2016.08.19
修改日期:2016.08.20-2016.08.21
交流QQ:992591601
参考资料:《spring源码深度解析》、《spring技术内幕》、传值播客spring教学视频
http://www.cnblogs.com/xing901022/p/4264334.html
http://www.cnblogs.com/digdeep/p/4528353.html
一,动态代理、java InvocationHandler实现、Cglib实现
要了解动态代理,可阅读设计模式相关书籍,不难理解。这篇博文我简单解释,动态代理就是与静态代理相对应的,在程序运行过程中动态创建的代理类。代理类可以简单理解为一种对目标类的增强,之后我们要使用目标类的话,只需用代理类就可以了。
实现动态代理有两种方式,其一是java自带的动态代理功能,另外就是使用Cglib库实现。前者的使用有一个必须的条件,那就是目标类必须实现接口。而后者的使用则是对前者的一种补充。
假设我们有这样两个bean,其一为AlienFishServiceBean,不实现任何接口。
其一为FishServiceImpl,实现FishService接口。
对于前者,我们需要使用Cglib来实现动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):
- package cn;
- import java.lang.reflect.Method;
- import cn.aop.service.AlienFishServiceBean;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- /**
- * @ClassName: CGlibProxyFactory
- * @Description: CGlibProxyFactory
- * @author 无名
- * @date 2016-8-14 17:31:48
- * @version 1.0
- */
- public class CGlibProxyFactory implements MethodInterceptor {
- private Object targetObject;
- public Object createProxyIntance(Object targetObject) {
- this.targetObject = targetObject;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.targetObject.getClass());
- enhancer.setCallback(this);
- return enhancer.create();
- }
- public Object intercept(Object proxy, Method method, Object[] args,
- MethodProxy methodProxy) throws Throwable {
- AlienFishServiceBean bean = (AlienFishServiceBean) this.targetObject;
- Object result = null;
- if(bean.getFishName()!=null) {
- result = methodProxy.invoke(targetObject, args);
- }
- return result;
- }
- }
对于后者,我们可以使用java自带的动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):
- package cn;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import cn.aop.service.FishService;
- import cn.aop.service.FishServiceImpl;
- /**
- * @ClassName: JDKProxyFactory
- * @Description: JDKProxyFactory
- * @author 无名
- * @date 2016-8-13 下午11:55:31
- * @version 1.0
- */
- public class JDKProxyFactory implements InvocationHandler {
- private Object targetObject;
- public Object createInstance(Object targetObject) {
- this.targetObject = targetObject;
- return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
- this.targetObject.getClass().getInterfaces(), this);
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- FishService fs = (FishServiceImpl)targetObject;
- Object result = null;
- if(fs.getFishName() != null) {
- result = method.invoke(targetObject, args);
- }
- return result;
- }
- }
上述两个代理方式使用时,先用代理类工厂创建对应代理类,然后使用代理类即可(此代理类是对目标类的代理,‘增强了’目标类的一些方面)。
二,Spring Aop
spring aop需要的jar包:
org.springframework.aop-xxx.jar(spring),aopalliance-1.0.jar,aspectjrt.jar, aspectjweaver.jar,cglib.jar
spring aop是在动态代理这种设计模式的基础之上的。
这里我说下aop的几个基本概念,都是基于我自己的理解,简单粗暴:
aspect:面,可以理解为一个事务,对该事务做对应处理。
pointcut:切入点,对应于面具体的切入的地方。
advice:spring定义了四个advice, BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。在触及面和点的时候,根据配置,执行对应通知。
spring aop的实现由两种,其一是配置文件方式,另外是注解方式。
我们首先,用配置文件方式:
目标类,接口和实现类:
- package cn.service;
- public interface FishService {
- public void say00();
- public void say01();
- }
- package cn.service.impl;
- import cn.service.FishService;
- public class FishServiceBean implements FishService {
- public void say00() {
- System.out.println("I am fish 00");
- }
- public void say01() {
- System.out.println("I am fish 01");
- }
- }
下面的类提供对应的advice
- package cn.service;
- import org.aspectj.lang.ProceedingJoinPoint;
- /**
- * 切面
- *
- */
- public class MyInterceptor {
- public void doBefore() {
- System.out.println("before");
- }
- public void doAfter() {
- System.out.println("after");
- }
- public void doFinal() {
- System.out.println("final");
- }
- public void doThrowing() {
- System.out.println("throwing");
- }
- public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
- System.out.println("进入方法");
- Object result = pjp.proceed();
- System.out.println("退出方法");
- return result;
- }
- }
配置文件(aop:config内设置aop,切面里设置切入点,及几种advice):
- <?xml version="1.0" encoding="UTF-8"?>
- <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"
- xmlns:aop="http://www.springframework.org/schema/aop"
- 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/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- <aop:aspectj-autoproxy/>
- <bean id="fishService" class="cn.service.impl.FishServiceBean"></bean>
- <bean id="aspetbean" class="cn.service.MyInterceptor"/>
- <aop:config>
- <aop:aspect id="asp" ref="aspetbean">
- <aop:pointcut id="mycut" expression="execution(* cn.service..*.*(..))"/>
- <aop:before pointcut-ref="mycut" method="doBefore"/>
- <aop:after-returning pointcut-ref="mycut" method="doFinal"/>
- <aop:after-throwing pointcut-ref="mycut" method="doThrowing"/>
- <aop:after pointcut-ref="mycut" method="doAfter"/>
- <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
- </aop:aspect>
- </aop:config>
- </beans>
测试类:
- package junit.test;
- import org.junit.BeforeClass;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import cn.service.FishService;
- public class SpringAOPTest {
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- }
- @Test public void interceptorTest(){
- ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml");
- FishService fishService = (FishService)cxt.getBean("fishService");
- fishService.say00();
- fishService.say01();
- }
- }
运行结果:
与配置文件方式相对应的便是注解的方式
注解方式只需在spring的xml文件里保留 <aop:aspectj-autoproxy/> 就可以了。
而上面对应的MyInterceptor类需要多写相对的注解(著名切入点、advice等)。
三,Spring源码分析
首先自己设想一下spring实现aop的思路,大概是:1,获取、寻找所有bean,如果为AspectJ注解的类,则进行对应处理。
2,对标记为AspectJ注解的类进行增强器的提取(前文提到动态代理是对目标类的一种增强)
3,创建动态代理。
首先看一个类AopNamespaceHandler,这里是aop注解对应的解析器:
- public class AopNamespaceHandler extends NamespaceHandlerSupport {
- public void init() {
- // In 2.0 XSD as well as in 2.1 XSD.
- registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
- registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
- registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
- // Only in 2.0 XSD: moved to context namespace as of 2.1
- registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
- }
- }
我们看registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());这一句,可知其内涵是向NamespaceHandlerSupport这一类的
private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>(); 这一map数据结构中注册对应解析器。
此后 一旦遇到aspectj-autoproxy注解,便自然会从map中取到对应解析器,并使用解析器AspectJAutoProxyBeanDefinitionParser解析。AspectJAutoProxyBeanDefinitionParser是继承了BeanDefinitionParser接口的。
- public BeanDefinition parse(Element element, ParserContext parserContext) {
- AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
- extendBeanDefinition(element, parserContext);
- return null;
- }
- public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
- ParserContext parserContext, Element sourceElement) {
- BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
- parserContext.getRegistry(), parserContext.extractSource(sourceElement));
- useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
- registerComponentIfNecessary(beanDefinition, parserContext);
- }
首先看下进入该函数时参数的状态:
sourceElement的name为aop:aspectj-autoproxy。这个函数就是专门用于注册aop:aspectj注解的。
这个方法三句代码,各有其作用,分别是:1注册AnnotationAwareAspectJAutoProxyCreator(AOP的实现都靠这个),返回的BeanDefinition设置对应的AnnotationAwareAspectJAutoProxyCreator
2处理proxy-target-class(CgLib或jdk)以及expose-proxy属性
3注册组件并通知
从spring的 DefaultAopProxyFactory类入手,该类继承自AopProxyFactory接口。
- package org.springframework.aop.framework;
- /**
- * Interface to be implemented by factories that are able to create
- * AOP proxies based on {@link AdvisedSupport} configuration objects.
- *
- * <p>Proxies should observe the following contract:
- * <ul>
- * <li>They should implement all interfaces that the configuration
- * indicates should be proxied.
- * <li>They should implement the {@link Advised} interface.
- * <li>They should implement the equals method to compare proxied
- * interfaces, advice, and target.
- * <li>They should be serializable if all advisors and target
- * are serializable.
- * <li>They should be thread-safe if advisors and target
- * are thread-safe.
- * </ul>
- *
- * <p>Proxies may or may not allow advice changes to be made.
- * If they do not permit advice changes (for example, because
- * the configuration was frozen) a proxy should throw an
- * {@link AopConfigException} on an attempted advice change.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- */
- public interface AopProxyFactory {
- /**
- * Create an {@link AopProxy} for the given AOP configuration.
- * @param config the AOP configuration in the form of an
- * AdvisedSupport object
- * @return the corresponding AOP proxy
- * @throws AopConfigException if the configuration is invalid
- */
- AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
- }
我们看最核心的createAopProxy方法:
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class targetClass = config.getTargetClass();
- if (targetClass == null) {
- throw new AopConfigException("TargetSource cannot determine target class: " +
- "Either an interface or a target is required for proxy creation.");
- }
- if (targetClass.isInterface()) {
- return new JdkDynamicAopProxy(config);
- }
- if (!cglibAvailable) {
- throw new AopConfigException(
- "Cannot proxy target class because CGLIB2 is not available. " +
- "Add CGLIB to the class path or specify proxy interfaces.");
- }
- return CglibProxyFactory.createCglibProxy(config);
- }
- else {
- return new JdkDynamicAopProxy(config);
- }
- }
这段代码逻辑很清晰,判断如果目标类实现接口则使用jdk创建代理,否则使用cglib。
再仔细看这段代码,发现无论是jdk还是cglib,都要使用AdvisedSupport config这个参数。
设置断点,查看该变量,发现果然信息量很大,对应的AdvisedSupport类代码我就不贴了,看下面截图也可以想象个大概了。
具体看下advice的内容,我们配置文件里配置的内容都在里面了:
至于这个config是怎样生成的,其实很好想象,就是前面讲的,对应注解的解析,注册。
new JdkDynamicAopProxy(config)
- /**
- * Construct a new JdkDynamicAopProxy for the given AOP configuration.
- * @param config the AOP configuration as AdvisedSupport object
- * @throws AopConfigException if the config is invalid. We try to throw an informative
- * exception in this case, rather than let a mysterious failure happen later.
- */
- public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
- Assert.notNull(config, "AdvisedSupport must not be null");
- if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("No advisors and no TargetSource specified");
- }
- this.advised = config;
- }
CglibProxyFactory.createCglibProxy(config)
- /**
- * Create a new Cglib2AopProxy for the given AOP configuration.
- * @param config the AOP configuration as AdvisedSupport object
- * @throws AopConfigException if the config is invalid. We try to throw an informative
- * exception in this case, rather than let a mysterious failure happen later.
- */
- public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
- Assert.notNull(config, "AdvisedSupport must not be null");
- if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("No advisors and no TargetSource specified");
- }
- this.advised = config;
- this.advisedDispatcher = new AdvisedDispatcher(this.advised);
- }
都是用config设置对应的advise属性。
小小总结:最后我又单步调试一通,结合上一章节内容《spring源码分析(一)IoC、DI》,发现AOP代理类的创建时CreateBean这一阶段开始的,其实这时候就是对应目标类用代理类取代了。
这是断点运行时,观察createBean返回的结果,可见创建的fishService bean实质上是JdkDynamicAopProxy代理类。在那之后我们使用fishService实际上就是使用对应的JdkDynamicAopProxy代理类了,之前注册的那些advice也就生效了:
(在这一过程中值得一提的还有AbstractAutoProxyCreator的postProcessAfterInitialization方法,正如注释所说,作用是Create a proxy with the configured interceptors if the bean is identified as one to proxy by the subclass.
AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法,作用是获取增强器)