Junhc

岂止于博客

Spring常见问题

Spring 自定义注解

Annotation不支持继承其他接口
Annotation支持三种类型:基本类型、数组类型、枚举类型

default关键字,设置默认值
value属性关键字,当且仅有一个属性时,使用时可省略

/**
 * 名    称:OperateType
 * 功    能:
 * 创 建 人:K.K
 * 创建时间:2015/10/21 13:51
 * 修 改 人:
 * 修改时间:
 * 说    明:
 * 版 本 号:
 */
public enum OperateType {
    NIL(0),
    GET(1),
    ADD(2),
    UPDATE(3),
    DELETE(4);

    private int value;

    private OperateType(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return String.valueOf(this.value);
    }
}
import java.lang.annotation.*;

/**
 * 名    称:Operation
 * 功    能:
 * 创 建 人:K.K
 * 创建时间:2015/10/21 13:16
 * 修 改 人:
 * 修改时间:
 * 说    明:
 * 版 本 号:
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Operation {
    String description() default "";

    OperateType type() default OperateType.NIL;

    String[] params() default {};
}

@Target 描述注解的使用范围

  • CONSTRUCTOR 构造器
  • FIFLD 域
  • LOCAL_VARIABLE 局部变量
  • METHOD 方法
  • PACKAGE 包
  • PARAMETER 参数
  • TYPE 类或接口

@Retention 注解的生命周期

  • SOURCE 在源文件中有效
  • CLASS 在class文件中有效
  • RUNTIME 在运行时有效

@Documented 允许javadoc将Annotation生成到doc

@Inherited 允许子类继承Annotation标记

Spring获取ApplicationContext对象
1. FileSystemXmlApplicationContext
// 重新加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext(...);
2. ClassPathXmlApplicationContext
ApplicationContext ctx = new ClassPathXmlApplication(...);
3. WebApplicationContextUtils
ServletContext servletContext = request.getSession().getServletContext();
// 有可能返回null      
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
4. ContextLoader.getCurrentWebApplicationContext()
5. RequestContextUtils.findWebApplicationContext(..)
6. 创建一个类让其实现org.springframework.context.ApplicationContextAware接口来让Spring在启动的时候为我们注入ApplicationContext对象

Spring中提供一些Aware相关的接口,BeanFactoryAware、 ApplicationContextAware、ResourceLoaderAware、ServletContextAware等等,其中最常用到的是ApplicationContextAware。实现ApplicationContextAware的Bean,在Bean被初始后,将会被注入ApplicationContext的实例。ApplicationContextAware提供了publishEvent()方法,实现Observer(观察者)设计模式的事件传播机,提供了针对Bean的事件传播功能。通过Application.publishEvent方法,我们可以将事件通知系统内所有的ApplicationListener。

Spring事件处理一般过程:

定义Event类,继承org.springframework.context.ApplicationEvent。

编写发布事件类Publisher,实现org.springframework.context.ApplicationContextAware接口。

覆盖方法setApplicationContext(ApplicationContext applicationContext)和发布方法publish(Object obj)。
定义时间监听类EventListener,实现ApplicationListener接口,实现方法onApplicationEvent(ApplicationEvent event)。

@Component
@Lazy(false)
public class Loading implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
解决Spring中不同配置文件中存在name或者id相同的bean引起的问题
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
...
private boolean allowBeanDefinitionOverriding = true;
...
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    if(beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition)beanDefinition).validate();
        } catch (BeanDefinitionValidationException var7) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var7);
        }
    }

    Map ex = this.beanDefinitionMap;
    synchronized(this.beanDefinitionMap) {
        Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if(oldBeanDefinition != null) {
            if(!this.allowBeanDefinitionOverriding) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean \'" + beanName + "\': There is already [" + oldBeanDefinition + "] bound.");
            }

            if(this.logger.isInfoEnabled()) {
                this.logger.info("Overriding bean definition for bean \'" + beanName + "\': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }
        } else {
            this.beanDefinitionNames.add(beanName);
            this.frozenBeanDefinitionNames = null;
        }

        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    this.resetBeanDefinition(beanName);
}
public class ContextLoader {
...
/**
 * Config param for which {@link ApplicationContextInitializer} classes to use
 * for initializing the web application context: {@value}
 * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
 */
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
...
/**
 * Return the {@link ApplicationContextInitializer} implementation classes to use
 * if any have been specified by {@link #CONTEXT_INITIALIZER_CLASSES_PARAM}.
 * @param servletContext current servlet context
 * @see #CONTEXT_INITIALIZER_CLASSES_PARAM
 */
@SuppressWarnings("unchecked")
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
    determineContextInitializerClasses(ServletContext servletContext) {
  String classNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
  List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
    new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
  if (classNames != null) {
    for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
      try {
        Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
        Assert.isAssignable(ApplicationContextInitializer.class, clazz,
            "class [" + className + "] must implement ApplicationContextInitializer");
        classes.add((Class<ApplicationContextInitializer<ConfigurableApplicationContext>>)clazz);
      }
      catch (ClassNotFoundException ex) {
        throw new ApplicationContextException(
            "Failed to load context initializer class [" + className + "]", ex);
      }
    }
  }
  return classes;
}
public class ForbidBeanDefinitionDuplicateContextInitializer implements ApplicationContextInitializer<XmlWebApplicationContext> {
    @Override
    public void initialize(XmlWebApplicationContext xmlWebApplicationContext) {
        xmlWebApplicationContext.setAllowBeanDefinitionOverriding(false);
    }
}
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-service.xml</param-value>
</context-param>
<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>com.xxx.xxx.ForbidBeanDefinitionDuplicateContextInitializer</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>basics-orderService</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-web.xml</param-value>
    </init-param>
    <init-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>com.xxx.xxx.ForbidBeanDefinitionDuplicateContextInitializer</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
BeanFactoryFactoryBean的区别

BeanFactory是IOC容器的基本接口。
FactoryBean一个能产生或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

public interface BeanFactory {

  / **
  * 用于提取FactoryBean实例并将其区分开来
  * 由FactoryBean创建的bean。例如,如果bean命名为myJndiObject是一个FactoryBean,
  * 使用&myJndiObject将返回工厂,而不是工厂返回的实例。
  * /
	String FACTORY_BEAN_PREFIX = "&";
  ...
}