Java tutorial
/* * Copyright 2009 Kristian Rosenvold * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.rosenvold.spring; import org.springframework.context.ApplicationContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.annotation.Annotation; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.ArrayList; import javax.annotation.Resource; /** * @author <a href="mailto:kristian AT zenior no">Kristian Rosenvold</a> */ public class SpringContextAnalyzer { private final ApplicationContext applicationContext; public SpringContextAnalyzer(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public String describe(List<Problem> problems) { if (problems.size() == 0) return null; StringBuilder resp = new StringBuilder(); for (Problem problem : problems) { resp.append(problem.describe()); } return resp.toString(); } public List<Problem> analyzeCurrentSpringContext(Filter<List<FieldProblem>> filter) { List<Problem> problems = new ArrayList<Problem>(); for (String beanName : applicationContext.getBeanDefinitionNames()) { final Object bean = applicationContext.getBean(beanName); BeanDefinition beanDefinition = getBeanDefinition(beanName); if (applicationContext.isSingleton(beanName) && !isSpringFrameworkClass(bean.getClass())) { final List<FieldProblem> problemsForClass = getFieldProblemsForSingletonBean(bean, beanDefinition); if (!problemsForClass.isEmpty() && filter.accept(problemsForClass)) { problems.add(new Problem(problemsForClass, beanName)); } } } return problems; } public List<Problem> analyzeCurrentSpringContext() { return analyzeCurrentSpringContext(new Filter<List<FieldProblem>>() { public boolean accept(List<FieldProblem> candidate) { return true; } }); } BeanDefinition getBeanDefinition(String beanName) { BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext; return beanDefinitionRegistry.getBeanDefinition(beanName); } private boolean isSpringFrameworkClass(Class clazz) { return clazz.getCanonicalName().startsWith("org.springframework"); } List<FieldProblem> getFieldProblemsForSingletonBean(String beanName) { Object bean = applicationContext.getBean(beanName); BeanDefinition beanDefinition = getBeanDefinition(beanName); return getFieldProblemsForSingletonBean(bean, beanDefinition); } List<FieldProblem> getFieldProblemsForSingletonBean(Object singletonBean, BeanDefinition beanDefinition) { List<FieldProblem> fieldProblems = new ArrayList<FieldProblem>(); Field[] fields = singletonBean.getClass().getDeclaredFields(); for (Field field : fields) { Class clazz = field.getDeclaringClass(); if (!isInLegalRuntimeState(singletonBean, field)) { // This is the best analysis. Simply checks that all declared fields have object values when spring is done. fieldProblems.add(new FieldProblem(field, FieldProblemType.NotInitializedAtRuntime)); } if (violatesProxyRequirementsForSingletonClient(singletonBean, field)) { fieldProblems.add(new FieldProblem(field, FieldProblemType.RequiresScopeProxy)); } /*else if (!isLegalForSingletonBean(field, beanDefinition)) { fieldProblems.add( new FieldProblem(field, FieldProblemType.NotVaildForSingleton)); } */ } return fieldProblems; } /** * Checks if the value in a given field can be accessed from a singleton bean without a proxy. * @param singletonBean The client bean * @param field The field to check * @return true if the requirements are verified not to be met, false in all other cases */ boolean violatesProxyRequirementsForSingletonClient(Object singletonBean, Field field) { Object injectedBean = getFieldValue(singletonBean, field); if (injectedBean == null) return false; // Dont care String[] beanNamesForType = applicationContext.getBeanNamesForType(injectedBean.getClass()); if (beanNamesForType.length != 1) return false; BeanDefinition injectedBeanDef = getBeanDefinition(beanNamesForType[0]); return (isWebScope(injectedBeanDef.getScope()) && !isProxy(injectedBean)); } private boolean isWebScope(String scope) { return "request".equals(scope) || "session".equals(scope); } private boolean isProxy(Object value) { return java.lang.reflect.Proxy.isProxyClass(value.getClass()); } private boolean isLegalForSingletonBean(Field field, BeanDefinition beanDefinition) { return isNonSpringManaged(field) || isAutowired(field) || isResource(field) || isStatic(field) || isFinal(field); } private boolean isInLegalRuntimeState(Object instance, Field field) { return isKnownException(instance, field) || isNonSpringManaged(field) || isInitialized(instance, field); } private boolean isKnownException(Object instance, Field field) { if ("java.util.Properties".equals(instance.getClass().getCanonicalName()) && "defaults".equals(field.getName())) return true; return false; } /* * Indicates if the supplied field has been initialized to a value. This will include any postconstruct spring blocks etc and is a quite reliable way of analyzing the result. */ private boolean isInitialized(Object instance, Field field) { return getFieldValue(instance, field) != null; } private Object getFieldValue(Object instance, Field field) { if (!field.isAccessible()) field.setAccessible(true); try { return field.get(instance); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private boolean isStatic(Field field) { return Modifier.isStatic(field.getModifiers()); } private boolean isFinal(Field field) { return Modifier.isFinal(field.getModifiers()); } boolean hasAutowiredField(Class clazz) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (isAutowired(field)) return true; } return false; } boolean isComponent(Class clazz) { final Annotation[] annotations = clazz.getAnnotations(); return containsAnnotation(annotations, Component.class); } private boolean isNonSpringManaged(Field field) { return containsAnnotation(field.getDeclaredAnnotations(), NonSpringManaged.class); } private boolean isAutowired(Field field) { return containsAnnotation(field.getDeclaredAnnotations(), Autowired.class); } private boolean isResource(Field field) { return containsAnnotation(field.getDeclaredAnnotations(), Resource.class); } private boolean containsAnnotation(Annotation[] annotations, Class<? extends Annotation> requestedType) { for (Annotation annotation : annotations) { if (annotation.annotationType().equals(requestedType)) { return true; } if (contains1LevelNestedAnnotation(annotation, requestedType)) { return true; } } return false; } private boolean contains1LevelNestedAnnotation(Annotation annotation, Class<? extends Annotation> requestedType) { final Annotation[] declaredAnnotations = annotation.annotationType().getDeclaredAnnotations(); for (Annotation anAnnotation : declaredAnnotations) { if (anAnnotation.annotationType().equals(requestedType)) { return true; } } return false; } }