一、Spring框架概念
它是一个容器.它是整合其它框架的框架.它的核心是IOC和AOP.它由20多个模块构成.它在很多领域都提供优秀的解决方案.
-
轻量级
由20多个模块构成,每个jar包都很小,小于1M,核心包也就3M左右, 对代码无污染.
-
面向接口编程
使用接口,就是面向灵活,项目的可扩展性,可维护性都极高.接口不关心实现类的类型.使用时接口指向实现类,切换实现类即可切换整个功能.
-
AOP:面向切面编程
就是将公共的,通用的,重复的代码单独开发,在需要的时候反织回去.底层的原理是动态代理.
-
整合其它框架
它整合后使其它框架更易用.
二、spring的控制反转(IOC)
2.1、ioc的配置文件注入方式
一般来说是由自己进行对象的创建和赋值,但是ioc可以将这个过程交给spring来管理,也就是由容器来完成,这就是反转
控制反转由两种方式,setter注入和构造方法注入
-
setter注入 :本质上就是使用对象的set方法来对对象赋值,必须存在setter和无参数的构造方法
其中简单类型和引用类型的方式不一样,如下
<!--创建学生对象-->
<bean id="stu" class="com.bjpowernode.pojo2.Student">
<property name="name" value="李四"></property> ===>简单类型注入
<property name="age" value="22"></property>
<property name="school" ref="school"></property> ===>引用类型注入
</bean>
<!--创建学校对象-->
<bean id="school" class="com.bjpowernode.pojo2.School">
<property name="name" value="清华大学"></property>
<property name="address" value="海淀区"></property>
</bean>
ps: 在三层架构的时候使用这种注入方式来管理服务层或者数据层的接口对象的时候,必须要在类中创建一个set方法,不然无法注入。
-
构造方法注入:构造方法有三种注入方式,只需要一个有参数的构造方法就可以,值得注意的是,在使用名称的时候用的是构造方法输入值的名称
Student stu = new Student("张三",22);
a.使用构造方法的参数名称进行注入值
<bean id="school" class="com.bjpowernode.pojo3.School">
<constructor-arg name="name1" value="清华大学"></constructor-arg>
<constructor-arg name="address1" value="海淀区"></constructor-arg>
</bean>
b.使用构造方法参数的下标注入值
<bean id="stu" class="com.bjpowernode.pojo3.Student">
<constructor-arg index="0" value="钱七"></constructor-arg>
<constructor-arg index="1" value="22"></constructor-arg>
<constructor-arg index="2" ref="school"></constructor-arg>
</bean>
c.使用默认的构造方法的参数的顺序注入值
<bean id="stuSequence" class="com.bjpowernode.pojo3.Student">
<constructor-arg value="陈十"></constructor-arg>
<constructor-arg value="22"></constructor-arg>
<constructor-arg ref="school"></constructor-arg>
</bean>
2.2、基于注解的IOC
创建对象的注解
-
@Component:可以创建任意对象.创建的对象的默认名称是类名的驼峰命名法.也可以指定对象的名称@Component(“指定名称”).
-
@Controller:专门用来创建控制器的对象(Servlet),这种对象可以接收用户的请求,可以返回处理结果给客户端.
-
@Service:专门用来创建业务逻辑层的对象,负责向下访问数据访问层,处理完毕后的结果返回给界面层.
-
@Repository:专门用来创建数据访问层的对象,负责数据库中的增删改查所有操作.
上述注入和创建对象的用法,一般的对象直接使用@Component,简单类型使用@Value
对象具体属性的参数注入:
-
引用类型使用@Autowired,这一个注解是使用类型寻找的,在没有父子类的时候可以直接使用,但是如果存在父子类就最好加上名称寻找,这是最好的。
案例:
@Component("stu") //交给Spring去创建对象,就是在容器启动时创建
public class Student {
@Value("张三") ===>简单类型的值注入
private String name;
@Value("22")
private int age;
@Autowired
@Qualifier("test")
private test t;
-
简单类型(8种基本类型+String)的注入
@Value:用来给简单类型注入值
-
引用类型的注入
@Autowired:使用类型注入值,从整个Bean工厂中搜索同源类型的对象进行注入.
-
同源类型也可注入.
被注入的类型(Student中的school)与注入的类型是完全相同的类型
被注入的类型(Student中的school父)与注入的类型(子)是父子类
被注入的类型(Student中的school接口)与注入的类型(实现类)是接口和实现类的类型
注意:在有父子类的情况下,使用按类型注入,就意味着有多个可注入的对象.此时按照名称进行二次筛选,选中与被注入对象相同名称的对象进行注入.
-
@Autowired
-
@Qualifier("名称"):使用名称注入值,从整个Bean工厂中搜索相同名称的对象进行注入.
注意:如果有父子类的情况下,直接按名称进行注入值.
2.3、配置文件扫描
注入之后还需要在xml文件中加入扫描包,这样spring就会自动扫描这些包下的注解
<context:component-scan base-package="spring1"></context:component-scan>
尽量扫描小一点的包,也可以写多个。 三种写法
<context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
<context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan>
<context:component-scan base-package="com.bjpowernode.dao"></context:component-scan>
<context:component-scan base-package="com.bjpowernode.controller com.bjpowernode.service ,com.bjpowernode.dao"></context:component-scan>
<context:component-scan base-package="com.bjpowernode"></context:component-scan>
分层配置xml文件,然后通过一个xml文件整合
<import resource="applicationContext.xml"></import>
<import resource="applicationContext2.xml"></import>
<import resource="applicationContext3.xml"></import>
<import resource="applicationContext4.xml"></import>
三、spring的面相切面编程
3.1、 AspectJ常见通知类型
AspectJ 中常用的通知有四种类型:
-
前置通知@Before
-
后置通知@AfterReturning
-
环绕通知@Around
-
最终通知@After
-
定义切入点@Pointcut(了解)
使用切面框架的步骤
- 引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--添加aspectj的依赖-->
- 创建接口和实现类(并在实现类加上@Service注解)
-
public interface Service { public String aop(int a,String b); } @org.springframework.stereotype.Service public class ServiceImol implements Service { @Override public String aop(int a, String b) { System.out.println("服务方法执行"); return "abcd"; } }
- 创建切面类(切面类加上@Aspect @Component)
-
@Aspect @Component public class MyAop { @Before(value = "execution(* com.aop.*.*(..))") public void before(){ System.out.println("前置通知"); } @Around(value = "execution(* com.aop.*.*(..))") public Object around(ProceedingJoinPoint p) throws Throwable { System.out.println("环绕中的前置通知"); Object o=p.proceed(p.getArgs()); System.out.println("环绕中的后置通知"); return o; } @After(value = "execution( * com.aop.*.*(..))") public void after(){ System.out.println("最终通知"); } @AfterReturning(value = "execution( * com.aop.*.*(..))",returning = "o") public Object afterReturning(Object o){ System.out.println("后置通知功能"); return o.toString().toUpperCase(); } }
- 在xml中绑定
<context:component-scan base-package="com.aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试
@Test
public void after(){
ApplicationContext ap=new ClassPathXmlApplicationContext("application.xml");
Service service= (Service) ap.getBean("serviceImol");
service.aop(12,"dsa");
}
至于不基于注解的太复杂了,没必要。
3.2、事务
基于注解的事务添加步骤
- 在applicationContext_service.xml文件中添加事务管理器
- 在applicationContext_service.xml文件中添加事务的注解驱动
<tx:annotation-driven transaction-manager=“transactionManager”></tx:annotation-driven>
- 在业务逻辑的实现类上添加注解[@Transactional(propagation ](/Transactional(propagation ) = Propagation.REQUIRED)
- REQUIRED表示增删改操作时必须添加的事务传播特性
@Transactional注解参数详解
@Transactional(propagation = Propagation.REQUIRED,//事务的传播特性
noRollbackForClassName = "ArithmeticException", //指定发生什么异常不回滚,使用的是异常的名称
noRollbackFor = ArithmeticException.class,//指定发生什么异常不回滚,使用的是异常的类型
rollbackForClassName = "",//指定发生什么异常必须回滚
rollbackFor = ArithmeticException.class,//指定发生什么异常必须回滚
timeout = -1, //连接超时设置,默认值是-1,表示永不超时
readOnly = false, //默认是false,如果是查询操作,必须设置为true.
isolation = Isolation.DEFAULT//使用数据库自已的隔离级别
-
注解式的事务 使用@Transactional注解完成事务控制,此注解可添加到类上,则对类中所有方法执行事务的设定.此注解可添加到方法上,只是对此方法执行事务的处理.
-
声明式事务(必须掌握),在配置文件中添加一次,整个项目遵循事务的设定.
Spring中事务的五大隔离级别
-
未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
-
提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
-
可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
-
串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
-
使用数据库默认的隔离级别isolation = Isolation.DEFAULT
-
MySQL:mysql默认的事务处理级别是’REPEATABLE-READ’,也就是可重复读
-
Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。默认系统事务隔离级别是READ COMMITTED,也就是读已提交
为什么添加事务管理器
JDBC: Connection con.commit(); con.rollback();
MyBatis: SqlSession sqlSession.commit(); sqlSession.rollback();
Hibernate: Session session.commit(); session.rollback();
事务管理器用来生成相应技术的连接+执行语句的对象。即能生成上面的Connection、SqlSession、Session等数据库的操作对象
如果使用MyBatis框架,必须使用DataSourceTransactionManager类完成处理
项目中的所有事务,必须添加到业务逻辑层上.
Spring事务的传播特性
多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决.
-
常用
PROPAGATION_REQUIRED:必被包含事务(增删改必用)
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
-
不常用
PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
PROPAGATION_NESTED:嵌套事务