spring
核心概念
- IOC:控制反转,将创建对象的权利转交给框架
- OAP:面向切面编程,通过反射原理,使用框架对方法增强
第1章:使用spring的IOC解决程序耦合
1.1 案例前提准备
- 准备spring开发包(框架)
- 创建业务层接口和实现类
- 创建持久层接口和实现类
1.2 基于xml的配置
- 建立maven项目在pom.xml中加入依赖
4.0.0
com.itheima
day04_eesy_06tx_anno
1.0-SNAPSHOT
jar
org.springframework
spring-context
5.0.2.RELEASE
org.springframework
spring-jdbc
5.0.2.RELEASE
org.springframework
spring-tx
5.0.2.RELEASE
org.springframework
spring-test
5.0.2.RELEASE
mysql
mysql-connector-java
5.1.6
c3p0
c3p0
0.9.1.2
org.aspectj
aspectjweaver
1.8.7
junit
junit
4.12
- 在类的根路径下创建一个任意名称的xml文件(不能是中文)(maven中要放在resources目录下)
- 配置上述的xml文件
- 导入约束
- 让spring管理资源,在配置文件中配置service和dao
- 测试配置是否成功
1.3 spring中的常用接口与类
- BeanFactory:spring容器中的顶层接口
- ApplicationContext:BeanFactory的子接口
- ApplicationContext只要一读取配置文件,默认情况就会创建对象
- BeanFactory:什么时候用什么时候创建对象
- ApplicationContext接口的实现类
- ClassPathXmlApplicationContext:从类的根目录下加载配置文件
- AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类创建spring 容器。它用来读取注解。
1.4 xml配置文件中的标签
bean标签
- 作用:
- 用于配置对象让spring来创建的。
- 默认情况(无子标签constructor-arg)需要调用类中无参构造函数,没有则不能创建成功
- 属性:
- id:创建的对象的唯一标识,用于获取对象
- class:对象的全限定类名,用于反射创建对象
- 代表要创建的对象的所属类
- 代表要创建的对象的静态工厂类(后面需要加上factory-method属性)
- factory-method:当class属性是静态工厂类或有factory-bean属性指明实例工厂,则这个属性需要指明工厂类中生产所需对象的方法
- factory-bean:用于指定实例工厂bean的id
- scope:指定对象的作用范围
- singleton :默认值,单例
- 生命周期跟随容器
- prototype :多例
- 生命周期:不用了就会被回收
- request :WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中.
- session :WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中.
- global session :服务器群共享的session
- singleton :默认值,单例
- init-method:指定类中的某个方法为初始化方法(即该对象初始化后就会自动执行该方法)
destroy-method:指定类中的某个方法为销毁方法
- 子标签:
- constructor-arg :用于有参数的构造函数注入
- 属性
- index: 指定参数在构造函数参数列表的索引位置
- type: 指定参数在构造函数中的数据类型
- name: 指定参数在构造函数中的名称
- value: 它能赋的值是基本数据类型和String类型
- ref: 它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
- 属性
- property:用于set注入
- 属性
- name:找的是类中set方法后面的部分
- ref:给属性赋值是其他bean类型
- value:给属性赋值是基本数据类型和string类型
- 子标签
- list :注入list结构数据
- 子标签:value
- array:注入list结构数据
- 子标签:value
- set:注入list结构数据
- 子标签:value
- map:注入map结构数据
- 子标签entry:
- 属性:key
- 属性:value
- 子标签entry:
- props:注入map结构数据
- 子标签prop
- 属性:key
- 子标签prop
- list :注入list结构数据
- 属性
- constructor-arg :用于有参数的构造函数注入
AAA
BBB
CCC
aaa
bbb
第二章
2.1 使用spring的IoC的实现账户的CRUD
环境搭建
- 创建maven工程,在pom.xml中加入相关依赖
- 创建数据库和编写实体类
- 编写持久层代码
- 编写业务层代码
- 创建并编写配置文件如bean.xml(maven项目中放在根目录的resources目录下)
2.2 基于注解的IOC配置
- 环境搭建
- 创建maven工程,加入相关依赖
- 使用注解配置管理的资源
- 创建spring的xml配置文件并开启对注解的支持
2.3 常用注解
- 用于创建对象的
- @Component
- value:指定bean的id,不指定则默认为类名第一个字母小写
- @Controller:表现层注解
- @Service:业务层注解
- @Repository:持久层注解
- @Component
- 用于注入数据的
- @Autowired:作用于成员变量(常用)和方法上
- 自动按照类型注入
- 类中可省去set方法
- 多个类型(容器中有多个类型相同的对象)匹配时,谁的id与该成员变量相同谁匹配,否则报错
- @Qualifier:自动按照类型注入的基础上,再按照Bean的id注入
- 属性
- value:用于指定bean的id
- 给字段注入时不能独立使用,必须和@Autowire一起使用
- 给方法参数注入时,可以独立使用
- 属性
- @Resource:直接按照Bean的id注入
- 属性
- name:用于指定Bean的id
- 属性
- @Value:用于注入基本数据类型和String类型的数据
- 属性:
- value:用于指定值
- 属性:
- @Autowired:作用于成员变量(常用)和方法上
- 用于改变作用范围的
- @Scope:指定Bean的作用范围
- 属性
- value:指定范围的值
- singleton
- prototype
- request
- session
- globalsession
- value:指定范围的值
- 属性
- @Scope:指定Bean的作用范围
- 与生命周期相关的
- @PostConstruct:用于指定方法为初始化方法
- @PreDestroy:用于指定方法为销毁方法
- 新注解
- @Configuration:用于指定当前类是一个spring配置类
- 作用于类上
- @ComponentScan:指定spring在初始化容器时要扫描的包
- 与@Configuration一起使用
- 属性
- value:别名basePackages,用于指定要扫描的包
- 例子
- @ComponentScan("com.liu") //这个包下的所有都扫描
- @ComponentScan(value="com.liu")
- @Bean:作用于方法上,表明用此方法创建一个对象,并放入spring容器
- 属性:
- name:为当前注解方法所创建的对象指定一个id
- 创建的对象的类型是该方法返回值的类型
- 当使用注解配置时,如果方法有参数,则会自动取容器中查找,查找方式与@Autowired一样
- 属性:
- @PropertySource(或@PropertySources):用于加载.properties文件中的配置
- 在配置类上定义
- 属性:
- value[]:用于指定properties文件的位置,如果在类路径下,需要写上classpath:
- 例子
- @PropertySource("classpath:com/liu/jdbcConfig.properties")
- @Import:用于导入其他配置类,其他配置类可以不用再写@Configuration注解,写了也没错
- 属性:
- value[] :用于指定其他配置类的字节码,
- 例子
- @Import({JdbcConfig.class})
- 属性:
- @Configuration:用于指定当前类是一个spring配置类
- 通过注解获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
Spring整合Junit
配置步骤
- ·创建maven工程,导入坐标(junit的坐标,版本要在4.12之上)
- 使用@RunWith 注解替换原有运行器
- 这一步的作用就是把junit的main方法替换成spring容器中整合的Junit的main方法,使得能够配置
- 使用@ContextConfiguration 指定 spring 配置文件的位置
- 参数
- 可以是配置xml文件
- 可以是配置类
- 使用@Autowired 给测试类中的变量注入数据
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:bean.xml"}) //表明配置基于xml
@@ContextConfiguration(classes = SpringConfiguration.class) //表明基于配置类(两者二选一)
public class AccountServiceTest {
@Autowired
private IAccountService as ;
@Test
void testOne(){
....
}
}
第三章
3.1 AOP相关概念
- 作用:程序运行期间,不修改源码对已有方法进行增强
- 优势:
- 减少重复代码
- 提高开发效率
- 维护方便
- 实现方式:动态代理技术
应用:在实际开放中,业务层来控制事务的提交与回滚
3.2 动态代理
基于接口的动态代理
通过JDK官方的Proxy类创建代理对象
- 要求有被代理类
要求有被代理类实现的接口
public static void main(String[] args) {
final Producer producer = new Producer();
IProducer proxyProducer =
(IProducer) Proxy.newProxyInstance(
producer.getClass().getClassLoader(), //被代理者字节码文件
producer.getClass().getInterfaces(), //实现接口
new InvocationHandler() { //代理细节
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f);
}
基于子类的动态代理
- 通过使用CGLib的Enhancer类创建代理对象(所以需要导入asm.jar包)
- 要求有代理类
- 但是代理类不能被final修饰,否则不能够被继承
public static void main(String[] args) {
//这可不是被代理类被final修饰了,只是代表成员变量只能赋值一次,且不能更改
final Actor actor = new Actor();
Actor cglibActor = (Actor) Enhancer.create(
actor.getClass(), //被代理类的字节码类型
new MethodInterceptor() { //具体代理实现
@Override
public Object intercept(
Object proxy, //被代理者
Method method, //被代理者的方法
Object[] args, //被代理者的方法中的参数
MethodProxy methodProxy //当前执行方法的代理对象
) throws Throwable {
String name = method.getName();
Float money = (Float) args[0];
Object rtValue = null;
if("basicAct".equals(name)){
if(money > 2000){
rtValue = method.invoke(actor, money/2);
}
}
if("dangerAct".equals(name)){
//危险演出
if(money > 5000){
rtValue = method.invoke(actor, money/2);
}
}
return rtValue;
}
});
cglibActor.basicAct(10000);
cglibActor.dangerAct(100000);
}
业务层代理
public class BeanFactory {
/**
* 创建账户业务层实现类的代理对象
* @return
*/
public static IAccountService getAccountService() {
//1.定义被代理对象
final IAccountService accountService = new AccountServiceImpl();
//2.创建代理对象
IAccountService proxyAccountService =
(IAccountService) Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
//添加事务管理
@Override
public Object invoke(
Object proxy,
Method method,
Object[] args
) throws Throwable {
Object rtValue = null;
try {
//开启事务
TransactionManager.beginTransaction();
//执行业务层方法
rtValue = method.invoke(accountService, args);
//提交事务
TransactionManager.commit();
}catch(Exception e) {
//回滚事务
TransactionManager.rollback();
e.printStackTrace();
}finally {
//释放资源
TransactionManager.release();
}
return rtValue;
}
});
return proxyAccountService;
}
}
3.3 Spring中的AOP
相关术语
- Joinpoint(连接点):可以被增强的方法
- Pointcut(切入点):进行了增强的方法 就是说并不是可以被增强我就去一定去增强
- Advice(通知/增强):拦截到切入点之后要做的事情就是通知
- 前置通知
- 后置通知
- 异常通知
- 最终通知
- 环绕通知
- Introduction(引介):一种特殊的通知
- Target(目标对象):代理的目标对象
- Weaving(织入):动词,是指把增强应用到目标对象来创建新的代理对象的过程
- Proxy(代理):增强后的结果代理类
- Aspect(切面):是切入点与通知(引介)的结合
学习spring的开发步骤
- 编写核心业务代码(主线开发)
- 把共用代码抽取出来,制作成 通知
- 在配置文件中,声明 切入点 与 通知 的关系,即 切面
3.4 基于xml的AOP配置
- 环境搭建:准备好相关代码
- 引入坐标(配置依赖)
- 创建spring的配置文件并导入约束
- 配置spring的IOC
- 抽取公共代码制作成通知
//这个类是用来作为通知的,所以方法一般都是通知,即重复使用的代码
public class TransactionManager {
private DBAssit dbAssit ;
public void setDbAssit(DBAssit dbAssit) {...}
//开启事务
public void beginTransaction() {...}
//提交事务
public void commit() {...}
//回滚事务
public void rollback() {...}
//释放资源
public void release() {...}
}
- 完成切面(重点)
- 把通知类用 bean 标签配置起来
- 使用 aop:config 标签声明 aop 配置
- 在aop:config 标签下使用 aop:aspect 标签配置切面
- 在aop:config 标签下使用 aop:pointcut标签 配置切入点表达式
- 在aop:aspect标签下使用 aop:xxx 标签配置对应的通知类型
- aop:before
- aop:after-returning
- aop:after-throwing
- aop:after
- aop:around
- 把通知类用 bean 标签配置起来
对于环绕通知的配置:
问题:
当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
分析:
通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
解决:
Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
spring中的环绕通知:
它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
public class Logger {
//环绕通知内容
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
}
3.5 基于注解的AOP配置
- 准备必要的代码和导入坐标(依赖)
- 在配置文件中导入 context 的名称空间
- 把资源使用注解配置
- 在配置文件中指定 spring 要扫描的包
- 切面配置(重点)
- 把通知类也使用注解配置
- 在通知类上使用@Aspect 注解声明为切面
- 在增强的方法上使用注解配置通知
- 在 spring 配置文件中开启 spring 对注解 AOP 的支持
@Component("txManager")
@Aspect //表明当前类是一个切面类
public class TransactionManager {
//定义一个 DBAssit
@Autowired
private DBAssit dbAssit ;
// 切入点表达式注解
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt1() {}
@Before("execution(* com.itheima.service.impl.*.*(..))")
public void beginTransaction() {
try {
dbAssit.getCurrentConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Around("pt1()") //注意:千万别忘了写括号
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
}
不使用xml的配置方式
@Configuration
@ComponentScan(basePackages="com.liu")
@EnableAspectJAutoProxy
public class SpringConfiguration { }
第四章
4.1 Spring中的JdbcTemplate
- JdbcTemplate对象的创建
public JdbcTemplate() //通过set方法设置数据源
public JdbcTemplate(DataSource dataSource) //构造函数指定好数据源
public JdbcTemplate(DataSource dataSource, boolean lazyInit) //可以指定是否懒加载
4.2 spring中配置数据源
- 环境搭建(坐标)
- 编写spring的配置文件
- 配置数据源
- 可以使用C3P0数据源
- 可以使用DBCP数据源
- 可以使用spring内置数据源
将数据库连接的信息配置到属性文件中
4.3 JdbcTemplate的增删改查操作
- 创建好数据库和表
- 在 spring 配置文件中配置 JdbcTemplate(当然也要配置好数据源等东西)
4.4 在dao中使用JdbcTemplate
- 准备实体类
- 第一种方式:在 dao 中定义 JdbcTemplate
- 第二种方式:让 dao 继承 JdbcDaoSupport
- JdbcDaoSupport是spring框架为我们提供的一个类,该类中定义了一个JdbcTemplate 对象,但是还是需要为其提供数据源,所以我们需要在配置文件注入数据源
- 两种方式的区别
- 第一种在 Dao类中定义 JdbcTemplate 的方式,适用于所有配置方式(xml和注解都可以)。
- 第二种让 Dao继承 JdbcDaoSupport 的方式,只能用于基于 XML 的方式,注解用不了。
- 第一种在 Dao类中定义 JdbcTemplate 的方式,适用于所有配置方式(xml和注解都可以)。
4.5 spring中的事务控制
第一:
JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解决方案。
第二:
spring 框架为我们提供了一组事务控制的接口。这组接口是在 spring-tx-5.0.2.RELEASE.jar 中。
第三:
spring 的事务控制都是基于AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现
Spring中事务控制的 API介绍
- PlatformTransactionManager:spring中的事务管理器(接口)
- 方法
- TransactionStatus getTransaction(TransactionDefinition definition)
- 获取事务状态信息
- void commit(TransactionStatus Status)
- 提交事务
- void rollback(TransactionStatus Status)
- 回滚事务
- TransactionStatus getTransaction(TransactionDefinition definition)
- 实现类
- DataSourceTransactionManager
- 使用springJDBC或iBatis进行持久化数据时使用
- HibernateTransactionManager
- 使用Hibernate版本进行持久化数据时使用
- DataSourceTransactionManager
- 方法
- TransactionDefinition:事务的定义信息对象
- 方法
- String getName():获取事务对象名称
- int getIsolationLevel():获取事务隔离级别
- ISOLATION_DEFAULT:默认级别
- ISOLATION_READ_UNCOMMITTED:可读取未提交数据
- ISOLATION_READ_COMMITTED:只能读取已提交的数据,解决脏读问题(Oracle默认级别)
- ISOLATION_REPEATABLE_READ:是否读取其他事物提交修改后的数据,解决不可重复读问题(mysql默认级别)
- ISOLATION_SERIALIZABLE:是否读取其他事物提交添加后的数据,解决幻读问题
- int getPropagationBehavior():获取事务传播行为
- int getTimeout() :获取事务超时时间
- boolean isReadOnly():获取事务是否只读
- 方法
- TransactionStatus:该接口描述某个时间点上事物对象的状态信息,包含6个具体操作
- 方法
- void flush():刷新事物
- boolean hasSavepoint():获取是否存在存储点
- boolean isCompleted():获取事务是否完成
- boolean isNewTransaction():获取事务是否为新事物
- boolean isRollbackOnly():获取事务是否回滚
- void setRollbackOnly():设置事务回滚
- 方法
基于xml的声明式事物控制(配置方式) 重点
- 创建工程,导入依赖(坐标)
- 创建spring的配置文件并导入约束
- 准备数据库表和实体类
- 编写业务层接口和实现类
- 编写持久层接口和实现类(使用到了JdbcTemplate类)
- 在配置文件中配置业务层和持久层
- 在配置文件中配置事物管理器
配置事物的通知引用事物管理器
- 配置事物的属性
- 配置 AOP 切入点表达式
配置切入点表达式和事务通知的对应关系
基于注解配置的声明式事物控制(配置方式) 重点
- 创建工程,导入依赖(坐标)
- 创建spring的配置文件并导入约束并配置扫描的包
- 准备数据库表和实体类
- 编写业务层接口和实现类并使用注解让spring管理
- 编写持久层接口和实现类(使用到了JdbcTemplate类)
- 在配置文件中配置业务层和持久层
- 配置事务管理器并注入数据源
- 在业务层使用@Transactional 注解
@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
。。。
//此操作需要读写
@Transactional(propagation= Propagation.REQUIRED,readOnly=false)
@Override
public void transfer(String sourceName, String targetName, Float money) {
。。。
}
}
- 在配置文件中开启 spring 对注解事务的支持
不使用xml的配置方式
@Configuration
@ComponentScan("com.liu")
@Import({JdbcConfig.class,TransactionConfig.class})
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement
public class SpringConfiguration {
}
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name="jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean(name="dataSource")
public DataSource createDataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
public class TransactionConfig {
@Bean(name="transactionManager")
public PlatformTransactionManager createTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
4.6 spring5新特性
还没学