1、springMVC执行流程:
1.用户发起请求到前端控制器(DispatcherServlet),该控制器会过滤出哪些请求可以访问Servlet、哪些不能访问,就是url-pattern的作用,并且会加载springmvc.xml配置文件
2.前端控制器会找到处理器映射器(HandlerMapping),通过HandlerMapping完成url到controller映射的组件,简单来说就是将在springmvc.xml中配置的或者注解的url与对应的处理类找到并进行存储,用map<url,handler>这样的方式来存储
3.HandlerMapping有了映射关系,并且找到url对应的处理器,HandlerMapping就会将其处理器(Handler)返回,在返回前,会加上很多拦截器
4.DispatcherServlet拿到Handler后,找到HandlerAdapter(处理器适配器),通过它来访问处理器,并执行处理器
5.执行处理器
6.处理器会返回一个ModelAndView对象给HandlerAdapter
7.通过HandlerAdapter将ModelAndView对象返回给前端控制器(DispatcherServlet)
8.前端控制器请求视图解析器(ViewResolver)去进行视图解析,根据逻辑视图名解析成真正的视图(jsp),其实就是将ModelAndView对象中存放视图的名称进行查找,找到对应的页面形成视图对象
9.返回视图对象到前端控制器
10.视图渲染,就是将ModelAndView对象中的数据放到request域中,用来让页面加载数据的(通过第8步,通过名称找到了对应的页面,通过第10步,request域中有了所需要的数据,那么就能够进行视图渲染了。最后将其返回即可)
图示:
2、bean生命周期:
1.是否有实现了BeanFactoryPostProcessor接口的实现类bean,回调该bean中的postPrcessorBeanFactory()方法来对Bean的配置元数据进行更改
2.实例化bean对象(bean的实例化是使用反射实现的)
3.bean属性注入
4.检查是否实现Aware相关接口(如BeanNameAware、BeanFactoryAware、ApplicationContextAware),设置相关依赖
5.若BeanPostProcessor后置处理器实例与bean实例关联,调用postProcessBeforeInitialization方法
6.是否带有@PostConstruct方法
7.是否实现InitializingBean接口,调用afterPropertySet方法
8.是否配置有自定义init-method
9.若BeanPostProcessor后置处理器实例与bean实例关联,调用postProcessAfterInitialization方法
10.bean的使用过程....................
11.是否带有@PreDestroy方法
12.是否实现DisposableBean接口,调用destroy方法
13.是否配置有自定义destroy-method
找工作的时候有些人会被问道Spring中Bean的生命周期,其实也就是考察一下对Spring是否熟悉,工作中很少用到其中的内容,那我们简单看一下。
在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean也类似,如下
1、实例化一个Bean--也就是我们常说的new;
2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
以上10步骤可以作为面试或者笔试的模板,另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了。
3、Spring IOC容器BeanFactory、ApplicationContext:
BeanFactory:
实现BeanFactory接口的简单容器,具备最基本功能
设计路径:
BeanFactory -> HierarchicalBeanFactory -> ConfigurableBeanFactory,这是一个主要的BeanFactory设计路径
BeanFactory:基本规范,比如说getBean()这样的方法
HierarchicalBeanFactory:管理双亲Ioc容器规范,比如说getParentBeanFactory()这样的方法
ConfigurableBeanFactory:定义BeanFactory的配置.ConfigurableBeanFactory中定义了太多太多的api,比如通过setParentBeanFactory()设置双亲Ioc容器,又比如类加载器、类型转化、属性编辑器、BeanPostProcessor、作用域、bean定义、处理bean依赖关系、合并其他ConfigurableBeanFactory、bean如何销毁。ConfigurableBeanFactory同时继承了HierarchicalBeanFactory 和 SingletonBeanRegistry 这两个接口,即同时继承了分层和单例类注册的功能。
ApplicationContext:
实现ApplicationContext接口的复杂容器,具备高级功能
设计路径:
BeanFactory -> ListableBeanFactory/HierarchicalBeanFactory -> ApplicationContext -> ConfigurableApplicationContext,这是一个主要的ApplicationContext设计路径
ListableBeanFactory:细化了许多BeanFactory的功能,比如说getBeanDefinitionNames()
ApplicationContext:通过继承EnvironmentCapable、MessageSource(国际化的消息访问 )、ResourceLoader、ApplicationEventPublisher接口,添加了许多高级特性
BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean,而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean
BeanFactory和ApplicationContext都支持BeanPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册
4、Spring扩展:
Spring留给了我们大量的扩展接口供开发者去自定义自己的功能,甚至于AOP就是在Spring预留的扩展接口中实现的,意思是只要基于Spring IOC,遵守Spring对扩展接口的约定,那么就能实现自己想要的功能
BeanFactoryPostProcessor接口
Spring允许在Bean创建之前,读取Bean的元属性,并根据自己的需求对元属性进行改变,比如将Bean的scope从singleton改变为prototype
BeanFactoryPostProcessor接口中只有postProcessBeanFactory方法。实现了该接口的类,可以在Bean被创建之前,获取容器中Bean的定义信息,并且可以进行修改。
实现类中的postProcessBeanFactory方法只会被执行一次,且先于BeanPostProcessor接口的方法。
InstantiationAwareBeanPostProcessor接口
在bean实例化前后做一些操作
InstantiationAwareBeanPostProcessor接口中,常用的方法是postProcessBeforeInstantiation和postProcessAfterInstantiation。每个Bean的实例化(即调用构造函数)之前和之后,会分别调用实现了该接口的类中的postProcessBeforeInstantiation和postProcessAfterInstantiation方法。
BeanNameAware接口
BeanNameAware中只有一个setBeanName方法,实现了BeanNameAware接口的类,可以在该Bean被加载的过程中获取当前Bean的id
BeanFactoryAware接口
BeanFactoryAware接口中只有一个setBeanFactory方法。实现了BeanFactoryAware接口的类,可以在该Bean被加载的过程中获取加载该Bean的BeanFactory,同时也可以获取这个BeanFactory中加载的其它Bean
ApplicationContextAware接口
ApplicationContextAware中只有一个setApplicationContext方法。实现了ApplicationContextAware接口的类,可以在该Bean被加载的过程中获取Spring的应用上下文ApplicationContext,通过ApplicationContext可以获取Spring容器内的很多信息
BeanPostProcessor接口
在每个bean初始化成前后做操作
BeanPostProcessor接口中有两个方法,分别为postProcessBeforeInitialization和postProcessAfterInitialization。实现了BeanPostProcessor接口的类,会在每个Bean初始化(即调用setter)之前和之后,分别调用这个类中的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,实现初始化的逻辑控制。
InitializingBean接口
在属性设置完毕后做一些初始化操作
InitializingBean接口中只有一个afterPropertiesSet方法,从方法的名称上很容易理解,这个方法是在Bean的属性都设置值后被调用,用于完成一些初始化工作
当然,在Spring的配置文件中init-method的配置也是在Bean的属性都设置值后被调用,用于完成一些初始化工作,不过在执行顺序上,接口的方法先于配置
值得注意的是,这两种方式都是用于完成一些初始化工作,所以相应的方法中不要编写一些复杂且执行时间很长的逻辑
DisposableBean接口
在关闭容器前做一些操作
DisposableBean接口中只有一个destroy方法,该方法会在Bean被销毁、生命周期结束之前被调用,用于做一些销毁的收尾工作。同样,在Spring的配置文件中destroy-method配置也完成同样的工作,不过在执行顺序上,接口的方法先于配置
FactoryBean接口
FactoryBean接口可以实现Bean实例化的个性定制,让Spring容器加载我们想要的Bean。实现了FactoryBean接口的类,可以通过实现getObject方法,实现加载我们想要的Bean
Spring五个接口十个扩展点:
BeanPostProcessor:实例化Bean后置处理器
postProcessBeforeInitialization方法:Bean实例化、依赖注入完毕,初始化之前完成一些定制的任务
如:BeanValidationPostProcessor完成@Valid注解Bean验证,InitDestroyAnnotationBeanPostProcessor完成@PostConstruct注解的初始化方法调用,ApplicationContextAwareProcessor完成一些Aware接口的注入(如EnvironmentAware、ResourceLoaderAware、ApplicationContextAware),其返回值将替代原始的Bean对象
postProcessAfterInitialization方法:Bean实例化、依赖注入、初始化完毕时执行
如:AspectJAwareAdvisorAutoProxyCreator完成xml风格的AOP配置的目标对象包装到AOP代理对象,AnnotationAwareAspectJAutoProxyCreator完成@Aspectj注解风格的AOP配置的目标对象包装到AOP代理对象,其返回值将替代原始的Bean对象
InstantiationAwareBeanPostProcessor:实例化Bean后置处理器(继承BeanPostProcessor)
postProcessBeforeInstantiation方法:在实例化目标对象之前执行,可以自定义实例化逻辑,如返回一个代理对象等,
postProcessAfterInitialization方法:Bean实例化完毕后执行的后处理操作,所有初始化逻辑、装配逻辑之前执行
postProcessPropertyValues方法:完成其他定制的一些依赖注入和依赖检查等
SmartInstantiationAwareBeanPostProcessor:智能实例化Bean后置处理器(继承InstantiationAwareBeanPostProcessor)
predictBeanType方法:预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过Bean定义无法得到Bean类型信息时就调用该回调方法来决定类型信息
determineCandidateConstructors方法:检测Bean的构造器,可以检测出多个候选构造器,再有相应的策略决定使用哪一个,如AutowiredAnnotationBeanPostProcessor实现将自动扫描通过@Autowired/@Value注解的构造器从而可以完成构造器注入
getEarlyBeanReference方法:处理循环依赖
MergedBeanDefinitionPostProcessor:合并Bean定义后置处理器(继承BeanPostProcessor)
postProcessMergedBeanDefinition方法:执行Bean定义的合并,在实例化完Bean之后执行
DestructionAwareBeanPostProcessor:销毁Bean后置处理器(继承BeanPostProcessor)
postProcessBeforeDestruction方法:销毁后处理回调方法,该回调只能应用到单例Bean,如InitDestroyAnnotationBeanPostProcessor完成@PreDestroy注解的销毁方法调用
Spring内置的一些BeanPostProcessor:
ApplicationContextAwareProcessor:容器启动时会自动注册,在postProcessBeforeInitialization回调方法中进行实施,注入那些实现ApplicationContextAware、MessageSourceAware、ResourceLoaderAware、EnvironmentAware、EmbeddedValueResolverAware、ApplicationEventPublisherAware标识接口的Bean需要的相应实例
CommonAnnotationBeanPostProcessor:CommonAnnotationBeanPostProcessor继承InitDestroyAnnotationBeanPostProcessor,当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持
AutowiredAnnotationBeanPostProcessor:当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对JSR-330规范注解的支持和Spring自带注解的支持@Autowired、@Value、@Inject
RequiredAnnotationBeanPostProcessor:当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对@Required注解的方法进行依赖检查支持
PersistenceAnnotationBeanPostProcessor:当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对JPA @javax.persistence.PersistenceUnit和@javax.persistence.PersistenceContext注解进行依赖注入的支持
AbstractAutoProxyCreator:AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator都是继承AbstractAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator提供对(<aop:config>)声明式AOP的支持,AnnotationAwareAspectJAutoProxyCreator提供对(<aop:aspectj-autoproxy>)注解式(@AspectJ)AOP的支持
BeanValidationPostProcessor:提供对JSR-303验证规范支持
MethodValidationPostProcessor:提供对方法参数/方法返回值的进行验证(即前置条件/后置条件的支持),通过JSR-303注解验证
ScheduledAnnotationBeanPostProcessor:当配置文件中有<task:annotation-driven>自动注册或@EnableScheduling自动注册,提供对注解@Scheduled任务调度的支持
AsyncAnnotationBeanPostProcessor:当配置文件中有<task:annotation-driven>自动注册或@EnableAsync自动注册,提供对@Async和EJB3.1的@javax.ejb.Asynchronous注解的异步调用支持
ServletContextAwareProcessor:在使用Web容器时自动注册,类似于ApplicationContextAwareProcessor,当你的Bean实现了ServletContextAware/ServletConfigAware会自动调用回调方法注入ServletContext/ServletConfig
扩展Spring的几种方式:
1.基于XML配置的扩展
1.1.定义schema:
要支持XML的配置方式,首先需要定义一套XML Schema来描述组件所提供的功能,所以Schema中就需要描述我们期望用户提供的namespace以及namespace之间的排序等元数据
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.ctrip.com/schema/apollo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.ctrip.com/schema/apollo"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:annotation>
<xsd:documentation><![CDATA[ Namespace support for Ctrip Apollo Configuration Center. ]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="config">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ Apollo configuration section to integrate with Spring.]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="namespaces" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The comma-separated list of namespace names to integrate with Spring property sources.
If not specified, then default to application namespace.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:int" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The order of the config, default to Ordered.LOWEST_PRECEDENCE, which is Integer.MAX_VALUE.
If there are properties with the same name in different apollo configs, the config with smaller order wins.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
1.2.创建NamespaceHandler:
除了XML Schema,我们还需要创建一个自定义的NamespaceHandler来负责解析用户在XML中的配置,继承NamespaceHandlerSupport,在init方法中注册处理我们自定义节点的BeanDefinitionParser
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("config", new BeanParser());
}
}
1.3.自定义BeanDefinitionParser:
自定义的BeanDefinitionParser负责解析xml中的config节点信息,记录用户的配置信息,为后面和Spring整合做好铺垫
记录用户配置的namespace和order
向Spring注册Bean:ConfigPropertySourcesProcessor,这个bean后面会实际处理用户配置的namespace和order,从而完成配置注入到Spring中的功能
public class BeanParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return ConfigPropertySourcesProcessor.class;
}
@Override
protected boolean shouldGenerateId() {
return true;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String namespaces = element.getAttribute("namespaces");
// default to application
if (Strings.isNullOrEmpty(namespaces)) {
namespaces = ConfigConsts.NAMESPACE_APPLICATION;
}
int order = Ordered.LOWEST_PRECEDENCE;
String orderAttribute = element.getAttribute("order");
if (!Strings.isNullOrEmpty(orderAttribute)) {
try {
order = Integer.parseInt(orderAttribute);
} catch (Throwable ex) {
throw new IllegalArgumentException(String.format("Invalid order: %s for namespaces: %s", orderAttribute, namespaces));
}
}
PropertySourcesProcessor.addNamespaces(NAMESPACE_SPLITTER.splitToList(namespaces), order);
}
}
1.4.注册Spring handler和Spring schema:
基于XML配置扩展Spring的主体代码基本就是上面这些,剩下的就是要让Spring解析xml配置文件的过程中识别我们的自定义节点,并且转交到我们的NamespaceHandler处理
META-INF/spring.handlers
首先需要在META-INF目录下创建一个spring.handlers文件,来配置我们自定义的XML Schema Namespace到我们自定义的NamespaceHandler映射关系
如:http\://www.ctrip.com/schema/apollo=com.ctrip.framework.apollo.spring.config.NamespaceHandler
META-INF/spring.schemas
我们还需要在META-INF目录下创建一个spring.schemas,来配置我们自定义的XML Schema地址到实际Jar包中的classpath映射关系(避免Spring真的去服务器上下载不存在的文件)。
http\://www.ctrip.com/schema/apollo-1.0.0.xsd=/META-INF/apollo-1.0.0.xsd
http\://www.ctrip.com/schema/apollo.xsd=/META-INF/apollo-1.0.0.xsd
1.5.基于XML配置的使用样例如下:
<?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:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.ctrip.com/schema/apollo http://www.ctrip.com/schema/apollo.xsd">
<apollo:config namespaces="application" order="1"/>
</beans>
2.基于Java配置的扩展
通过这种方式,我们在开发Spring项目的过程中再也不需要去配置繁琐的xml文件了,只需要在Configuration类中配置就可以了,大大的简化了Spring的使用,这也是Spring Boot默认的配置方式,所以建议也支持这一特性
@Import注解:
支持Java配置扩展的关键点就是@Import注解,Spring 3.0提供了这个注解用来支持在Configuration类中引入其它的配置类,包括Configuration类,ImportSelector和ImportBeanDefinitionRegistrar的实现类,我们可以通过这个注解来引入自定义的扩展Bean
自定义注解:
和基于XML配置类似的,我们需要提供给用户一个注解来配置需要注入到Spring Property Sources的namespaces和order,下面就是Apollo提供的@EnableApolloConfig注解,允许用户传入namespaces和order信息
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
int order() default Ordered.LOWEST_PRECEDENCE;
}
这里的关键点是在注解上使用了@Import(ApolloConfigRegistrar.class),从而Spring在处理@EnableApolloConfig时会实例化并调用ApolloConfigRegistrar的方法
自定义ImportBeanDefinitionRegistrar实现:
ImportBeanDefinitionRegistrar接口定义了registerBeanDefinitions方法,从而允许我们向Spring注册必要的Bean
Apollo的自定义ImportBeanDefinitionRegistrar实现(ApolloConfigRegistrar)主要做了两件事情:1.记录用户配置的namespace和order,2.向Spring注册Bean:PropertySourcesProcessor,这个bean后面会实际处理用户配置的namespace和order,从而完成配置注入到Spring中的功能
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));
String[] namespaces = attributes.getStringArray("value");
int order = attributes.getNumber("order");
PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(), PropertySourcesProcessor.class);
}
}
基于Java配置的使用样例如下:
@Configuration
@EnableApolloConfig(value = "application", order = 1)
public class AppConfig {}
3.Spring容器的扩展点
前面两点简单介绍了扩展Spring的两种方式:基于XML和基于Java的配置,通过这两种方式,我们可以在运行时收集到用户的配置信息,同时向Spring注册实际处理这些配置信息的Bean
但这些注册进去的Bean实际上是如何工作的?我们通过什么方式能使我们的程序逻辑和Spring容器紧密合作,并无缝插入到用户bean的生命周期中?
这里简单介绍Spring容器最常用的两个扩展点:BeanFactoryPostProcessor和BeanPostProcessor
扩展BeanFactoryPostProcessor:
BeanFactoryPostProcessor提供了一个方法postProcessBeanFactory,这个方法会被Spring在容器初始化过程中调用,调用时机是所有bean的定义信息都已经初始化好,但是这些bean还没有实例化
Apollo就利用这个时间点把配置信息注入到Spring Property Sources中,从而用户的bean在真正实例化时,所有需要的配置信息已经准备好了
public class PropertySourcesProcessor implements BeanFactoryPostProcessor {
private static final AtomicBoolean PROPERTY_SOURCES_INITIALIZED = new AtomicBoolean(false);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (!PROPERTY_SOURCES_INITIALIZED.compareAndSet(false, true)) {
// already initialized
return;
}
// initialize and inject Apollo config to Spring Property Sources
initializePropertySources();
}
}
扩展BeanPostProcessor:
BeanPostProcessor提供了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,主要针对bean初始化提供扩展
postProcessBeforeInitialization会在每一个bean实例化之后、初始化(如afterPropertiesSet方法)之前被调用,postProcessAfterInitialization则在每一个bean初始化之后被调用
我们常用的@Autowired注解就是通过(AutowiredAnnotationBeanPostProcessor)postProcessBeforeInitialization实现的
Apollo提供了@ApolloConfig注解来实现实例化时注入Config对象实例,所以实现逻辑和@Autowired类似
public class ApolloAnnotationProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class clazz = bean.getClass();
processFields(bean, clazz.getDeclaredFields());
return bean;
}
private void processFields(Object bean, Field[] declaredFields) {
for(Field field : declaredFields) {
ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
if (annotation == null) {
continue;
}
Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()), "Invalid type: %s for field: %s, should be Config", field.getType(), field);
String namespace = annotation.value();
Config config = ConfigService.getConfig(namespace);
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, config);
}
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
通过上面的代码就会发现Apollo在用户bean初始化前会根据@ApolloConfig的配置注入对应namespace的Config实例5、Filter、Interceptor、Aspect区别:
Filter:
Filter使用户可以改变一个request和修改一个response,Filter不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在离开servlet时处理response,filter其实是一个”servlet chaining”(servlet 链)
从doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)方法的参数可以看出,我们只能得到原始的request和response对象,不能得到这个请求被哪个Controller以及哪个方法处理了
自定义Filter需要两个步骤:实现Filter[javax.servlet.Filter]接口,实现doFilter方法;添加@Configuration注解,将自定义Filter加入过滤链
web.xml配置方式:
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.cppba.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/*</url-pattern>
<init-param>
<param-name>paramName</param-name>
<param-value>paramValue</param-value>
</init-param>
</filter-mapping>
@Configuration中增加一个@bean:
@Bean
public FilterRegistrationBean testFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new TestFilter());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("testFilter");
registration.setOrder(1);
return registration;
}
Application启动类中增加@ServletComponentScan:
@Component
@Order(1)
@WebFilter(filterName = "testFilter1", urlPatterns = "/*")
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {
System.out.println("TestFilter1");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
Interceptor:
自定义Interceptor需要实现HandlerInterceptor接口、或者继承HandlerInterceptorAdapter类,重写preHandle、postHandle、afterCompletion方法
preHandle在业务处理器处理请求之前被调用,postHandle在业务处理器处理请求执行完成后生成视图之前执行,afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
注册自定义Interceptor,把自定义Interceptor注入spring容器,实现WebMvcConfigurer或继承WebMvcConfigurerAdapter,重写addInterceptors方法,将自定义Interceptor进行注册
@Component
public class TimeInterceptor extends HandlerInterceptorAdapter {
private final NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("startTimeThreadLocal");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("time interceptor preHandle");
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取处理当前请求的 handler 信息
System.out.println("handler 类:" + handlerMethod.getBeanType().getName());
System.out.println("handler 方法:" + handlerMethod.getMethod().getName());
MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
for (MethodParameter methodParameter : methodParameters) {
String parameterName = methodParameter.getParameterName();
// 只能获取参数的名称,不能获取到参数的值
System.out.println("parameterName: " + parameterName);
}
// 把当前时间放入 threadLocal
startTimeThreadLocal.set(System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("time interceptor postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 从 threadLocal 取出刚才存入的 startTime
Long startTime = startTimeThreadLocal.get();
long endTime = System.currentTimeMillis();
System.out.println("time interceptor consume " + (endTime - startTime) + " ms"); System.out.println("time interceptor afterCompletion");
}
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
通过Interceptor中方法上的handler参数,我们就可以得到这个请求被哪个Controller以及哪个方法处理了,但是不能直接获取到这个方法上的参数值
Aspcet:
Aspcet不需要注册,只需要申明切面、定义切入点
@Aspect
@Component
public class TimeAspect {
//定义切入点
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) " +
"|| @annotation(org.springframework.web.bind.annotation.PostMapping) " +
"|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
"|| @annotation(org.springframework.web.bind.annotation.PutMapping)" +
"|| @annotation(org.springframework.web.bind.annotation.PatchMapping)" +
"|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void logAspect() {}
@Around("logAspect()")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("time aspect start");
Object[] args = pjp.getArgs();
for (Object arg : args) {
System.out.println("arg is " + arg);
}
long startTime = System.currentTimeMillis();
Object object = pjp.proceed();
long endTime = System.currentTimeMillis();
System.out.println("time aspect consume " + (endTime - startTime) + " ms");
System.out.println("time aspect end");
return object;
}
}
总结:
Filter:和框架无关,是java web里面的,可以控制最初的http请求,但是更细一点的类和方法控制不了。
Interceptor:可以控制请求的控制器和方法,但控制不了请求方法里的参数。
Aspect : 可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得
Filter->Interceptor->ControllerAdvice->Aspect->Controller
6、代理创建器
三种代理创建器(BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator)
BeanNameAutoProxyCreator是三种自动代理创建器之一,它是根据拦截器和设置的Bean的名称表达式做匹配来创建代理,下面是个例子
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(getClass() + "调用方法前");
Object ret=invocation.proceed();
System.out.println(getClass() + "调用方法后");
return ret;
}
}
public interface UserService {
void print();
}
public class UserServiceImpl implements UserService {
public void print(){
System.out.println(getClass() + "#print");
}
}
@Configuration
public class AppConfig {
// 要创建代理的目标Bean
@Bean
public UserService userService(){
return new UserServiceImpl();
}
// 创建Advice或Advisor
@Bean
public Advice myMethodInterceptor(){
return new MyMethodInterceptor();
}
// 使用BeanNameAutoProxyCreator来创建代理
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
// 设置要创建代理的那些Bean的名字
beanNameAutoProxyCreator.setBeanNames("userSer*");
// 设置拦截链名字(这些拦截器是有先后顺序的)
beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");
return beanNameAutoProxyCreator;
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService= applicationContext.getBean(UserService.class);
userService.print();
}
}
xml方式:
<bean id="log4jdbcInterceptor" class="net.sf.log4jdbc.DataSourceSpyInterceptor" />
<bean id="dataSourceLog4jdbcAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>log4jdbcInterceptor</value>
</list>
</property>
<property name="beanNames">
<list>
<value>dataSource</value>
</list>
</property>
</bean>
7、Spring5.0
基于Java 8,支持Java 9
响应式编程,使用Spring WebFlux提供响应式Web编程支持,Webflux是一个全新的非堵塞的函数式Reactive Web框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好
HTTP/2支持
WebFlux简介:
WebFlux模块中包含了对反应式 HTTP、服务器推送事件和WebSocket的客户端和服务器端的支持,在服务器端,WebFlux支持两种不同的编程模型:第一种是Spring MVC中使用的基于Java注解的方式,第二种是基于Java 8的lambda表达式的函数式编程模型。
这两种编程模型只是在代码编写方式上存在不同。它们运行在同样的反应式底层架构之上,因此在运行时是相同的。
WebFlux需要底层提供运行时的支持,WebFlux可以运行在支持Servlet 3.1非阻塞IO API的Servlet容器上,或是其他异步运行时环境,如 Netty 和 Undertow
Java注解编程模型:
@RestController
public class BasicController {
@GetMapping("/hello_world")
public Mono<String> sayHelloWorld() {
return Mono.just("Hello World");
}
}
WebFlux所使用的类型是与反应式编程相关的Flux和Mono等,而不是简单的对象,对于简单的Hello World示例来说,这两者之间并没有什么太大的差别,对于复杂的应用来说,反应式编程和负压的优势会体现出来,可以带来整体的性能的提升
REST API:
@Service
class UserService {
private final Map<String, User> data = new ConcurrentHashMap<>();
Flux<User> list() {
return Flux.fromIterable(this.data.values());
}
Flux<User> getById(final Flux<String> ids) {
return ids.flatMap(id -> Mono.justOrEmpty(this.data.get(id)));
}
Mono<User> getById(final String id) {
return Mono.justOrEmpty(this.data.get(id))
.switchIfEmpty(Mono.error(new ResourceNotFoundException()));
}
Mono<User> createOrUpdate(final User user) {
this.data.put(user.getId(), user);
return Mono.just(user);
}
Mono<User> delete(final String id) {
return Mono.justOrEmpty(this.data.remove(id));
}
}
@RestController
@RequestMapping("/user")
public class UserController {
private final UserService userService;
@Autowired
public UserController(final UserService userService) {
this.userService = userService;
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
@ExceptionHandler(ResourceNotFoundException.class)
public void notFound() {
}
@GetMapping("")
public Flux<User> list() {
return this.userService.list();
}
@GetMapping("/{id}")
public Mono<User>getById(@PathVariable("id") final String id) {
return this.userService.getById(id);
}
@PostMapping("")
public Mono<User> create(@RequestBody final User user) {
return this.userService.createOrUpdate(user);
}
@PutMapping("/{id}")
public Mono<User> update(@PathVariable("id") final String id, @RequestBody final User user) {
Objects.requireNonNull(user);
user.setId(id);
return this.userService.createOrUpdate(user);
}
@DeleteMapping("/{id}")
public Mono<User> delete(@PathVariable("id") final String id) {
return this.userService.delete(id);
}
}
服务器推送事件:
函数式编程模型:
8、BeanFactory和FactoryBean:
BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean,在Spring中所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似
BeanFactory定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范,在Spring代码中BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等
一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案
Spring为此提供了一个FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑,FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现,它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利
一个Bean想要实现FactoryBean,必须实现以下三个接口:
Object getObject() 返回由FactoryBean创建的Bean的实例
boolean isSingleton() 确定由FactoryBean创建的Bean的作用域是singleton还是prototype
getObjectType() 返回FactoryBean创建的Bean的类型
有一点需要注意,如果将一个实现了FactoryBean的类成功配置到了spring上下文中,那么通过该类对象的名称从spring的applicationContext或者beanFactory获取bean时,获取到的是FactoryBean创建的bean实例,而不是FactoryBean自己,如果想通过spring拿到FactoryBean,需要在名称前加&符号,如:applicationContext.getBean("&appleFactoryBean")
还有一点需要注意,FactoryBean管理的bean实际上也是由spring进行配置、实例化、管理,因此由FactoryBean管理的bean不能再次配置到spring配置文件中(xml、java类配置、注解均不可以),否则会报异常,如:
//@Component 这里不可以加注解 !!!!!!
public class AppleBean{
}
@Component
public class AppleFactoryBean implements FactoryBean{
public Object getObject() throws Exception {
return new AppleBean();
}
public Class<?> getObjectType() {
return AppleBean.class;
}
public boolean isSingleton() {
return false;
}
}
9、定制自己的异常页面:
定义一个MyErrorPageRegistrar实现ErrorPageRegistrar接口:
@Component
public class MyErrorPageRegistrar implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
//具体的错误码错误异常页面
ErrorPage e404 = new ErrorPage(HttpStatus.NOT_FOUND,"/404.html");
ErrorPage e500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/500.html");
//指定具体异常的错误定制页面
ErrorPage argspage = new ErrorPage(IllegalArgumentException.class,"/argsException.html");
registry.addErrorPages(e404,e500,argspage);
}
}