一、概述
spring核心概念
- IoC(核心中的核心):Inverse of Control,控制反转。对象的创建权力由程序反转给Spring框架。
- DI:Dependency Injection,依赖注入。在Spring框架负责创建Bean对象时,动态的将依赖对象注入到
Bean组件中!! - AOP:Aspect Oriented Programming,面向切面编程。在不修改目标对象的源代码情况下,增强IoC容器
中Bean的功能。 - Spring容器:指的就是IoC容器,底层也就是一个BeanFactory。
二、IOC
基于XML使用
IOC配置
bean实例化的三种方式:
-
第一种:使用默认无参构造函数(重点)
<bean id="userService" class="com.kkb.spring.service.UserServiceImpl/>
-
第二种:静态工厂(了解)
实体类需创建createUserService的静态方法
<bean id="userService" class="com.kkb.spring.factory.StaticFactory" factory-method="createUserService"></bean>
-
第三种:实例工厂(了解)
实体类需创建createUserService的普通方法
<bean id="instancFactory" class="com.kkb.factory.InstanceFactory"></bean> <bean id="userService" factory-bean="instancFactory" factory- method="createUserService"></bean>
DI依赖注入
- 第一种:使用构造函数
<bean id="userService" class="com.kkb.spring.service.UserServiceImpl">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="zhangsan"></constructor-arg>
</bean>
-
第二种:setter方法(重点)
//1.手动装配方式(XML方式) - 需要配置bean标签的子标签property - 需要配置的bean中指定setter方法。 //2.自动装配方式(注解方式,后面讲解) - @Autowired: - 作用一:查找实例,从spring容器中根据Bean的类型(byType)获取实例。 - 作用二:赋值,将找到的实例,装配给另一个实例的属性值。 - 注意事项:一个java类型在同一个spring容器中,只能有一个实例 - @Resource: - 作用一:查找实例,从spring容器中根据Bean的名称(byName)获取实例。 - 作用二:赋值,将找到的实例,装配给另一个实例的属性值。 - @Inject:
手动装配两种方式:
// 1.p名称空间注入数 //在schema的名称空间中加入该行: xmlns:p="http://www.springframework.org/schema/p" //xml配置: <bean id="person" class="com.kkb.spring.demo.Person" p:pname="老王" p:car2- ref="car2"/> <bean id="car2" class="com.kkb.spring.demo.Car2" />
// 2. 依赖注入不同类型的属性 //简单类型(value) <bean id="userService" class="com.kkb.spring.service.UserServiceImpl"> <property name="id" value="1"></property> <property name="name" value="zhangsan"></property> </bean> //引用类型(ref) <bean id="userService" class="com.kkb.spring.service.UserServiceImpl"> <property name="userDao" ref="userDao"></constructor-arg> </bean> <bean id="userDao" class="com.kkb.spring.dao.UserDaoImpl"></bean> //集合类型(数组) <bean id="collectionBean" class="com.kkb.demo5.CollectionBean"> <property name="arrs"> <list> <!-- 如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签 --> <value>美美</value> <value>小风</value> </list> </property> </bean> //如果是Properties集合的方式 <property name="pro"> <props> <prop key="uname">root</prop> <prop key="pass">123</prop> </props> </propert>
基于注解和XML混合使用
IOC配置
-
第一步:spring配置文件中,配置context:component-scan标签
// 第一种:扫描保证的注解 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" //需加 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/beans //需加 http://www.springframework.org/schema/beans/spring-beans.xsd"> //需加 <!-- 自动扫描包中的注解--> <context:component-scan base-package="spring_01"/> //需加 </beans> // 第二种:声明加载的注解 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/> <bean name="message" class="spring_01.test02.entity.Message"> <property name="message" value="1111111"/> </bean> </beans>
-
第二步:类上面加上注解@Component,或者它的衍生注解@Controller、@Service、@Repository
注意:
// IoC注解(创建对象)相当于
<bean id="" class="">
DI依赖注入
注意:
//DI注解(依赖注入)相当于
<property name="" ref="">
<property name="" value="">
1)注解介绍
-
@Autowired
- @Autowired默认按类型装配(byType),
- @Autowired是由AutowiredAnnotationBeanPostProcessor类实现
- @Autowired是spring自带的注解
- @Autowired默认情况下要求依赖对象必须存在,如果需要允许null值,可以设置它的required属性为
false,如:@Autowired(required=false) - 如果我们想按名称装配(byName)可以结合 @Qualifier 注解进行使用
-
@Qualifier
- 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
- 它在给字段注入时不能独立使用,必须和@Autowire 一起使用;
- 但是给方法参数注入时,可以独立使用。
-
@Resource
-
@Resource默认按名称装配(byName),可以通过@Resource的name属性指定名称,如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,当找不到与名称匹配的bean时才按照类型进行装配。
-
@Resource属于J2EE JSR250规范的实现
-
但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
推荐使用@Resource注解,因为它是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
-
-
@Inject
- @Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;
- @Inject是JSR330中的规范,需要导入javax.inject.Inject;实现注入。
- @Inject可以作用在变量、setter方法、构造函数上。
-
@Value
-
给基本类型和String类型注入值
-
可以使用占位符获取属性文件中的值。
@Value(“${name}”) //name是properties文件中的key private String name;
-
@Autowired、@Resource、@Inject区别
1. @Autowired是spring自带的,@Inject是JSR330规范实现的,@Resource是JSR250规范实现的,需要导入不
同的包
2. @Autowired、@Inject用法基本一样,不同的是@Autowired有一个request属性
3. @Autowired、@Inject是默认按照类型匹配的,@Resource是按照名称匹配的
4. @Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Name一起使用
2)改变Bean作用范围的注解
-
@Scope:指定 bean 的作用范围,相当于下面的配置:
<bean id="" class="" scope="">
-
属性:
value:指定范围的值。取值:singleton prototype request session globalsession
3)生命周期相关注解
- @PostConstruct
- @PreDestroy
相当于:
<bean id="" class="" init-method="" destroy-method="">
- @Scope
@Scope可选值 | 描述 |
---|---|
singleton | 在 spring 容器中的是单例,从容器中获取该 bean 时总是返回唯一的实例。不写时默认为 singleton |
prototype | 每次获取 bean 时,都会生成一个新的对象,相当于 new 操作 |
request | 在一次 http 请求内有效(只适用于 web 应用) |
session | 在一个用户会话内有效(只适用于 web 应用) |
globalSession | 在全局会话内有效(只适用于 web 应用) |
4)注解和XML的选择问题
-
注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。 -
XML 的优势:
修改时,不用改源码。不涉及重新编译和部署。 -
Spring 管理 Bean 方式的比较:
基于XML 基于注解 Bean定义 @Component及衍生@Controller、@Service、@Repository Bean名称 通过id或者name指定 @Component(“name”) Bean注入 p命名空间或者 @Autowired@Resource Bean生命周期 @PostConstruct 初始化@PreDestroy删除 适合场景 Bean来自第三方 Bean的实现类由用户自己开发
基于纯注解使用
1 @Configuration
-
作用:用于指定当前类是一个 spring 配置类, 当创建容器时会从该类上加载注解。 获取容器时需要使用 AnnotationApplicationContext(有@Configuration 注解的类.class)。
-
属性:value,用于指定配置类的字节码文件
-
注意事项:获取容器时需要使用AnnotationApplicationContext类,才可以加载@Configuration注解的类的字节码对象。相当于xml文件
2 @ComponentScan
- 作用:用于指定 spring 在初始化容器时要扫描的包。和配置文件中的
context:component-scan
标签作用一样 - 属性:basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样
3 @Bean
-
作用:该注解只能写在方法上,@Bean 会自动把方法返回的结果加入ioc容器。同时将容器中的对象注入到方法参数中。
-
属性:name:给当前@Bean 注解方法创建的对象指定一个名称(即相当于bean 标签的 id)。
initMethod:初始化方法
destroyMethod:销毁执行方法
-
@Bean 修饰的方法的参数:
1.自动根据参数类型去容器中对象注入;
2.如果类型有多个,就根据形参名称取容器找该名称对应的对象注入
3.如果要根据指定名称在容器中找对象注入参数,需要用@Qualifier注解
4 @PropertySource
-
作用:用于加载.properties 文件中的配置
-
属性:value[],用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
5 @Import
- 作用:用于导入其他配置类,也就是建立各配置类与主配置类的关系。
- 属性:value[]:用于指定其他配置类的字节码对象。
- 注意事项:在引入其他配置类时,可以不用再写@Configuration 注解,也可以写
三、AOP
AOP简介
1)AOP介绍
特点
术语解释
-
Joinpoint(连接点)-------方法
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
-
Pointcut(切入点)
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
-
Advice(通知/增强)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕 通知(切面要完成的功能)
-
Introduction(引介)
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
-
Target(目标对象)-----------类
代理的目标对象
-
Weaving(织入)
是指把增强应用到目标对象来创建新的代理对象的过程
-
Proxy(代理)
一个类被AOP织入增强后,就产生一个结果代理类
-
Aspect(切面)------------切点和通知集合
是切入点和通知的结合,以后咱们自己来编写和配置的
-
Advisor(通知器、顾问)
和Aspect很相似
2)AspectJ
- AspectJ是一个Java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具
有AspectJ的AOP功能(当然需要特殊的编译器) - 可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言。更幸运的是,AspectJ与java程序完全
兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易。
了解AspectJ应用到java代码的过程(这个过程称为织入),对于织入这个概念,可以简单理解为aspect(切
面)应用到目标函数(类)的过程。 - 对于织入这个过程,一般分为动态织入和静态织入,动态织入的方式是在运行时动态将要增强的代码织入到目
标类中,这样往往是通过动态代理技术完成的,如Java JDK的动态代理(Proxy,底层通过反射实现)或者
CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术 - ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器
(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标
类。
3)实现AOP原理
- Spring AOP是通过动态代理技术实现的
- 而动态代理是基于反射设计的。(关于反射的知识,请自行学习)
- 动态代理技术的实现方式有两种:基于接口的JDK动态代理和基于继承的CGLib动态代理。
基于AspectJ的AOP使用
1)使用
其实就是指的Spring + AspectJ整合,不过Spring已经将AspectJ收录到自身的框架中了,并且底层织入依然是采
取的动态织入方式。
切入点表达式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
说明:
-
execution:必须要
-
修饰符:可省略
-
返回值类型:必须要,但是可以使用*通配符
-
包名 :
** 多级包之间使用.分割 ** 包名可以使用*代替,多级包名可以使用多个*代替 ** 如果想省略中间的包名可以使用.
-
类名
** 可以使用*代替 ** 也可以写成*DaoImpl
-
方法名:
** 也可以使用*好代替 ** 也可以写成add*
-
参数:
** 参数使用*代替 ** 如果有多个参数,可以使用 ..代替
通知类型
通知类型(五种):前置通知、后置通知、最终通知、环绕通知、异常抛出通知。
-
前置通知
* 执行时机:目标对象方法之前执行通知 * 配置文件:<aop:before method="before" pointcut-ref="myPointcut"/> * 应用场景:方法开始时可以进行校验
-
后置通知
* 执行时机:目标对象方法之后执行通知,有异常则不执行了 * 配置文件:<aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="result"/> * 应用场景:可以修改方法的返回值
-
最终通知:
* 执行时机:目标对象方法之后执行通知,有没有异常都会执行 * 配置文件:<aop:after method="after" pointcut-ref="myPointcut"/> * 应用场景:例如像释放资源
-
环绕通知(很强大,可以直接代替其他四个通知):
* 执行时机:目标对象方法之前和之后都会执行。方法执行前后分别执行,可以阻止方法的执行,必须手动执行目标方法 * 配置文件:<aop:around method="around" pointcut-ref="myPointcut"/> * 应用场景:事务、统计代码执行时机
-
异常抛出通知:
* 执行时机:在抛出异常后通知 * 配置文件:<aop:after-throwing method=" afterThrowing " pointcut- ref="myPointcut"/> * 应用场景:包装异常
2)XML实现
在pom.xml导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>spring-action</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- aspect依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy expose-proxy="true"/>
<bean id="customer" class="spring_02.test01.entity.CustomImpl"/>
<bean id="aspect" class="spring_02.test01.entity.Aspect"/>
<aop:config>
<aop:aspect id="logAspect" ref="aspect">
<!-- <aop:pointcut id="aspectBefore" expression="execution(* spring_02.test01.entity.Custom.addCustomer(..))"/>-->
<!-- <aop:before method="logBefore" pointcut-ref="aspectBefore"/>-->
<!-- 前置通知 -->
<aop:before method="logBefore" pointcut="execution(* spring_02.test01.entity.Custom.addCustomer(..))"/>
<!-- 最终通知,异常也执行-->
<aop:after method="logAfter" pointcut="execution(* spring_02.test01.entity.Custom.addCustomer(..))"/>
<!-- 后置通知,异常不执行-->
<aop:after-returning method="logAfterReturning"
pointcut="execution(* spring_02.test01.entity.Custom.addCustomerReturnValue(..))"
returning="result1"/>
<!-- 异常抛出通知,抛出异常后通知-->
<aop:after-throwing method="logAfterThrowing"
pointcut="execution(* spring_02.test01.entity.Custom.addCustomerThrowException(..))"
throwing="error"/>
<!-- 环绕通知-->
<aop:around method="logAround"
pointcut="execution(* spring_02.test01.entity.Custom.addCustomerAround(..))"/>
</aop:aspect>
</aop:config>
</beans>
3)注解实现
在pom.xml导入依赖(同上)
注解替换
-
@Aspect:作用是把当前类标识为一个切面供容器读取
<aop:aspect id="logAspect" ref="aspect"/>
-
@Before(value = “execution(* spring_02…CustomerBo.addCustomer(…))”)
@After(value = “LoggingAspect.com()”)
@AfterReturning(pointcut = “LoggingAspect.com()”, returning = “result”)
@AfterThrowing(pointcut = “LoggingAspect.com()”, throwing = “error”)
@Around(value = “LoggingAspect.com()” )
@Pointcut(value = “execution(* spring_02.test02.entity.CustomerBo.*(…))”) #通用的切入点
<!-- 前置通知Before --> <aop:before method="logBefore" pointcut="execution(* spring_02..Custom.addCustomer(..))"/> <!-- 最终通知,异常也执行After--> <aop:after method="logAfter" pointcut="execution(* spring_02..Custom.addCustomer(..))"/> <!-- 后置通知,异常不执行AfterReturning--> <aop:after-returning method="logAfterReturning" pointcut="execution(* spring_02..Custom.addCustomerReturnValue(..))" returning="result1"/> <!-- 异常抛出通知,抛出异常后通知AfterThrowing--> <aop:after-throwing method="logAfterThrowing" pointcut="execution(* spring_02..Custom.addCustomerThrowException(..))" throwing="error"/> <!-- 环绕通知Around--> <aop:around method="logAround" pointcut="execution(* spring_02..Custom.addCustomerAround(..))"/>
-
@EnableAspectJAutoProxy 开启aspect注解
<aop:aspectj-autoproxy expose-proxy="true"/>
实现
-
config配置
@Configuration @ComponentScan(basePackages = "spring_02.test02") @EnableAspectJAutoProxy
-
切面
@Aspect @Component public class LoggingAspect { @Before(value = "LoggingAspect.com()") public void logBefore(JoinPoint joinPoint) { ... }
-
测试单元
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=Config.class) public class Test { @Resource private CustomerBo customerBo; @org.junit.Test public void test(){ customerBo.addCustomer(); System.out.println("=============="); } }
四、组件支撑篇
整合Junit
1)单元测试实现过程
-
第一步:添加依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency>
-
第二步:测试类
package spring_02.test02.test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import spring_02.test02.entity.Config; import spring_02.test02.entity.CustomerBo; import spring_02.test02.entity.CustomerBoImpl; import javax.annotation.Resource; /** * @Author monkey * @Date 2021/11/10 14:19 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=Config.class) //config配置类 //@ContextConfiguration(locations="classpath:xxx.xml") //xml配置文件 public class Test { @Resource private CustomerBo customerBo; @org.junit.Test public void test(){ System.out.println("=============="); } }
事务支持
1)事务介绍
-
事务特征(ACID):原子性,一致性,隔离性,持久性
-
事务隔离级别
-1 **原子性(atomicity)** :事务最小工作单元,要么全成功,要么全失败 。 -2 **一致性(consistency)**: 事务开始和结束后,数据库的完整性不会被破坏 。 -3 **隔离性(isolation)** :不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提 交)、RR(可重复读)、SERIALIZABLE (串行化)。 -4 **持久性(durability)** :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失