Spring基础学习
0. 什么是Spring,有什么好处;
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。
- 通过依赖注入和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程;(注解)
1. Spring IOC
IOC主要是为了解决创建对象难的问题;
1.1 IOC是什么
控制反转,就是把原本应该由程序员负责的管控的对象交给IOC进行处理
1.1 IOC解决了什么问题
有如下场景:
假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。
这样的设计看起来没问题,但是可维护性却很低。假设设计完工之后,上司却突然说根据市场需求的变动,要我们把车子的轮子设计都改大一码。这下我们就蛋疼了:因为我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改。
具体代码如下:使用起来很简单,只需要new Car();
package IOC解决的问题;
public class Main {
public static void main(String[] args) {
Car car = new Car();
}
}
/*
轮子
*/
class Lz {
private int size = 0;
public Lz() {
size = 30;
System.out.println("轮子被创建轮子大小"+size);
}
}
/*
底盘
*/
class Dp {
public Dp() {
Lz lz = new Lz();
System.out.println("底盘被创建");
}
}
/*
车身
*/
class Cs {
public Cs() {
Dp dp = new Dp();
System.out.println("车身被创建");
}
}
class Car {
public Car() {
Cs cs = new Cs();
System.out.println("车创建完成");
}
}
但是如果某一天需求发生变化 轮子 Lz类需要自己指定大小即增加一个新的构造函数;
代码如下:可以看到所有上层类的狗仔函数都发生了变化;这种代码是不可维护的;因为上层可能有很多;
package IOC解决的问题.普通写法;
public class Main {
public static void main(String[] args) {
Car car = new Car();
}
}
/*
轮子
*/
class Lz {
private int size = 0;
public Lz(int size) {
this.size = size;
System.out.println("轮子被创建轮子大小"+size);
}
}
/*
底盘
*/
class Dp {
public Dp(int size) {
Lz lz = new Lz(size);
System.out.println("底盘被创建");
}
}
/*
车身
*/
class Cs {
public Cs(int size) {
Dp dp = new Dp(size);
System.out.println("车身被创建");
}
}
class Car {
public Car(int size) {
Cs cs = new Cs(size);
System.out.println("车创建完成");
}
}
这时我们引入依赖注入思想:(就是直接把类通过构造函数传进去)
代码示例:可以看到,这样实现,上层不依赖于下层,且下层修改也不会影响到上层;
package IOC解决的问题.DI写发;
public class Main {
public static void main(String[] args) {
Lz lz = new Lz(1);
Dp dp = new Dp(lz);
Cs cs = new Cs(dp);
Car car = new Car(cs);
}
}
/*
轮子
*/
class Lz {
private int size = 0;
public Lz(int size) {
this.size = size;
System.out.println("轮子被创建轮子大小"+size);
}
}
/*
底盘
*/
class Dp {
public Dp(Lz lz) {
System.out.println("底盘被创建");
}
}
/*
车身
*/
class Cs {
public Cs(Dp dp) {
System.out.println("车身被创建");
}
}
class Car {
public Car(Cs cs) {
System.out.println("车创建完成");
}
}
上述代码存在的问题是我们在创建对象的时候需要知道其下层的创建方式(由程序员new对象);这违背了面向对象原则;
所以引入控制反转思想:
解决这块代码:
伪代码:
package IOC解决的问题.DI写发;
public class Main {
@Autowired
Car car;
public static void main(String[] args) {
car.run();
}
}
/*
轮子
*/
class Lz {
private int size = 0;
public Lz(int size) {
this.size = size;
System.out.println("轮子被创建轮子大小"+size);
}
}
/*
底盘
*/
class Dp {
@Autowired
Lz lz;
public Dp() {
System.out.println("底盘被创建");
}
}
/*
车身
*/
class Cs {
@Autowired
Dp dp;
public Cs() {
System.out.println("车身被创建");
}
}
class Car {
@Autowired
Cs cs;
public Car() {
System.out.println("车创建完成");
}
}
2. Spring bean
2.1 什么是Spring bean
那些被Spring容器管理的java对象就叫Spring bean;
2.2 bean的创建
首先创建有三种方式:这里简单的说下前两种;
1.自动化装配;
2.java代码装配;
3.xml装配‘
1.自动化装配;
在类上打上Component/Controller/Service注解;
或者开启组件扫描;
@MapperScan
2.java代码装配;
两个关键注解 @Configuration 和 @Bean
方法的返回值就是实例化的对象;
可以指定Bean的名称方便注入:
3. Spring AOP
3.1 什么是Spring Aop
一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
3.2 两种代理方式
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
- JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
- 如果代理类没有实现 InvocationHandler 接口,那么SpringAOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
3.2 5种类型的通知
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
执行顺序:
1.没有异常情况下的执行顺序:
环绕通知–》前置通知–》目标方法–》环绕通知–》后置通知–》返回通知
2.没有异常情况下的执行顺序:
环绕通知–》前置通知–》目标方法–》环绕通知–》后置通知–》异常通知
4. Spring 常用注解
4.1 装配类的注解
-
@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring
的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。 -
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。
-
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service而不是 @Component,因为它以更好的方式指定了意图。
4.2 注入类的注解
-
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
-
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
-
@Qualifier 当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
4.3 java配置类相关注解
- @Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
- @Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
- @Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
- @ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
4.4 切面(AOP)相关注解
- @Aspect 声明一个切面(类上)
- 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
- @After 在方法执行之后执行(方法上)
- @Before 在方法执行之前执行(方法上)
- @Around 在方法执行之前与之后执行(方法上)
- @PointCut 声明切点
5. Spring事务
5.1@Transactional
@Transactional不写默认使用数据的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE):串行化