spring基本知识概览
1.历史及目标
Rod Johson在2002年《Expert one to one J2EE design and development》,2004年他又推出了一部堪称经典的力作《Expertone-to-one J2EE Development without EJB》,致力于挑战javaEE及EJB的笨重臃肿,将困难的开发任务进行简化,spring不断发展,新增了很多新特性,比如支持REST风格的springMVC,增强安全性的spring security,缓存,消息的支持,spring Boot,spring Cloud等
2.spring的核心
依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)
3.spring简化开发的四个策略
- 基于pojo的轻量级和最小侵入性编程(不强制实现spring接口或继承spring类)
- 通过DI和面向接口实现低耦合
- 基于切面和惯例进行声明式编程
- 通过切面和模板(Template)减少样板式代码
tips:
- 对象通过接口表明依赖关系,就能在对象本身不知情的情况下,用不同实现进行替换,只有spring通过它的配置,能了解这些组成部分是如何装配(wiring)起来的
- 如果由一个对象自己决定做具体实现,那么会导致紧耦合和难以扩展,不如让依赖的对象自己注入进来,由它自己决定要做什么
- spring通过应用上下文(Application Context)装载bean的定义并组装,不同的上下文实现的区别在于如何加载配置
- aop允许你把遍布应用各处的功能分离出来形成可重用的组件,如日志、事务、安全
- Q:我一个接口只有一般只有一种实现,为什么要使用接口?即使也多个实现也不需要进行实现的替换,为什么还要使用spring的DI呢?
A:之所以使用接口正是“面向接口编程”这一关注点,对象之间只需要知道规范,不必关心实现;使用spring的DI不仅可以进行解耦,并且它利用容器的单例管理帮助你实现了单例,减少了很多创建销毁对象的性能损耗;最后,使用注入,可以更好的使用AOP,用代理类替代真正的实现类。
4.spring容器(container)
- spring应用中,对象由spring容器创建和装配,并存在容器中
- spring有两种类型的容器实现:bean Factory和ApplicationContext,bean工厂往往太低级,应用上下文更受欢迎
-
- AnnotationConfigApplicationContext--java配置类加载
- AnnotationConfigWebApplicationContext--java配置类加载web应用上下文,
- ClasspathXmlApplicationContext--类路径下的xml加载,
- FileSystemXmlapplicationcontext--文件系统下的xml加载,
- XmlWebApplicationContext--web应用下的xml加载
5.spring模块
6.spring boot
以spring的视角,致力于简化spring本身,大量依赖自动配置技术,消除大部分甚至全部spring配置
7.装配bean的三种方式:
- xml显式配置(维护已有旧配置时使用,最次)
- java显式配置(配置不是自己维护的源码,而你需要为这些代码配置bean,次之)
- 隐式的bean发现机制和自动装配(最优)
8.自动化装配bean:
- 组件扫描(component scanning):spring自动发现上下文中创建的bean
- 自动装配(autowiring):spring自动满足bean之间的依赖
使用方法:
-
组件上添加Component注解
@Component public class BraveKnight implements Knight {
- 在JavaConfig类(通常会将它放入单独的包中)上启用组件扫描:
@Configuration @ComponentScan public class Config { }
- 使用junit4测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) public class Test { @Autowired private Knight knight; @org.junit.Test public void test1(){ assertNotNull(knight); } }
tips:
- bean的默认ID为类名的第一个字母变小写,使用Autowired注入成员变量时,如果接口只有一个实现,那么变量名称可以随便取都可以找到,当有两个及以上实现时,变量名称需取bean的ID才能找到
- 设置组件扫描包:
@ComponentScan(“com.xx”)
9.通过java代码装配bean
比起xml装配更强大、类型安全且对重构友好
@Bean public Knight knight(){ return new BraveKnight() ; }
bean的ID与方法名相同,依赖可以使用构造器注入(强依赖)和setter()方法(可选性依赖)注入
10.混合配置
@Import(ss.class) @ImportResource("xx.xml")
<bean class="ssConfig" /> <import resource="xx.xml">
11.多环境装配
@Profile
12.条件化的bean
@Conditional
13.表示首选bean
@Primary
14.bean的作用域
单例(Singleton):默认,在整个应用只创建bean的一个实例,对象无状态时使用
原型(Prototype):每次注入或者通过上下文获取的时候,都会创建一个新的bean实例,bean可变,有状态时使用
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
会话(Session):在Web应用中,每个会话创建一个bean
请求(Request):在Web应用中,每个请求创建一个bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode=ScopedProxyMode.INTERFACES)
tips:
- 在使用Session和Request作用域时,需设置proxyMode属性,考虑将Session或Request属性的bean注入在单例bean时,若系统启动创建单例bean时,此时会话作用域的bean还未存在;另外,系统中会存在多个Session或Request作用域的实例,我们希望单例bean中注入的恰好是当前Session或Request所对应的bean。因此,设置代理后,系统会在被依赖的地方注入一个代理,当代理被调用时,代理会将调用委托给真正的bean
- bean没有接口时,需使用CGlib生成基于类的代理
proxyMode=ScopedProxyMode.TARGET_CLASS
15.AOP术语
- 通知(Advice):描述切面要完成的工作及什么时候执行
前置通知(Before),后置通知(After),返回通知(After-returning),异常通知(After-throwing),环绕通知(Around) - 连接点(Join point):应用执行中能够插入切面的一个点
- 切点(Pointcut):匹配通知要织入的一个或多个连接点
- 切面(Aspect):通知和切点的结合(要做什么,何时,何处)
- 引入(Introduction):引入允许我们添加新方法或属性
- 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程
16.spring的AOP
- 织入:代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean,当代理拦截到方法调用时,在调用目标bean方法之前或之后,会执行切面逻辑
- 引入:当引入接口的方法被调用时,代理会将调用委托给实现了新接口的某个其他对象。实际上,一个bean的实现被拆分到了多个类中
- spring在运行时用到被代理的bean时,才会创建代理对象
- spring只支持方法连接点,AspectJ和JBoss,除了方法切点,还支持字段和构造器接入点
织入示例:
- 定义切面
@org.aspectj.lang.annotation.Aspect @Component public class Aspect { @Pointcut("execution(** springDemo.Knight.embarkOnQuest(..))") public void pointCut(){} @Before("pointCut()") public void before(){ System.out.println("before"); } @Around("pointCut()") public void around(ProceedingJoinPoint jp){ System.out.println("before"); try { jp.proceed(); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } System.err.println("end"); } }
- 在javaConfig中启用自动代理功能
@Configuration @ComponentScan @EnableAspectJAutoProxy public class Config { }
xml方式:<aop:aspjectj-autoproxy />
- 在切点前后即可看到通知生效
引入示例:
- 创建引入的接口及一个具体实现:
public interface ImportMethod { public void add(); }
public class ImportMethodImpl implements ImportMethod { @Override public void add() { System.out.println("add"); } }
- 切面定义:
@org.aspectj.lang.annotation.Aspect @Component public class Aspect { @DeclareParents(value="com.alibaba.springDemo.Knight+",defaultImpl=ImportMethodImpl.class) public ImportMethod importMethod; }
其中value指明了那种类型的bean要引入该接口,“+”表示所有子类型,
defaultImpl指定了为引入功能提供实现的具体类,
@DeclareParents注解了要引入的接口 - 使用
ImportMethod im = (ImportMethod)knight;//将bean转为新的接口类型 im.add();//执行引入方法