/* * Copyright 2002-2004 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.beans.factory; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.List; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DelegatingIntroductionInterceptor; import org.springframework.aop.support.Pointcuts; import org.springframework.aop.support.StaticMethodMatcherPointcut; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.PropertyOverrideConfigurer; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.config.PropertyResourceConfigurer; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AbstractBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.util.StringUtils; /** * * @author Rod Johnson */ public class Configurer { private BeanDefinitionRegistry bdi; private AbstractBeanFactory abf; private int defaultAutowireMode = AbstractBeanDefinition.AUTOWIRE_BY_TYPE; public Configurer(BeanDefinitionRegistry bdi) { this.bdi = bdi; if (bdi instanceof AbstractBeanFactory) { abf = (AbstractBeanFactory) bdi; } else if (bdi instanceof ConfigurableApplicationContext) { BeanFactory bf = ((ConfigurableApplicationContext) bdi).getBeanFactory(); if (bf instanceof AbstractBeanFactory) { abf = (AbstractBeanFactory) bf; } } } public void setDefaultAutowireMode(int mode) { this.defaultAutowireMode = mode; } // TODO resource!? public PropertyResourceConfigurer properties(String location) { return (PropertyResourceConfigurer) add(PropertyPlaceholderConfigurer.class). prop("location", location); } public PropertyResourceConfigurer properties(Class clazz, String name) { String location = StringUtils.replace(clazz.getPackage().getName(), ".", "/") + "/" + name; return (PropertyResourceConfigurer) add(PropertyOverrideConfigurer.class). prop("location", "classpath:" + location); } public void xml(Class clazz, String name) { String location = StringUtils.replace(clazz.getPackage().getName(), ".", "/") + "/" + name; XmlBeanDefinitionReader xdr = new XmlBeanDefinitionReader(bdi); xdr.loadBeanDefinitions(new ClassPathResource(location)); } public int getDefaultAutowireMode() { return this.defaultAutowireMode; } public String addSingleton(Object o) { String generatedName = o.getClass().getName(); addSingleton(generatedName, o); return generatedName; } public void addSingleton(String name, Object o) { if (abf != null) { abf.registerSingleton(name, o); } else { throw new UnsupportedOperationException("Can't register singleton"); } } // TODO what about adding an object and autowiring it!? But order will // matter // add and autowire (allows custom create) public Definition add(Class clazz) { // TODO counts to ensure unique naming? // TODO what about autowiring? return add(clazz.getName(), clazz); } public Definition add(String name, Class clazz) { DefinitionImpl def = new DefinitionImpl(name, clazz, defaultAutowireMode); return add(def); } public Definition addFactoryBean(String name, String factoryBean, String factoryMethod) { DefinitionImpl def = new DefinitionImpl(name, factoryBean, factoryMethod, defaultAutowireMode); // TODO allow instantiation!? //return add(def); bdi.registerBeanDefinition(def.getBeanDefinitionName(), def.getBeanDefinition()); return def; } // public Definition add(String name, String parent) { // DefinitionImpl def = new DefinitionImpl(name, clazz, // defaultAutowireMode); // return add(def); // } public Definition add(Definition def) { bdi.registerBeanDefinition(def.getBeanDefinitionName(), def.getBeanDefinition()); return instantiate(def); } public AdvisedSupport advise(Definition def) { Definition outerDefinition = new DefinitionImpl(def.getBeanDefinitionName(), ProxyFactoryBean.class, AbstractBeanDefinition.AUTOWIRE_NO); outerDefinition.prop("target", new BeanDefinitionHolder(def .getBeanDefinition(), "(inner bean)")); bdi.registerBeanDefinition(outerDefinition.getBeanDefinitionName(), outerDefinition.getBeanDefinition()); return (AdvisedSupport) instantiate(outerDefinition); } /** * Last definition recordable WAS STATIC */ private Definition instantiate(final Definition def) throws BeansException { // TODO: NEEDS TO USE BeanFactory.getType AbstractBeanDefinition bd = (AbstractBeanDefinition) def.getBeanDefinition(); Object target = BeanUtils.instantiateClass(bd.getBeanClass()); ProxyFactory pf = new ProxyFactory(target); pf.setProxyTargetClass(true); pf.addAdvice(new DelegatingIntroductionInterceptor(def)); pf.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, new RecordingBeforeAdvice(def))); // ProxyFactoryBeans get a special interceptor to capture // the names of interceptors added by addAdvisor pf.addAdvisor(new DefaultPointcutAdvisor( new StaticMethodMatcherPointcut() { public boolean matches(Method m, Class targetClass) { return ProxyFactoryBean.class .isAssignableFrom(targetClass) && m.getName().startsWith("add"); } }, new InterceptorNameCaptureInterceptor(def))); // Other methods aren't config methods //, but can probably permit them anyway with // before advice rather than around advice // pf.addAdvice(new MethodInterceptor() { // public Object invoke(MethodInvocation mi) throws Throwable { // throw new UnsupportedOperationException(mi.getMethod() // .getName() // + " is not a setter or other config method: " // + "disallowing at config time"); // } // }); //System.err.println(pf.toProxyConfigString()); Definition proxy = (Definition) pf.getProxy(); return proxy; } /** * Advice for setter methods that saves the values to the backing * BeanDefinition. Supports references to other beans in the factory, as * well as simple types. */ private static class RecordingBeforeAdvice implements MethodBeforeAdvice { private final Definition def; private RecordingBeforeAdvice(Definition def) { this.def = def; } /** * @see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method, java.lang.Object[], java.lang.Object) */ public void before(Method m, Object[] args, Object target) throws Throwable { String propName = m.getName().substring(3); propName = propName.substring(0, 1).toLowerCase() + propName.substring(1); Object value = args[0]; if (value instanceof Definition) { String refName = ((Definition) value).getBeanDefinitionName(); System.err.println("Recorded reference to name " + refName); value = new RuntimeBeanReference(refName); } // TODO complex types that are not references!? Should probably // allow them PropertyValue pv = new PropertyValue(propName, value); def.getBeanDefinition().getPropertyValues().addPropertyValue(pv); System.out.println("Added " + pv); } } /** * Applies only to ProxyFactoryBeans. */ private final class InterceptorNameCaptureInterceptor implements MethodInterceptor { private List interceptorNames = new LinkedList(); private final Definition def; private InterceptorNameCaptureInterceptor(Definition def) { this.def = def; } public Object invoke(MethodInvocation mi) throws Throwable { Object arg = mi.getArguments()[0]; if (arg instanceof Definition) { Definition d = (Definition) arg; addInterceptorName(d.getBeanDefinitionName()); } else { // Register the bean and add its name addInterceptorName(addSingleton(arg)); } return null; } private void addInterceptorName(String name) { System.out.println("Ref to interceptor with name=" + name); //((ProxyFactoryBean) // AopContext.currentProxy()).setInterceptorNames(new String[] // {name}); interceptorNames.add(name); def.getBeanDefinition().getPropertyValues().addPropertyValue( "interceptorNames", interceptorNames); } } }