Spring由7个模块组成:
Sping的容器可以分为两种类型
1. BeanFactory:(org.springframework.beans.factory.BeanFactory接口定义)是最简答的容器,提供了基本的DI支持。最常用的BeanFactory实现就是XmlBeanFactory类,它根据XML文件中的定义加载beans,该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。
2. ApplicationContext应用上下文:(org.springframework.context.ApplicationContext)基于BeanFactory之上构建,并提供面向应用的服务。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Inversion of Control, 一般分为两种类型:依赖注入DI(Dependency Injection)和依赖查找(Dependency Lookup).依赖注入应用比较广泛。
Spring IOC扶着创建对象,管理对象(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
优点:把应用的代码量降到最低。容器测试,最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。
DI依赖注入是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用床架对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述组件需要哪些服务,之后一个IOC容器辅助把他们组装起来。
IOC的注入方式:1. 构造器依赖注入;2. Setter方法注入。
id
name
class
init-method:Bean实例化后会立刻调用的方法
destory-method:Bean从容器移除和销毁前,会调用的方法
factory-method:运行我们调用一个指定的静态方法,从而代替构造方法来创建一个类的实例。
scope:Bean的作用域,包括singleton(默认),prototype(每次调用都创建一个实例), request,session, global-session(注意spring中的单例bean不是线程安全的)
autowired:自动装配 byName, byType, constructor, autodetect(首先阐释使用constructor自动装配,如果没有发现与构造器相匹配的Bean时,Spring将尝试使用byType自动装配)
default-init-method
default-destory-method
default-autowire:默认为none,应用于Spring配置文件中的所有Bean,注意这里不是指Spring应用上下文,因为你可以定义多个配置文件
1) 创建Bean的实例(factory-method, autowireConstrutor)
2) 属性注入(autowireByName, autowireByType)
3) 初始化Bean
3.1 激活Aware方法:(invokeAwaresMethods)Spring中提供了一些Aware相关接口,比如BeanNameAware, BeanFactoryAware, ApplicationContextAware等,实现这些Aware接口的bean在被初始化之后,可以取得一些相对应的资源。
private void invokeAwareMethods(final String beanName, final Object bean){ if(bean instanceof Aware) { if(bean instanceof BeanNameAware){ ((BeanNameAware) bean).setBeanName(beanName); } if(bean instanceof BeanClassLoaderAware){ ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader()); } if(bean instanceof BeanFactoryAware){ ((BeanFactoryAware) bean).setBeanFactory(AbstactAutowire CapableBeanFactory.this); } } }
3.2 处理器的应用(BeanPostProcessor接口):调用客户自定义初始化方法前以及调用自定义初始化方法后分别会调用BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,使用户可以根据自己的业务需求进行响应的处理。3.3 激活自定义的init方法(init-method & 自定义实现InitializingBean接口)
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinetion mbd){ if(System.getSecurityManager() != null){ AccessController.doPrivileged(new PrivilegedAction<Object>(){ @Override public Object run() { invokeAwareMethods(beanName,bean); return null; } }); } else{ //对特殊的bean处理:Aware, BeanClassLoaderAware, BeanFactoryAware invokeAwareMethods(beanName,bean); } Object wrappedBean = bean; if(mbd == null !! !mbd.isSynthetic()){ wrappedBean = applyBeanPostProcessorsBeforeInitialization(wappedBean,beanName); } try{ invokeInitMethods(beanName, wappedBean, mbd); } catch(Throwable ex){ throw new BeanCreationException((mbd != null ? mbd.getResourceDescription():null),beanName,"Invocation of init method failed",ex); } if(mbd == null || !mbd.isSynthetic()){ wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wappedBean; }
4) 使用Bean。 驻留在应用的上下文中,直到该应用上下文被销毁。5) 销毁(destory-mthod & 实现DisposableBean接口)
Or represent like this:
1. Bean的构造
2. 调用setXXX()方法设置Bean的属性
3. 调用BeanNameAware的setBeanName()
4. 调用BeanFactoryAware的setBeanFactory()方法
5. 调用BeanPostProcessor的postProcessBeforeInitialization()方法
6. 调用InitializingBean的afterPropertiesSet()方法
7. 调用自定义的初始化方法
8. 调用BeanPostProcessor类的postProcessAfterInitialization()方法
9. 调用DisposableBean的destroy()方法
10. 调用自定义的销毁方法
<property name="xxx"><null/></property>
有助于减少甚至消除配置<property>和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。<context:annotation-config/>与之对应的是:自动检测(autodiscovery),比自动装配更近了一步,让Spring能够自动识别哪些类需要被配置成SpringBean,从而减少对<bean>元素的使用。<context:component-scan>
Spring容器默认禁用注解装配。最简单的开启方式<context:annotation-config/>。Spring支持的几种不同的用于自动装配的注解:
@Autowired具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性或参数中,自动装配就会失败,抛出NoSuchBeanDefinitionException.属性不一定非要装配,null值也是可以接受的。在这种场景下可以通过设置@Autowired的required属性为false来配置自动装配是可选的,如:
@Autowired(required=false) private Object obj;
注意required属性可以用于@Autowired注解所使用的任意地方。但是当使用构造器装配时,只有一个构造器可以将@Autowired的required属性设置为true。其他使用@Autowired注解所标注的构造器只能将required属性设置为false。此外,当使用@Autowired标注多个构造器时,Spring就会从所有满足装配条件的构造器中选择入参最多的那个构造器。可以使用@Qualifier明确指定要装配的Bean.如下:
@Autowired @Qualifier("objName") private Object obj;
@Target({ElementType.FIELF, ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @Interface SpecialQualifier{}
此时,可以通过自定义的@SpecialQualifier注解来代替@Qualifier来标注,也可以和@Autowired一起使用:
@Autowired @SpecialQualifier private Object obj;
此时,Spring会把自动装配的范围缩小到被@SpecialQualifier标注的Bean中。如果被@SpecialQualifier标注的Bean有多个,我们还可以通过自定义的另一个限定器@SpecialQualifier2来进一步缩小范围。
Spring的@Autowired注解是减少Spring XML配置的一种方式。但是它的类会映入对Spring的特定依赖(即使依赖只是一个注解)。
和@Autowired注解一样,@Inject可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性。因此@Inject注解所标注的依赖关系必须存在,如果不存在,则会抛出异常。
相对于@Autowired对应的Qualifier,@Inject所对应的是@Named注解。
@Inject @Named("objName") private Object obj;
语法形式在#{}中使用表达式,如:
<property name="count" value="#{5}"/>
@Value是一个新的装配注解,可以让我们使用注解装配String类型的值和基本类型的值,如int, boolean。我们可以通过@Value直接标注某个属性,方法或者方法参数,并传入一个String类型的表达式来装配属性,如:
@Value("Eruption") private String song;
@Value可以配合SpEL表达式一起使用,譬如有些情况下需要读取properties文件中的内容,可以使用:
@Value("#{configProperties['ora_driver']}")
详细可以参考 Spring+Mybatis多数据源配置(三)——Spring如何获取Properties文件的信息
<context:component-scan>元素除了完成与<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean.<context:component-scan>元素会扫描指定的包和其所有子包,如下:
<context:component-scan base-package="com.zzh.dao" />
默认情况下,查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下:
- @Component:通用的构造型注解,标志此类为Spring组件
- @Controller:标识将该类定义为SpringMVC controller
- @Repository:标识将该类定义为数据仓库
- @Service:标识将该类定义为服务
以@Component为例:
@Component public class Guitar implements Intrument{}
这里@Component会自动注册Guitar 为Spring Bean,并设置默认的Bean的Id为guitar,首字母大写变小写。注意如果第一个和第二个字母都是大写,默认的Bean的id会有特殊处理。也可以指定Bean的Id如:
@Component("guitarOne") public class Guitar implements Intrument{}
面向切面的编程AOP,是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。
AOP的核心是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在SpringAOP中,切面通过带有@Aspect注解的类实现。
关注点是应用中的一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。
横切关注点一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。
连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行Spring AOP的位置。
切点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。
引入运行我们在已存在的类中添加新的方法和属性。
通知是个在方法执行前后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码
Spring切面可以应用五种类型的通知:
before:前置通知,在一个方法执行前被调用。@Before
after: 在方法执行之后调用的通知,无论方法执行是否成功。@After
after-returning: 仅当方法成功完成后执行的通知。@AfterReturning
after-throwing: 在方法抛出异常退出时执行的通知。@AfterThrowing
around: 在方法执行之前和之后调用的通知。@Around
编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
如果在应用程序中直接使用JDBC来进行持久化,譬如博主采用的是Mybatis,DataSourceTransactionManager会为你处理事务边界。譬如:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="zzh" /> <property name="password" value="zzh" /> <property name="validationQuery" value="SELECT 1"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
如果你的事务需要跨多个事务资源(例如:两个或多个数据库;或者如 Sping+ActiveMQ整合 需要将ActiveMQ和数据库的事务整合起来),就需要使用JtaTransactionManager:
<bean id="jtaTransactionManager"class="org.springframework.transaction.jta.JtaTransactionManager"/>
JtaTransactionManager将事务管理的职责委托给了一个JTA的实现。JTA规定了应用程序与一个或多个数据源之间协调事务的标准API。transactionManagerName属性指明了要在JNDI上查找的JTA事务管理器。JtaTransactionManager将事务管理的职责委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象。通过UserTransaction.commit()方法来提交事务。类似地,如果事务失败,UserTransaction的rollback()方法将会被调用。
尽管Spring提供了多种声明式事务的机制,但是所有的方式都依赖这五个参数来控制如何管理事务策略。因此,如果要在Spring中声明事务策略,就要理解这些参数。(@Transactional)
如果事务只进行读取的动作,则可以利用底层数据库在只读操作时发生的一些最佳化动作,由于这个动作利用到数据库在只读的事务操作最佳化,因而必须在事务中才有效,也就是说要搭配传播行为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED来设置。
有的事务操作可能延续很长一段的时间,事务本身可能关联到数据表的锁定,因而长时间的事务操作会有效率上的问题,对于过长的事务操作,考虑Roll back事务并要求重新操作,而不是无限时的等待事务完成。 可以设置事务超时期间,计时是从事务开始时,所以这个设置必须搭配传播行为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED来设置。
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice>
核心架构的具体流程:
SpringMVC的核心是DispatcherServlet,这个Servlet充当SpringMVC的前端控制器。与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml文件中进行配置。
<servlet> <servlet-name>viewspace</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet>
默认情况下,DispatcherServlet在加载时会从一个基于这个Servlet名字的XML文件中加载Spring应用上下文。因为servlet的名字是viewspace,所以配置文件的名称为viewspace-servlet.xml。接下来,必须申明DispatcherServlet处理那些URL:
<servlet-mapping> <servlet-name>viewspace</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
通过将DispatcherServlet映射到/,声明了它会作为默认的servlet并且会处理所有的请求,包括对静态资源的请求。可以配置:
<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926" /> <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926" /> <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926" />
处理静态资源。
Spring自带了多个处理器映射实现:
<mvc:annotation-driven /> <bean id="defaultAnnotationHandlerMapping" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> </bean>
在构建控制器的时候,我们还需要使用注解将请求参数绑定到控制器的方法参数上进行校验以及信息转换。提供注解驱动的特性。
<bean id="annotationMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
在SpringMVC中大龄使用了约定优于配置的开发模式。InternalResourceViewResolver就是一个面向约定的元素。它将逻辑视图名称解析为View对象,而该对象将渲染的任务委托给Web应用程序上下文中的一个模板。
<!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
当DispatcherServlet要求InternalResourceViewResolver解析视图的时候,它将获取一个逻辑视图名称,添加”/WEB-INF/jsp/”前缀和”.jsp”后缀。等待的结果就是渲染输出的JSP路径。在内部,InternalResourceViewResolver接下来会将这个路径传递给View对象,View对象将请求传递给JSP.
持续更新~~~请保留转载连接~
参考文献
1. 《Sping In Action》 Craig Walls.
2. 69道Spring面试题和答案
3. Sping+ActiveMQ整合