Spring
官方文档 https://spring.io/projects/spring-framework#learn
IOC
控制反转是一种通过描述并通过第三方去生产或获取特定对象的方式
IOC创建对象方式
通过无参构造方法来创建
通过有参构造方法来创建
配置
别名
Bean的配置
import
DI
构造器注入同上
setter注入
要求被注入的属性,必须有set方法,set方法的方法名由set + 属性首字母大写,如果属性是boolean类型,没有set方法,是 is
常量注入
Bean注入
数组注入
西游记 红楼梦 List注入
听歌 看电影 Map注入
set注入
LOL BOB Null注入
Properties注入
男 小明
拓展注入方式
p命名空间注入
导入约束:xmlns:p="http://www.springframework.org/schema/p"
c命名空间注入
导入约束:xmlns:c="http://www.springframework.org/schema/c"
Bean的作用域
- singleton 单例
- Prototype 原型
- Request
- Session
- global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
自动装配
按名称自动装配
通过set方法查找
按类型自动装配
需要同一类型的对象在Spring容器中唯一
@Autowired
按类型自动装配
public class User {
@Autowired
private Cat cat;
public Cat getCat() {
return cat;
}
}
此时配置文件内容
required
默认为true,@Autowired(required=false)说明对象可以为null
@Qualifier
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
@Qualifier不能单独使用
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
@Resource(name = "cat2")
private Cat cat;
@Autowired与@Resource异同:
- @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
- @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
- @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
注解开发
配置文件中引入context约束
Bean的实现
配置扫描哪些包
扫描过滤
>//包含
>//排除
过滤方式
annotation:注解
assignable:指定class或interface的全名
aspectj:AspectJ语法
regex:正则
如果配置了
那么
标签就可以不用再xml中配置了,因为前者包含了后者
给类加上注解
@Component
@Controller:web层
@Service:service层
@Repository:dao层
属性注入
@Component("user")
// 相当于配置文件中
public class User {
@Value("小明")
// 相当于配置文件中
public String name;
}
或者在set方法上添加@value("值")
作用域
@Controller("user")
@Scope("prototype")
public class User {
@Value("小明")
public String name;
}
基于Java类的纯注解配置
@Configuration//代表这是一个配置类
@Import(MyConfig2.class)//导入合并其他配置类,类似于配置文件中的 inculde 标签
@ComponentScan("com.pojo")//指定扫描的包
public class MyConfig {
@Bean(name="dog2")//通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!也可以手动指定名称
public Dog dog(){
return new Dog();
}
}
AOP
官方文档https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html#aop
相关术语
- Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在 Spring 中,这些点指的是方法,因为 Spring 只支持方法类型的连接点。 - Pointcut(切入点):
所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。 - Advice(通知/增强):
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 - Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或 Field。 - Target(目标对象):
代理的目标对象。 - Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。
Spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。 - Proxy(代理) :
一个类被 AOP 织入增强后,就产生一个结果代理类。 - Aspect(切面):
是切入点和通知(引介)的结合。
通知类型
通知类型 | 名称 | 说明 |
---|---|---|
前置通知 | Before advice | 连接点前执行,除非抛出异常,否则不能阻止方法的继续执行 |
后置通知(最终通知) | After (finally) advice | 连接点执行完成后执行,总会执行 |
正常返回通知 | After returning advice | 在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行 |
异常返回通知 | After throwing advice | 在连接点抛出异常后执行 |
环绕通知 | Around advice | 连接点前后执行 |
切入点表达式语法
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
* 表示任意一个
.. 表示多个
xml中连接子表达式用 and, or, not
注解中用 &&, ||, !
XML配置AOP
注解配置AOP
@Component("txManager")
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
开启注解AOP的支持
在 spring 配置文件中开启 spring 对注解 AOP 的支持
不使用 XML 的配置方式
@Configuration
@ComponentScan(basePackages="...")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
aop:aspectj-autoproxy:说明
通过aop命名空间的 声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被 隐藏起来了
有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为 时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
整合MyBatis(MyBatis-Spring)
官方文档:http://mybatis.org/spring/zh/index.html
Maven依赖
org.mybatis
mybatis-spring
2.0.2
版本对应
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
方式一
引入Spring配置文件的beans.xml
配置数据源替换mybatis的数据源
配置SqlSessionFactory,关联MyBatis
注册SqlSessionTemplate,关联SqlSessionFactory
增加Dao接口的实现类;私有化SqlSessionTemplate
public class UserDaoImpl implements UserMapper { //sqlSession不用我们自己创建了,Spring来管理 private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List
selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } } 注册bean实现
方式二:SqlSessionDaoSupport
官方文档http://mybatis.org/spring/zh/sqlsession.html#SqlSessionDaoSupport
声明式事务
官方文档https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/data-access.html#transaction-declarative
事务
事务四个属性ACID
原子性(atomicity)
事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
一致性(consistency)
一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
隔离性(isolation)
可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
持久性(durability)
事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
事务传播行为
Spring支持7种传播行为
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
事务隔离级别
spring中事务的隔离级别可以通过隔离属性指定
DEFAULT | 使用底层数据库的默认隔离级别,大部分数据库,默认隔离级别都是READ_COMMITED |
READ_COMMITED | 只允许事务读取已经被其他事务提交的更改,可以避免脏读,但不可重复读和幻读问题仍然可能出现 |
READ_UNCOMMITED | 允许事务读取未被其他事务提交的更改。脏读,不可重复读,幻读都可能会出现 |
REPEATABLE_READ | 确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但是幻读的问题依然存在 |
SERIALIZABLE | 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新,删除。所有的并发问题都能避免,但是性能比较低。 |
注意:事务的隔离级别需要底层数据库引擎的支持,而不是应用程序或者框架的支持
- Oracle支持2种事务隔离级别:READ_COMMITED,SERIALIZABLE
- MySQL支持4种事务隔离级别
基于XML
导入 aop 和 tx 两个名称空间
步骤:
配置事务管理器
配置事务的通知引用事务管理器
配置事务的属性
配置 AOP 切入点表达式
配置切入点表达式和事务通知的对应关系
基于注解
配置事务管理器并注入数据源
在业务层使用 @Transactional 注解
@Service("accountService") @Transactional(readOnly=true,propagation=Propagation.SUPPORTS) public class AccountServiceImpl implements IAccountService { ... }
- 该注解的属性和 xml 中的属性含义一致。该注解可- 以出现在接口上,类上和方法上。
- 出现接口上,表示该接口的所有实现类都有事务支持。
- 出现在类上,表示类中所有方法有事务支持
- 出现在方法上,表示方法有事务支持。
- 以上三个位置的优先级:方法>类>接口
在配置文件中开启 spring 对注解事务的支持
使用java配置类替换xml实现全注解
@Configuration
@EnableTransactionManagement
public class SpringTxConfiguration {
//里面配置数据源,配置 JdbcTemplate,配置事务管理器
}
知识点
Spring使用到的设计模式
- 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
- 单例模式:Bean默认为单例模式。
- 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
- 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
- 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。
Spring如何处理线程并发问题?
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
Spring事务的实现方式和实现原理
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
Spring Bean的生命周期?
Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Bean生命周期也类似,如下:
一
实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
InitializingBean 与 init-method:
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
二
实例化bean对象
注入bean的所有属性
设置bean的id
调用BeanNameAware接口的setBeanName(String)方法
设置bean工厂
调用BeanFactoryAware接口的setBeanFactory()方法
设置实例所在的上下文空间
调用ApplicationContextAware接口的setApplicationContext()方法,传入Spring上下文
调用后置处理器的预初始化方法
调用BeanPostProcessor接口的postProcessorBeforeInitialization()方法
执行InitializingBean的afterPropertiesSet()
调用使用init-method配置的自定义初始化方法
调用后置处理器的后初始化方法
调用BeanPostProcessor接口的postProcessorAfterInitialization()方法
调用DisPosableBean接口的destory()方法
调用使用destroy-method配置的自定义销毁由方法
@Component 和 @Bean 的区别
- 作用对象不同: @Component 注解作用于类,而@Bean注解作用于方法。
- @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。
- @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
Spring中的核心类有那些,各有什么作用?
- BeanFactory:产生一个新的实例,可以实现单例模式
- BeanWrapper:提供统一的get及set方法
- ApplicationContext:提供框架的实现,包括BeanFactory的所有功能
Bean的调用方式
使用BeanWrapper
HelloWorld hw=new HelloWorld(); BeanWrapper bw=new BeanWrapperImpl(hw); bw.setPropertyvalue("msg","HelloWorld"); system.out.println(bw.getPropertyCalue("msg"));
使用BeanFactory
InputStream is=new FileInputStream("config.xml"); XmlBeanFactory factory=new XmlBeanFactory(is); HelloWorld hw=(HelloWorld) factory.getBean("HelloWorld"); system.out.println(hw.getMsg());
使用ApplicationConttext
ApplicationContext actx=new FleSystemXmlApplicationContext("config.xml"); HelloWorld hw=(HelloWorld) actx.getBean("HelloWorld"); System.out.println(hw.getMsg());