分层 ,轻量级开源框架,以反转控制(Inverse of Control,IoC)和面向切面编程(Aspect Oriented Programming,AOP)为内核,提供了展现层Spring MVC、持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,整合第三方框架和类库
1、IoC方便解耦,简化开发
通过Spring提供的IoC容器,将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。如不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,更加专注于上层应用;通过框架或IoC容器在对象生成或初始化时注入数据或所依赖对象的引用并管理;可递归;亦称DI(依赖倒置)
将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建、拼接、管理、获取等工作
BeanFactory接口是Spring框架的核心接口,BeanFactory的实现和高级形态ApplicationContext应用上下文;Context模块构建于核心模块之上,扩展了BeanFactory的功能,添加了i18n国际化、Bean生命周期控制、框架事件体系、资源加载透明化等多项功能,还提供了许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问等。ApplicationContext是Context模块的核心接口
ClassPathResource获取配置文件资源(不同来源对应不同实现类),封装为Resource类型后传给读取器的构造器;getBean():通过名字检索容器中管理的Bean;带参数则检索时类型检查
2、AOP编程
面向切面编程应对许多不容易用传统面向对象编程(OOP)实现的功能;是进行横切逻辑编程的思想;基于JDK动态代理或cglib(一个代码类库)动态代理,前者的被代理类必须实现一个接口;运行期实现;集成静态代理AspectJ(编译期实现,性能好),声明式事务处理;代理对象,拦截器,通知,权限,缓存,错误处理,调试,记录跟踪,持久化,同步,事务,等
3、声明式事务的支持
从编程式事务中解脱,声明式事务灵活地进行事务管理,提高开发效率和质量
4、方便程序测试
5、spring入口:listener;监听器为
6、JDBC/ORM数据访问:提供常用的“对象/关系”映射APIs如Hibernate/MyBatis;封装JDBC如JdbcTemplate模板类(提供数据库操作方法),消除样板式代码
7、Web MVC:springmvc;关联远程调用
=============================================================================================
1、持久层:从数据库表中加载数据并实例化领域对象,或将领域对象持久化到数据表中
2、传统的JDBC API:太底层,即使用户执行一条最简单的数据查询操作,都必须执行如下的过程:获取连接→创建Statement→执行数据操作→获取结果→关闭Statement→关闭结果集→关闭连接,除此之外还需要进行异常处理的操作
3、Spring JDBC:薄层的封装,通过一个模板类org.springframework. jdbc.core.JdbcTemplate封装样板式的代码,用户通过模板类就可以轻松地完成大部分数据访问的操作
4、传统EJB:厚重,编写满足EJB容器规范的EJB组件才能获取服务
5、POJO或JavaBean:最小侵入性(应用开发对接口而非类编程);通过依赖注入和面向接口实现松耦合;基于切面和惯例进行声明式编程;通过切面和模板减少样板式代码
================================================================================================
动态代理:接口与实现类不直接关联,在运行时实现动态关联;基于接口的JDK动态代理:实现InvocationHandler接口并重写invoke();基于类的CGLIB动态代理
bean装载后的生命周期:
实例化bean
将值和bean的引用注入到bean的属性
根据bean实现的接口调用不同方法(依次)
BeanNameAware:setBeanName(beanId)
BeanFactoryAware:setBeanFactory(beanFactory)
ApplicatonContextAware:setApplicationContext(applicationContext)
…
bean已就绪,驻留在上下文中供程序使用(工作)
(如果实现DisposableBean)调用destory()和自定义销毁方法
@RequestScope,@SessionScope,@ApplicationScope:定义Bean的生命周期(Bean跟随请求、会话和应用程序的生命周期来创建)
singleton | spring默认单例(无论注入多少次都是同一个实例);使用ThreadLocal保证线程安全;类是易变、会保存状态的则不应为单例 |
prototype原型 | 每次注入或上下文获取时,都会新生成一个实例(singleton=”false”时) |
request | 每个 http请求都创建一个实例(web环境) |
session | 同一个session共享一个实例,不同的session创建不同实例(web环境) |
request和session作用域的bean有proxyMode属性,即设置代理并延迟加载(当此bean注入到一个单例bean时,单例已经初始化,但此bean还不存在;委托相同功能的代理bean解析和延迟加载,真正调用是才使用此bean);当bean为接口时,值ScopedProxyMode.INTERFACES;当bean为类时,需要使用CGLIB生成代理类,值ScopedProxyMode.TARGET_CLASS;它对应aop命名空间中的标签
不需要在启动时立即加载bean到容器中,可延迟加载(即等到调用getBean()时初始化):
bean其他属性:
init-method,destroy-method的值为方法名设置启动和关闭时初始方法和销毁方法;基于XML配置的,在getBean()时默认调用了init-method,手动context.destroy()/close();基于自动装配的,方法上使用@PostConstruct和@PreDestroy
factory-method:静态工厂方法,方法中业务一般为new新对象
factory-bean:实例/动态工厂方法,即先构建方法所在类/工厂,由工厂生产方法
1、XML显式配置,创建XML文件并以
前几行引入库/模式用来管理xml,如使用标签元素则需要spring-bean模式
setter()参数注入依赖的bean或注入自身field :
可换方案:引入p-命名空间模式,然后做bean标签的属性
构造器参数传入依赖的bean或注入自身field:
可换方案:使用C命名空间,c-作为bean标签的属性;使用方法同上
当参数是集合时子标签
组合:
使用类ClassPathXmlApplicationContext(参数为xml路径)获取context;
2、java显式配置:更解耦和无侵入,类型安全,重构友好
创建被@Configuration修饰的配置类(BeanConfig)
在返回bean对象的方法上使用@Bean表明该对象要声明为上下文中的bean;属性name或重命名方法名为bean命名(默认beanId首字母小写);应用:配合条件如随机数产生并使用各种bean;参数为类.class或配置文件的classpath
当此bean需要依赖其他bean时:
方式1:构造器参数为依赖的bean对象(由对应声明在容器中的bean的@Bean方法返回即去寻找产生bean的地方并使用而不是产生bean;因为默认单例,spring会拦截此方法,直接返回bean)
方式2:依赖的bean直接作方法参数传入,方法体中可使用构造器注入和setter()注入
组合:@Import(其他配置类列表.class);@ImportResource(“classpath:配置文件.xml”)
3、bean发现机制(隐式)和自动装配(推荐;而想要装配第三方库中的bean时只能依靠显式)
组件扫描:自动发现上下文中的bean
@Component声明类是一个bean/组件(可具体为@Bean,@Service,@Controller等);属性给bean命名(spring默认给所有bean的ID为类名首字母小写);可换为@Named(来自javaDI规范)
@ComponentScan启用bean发现/扫描(默认不开启;默认扫描基础包为它修饰的类的包及子包;参数可以指定扫描包);属性value或basePackages以字符串形式(类型不安全)指定扫描基础包(数组),或basePackageClasses(如={A.class,B.class})以类/接口形式(安全)
一般主类与扫描装配bean需要解耦,所以新建类NewClass并标注@ComponentScan和@Configuration,主类中调用即可:ApplicationContext context=new AnnotationConfigApplicationContext(NewClass.class/或配置文件的classpath);如此,所有满足的bean都可从context中获取:context.getBean(BeanName.class),重载参数类型可以为类id字符串即
自动装配/注入/满足依赖
@Autowired(按类型,spring提供的注解)/@Resource(按名字匹配,java提供的注解)在构造器或者setter()的参数(其实可用在任何方法上)需要引用其他bean时;required属性为false设置bean暂时处于未装配状态(否则若没有匹配的bean,上下文在创建时抛异常;或有多个满足的bean时抛出异常),而后进行null检查;可换为@Inject(来自javaDI规范)
自动装配的模式:no默认不自动,手动ref;byName通过参数名;byType通过参数的类型,多个bean符合则抛出异常;constructor通过构造器的参数的类型,必须有bean符合;autodetect如果有构造器,则用constructor,否则byType;
不能装配原生数据类型;模糊装配即没有自定义装配明确
用于构造器上比用于属性上装配效率高,还可用于setter()上或任意有类关联/级联的方法上;有继承或实现的,@Component标注在实现类上,而@Autowired则通常用于接口或父类。所以可能出现红色下划线(但项目依然可运行),表名装配的bean有歧义,不唯一,NoUniqueBeanDefinitionException;可对bean设置为首选@Primary(有局限,在设计时不确实谁是首选);或者@Autowired用于具体实现类;或者对bean标注@Qualifier,参数为bean别名(等同于为@Component指定id参数值),然后在@Autowired下使用@Qualifier(别名/id);不指定id则使用bean默认id即类名首字母小写;JDK自带的注解@Resource(name="id")等同于@Autowired和@Qualifier的作用;基于java显式配置文件的装配,这些注解应用在对应的@Bean下;@Qualifier对应基于XML文件中
复杂类型数组、集合List/Map/Set,都有对应标签
运行时值注入:改善以上(直接value=””)硬编码(通常针对field注入)
1、属性占位符:配置属性源文件,内容一般为key=value列表;它被spring加载到Environment
@PropertySource(“classpath:….properties”)引用属性源
方式1:类中@Autowired注入Environment后使用其方法getProperty(key[,defaultValue])获取value(重载参数还可将String型转为其他类型,略),传给参数(不能传到xml配置中);getRequiredProperty()在文件中没有属性定义时IllegalStateException;boolean containsProperty();getPropertyAsClass()
方式2:占位符${key};在每一个参数前@Value(“${key}”),或xml配置中如c:_name=”${name}”;使用占位符需要配置类PropertySourcePlaceholderConfigurer或配置
2、springEL表达式:“#{java代码表达式}”
一般注解用于public方法(如@Transactional用于其他可见度的方法,不会报错但事务设置无效);spring一般对运行时异常进行事务回滚,而checked异常不回滚
依赖检查:注入时判断属性的数据类型是否匹配即能否注入成功;
none:默认,无检查;simple:基本数据类型和集合类型;objects:检查复合类型;all:全检查
使用@Required的依赖检查:自由配置针对特定属性;两种方式
包含
注册
横切关注点:散布于应用中的功能(日志、安全和事务管理)
切面Advisor:将横切关注点抽象和模块化
通知advice:切面的工作内容(是什么及何时用)
基于动态代理,入口java.lang.proxy
@Before:方法(即属性指定的切点)执行前执行;对应
@After:方法之后一定执行;对应
@AfterReturning:方法成功返回后才执行;对应
@AfterThrowing:抛出异常时执行;对应
@Around:较复杂,可视为通知方法(由这些注解标注的方法)中实现了前置通知和后置通知;此通知方法必有参数proceedingJoinPoint,在方法体中任意位置通过它的proceed()方法来调用被通知方法(切点),即控制转移,执行完后返回继续执行后边代码;对应
连接点(仅方法级别):能够应用通知的点;
切点pointcut:判断在何处使用及是否织入后的具体连接点
织入:把切面应用到目标对象(的编译期如AspectJ、类加载期、运行期如AOP)并创建代理对象;基于动态代理(代理类包裹了目标对象);限于方法拦截
1、编写advice:
public class Advice {
//前置通知
public void beforeAdvice(){
System.out.println("Before");
}
//无论是否异常,一定调用此通知
public void afterAdvie(){
System.out.println("After");
}
//出现异常后不再调用此通知
public void afterReturnAdvie(){
System.out.println("AfterReturn");
}
//环绕通知:固定写法
public Object aroundAdvie(ProceedingJoinPoint point){
System.out.println("Around-Before");
Object proceed=null;
try {
proceed = point.proceed();
}
catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("Around-After");
return proceed;
}
//异常通知:出现异常才调用
public void exceptionAdvie(){
System.out.println("Exeption");
}
}
2、java显式配置:更解耦和无侵入,类型安全,重构友好
创建被@Configuration修饰的配置类(BeanConfig)
在返回bean对象的方法上使用@Bean表明该对象要声明为上下文中的bean;属性name或重命名方法名为bean命名(默认beanId首字母小写);应用:配合条件如随机数产生并使用各种bean;参数为类.class或配置文件的classpath
当此bean需要依赖其他bean时:
方式1:构造器参数为依赖的bean对象(由对应声明在容器中的bean的@Bean方法返回即去寻找产生bean的地方并使用而不是产生bean;因为默认单例,spring会拦截此方法,直接返回bean)
方式2:依赖的bean直接作方法参数传入,方法体中可使用构造器注入和setter()注入
组合:@Import(其他配置类列表.class);@ImportResource(“classpath:配置文件.xml”)
3、bean发现机制(隐式)和自动装配(推荐;而想要装配第三方库中的bean时只能依靠显式)
组件扫描:自动发现上下文中的bean
@Component声明类是一个bean/组件(可具体为@Bean,@Service,@Controller等);属性给bean命名(spring默认给所有bean的ID为类名首字母小写);可换为@Named(来自javaDI规范)
@ComponentScan启用bean发现/扫描(默认不开启;默认扫描基础包为它修饰的类的包及子包;参数可以指定扫描包);属性value或basePackages以字符串形式(类型不安全)指定扫描基础包(数组),或basePackageClasses(如={A.class,B.class})以类/接口形式(安全)
一般主类与扫描装配bean需要解耦,所以新建类NewClass并标注@ComponentScan和@Configuration,主类中调用即可:ApplicationContext context=new AnnotationConfigApplicationContext(NewClass.class/或配置文件的classpath);如此,所有满足的bean都可从context中获取:context.getBean(BeanName.class),重载参数类型可以为类id字符串即
自动装配/注入/满足依赖
@Autowired(按类型,spring提供的注解)/@Resource(按名字匹配,java提供的注解)在构造器或者setter()的参数(其实可用在任何方法上)需要引用其他bean时;required属性为false设置bean暂时处于未装配状态(否则若没有匹配的bean,上下文在创建时抛异常;或有多个满足的bean时抛出异常),而后进行null检查;可换为@Inject(来自javaDI规范)
自动装配的模式:no默认不自动,手动ref;byName通过参数名;byType通过参数的类型,多个bean符合则抛出异常;constructor通过构造器的参数的类型,必须有bean符合;autodetect如果有构造器,则用constructor,否则byType;
不能装配原生数据类型;模糊装配即没有自定义装配明确
用于构造器上比用于属性上装配效率高,还可用于setter()上或任意有类关联/级联的方法上;有继承或实现的,@Component标注在实现类上,而@Autowired则通常用于接口或父类。所以可能出现红色下划线(但项目依然可运行),表名装配的bean有歧义,不唯一,NoUniqueBeanDefinitionException;可对bean设置为首选@Primary(有局限,在设计时不确实谁是首选);或者@Autowired用于具体实现类;或者对bean标注@Qualifier,参数为bean别名(等同于为@Component指定id参数值),然后在@Autowired下使用@Qualifier(别名/id);不指定id则使用bean默认id即类名首字母小写;JDK自带的注解@Resource(name="id")等同于@Autowired和@Qualifier的作用;基于java显式配置文件的装配,这些注解应用在对应的@Bean下;@Qualifier对应基于XML文件中
复杂类型数组、集合List/Map/Set,都有对应标签
运行时值注入:改善以上(直接value=””)硬编码(通常针对field注入)
1、属性占位符:配置属性源文件,内容一般为key=value列表;它被spring加载到Environment
@PropertySource(“classpath:….properties”)引用属性源
方式1:类中@Autowired注入Environment后使用其方法getProperty(key[,defaultValue])获取value(重载参数还可将String型转为其他类型,略),传给参数(不能传到xml配置中);getRequiredProperty()在文件中没有属性定义时IllegalStateException;boolean containsProperty();getPropertyAsClass()
方式2:占位符${key};在每一个参数前@Value(“${key}”),或xml配置中如c:_name=”${name}”;使用占位符需要配置类PropertySourcePlaceholderConfigurer或配置
2、springEL表达式:“#{java代码表达式}”
一般注解用于public方法(如@Transactional用于其他可见度的方法,不会报错但事务设置无效);spring一般对运行时异常进行事务回滚,而checked异常不回滚
依赖检查:注入时判断属性的数据类型是否匹配即能否注入成功;
none:默认,无检查;simple:基本数据类型和集合类型;objects:检查复合类型;all:全检查
使用@Required的依赖检查:自由配置针对特定属性;两种方式
包含
注册
横切关注点:散布于应用中的功能(日志、安全和事务管理)
切面Advisor:将横切关注点抽象和模块化
通知advice:切面的工作内容(是什么及何时用)
基于动态代理,入口java.lang.proxy
@Before:方法(即属性指定的切点)执行前执行;对应
@After:方法之后一定执行;对应
@AfterReturning:方法成功返回后才执行;对应
@AfterThrowing:抛出异常时执行;对应
@Around:较复杂,可视为通知方法(由这些注解标注的方法)中实现了前置通知和后置通知;此通知方法必有参数proceedingJoinPoint,在方法体中任意位置通过它的proceed()方法来调用被通知方法(切点),即控制转移,执行完后返回继续执行后边代码;对应
连接点(仅方法级别):能够应用通知的点;
切点pointcut:判断在何处使用及是否织入后的具体连接点
织入:把切面应用到目标对象(的编译期如AspectJ、类加载期、运行期如AOP)并创建代理对象;基于动态代理(代理类包裹了目标对象);限于方法拦截
1、编写advice:
2、编写连接点/切点:advice应用在的地方处
声明一个有f方法的接口A
spring借助AspectJ的切点表达式语言(指示器)来定义切点;常用execution()匹配是连接点的方法,如execution(* 包.A.f(..));*表示不关心返回类型,..表示任意参数;!(not),||(or),&&(and)用于连接指示器;bean()指示器参数为beanId或bean名称,限制切点匹配特定的bean;args(参数名)指示器控制切点方法中的参数到切面里通知方法的参数转移
3、织入
注解方式:@After,@AfterReturning,@AfaterThrowing,@Around,@Before等声明方法为通知,属性为切点(即在切点前/后调用通知);@Pointcut()提取连接点为切点,形式是一个方法(方法名为切点命名),如@Pointcut(“execution(** 完全限定名.f(..))”) public void fName(){};然后切点应用和重用如@Before(“fName()”),@After(“fName()”);对应
启用切面/代理:
@EnableAspectJAutoProxy标注配置类启用自动代理功能;对应
以上是用切面对方法进行功能扩充;还可利用切面对对象扩充方法,@DeclareParents,略
-> getBean(代理类)
以上对所有方法都通知了;可指定切点(不推荐使用配置,而使用AspectJ支持即@对应的通知类型注解):名称匹配或者正则表达式匹配
开启注解支持
由于在每个方法前写@Before(...)...等比较繁琐,将他们抽离出来形成切点方法:
@Pointcut(…)
public void pointcut(){…}
然后@Before(“类.pointcut()”)
特殊切面之事务管理/拦截器:封装了在数据库层面实现的事务
声明式事务:方法级别
编程式事务:重复且复杂,Bean上使用注解@Transactional开启支持
顶层事务管理器:PlatformTransactionManager;定义方法getTransaction()开启事务,rollback()回滚,commit()提交
消除JDBC的自动提交(不能回滚):connection.setAutoCommit(false);所有sql执行完后commit();异常时rollback()
事务名称空间支持:spring-tx.xsd;配置事务管理器DataSourceTransactionManager管理数据源;设置事务通知advice:
注解方式:
springAOP拦截器只能拦截spring管理Bean的访问service
@EnableTransactionManagement:开启事务管理
@Transactional:开启事务;它的属性设置事务的传播性(由TransactionDefinition接口中的常量定义),隔离性,回滚条件;默认与数据库一致
@TransactionalEventListener:配置事务的回调方法
特殊切面之日志记录:
配置log4j.properties并设置日志级别为info,将日志输入到控制台和指定文件,;编写切面类LogAspect,通过连接点类ProceedingJoinPoint获取目标类名和方法,调用LOG.info()记录进入方法时日志信息,peoceed()被try-catch,且LOG.error记录异常信息;配置aop-config
数据缓存
启用缓存支持:@EnableCaching标注配置类;对应
配置类中配置缓存管理器bean(@Bean标注返回这些对象的方法):
ConcurrentMapCacheManager:基于内存(生命周期随应用)
SimpleCacheManage:
NoOpCacheManager:
CompositeCacheManager:
EhCacheCacheManager:
RedisCacheManager:SpringDataRedis提供
CompostiteCacheManager构建List,迭代使用多个管理器
缓存设置:
不同缓存方式对应不同标签和配置;
使用缓存:
@Cacheable:标注的方法调用之前,从缓存查找方法返回值;无则方法调用并将值存储到缓存;常用于频繁请求如findOne();
@CachePut:不检查缓存,方法始终被调用,值存放到缓存;
共有属性:cache;value缓存名;condition是否将缓存应用到方法调用上,springel表达式,不应用则不查找也不缓存;key通过key查找,如通过id查找的方法,key为传递到方法的id参数,springel表达式自定义key略;method;unless是否将返回值存放缓存(true为不放),之前会从缓存查找
@CacheEvict:在缓存中清除值;常用于remove();
@Caching:缓存注解的组合;
可用于类上(对所有方法作用)
以上标签被包裹于
数据访问对象DAO实现/委托Repository接口;spring提供大量继承自DataAccessException的数据库操作异常体系;spring根据数据的固定和可变,将类分为模板和回调;模板类如JdbcTemplate处理数据访问的固定部分(事务管理,资源管理和异常处理);回调实现数据访问(语句,参数绑定,处理结果集)
JNDI数据源:
此种配置让数据源在应用程序之外进行处理,仅在访问时查找即可;JndiObjectFactoryBean实现查找dataSource;对应jee命名空间
JDBC数据源(每次请求都会新建连接,费性能):spring自带的DriverManagerDataSource/SimpleDriverDataSource/SingleConnectionDataSource(单线程)
连接池数据源(推荐):DBCP/c3p0/JDBC的
嵌入式数据库
@Profile()运行时选择数据源;属性值”development”使用开发数据源;”qa”QA数据源;”production”生产环境数据源;对应