作者:zuoxiaolong8810(左潇龙),转载请注明出处。
最近工作很忙,但当初打算学习spring源码的事已经基本告一段落,只是一直没时间写这些记录性的文字。
本次鄙人学习spring的源码,有点囫囵吞枣的感觉,其实这样并非就不好,spring作为一个应用平台,自然不是那么好研究透彻的,而且也不太可能有人把spring的源码全部清楚的过上一遍,哪怕是spring的缔造者。不过最主要的原因是我们确实没有必要把源码全部过一遍。
在看spring源码的过程中,学习到了很多东西,这就足够了,而且现在如果在使用spring的过程中出现任何问题,不会再像以前一样有时候会束手无策,甚至把全部希望寄托于度娘,这其实是不正确的思想。从源码出发,可以发现,其实以前觉得无头绪的问题,现在都会迎刃而解。这就是研究源码最大的收获吧。
言归正传,来说说AOP。AOP的中文翻译是面向切面编程,与面向对象,面向接口,面向服务等概念看起来挺相似的。其实最近一直看有关JAVA的书籍,多多少少也对于这些有些理解,所谓面向切面,即是使用切面与其它事物产生关系。面向对象强调一切皆对象,也可以说面向接口是一切皆接口,面向服务则是一切皆服务,而切面也是一样,一切皆切面。
为什么这么说,面向对象就不说了,就拿面向接口来说吧,强调的就是我们操纵的都是一些接口,所以我们永远不会依赖于实现,面向切面的思想,其实也很类似,即我们操纵的都是一些切面,不会依赖于流程。也正是因为如此,所以我们可以优雅的解决WEB应用中一些原本很棘手或者不好处理的问题。
以上都是一些理论性的东西,也都是鄙人自己最近的感悟,虽然不知对错,但我觉得这种感悟很重要。
下面我们具体说说AOP,目前由AOP联盟给出了AOP的标准,AOP联盟的规范只是提供了几个接口定义,为了统一AOP的标准,下面来看看主要的几个接口。
Advice接口:
这是一个空接口,里面没有任何方法,只是用来标识一个通知,在spring中,所有的advice都是此接口的扩展,比如BeforeAdvice,AfterAdvice等。
Interceptor接口:
Advice的子接口,这个接口和advice都不直接使用,一般是要扩展以后去实现特殊的拦截。
Joinpoint接口:
代表了一个运行时的连接点。
Invocation接口:
代表了程序中的一个调用,可以被拦截器Interceptor拦截。
下面还有几个接口,不再一一介绍,从下一层继承开始,interpretor的继承体系已经开始依赖于invocation。这从某种意义上来说,advice是依赖于joinpoint的,但这并非绝对。
下面给出这几个接口的UML图,关系比较清晰。
下面介绍下spring中的AOP核心接口。
Advice体系:
Spring采用AOP联盟的Advice作为超级接口,扩展了很多子接口,比如BeforeAdvice,AfterAdvice等等,稍后以AfterReturningAdvice为例,讨论下spring的通知体系。
Pointcut接口:
spring采用Pointcut作为切点的抽象,其中有一个方法返回一个MethodMatcher,作用很明显,就是说切点决定了要切入哪些方法。这里其实是定义了一个匹配规则。比如正则匹配,可以只匹配前缀为save的方法等等。
Advisor:
通知器或者说通知者,我们从现实角度来说,通知者当然需要知道要通知什么。所以Advisor依赖于Advice,而Advisor旗下的子接口PointAdvisor还依赖于Pointcut,也就是说这个接口更确切的定位应该是包含了要通知谁和要通知什么,也就是说要能获得Advice和Pointcut。
下面我们先用一个例子说明spring的AOP是如何工作的,在spring的bean中有一种特殊的bean,叫FactoryBean。这并不是一个普通的bean,而是用来产生bean的一个bean。这样说起来有点绕口,但这个接口就是用来做这个事的,它是为了实现一系列特殊加工过的bean而产生的接口。
比如AOP中,我们其实就是要对一个bean进行增强,进行加工,让它在运行的过程中可以做一些特殊的事情。
ProxyFactoryBean就是一个为了AOP实现的特殊的FactoryBean,它的作用很明显就是产生proxy的bean,也就是被我们增强过的bean,在这里给出它的源码。
- public class ProxyFactoryBean extends ProxyCreatorSupport
- implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
- /**
- * This suffix in a value in an interceptor list indicates to expand globals.
- */
- public static final String GLOBAL_SUFFIX = "*";
- protected final Log logger = LogFactory.getLog(getClass());
- private String[] interceptorNames;
- private String targetName;
- private boolean autodetectInterfaces = true;
- private boolean singleton = true;
- private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
- private boolean freezeProxy = false;
- private transient ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
- private transient boolean classLoaderConfigured = false;
- private transient BeanFactory beanFactory;
- /** Whether the advisor chain has already been initialized */
- private boolean advisorChainInitialized = false;
- /** If this is a singleton, the cached singleton proxy instance */
- private Object singletonInstance;
在这里,一样没有贴出全部的源码,我们主要关心它的属性,其中interpretorNames和targetName就是这个类最主要的属性,前者代表的是需要加强哪些东西以及需要怎样加强,也就是advice和pointcut。而后者代表的则是我们针对谁来做这些加强,即我们的目标对象。
这里面的增强比较灵活,我来说一下需要的属性。
1.首先interpretorNames是必须赋予的属性,这个属性指定了通知器或者是通知的名字。如果传入的是通知Advice,则会被自动包装为通知器。
2.targetName是我们要增强的目标对象,这个对象如果有实现的接口,则会采用JDK的动态代理实现,否则将需要第三方的jar包cglib。
下面给出一段代码,来测试一下这种增强方式。首先我们需要一个bean.xml的配置文件,去配置这些属性。这样就将IOC和AOP结合起来使用了。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="testAdvisor" class="com.springframework.aop.test.TestAdvisor"></bean>
- <bean id="testTarget" class="com.springframework.aop.test.TestTarget"></bean>
- <bean id="testAOP" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="targetName">
- <value>testTarget</value>
- </property>
- <property name="interceptorNames">
- <list>
- <value>testAdvisor</value>
- </list>
- </property>
- </bean>
- </beans>
- public class TestTarget{
- public void test() {
- System.out.println("target.test()");
- }
- public void test2(){
- System.out.println("target.test2()");
- }
- }
- public class TestAdvisor implements PointcutAdvisor{
- public Advice getAdvice() {
- return new TestAfterAdvice();
- }
- public boolean isPerInstance() {
- return false;
- }
- public Pointcut getPointcut() {
- return new TestPointcut();
- }
- }
- public class TestAfterAdvice implements AfterReturningAdvice{
- public void afterReturning(Object returnValue, Method method,
- Object[] args, Object target) throws Throwable {
- System.out.println("after " + target.getClass().getSimpleName() + "." + method.getName() + "()");
- }
- }
- public class TestPointcut implements Pointcut{
- public ClassFilter getClassFilter() {
- return ClassFilter.TRUE;
- }
- public MethodMatcher getMethodMatcher() {
- return new MethodMatcher() {
- public boolean matches(Method method, Class<?> targetClass, Object[] args) {
- if (method.getName().equals("test")) {
- return true;
- }
- return false;
- }
- public boolean matches(Method method, Class<?> targetClass) {
- if (method.getName().equals("test")) {
- return true;
- }
- return false;
- }
- public boolean isRuntime() {
- return true;
- }
- };
- }
- }
- public class TestAOP {
- public static void main(String[] args) {
- ApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:beans.xml");
- TestTarget target = (TestTarget) applicationContext.getBean("testAOP");
- target.test();
- System.out.println("------无敌分割线-----");
- target.test2();
- }
- }
这一章主要是和各位一起感受一下springAOP的神奇,下一章,将会是spring学习之路的最后一章,我会和各位一起看一下上述我们的增强过程是如何完成的。
作者:zuoxiaolong8810(左潇龙),转载请注明出处。
上一章和各位一起看了一下springAOP的工作流程,当我们给出AOP相关的配置以后,直接从IOC容器中拿出来的就是已经加强过的bean。这说明spring在这个过程中一定做了什么。
本章我们就一起来看一下spring是如何完成对bean的增强的,首先我们来看一下,FactoryBean接口中一个方法的定义。
- public interface FactoryBean<T> {
- /**
- * Return an instance (possibly shared or independent) of the object
- * managed by this factory.
- * <p>As with a {@link BeanFactory}, this allows support for both the
- * Singleton and Prototype design pattern.
- * <p>If this FactoryBean is not fully initialized yet at the time of
- * the call (for example because it is involved in a circular reference),
- * throw a corresponding {@link FactoryBeanNotInitializedException}.
- * <p>As of Spring 2.0, FactoryBeans are allowed to return <code>null</code>
- * objects. The factory will consider this as normal value to be used; it
- * will not throw a FactoryBeanNotInitializedException in this case anymore.
- * FactoryBean implementations are encouraged to throw
- * FactoryBeanNotInitializedException themselves now, as appropriate.
- * @return an instance of the bean (can be <code>null</code>)
- * @throws Exception in case of creation errors
- * @see FactoryBeanNotInitializedException
- */
- T getObject() throws Exception;
getObject这个方法,就是用来获取被这个factorybean加强后的对象的,上一章测试的过程中,最终就是调用了这个方法,来完成了对bean的加强。我们来跟踪一下上一次测试的代码,看看到底是在什么地方调用的。这里再次贴出来上次测试的代码,方便解释。
- public class TestAOP {
- public static void main(String[] args) {
- ApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:beans.xml");
- TestTarget target = (TestTarget) applicationContext.getBean("testAOP");
- target.test();
- System.out.println("------无敌分割线-----");
- target.test2();
- }
- }
下面我们去跟进第二行获取testAOP的代码,来看一下究竟。首先我们会找到AbstractApplicationContext中的getBean方法,但是这个类并不负责bean的实例化工作,而是交给了bean工厂,我们跟踪bean工厂的方法,能找到上述第二行其实是调用了如下这个方法。
- @SuppressWarnings("unchecked")
- protected <T> T doGetBean(
- final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
- throws BeansException {
- final String beanName = transformedBeanName(name);
- Object bean;
- // Eagerly check singleton cache for manually registered singletons.
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- if (logger.isDebugEnabled()) {
- if (isSingletonCurrentlyInCreation(beanName)) {
- logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
- "' that is not fully initialized yet - a consequence of a circular reference");
- }
- else {
- logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
- }
- }
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- }
- else {
- // Fail if we're already creating this bean instance:
- // We're assumably within a circular reference.
- if (isPrototypeCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
- // Check if bean definition exists in this factory.
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // Not found -> check parent.
- String nameToLookup = originalBeanName(name);
- if (args != null) {
- // Delegation to parent with explicit args.
- return (T) parentBeanFactory.getBean(nameToLookup, args);
- }
- else {
- // No args -> delegate to standard getBean method.
- return parentBeanFactory.getBean(nameToLookup, requiredType);
- }
- }
- if (!typeCheckOnly) {
- markBeanAsCreated(beanName);
- }
- final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- checkMergedBeanDefinition(mbd, beanName, args);
- // Guarantee initialization of beans that the current bean depends on.
- String[] dependsOn = mbd.getDependsOn();
- if (dependsOn != null) {
- for (String dependsOnBean : dependsOn) {
- getBean(dependsOnBean);
- registerDependentBean(dependsOnBean, beanName);
- }
- }
- // Create bean instance.
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, new ObjectFactory() {
- public Object getObject() throws BeansException {
- try {
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- else if (mbd.isPrototype()) {
- // It's a prototype -> create a new instance.
- Object prototypeInstance = null;
- try {
- beforePrototypeCreation(beanName);
- prototypeInstance = createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
- }
- else {
- String scopeName = mbd.getScope();
- final Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
- }
- try {
- Object scopedInstance = scope.get(beanName, new ObjectFactory() {
- public Object getObject() throws BeansException {
- beforePrototypeCreation(beanName);
- try {
- return createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- }
- });
- bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
- }
- catch (IllegalStateException ex) {
- throw new BeanCreationException(beanName,
- "Scope '" + scopeName + "' is not active for the current thread; " +
- "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
- ex);
- }
- }
- }
- // Check if required type matches the type of the actual bean instance.
- if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
- throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
- }
- return (T) bean;
- }
- Object sharedInstance = getSingleton(beanName);
- protected Object getObjectForBeanInstance(
- Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
- // Don't let calling code try to dereference the factory if the bean isn't a factory.
- if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
- throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
- }
- // Now we have the bean instance, which may be a normal bean or a FactoryBean.
- // If it's a FactoryBean, we use it to create a bean instance, unless the
- // caller actually wants a reference to the factory.
- if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
- return beanInstance;
- }
- Object object = null;
- if (mbd == null) {
- object = getCachedObjectForFactoryBean(beanName);
- }
- if (object == null) {
- // Return bean instance from factory.
- FactoryBean factory = (FactoryBean) beanInstance;
- // Caches object obtained from FactoryBean if it is a singleton.
- if (mbd == null && containsBeanDefinition(beanName)) {
- mbd = getMergedLocalBeanDefinition(beanName);
- }
- boolean synthetic = (mbd != null && mbd.isSynthetic());
- object = getObjectFromFactoryBean(factory, beanName, !synthetic);
- }
- return object;
- }
- protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
- if (factory.isSingleton() && containsSingleton(beanName)) {
- synchronized (getSingletonMutex()) {
- Object object = this.factoryBeanObjectCache.get(beanName);
- if (object == null) {
- object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
- this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
- }
- return (object != NULL_OBJECT ? object : null);
- }
- }
- else {
- return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
- }
- }
- private Object doGetObjectFromFactoryBean(
- final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
- throws BeanCreationException {
- Object object;
- try {
- if (System.getSecurityManager() != null) {
- AccessControlContext acc = getAccessControlContext();
- try {
- object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return factory.getObject();
- }
- }, acc);
- }
- catch (PrivilegedActionException pae) {
- throw pae.getException();
- }
- }
- else {
- object = factory.getObject();
- }
- }
- catch (FactoryBeanNotInitializedException ex) {
- throw new BeanCurrentlyInCreationException(beanName, ex.toString());
- }
- catch (Throwable ex) {
- throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
- }
- // Do not accept a null value for a FactoryBean that's not fully
- // initialized yet: Many FactoryBeans just return null then.
- if (object == null && isSingletonCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(
- beanName, "FactoryBean which is currently in creation returned null from getObject");
- }
- if (object != null && shouldPostProcess) {
- try {
- object = postProcessObjectFromFactoryBean(object, beanName);
- }
- catch (Throwable ex) {
- throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
- }
- }
- return object;
- }
- public Object getObject() throws BeansException {
- initializeAdvisorChain();
- if (isSingleton()) {
- return getSingletonInstance();
- }
- else {
- if (this.targetName == null) {
- logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
- "Enable prototype proxies by setting the 'targetName' property.");
- }
- return newPrototypeInstance();
- }
- }
- private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
- if (this.advisorChainInitialized) {
- return;
- }
- if (!ObjectUtils.isEmpty(this.interceptorNames)) {
- if (this.beanFactory == null) {
- throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
- "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
- }
- // Globals can't be last unless we specified a targetSource using the property...
- if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
- this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("Target required after globals");
- }
- // Materialize interceptor chain from bean names.
- for (String name : this.interceptorNames) {
- if (logger.isTraceEnabled()) {
- logger.trace("Configuring advisor or advice '" + name + "'");
- }
- if (name.endsWith(GLOBAL_SUFFIX)) {
- if (!(this.beanFactory instanceof ListableBeanFactory)) {
- throw new AopConfigException(
- "Can only use global advisors or interceptors with a ListableBeanFactory");
- }
- addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
- name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
- }
- else {
- // If we get here, we need to add a named interceptor.
- // We must check if it's a singleton or prototype.
- Object advice;
- if (this.singleton || this.beanFactory.isSingleton(name)) {
- // Add the real Advisor/Advice to the chain.
- advice = this.beanFactory.getBean(name);
- }
- else {
- // It's a prototype Advice or Advisor: replace with a prototype.
- // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
- advice = new PrototypePlaceholderAdvisor(name);
- }
- addAdvisorOnChainCreation(advice, name);
- }
- }
- }
- this.advisorChainInitialized = true;
- }
值得注意的是在这个过程中,触发了一个这样的方法this.advisorAdapterRegistry.wrap(next)。这个方法就是用来包装通知器的,如果不是advisor而是advice,就会包装一下返回。
好了,接着刚才的过程,初始化通知器链完成以后,就会进入getSingletonInstance方法,这是用来获取单例实例的,而真正的加强也是在这里发生的,我们来看一下。
- private synchronized Object getSingletonInstance() {
- if (this.singletonInstance == null) {
- this.targetSource = freshTargetSource();
- if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
- // Rely on AOP infrastructure to tell us what interfaces to proxy.
- Class targetClass = getTargetClass();
- if (targetClass == null) {
- throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
- }
- setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
- }
- // Initialize the shared singleton instance.
- super.setFrozen(this.freezeProxy);
- this.singletonInstance = getProxy(createAopProxy());
- }
- return this.singletonInstance;
- }
此时第一次获取,单例实例为null,所以会进入if块,首先刷新targetSource,因为我们的Target类没有实现targetSource接口,所以会由spring帮我们产生一个targetSource适配,这里是使用的适配器的模式,有兴趣可以进去看一下,我们此处不关注这个。接下来,会去判断代理接口,并且设置代理接口,但是我们的target未实现任何接口,所以此处interfaces仍然为空的,所以最后一步createAopProxy时,会帮我们创建cglib的proxy。最终由cglib生成代理返回。
执行下国际惯例,说完以后总要稍微总结一下,主要说几点:
1.在IOC容器初始化的过程中,并没有发生增强的动作,而是初始化了proxyFactoryBean。
2.如果配置中不指定,所有bean默认都是单例和非延迟加载的,也就是说所有的bean都将在第一次IOC容器初始化时全部实例化,所以上一章中所配置的三个bean都是在IOC容器初始化时进行的实例化。
3.springAOP代理有两种方式,一种是JDK提供的动态代理,一种是cglib字节码生成的技术,当要代理的类有实现的接口的时候,就会针对接口进行代理,否则就会采用cglib直接生成字节码产生子类。
到此处,我们已经基本上完全跟了一遍整个bean增强的过程,也大概了解了springAOP的大概原理,相信各位心中应该有个大概的印象了,其实springAOP增强的原理已经浮现出来了,接下来再研究下去,可能会收获甚微,还是要结合平时的应用和自己的兴趣去体会,始终不赞同一头扎进去就埋头苦干的作风。
好了,spring源码学习之路就圆满结束了,虽说时间不长,但收获甚大。各位如果有兴趣,相信现在也完全有能力自己去看源码了,以后有问题,不要找度娘了,找源码吧。