目录
3 BeanFactory和ApplicationContext的区别
1 Spring Bean的生命周期
从上图可以看到,Spring IoC容器对Bean的管理还是比较复杂的,Spring IoC容器在执行了初始化和依赖注入后,会执行一定的步骤来完成初始化,通过这些步骤我们就能自定义初始化,而在Spring IoC容器正常关闭的时候,它也会执行一定的步骤来关闭容器,释放资源。除需要了解整个生命周期的步骤外,还要知道这些生命周期的接口是针对什么而言的,首先介绍生命周期的步骤:
- 如果Bean实现了接口BeanNameAware的setBeanName方法,那么它就会调用这个方法。
- 如果Bean实现了接口BeanFactoryAware的setBeanFactory方法,那么它就会调用这个方法。
- 如果Bean实现了接口ApplicationContextAware的setApplicationContext方法,且Spring IoC容器也必须是一个ApplicationContext接口的实现类,那么才会调用这个方法,否则是不调用的。
- 如果Bean实现了接口BeanPostProcessor的postProcessBeforeInitialization方法,那么它就会调用这个方法。
- 如果Bean实现了接口InitializingBean的afterPropertiesSet方法,那么它就会调用这个方法。
- 如果Bean自定义了初始化方法,它就会调用已定义的初始化方法。
- 如果Bean实现了接口BeanPostProcessor的postProcessAfterInitialization方法,完成了这些调用,这个时候Bean就完成了初始化,那么Bean就生存在Spring IoC的容器中了,使用者就可以从中获取Bean的服务。
当服务器正常关闭,或者遇到其他关闭Spring IoC容器的事件,它就会调用对应的方法完成Bean的销毁,其步骤如下:
- 如果Bean实现了接口DisposableBean的destroy方法,那么就会调用它。
- 如果定义了自定义销毁方法,那么就会调用它。
2 面向切面编程的术语
2.1 切面(Aspect)
切面就是在一个怎么样的环境中工作。比如数据库的事务直接贯穿了整个代码层面,这就是一个切面,它可以定义后面需要介绍的各类通知、切点和引入等内容,然后Spring AOP会将其定义的内容织入到约定的流程中,在动态代理中可以把它理解成一个拦截器。
2.2 通知(Advice)
通知是切面开启后,切面的方法。它根据在代理对象真实方法调用前、后的顺序和逻辑区分。
- 前置通知(before):在动态代理反射原有对象方法或者执行环绕通知前执行的通知功能。
- 后置通知(after):在动态代理反射原有对象方法或者执行环绕通知后执行的通知功能。无论是否抛出异常,它都会被执行。
- 返回通知(afterReturning):在动态代理反射原有对象方法或者执行环绕通知后正常返回(无异常)执行的通知功能。
- 异常通知(afterThrowing):在动态代理反射原有对象方法或者执行环绕通知产生异常后执行的通知功能。
- 环绕通知(around):在动态代理中,它可以取代当前被拦截对象的方法,提供回调原有被拦截对象的方法。
2.3 引入(Introduction)
引入允许我们在现有的类里添加自定义的类和方法。
2.4 切点(Pointcut)
这是一个告诉Spring AOP在什么时候启动拦截并织入对应的流程,因为并不是所有的开发都需要启动AOP的,它往往通过正则表达式进行限定。
2.5 连接点(join point)
连接点对应的是具体需要拦截的东西,比如通过切点的正则表达式去判断哪些方法是连接点,从而织入对应的通知。
2.6 织入(Weaving)
织入是一个生成代理对象并将切面内容放入到流程中的过程。实际代理的方法分为静态代理和动态代理(详见笔者之前的文章《设计模式之代理模式》)。静态代理是在编译class文件时生成的代码逻辑,但是在Spring中并不使用这样的方式。一种是通过ClassLoader也就是在类加载的时候生成的代码逻辑,但是它在应用程序代码运行前就生成对应的逻辑。还有一种是在运行期动态生成代码的方式,这是Spring AOP所采用的方式,Spring是以JDK和CGLIB动态代理来生成代理对象的。
3 BeanFactory和ApplicationContext的区别
ApplicationContext接口继承BeanFactory接口,Spring核心工厂是BeanFactory,BeanFactory采取延迟加载,第一次getBean时才会初始化Bean,ApplicationContext是会在加载配置文件时初始化Bean。
ApplicationContext是对BeanFactory的扩展,它可以进行国际化处理、事件传递和bean自动装配以及各种不同应用层的Context实现。
开发中基本都在使用ApplicationContext,web项目使用WebApplicationContext,很少用到BeanFactory。
4 依赖注入的方式
4.1 构造器注入
public class Role {
private Long id;
private String roleName;
private String note;
public Role(String roleName, String note) {
this.roleName = roleName;
this.note = note;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
<bean id="role1" class="com.hys.spring.example2.pojo.Role">
<constructor-arg index="0" value="初级工程师" />
<constructor-arg index="1" value="基层开发人员" />
</bean>
4.2 Setter方法注入
<bean id="role2" class="com.hys.spring.example2.pojo.Role">
<property name="roleName" value="高级工程师" />
<property name="note" value="架构师" />
</bean>
5 Spring bean作用域
Spring框架支持以下五种bean的作用域:
5.1 singleton
bean在每个Spring IoC容器中只有一个实例。
5.2 prototype
一个bean的定义可以有多个实例。
5.3 request
每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
5.4 session
在一个HTTP Session中,一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效。
5.5 global-session
在一个全局的HTTP Session中,一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效。
6 在Spring中注入java集合
Spring提供以下几种集合的配置元素:
<list>类型用于注入一列值,允许有相同的值。
<set>类型用于注入一组值,不允许有相同的值。
<map>类型用于注入一组键值对,键和值都可以为任意类型。
<props>类型用于注入一组键值对,键和值都只能为String类型。
7 自动装配的不同方式
有五种自动装配的方式,用来指导Spring容器用自动装配方式进行依赖注入:
no:默认的方式是不进行自动装配,通过显示设置ref属性来进行装配。
byName:通过参数名自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byName,之后容器试图匹配,装配和该bean的属性具有相同名字的bean。
byType:通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配,装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误(@Autowired注解就是通过类型来进行匹配的,所以说如果有多个相同类型的Bean需要注入,启动项目会抛出异常。解决办法是和@Qualifier注解一起搭配使用)。
constructor:这个方式类似于byType,但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。
8 Spring事务的传播行为
REQUIRED:存在事务就融入该事务,不存在就创建事务
SUPPORTS:存在事务就融入该事务,不存在则不创建事务
MANDATORY:存在事务就融入该事务,不存在则抛异常
REQUIRES_NEW:总是创建新事务
NOT_SUPPORTED:存在事务则挂起,一直执行非事务操作
NEVER:总是执行非事务,如果当前存在事务则抛异常
NESTED:嵌入式事务
9 Spring IoC底层实现原理
Spring IoC底层是使用反射来实现的。IoC容器本身是一堆的工厂类结构,其中最顶层为BeanFactory,其他的类都是派生自它的。可以把IoC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用反射,根据配置文件中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
10 Spring启动流程
- 执行web.xml中的ContextLoaderListener上下文监听器,初始化IoC容器,加载Bean。
- 初始化DispatcherServlet(Spring MVC核心控制器),初始化HandlerMapping、HandlerAdapter。
11 AOP的实现方式
1.配置(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:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 声明一个业务类 -->
<bean id="userManager" class="com.spring.service.impl.UserManagerServiceImpl">
<property name="name" value="lixiaoxi"></property>
</bean>
<!-- 声明通知类 -->
<bean id="aspectBean" class="com.spring.aop.AopAspect" />
<aop:config>
<aop:aspect ref="aspectBean">
<aop:pointcut id="pointcut" expression="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))"/>
<aop:before method="doBefore" pointcut-ref="pointcut"/>
<aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:after method="doAfter" pointcut-ref="pointcut" />
<aop:around method="doAround" pointcut-ref="pointcut"/>
<aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
2.注解:
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Before;
@Aspect
@Component
public class RoleAspect {
@Before("execution(* com.hys.spring.example5.impl.RoleServiceImpl.printRole(..))")
public void before() {
System.out.println("before...");
}
@After("execution(* com.hys.spring.example5.impl.RoleServiceImpl.printRole(..))")
public void after() {
System.out.println("after...");
}
@AfterReturning("execution(* com.hys.spring.example5.impl.RoleServiceImpl.printRole(..))")
public void afterReturning() {
System.out.println("afterReturning...");
}
@AfterThrowing("execution(* com.hys.spring.example5.impl.RoleServiceImpl.printRole(..))")
public void afterThrowing() {
System.out.println("afterThrowing...");
}
}
12 Spring MVC请求流程
第一步:发起请求到前端控制器(DispatcherServlet)。
第二步:前端控制器请求HandlerMapping查找Handler,可以根据xml配置、注解进行查找。找到后处理器映射器HandlerMapping向前端控制器返回Handler。
第三步:前端控制器调用HandlerAdapter去执行Handler。Handler执行完成后给适配器返回ModelAndView。
第四步:HandlerAdapter向前端控制器返回ModelAndView。ModelAndView是spring mvc框架的一个底层对象,包括Model和View。
第五步:前端控制器请求ViewResolver进行视图解析,根据逻辑视图名解析成真正的视图(jsp)。注意,这一步取决于是否适用逻辑视图,如果是逻辑视图,那么视图解析器就会解析它,然后把模型渲染到视图中去,最后响应用户的请求;如果不是逻辑视图,则不会进行处理,而是直接通过视图渲染数据模型。视图解析器解析完成后向前端控制器返回View。
第六步:前端控制器进行视图渲染。视图渲染将模型数据(在ModelAndView对象中)填充到request域。之后前端控制器向用户响应结果。
13 MyBatis中“#”和“$”的区别
${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换。比如${driver}会被静态替换为com.mysql.jdbc.Driver。但是${}可能会导致SQL注入攻击,如果在输入时包含“ or '1'='1' ”这样的条件,可能会因为参数校验不足从而导致安全隐患。
#{}是sql的参数占位符,MyBatis会将sql中的#{}替换为“?”号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的“?”号占位符设置参数值,比如ps.setInt(0,parameterValue),#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()。
14 MyBatis缓存
MyBatis的缓存分为一级缓存和二级缓存,一级缓存放在SqlSession里面,默认就有。对于不同的SqlSession,对象是不能共享的;二级缓存是在SqlSessionFactory上的缓存,默认是关闭的。使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),在它的映射文件中配置<cache />即可。
15 MyBatis的生命周期
15.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder的作用在于创建SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder就失去了作用,所以它只能存在于创建SqlSessionFactory的方法中,而不要让其长期存在。
15.2 SqlSessionFactory
SqlSessionFactory的作用是创建SqlSession接口对象,它的生命周期存在于整个MyBatis的应用之中,所以等同于Mybatis的应用周期。
15.3 SqlSession
SqlSession的生命周期是在请求数据库处理事务的过程中,比如查询,插入等操作,其存活在一个业务请求中。处理完整个请求后,应该关闭这条连接,让它归还给SqlSessionFactory。
15.4 Mapper
Mapper是一个接口,它由SqlSession所创建,所以它的最大生命周期至多和SqlSession保持一致。