重新回顾一遍Spring,希望能够温故知新,有新的见解。
学习Spring的好处
- 方便解耦,简化开发
通过Spring提供的IoC容器,我们可以讲对象之间的依赖关系交由Spring进行控制,避免硬编码锁造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。 - AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。 - 声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务管理。 - 方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,Spring对Junit4的支撑,可以通过注解方便地进行测试。 - 方便继承各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate,Hessian,Quartz等)的支撑。 - 降低Java EE API 的使用难度
Spring对很多难用的Java EE API 提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API 的使用难度大幅下降。 - Java源码是学习的典范
Spring源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。
关键字: IoC,DI,AOP,Spring容器
IoC:控制反转(Inversion of Control),创建对象的控制权不是由用户创建,而是反转给Spring框架来管理。是一种设计思想,不是技术,满足这种思想的技术可以有很多种,Spring IoC是其中一种。
DI:依赖注入(Dependency Injecetion),IoC是指将对象的创建权反转给了Spring容器,而DI则是Spring框架负责创建Bean对象是,动态地将依赖对象注入到Bean组件中。DI和IoC是相辅相成,缺一不可。
AOP:Aspect Oriented Programming,面向切面编程。在不修改目标对象的源代码情况下,增强IoC容器中Bean的功能。
Spring容器:IoC容器,存储Spring生成的Bean对象,底层也是一个BeanFactory。
Spring的两大特性之一:IoC / DI
1. Spring IoC/DI 基于xml方式的应用
IoC配置
创建ApplicationContext是通过读取applicationContext.xml文件,然后通过解析xml标签获取响应的信息创建applicationContext对象的。同理,在applicationContext.xml中通过读取bean标签,也可以创建响应的对象,从而交给Spring管理。
- bean标签的作用:
用于配置被Spring容器管理的bean的信息
默认情况下它调用的是类中的 无参构造器。如果没有无参构造器则不能创建成功。 - bean标签的属性:
- id:给对象在容器中提供一个唯一标识,用于获取对象。
- class:指定类的全限定名。用于反射创建对象。默认情况下调用 无参构造器。
- init-method:指定类中的初始化方法,在创建对象后马上执行。
- destroy-method:指定类的销毁方法名称,在销毁对象之前执行。比如DataSource的配置中一般需要指定destroy-method='close'。
- scope:指定对象的作用范围:
- singleton:默认值,单例(在整个容器中只有一个对象)
- prototype:多例的,每次访问对象时,都会重新创建对象实例。
DI配置
依赖注入可通过构造函数注入,setter方法注入。
构造函数注入,该方式需要满足指定参数的构造 :
setter方式注入一:手动装配(xml方式):
须配置bean标签的子标签property,bean对象中需存在setter方法。
2. Spring IoC/DI 基于xml和注解混合的使用
IoC配置
第一步:在spring配置文件中配置context:component-scan标签,指定扫描的范围。带有第二步注解的类都能被扫描到。
第二步:在类上加上注解@Component,或者它衍生的注解@Controller,@Service,@Repository。这四个注解的作用都是一样的,只是编码时用于区分层次。
@Component 注解
这个注解的作用就是把该类交给Spring管理,相当于在xml中配置了一个bean。
属性value:指定bean的id,如果不指定value属性,默认bean的id是当前类的类名,首字母小写。‘’
DI配置
setter方式注入二: 自动装配(注解方式)
- @Autowired:从Spring容器中根据Bean的类型(Class)获取实例,将找到的实例装配给另一个实例的属性。注意:一个Java类型(Class)在同一个Spring容器中只能有一个实例。
- @Resource:从Spring容器中根据Bean的名称(name)获取实例,然后进行赋值
- @Inject:根据类型进行自动装配,如果需要根据名称则需要配合@named。可以用作在变量、setter方法、构造函数上。
3. Spring IoC/DI 基于纯注解的使用
Spring 纯注解的使用和SpringBoot是无缝衔接的,有些在Springboot中经常用到的注解,实际上是Spring的,比如@Configration,@ComponentScan等。
@Configration 注解
相当于Spring的xml配置文件,可替换掉使用xml方式的配置文件。
配置类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean对象,初始化Spring容器。
@Configuration
public class SpringConfiguration {
// Spring容器初始化时,会调起配置类的无参构造器
public SpringConfiguration() {
System.out.println("spring容器初始化....");
}
}
@Bean 注解
相当于
@Bean标注在方法上,将该方法返回的实例交给Spring管理
@Bean(name = "common")
@Scope("prototype")
public CommonProperties constantsProperties() {
return new CommonProperties();
}
属性 name:给当前@Bean注解方法创建的对象指定一个名称,注入时可根据该名称注入。如果不指定,默认与注解的方法名相同。
@Bean注解默认作用域为单例singleton,可通过@Scope("prototype")设置为多例。
@ComponentScan 注解
相当于
该注解贴在类上面,一般是和@Configration注解一起使用。
@Configuration
@ComponentScan(basePackages = "com.xmotor.gzyz")
public class SpringConfiguration {
// Spring容器初始化时,会调起配置类的无参构造器
public SpringConfiguration() {
System.out.println("spring容器初始化....");
}
}
属性:
basePackages:用于指定要扫描的包。如果不指定,则默认扫同包名下的注解。
value:和basePackages一样。
@PropertySource 注解
相当于
@Configuration
@PropertySource("classpath:config.properties")
public class ConfigProperties {
@Value("${baseUrl}")
private String baseUrl;
}
属性 value[]:用于指定properties文件路径,如果是在类路径下,需要指定classpath。
@Import 注解
相当于spring配置文件中的
@Configuration
@ComponentScan(basePackages = "com.xmotor.gzyz")
@Import({JdbcConfiguration.class})
public class SpringConfiguration {
// Spring容器初始化时,会调起配置类的无参构造器
public SpringConfiguration() {
System.out.println("spring容器初始化....");
}
}
@PropertySource("classpath:config.properties")
public class JdbcConfiguration {
}
属性 value:用来指定其他配置类的字节码文件,可以指定多个。
创建纯注解方式上下文容器
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(SpringConfiguration.class);
ConfigProperties configProperties = applicationContext.getBean(ConfigProperties.class);
Spring的两大特性之二: AOP
AOP是一种编程思想,跟IoC类型,并不是一种技术,Spring AOP是实现了这种思想的一种技术。
最大作用就是将系统逻辑(权限验证,事务管理,记录操作日志等)和业务逻辑隔离开,提高了程序的可重用性,降低耦合度。在Spring AOP中,它甚至将事务逻辑都帮你写好了。
AOP术语:Joinpoint,Pointcut,Advice,Aspect
Joinpoint:连接点,被拦截到需要被增强的方法。
Pointcut:切入点,多个连接点的集合,一般指哪些包中的哪些类。
Advice:通知/增强,拦截到Joinpoint之后要做的事情,可以分为前置增强,后置增强,异常增强,最终增强,环绕增强,就是在执行Joinpoint的前后左右添加新的增强功能。
Aspect:切面:Pointcut + Advice,哪些地方,在什么时候,做什么增强。
Target:目标对象,代理的目标对象
Weaving:织入,是指把增强应用到目标对象来创建新的代理对象的过程。
Proxy:代理,一个类被AOP织入增强后,产生的代理类。
总结:Joinpoint,某一个方法,多个Joinpoint集合成Pointcut。Pointcut + Advice 组合成Aspect。把Advice 织入(Weaving)到Target中,得出Proxy对象。
静态代理和动态代理
静态代理 :在Spring容器启动的时候就已经确定好了代理类,只能服务于一种类型的对象。
动态代理 :在程序运行期间由JVM通过反射产生,不存在字节码文件。代理类和委托类的关系是在程序运行时确定。
1. Spring 基于AspectJ的AOP使用之xml方式
首先要编写增强类,也就是要做什么增强。在一个增强类里面可以写多个增强方法,但是增强的时候只会选择其中一个执行。
public class MyAdvice {
public void log(){
System.out.println("记录日志....");
}
}
其次配置增强,将增强类交给Spring容器管理
最后配置aop切面
切入点表达式:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
返回值类型:可以用 * 通配符
包名:可以用 * 代替一级包的通配符,可以用 .. 省略中间包名
类名:可以用 * 通配符,也可以写成 *ServiceImpl
方法名:可以用 * 通配符,也可以写成 *SaveImpl
参数:可以用 * 通配符,多个参数,可以使用 .. 代替
通知:同一个方法可以配置多个通知
通知类型:前置通知,后置通知,最终通知,环绕通知,异常抛出通知
前置通知
执行时机:目标对象方法之前执行
配置文件:
后置通知
执行时机:目标对象方法之后之后,抛出异常则不会执行
配置文件:
最终通知
执行时机:目标对象方法之后执行通知,有没有异常都会执行,顺序在后置通知之后
配置文件:
环绕通知
执行时机:目标对象方法之前和之后都会执行,包含before和after-returning
配置文件:
异常抛出通知
执行时机:在抛出异常时通知
配置文件:
2. Spring 基于AspectJ的AOP使用之xml和注解混合方式
编写切面类,注意不是通知类,是切面类,包含了通知和切入点。
@Component
@Aspect
public class MyAspect {
@Before(value = "execution(* *..*.*save(*))")
public void beforce() {
System.out.println("前置通知。。。");
}
@After(value = "execution(* *..*.*save(*))")
public void after() {
System.out.println("最终通知。。。");
}
@AfterReturning(value = "execution(* *..*.*save(*))")
public void afterReturn() {
System.out.println("后置通知。。。");
}
}
@Component 注解 再配置组件扫描,将该切面类交给Spring管理。
最后开启AOP自动代理
2. Spring 基于AspectJ的AOP使用之纯注解方式
纯注解方式重点在于转换混合方式配置组件扫描和开启自动代理两种xml配置。
@Configuration
@ComponentScan(basePackages = "com.pillow")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
@EnableAspectJAutoProxy 开启自动代理
Spring声明式事务
事务,真正的实现是由底层的数据库进行实现的,而Spring是对数据库底层进行一些简单的设置。配置好了以后,Spring会读取配置,传递给数据库,使数据库进行一些响应的改变。
需要引入依赖包 spring-tx
org.springframework
spring-tx
4.3.14.RELEASE
事务的四大特性:一致性,原子性,隔离性,持久性
隔离性将会导致一系列并发问题:
脏读:事务A读取到了事务B未提交的数据,事务B回滚后,事务A的数据就是错的。
不可重复度:事务A第一次读取数据时,事务B未开始。事务B执行了update操作,并提交的事务,A再读取时,发现两次读取的数据不一致。
幻读:事务A第一次读取时,事务B未开始。事务B执行了insert,delete操作,并提交的事务,A再读取时,发现两次读取的数据不一致。
针对不同的并发问题制定了不同的隔离级别
read uncommitted:读未提交,不能解决任何问题
read committed:读已提交,可以解决脏读。Oracle默认级别。
repeatable read:可重复度,可以解决脏读和不可重复读。Mysql默认级别,Mysql中幻读也不会出现。
Serializable:串行化,事务提交了一个才进行下一个,可以解决所有问题,性能也最差。
1. Spring声明式事务 基于xml方式的应用
2. Spring声明式事务 基于xml和注解混合的使用
在service类上或者方法上加注解:@Transactional
@Transactional
加在类上,表明该类所有方法都被事务管理
加在方法上,表明仅仅该方法被事务管理
3. Spring声明式事务 基于纯注解的使用
纯注解方式在于解决混合用法的开启事务注解的xml处理。
@EnableTransactionManagement 贴在配置类上即可。
@ComponentScan(basePackages = "com.pillow")
@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfiguration {
}