Spring是2003年兴起的一款轻量级的,非侵入式的IOC与AOP的一站式java开发框架,是为了简化企业级开发而诞生的
轻量级:Spring 的jar包都在1M左右,框架运行时的占用资源较小,运行效率较高
非侵入式:业务代码不需要继承或实现Spring中的接口或者类去实现业务代码,而是通过配置文件即可简化开发流程
IOC:控制反转,Spring能够帮助我们管理java中的对象
AOP:面向对象编程,减少业务开发过程中代码冗余,提高开发效率
一站式框架:Spring本身也提供了Web功能与数据访问功能,还能管理其他的框架
Spring体系结构图
控制反转,Inverse of Control简称IOC,IOC是一种设计思想,是将本来在程序中创建对象的权力移交给框架。
IOC容器是具有依赖注入功能的容器,容器能够帮助我们从创建对象,初始化对象,对象的组装与对外访问对象的方法到对象的销毁一并管理。IOC容器类似于一个Map集合,在对象被创建之后会被加入到这个集合中,其中的键值存储的是对象的名称或者类名,值存储的是对象的地址。需要使用对象时无需再手动创建对象,直接从容器中获取即可。
由Spring管理的对象统称为bean对象
通过XML配置来管理bean对象
在Spring的配置文件中通过bean标签进行对象的配置
bean标签中的属性:
id:对象名
name:对象的别名,可以由多个
class:全类名,通过反射机制创建对象
scope:对象的模式,由两个取值分别为singleton与prototype
singleton(默认值):在 Spring 中只存在一个 bean 实例, 单例模式.
prototype:每次使用对象的时候都会创建一次的该对象
request:每次 http 请求都会创建一个 bean, 仅用于 WebApplicationContext环境
session:同一个 http session 共享一个 Bean, 不同 Session 使用不同的 Bean,使用环境同上
xml中依赖注入(DI)的方式:
Spring 创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设置给该对象。
例如在配置adminService对象的同时将adminDao的对象依赖注入进入该对象
通过注解的方式管理bean对象
准备工作
首先需要导入注解需要的jar,在导入spring的jar依赖时一般会自动导入
然后需要在spring的配置文件中进行注解的配置
该配置还有一个前提是需要添加该配置的约束
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation= "http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
spring配置中的一些常用约束
管理bean的相关注解
@Component(value=“user”),该注解一般用于对实体类使用能够将该类的对象注入到容器中,相当于xml配置中的
< bean id="user" class="">< /bean>
@Service(value=“user”),用于将Servic层的类对象注入容器
@Repository(value=“user”),用于将Dao层的类对象注入容器
@Scope(value=“prototype”),用于注明类对象的模式,相当于bean标签的scope属性
前三个注解的功能相同,只是应用与不同层的对象
自动注入注解标签
@Autowired
该标签是由Spring框架提供的注解可以写在字段和 setter 方法上。如果写在字段上,那么就不需要再写 setter 方法。默认情况下它要求依赖对象必须存在(即已经注入容器内),如果允许 null 值,可以设置它的 required 属性为 false。
该注解有两种匹配对象的方式:
1.byType
该注解默认使用该种方式进行自动注入,注入时通过对象的类型来查询此对象
2.byName
该种方式是通过对象定义的id属性值来查找,需要搭配@Qualifier(value = "")标签使用,value值是对象的id属性
@Resource
该标签是jdk提供的,与@Autowired的用法类似
该注解也有两种匹配对象的方式:
1.byType
默认使用此方式
2.byName
@Resource(name="")
XML配置与注解的优缺点
注解:
优点:方便,直观,效率高,不用配置
缺点:修改需要重新编译代码再进行部署
XML配置:
优点:XML配置修改无需重新编译
缺点:开发工作量大,配置的开发需求多
面向切面编程,(Aspect Oriented Programming)简称AOP,AOP的核心思想是将业务逻辑代码与非业务逻辑代码进行隔离,降低业务代码与非业务代码的耦合性。AOP能够做到将非业务代码提取出来,在不修改原业务代码的情况下将非业务的代码功能添加进去。这样能够提高代码的复用率以及开发效率
注:非业务代码例如有,打印日志,事物提交,异常处理等
作用:减少重复代码,专注与业务开发,提高效率
AOP与OOP的区别:
OOP是对业务中的实体中的属性与行为的抽象封装,以获得更清晰的逻辑单元
AOP是在业务处理过程中,对业务代码以及非业务代码进行隔离,例如可以将业务代码与非业务代码看作不同切面的代码,将一个个方面的代码隔离开来,以降低代码的耦合性
Spring中的AOP底层实现是通过动态代理实现的,可以简单理解为,在业务代码中需要调用非业务代码时就通过框架提供生成的代理对象去调用非业务代码
连接点:类中可以被增强的方法被称为连接点,增强是指可以添加新的功能
切入点:类中实际被增强的方法被称为切入点
通知:在切入点中的被增强的具体功能被称为通知,通过按照通知调用的时间点又可以分为前置通知,后置通知,异常通知,最终通知以及环绕通知
切面:通知被切入点调用的整个过程
目标:代理的目标对象(连接点,切入点所在类)
代理(Proxy): 向目标对象应用通知时创建的代理对象
AOP并不是Spring中特有的编程思想,实际上很多的框架都实现的了AOP的编程思想,AspectJ是基于java语言实现的AOP编程框架,实现方式便捷且支持注解式的开发,Spring中引入了框架的实现
在Spring中实现AOP功能的搭建:
首先,增加AspectJ的jar依赖
org.springframework
spring-aspects
5.2.2.RELEASE
1.基于xml配置的实现方式
< aop:pointcut >标签中的expression属性值是业务代码方法的表达式
固定格式:execution(返回值 全类名.方法名(..))
返回值*代任意类型的返回值
类名使用*代表全部的类 例如com.example.test. *
方法名可以使用*代表所有方法
方法后的小括号代表参数列表".."代表任意长度的参数长度,不填的话代表无参的方法
通知类型
前置通知:在业务代码前调用通知
其中method表示通知,pointcut-ref表示切入点
后置通知:在业务代码后进行调用,如果业务代码出现异常则不会再进行调用
异常通知:业务代码中出现异常时会调用该通知
throwing表示通知中传入的参数,异常通知的参数列表中需要定义一个Throwable类的参数
最终通知:无论业务代码中出不出现异常都会被调用
环绕通知:可以单独同时实现上面四种通知调用
在环绕通知中需要传入一个ProceedingJoinPoint类的参数,该类代表的是切入点
环绕通知的实现方式:
ProceedingJoinPoint类中的proceed()方法表示执行切入点,根据该方法调用的相对位置的代码对应不同类型的通知,例如在前面编写的代码表示前置通知,catch代码块内的代码为异常通知
public void aroundAdvice(ProceedingJoinPoint point){
try {
System.out.println("前置通知");
point.proceed();
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知"+throwable.getMessage());
}
System.out.println("最终通知");
}
2.基于注解的方式实现
①首先需要在配置文件中添加aop的注解支持
②在AOP的的代码类的字段上使用@Component与@Aspect注解,前者是将该类的对象注入进Ioc容器,后者是代表该类是一个切面类
@Component @Aspect//表明该类是一个切面类 public class MyUtil {}
③在通知的字段上使用对应的通知注解
前置通知:@Before("execution(* com.yzl.spring.dao.AdminDao.save(..))")
后置通知@AfterReturning("execution(* com.yzl.spring.dao.AdminDao.save(..))")
异常通知:@AfterThrowing(value = "execution(* com.yzl.spring.dao.AdminDao.save(..))",throwing = "e")
最终通知:@After("execution(* com.yzl.spring.dao.AdminDao.save(..))")
环绕通知:@Around("execution(* com.yzl.spring.dao.AdminDao.save(..))")
通知内容的参数写业务代码对应方法的表达式,其中需要多个参数的通知例如异常通知需要使用value属性值写出表达式来区分throwing属性值中的参数
//前置通知
@Before("execution(* com.yzl.spring.dao.AdminDao.save(..))")
public void printLog(){
System.out.println("打印日志");
}
//后置通知
@AfterReturning("execution(* com.yzl.spring.dao.AdminDao.save(..))")
public void commit(){
System.out.println("事物提交");
}
//异常通知
@AfterThrowing(value = "execution(* com.yzl.spring.dao.AdminDao.save(..))",throwing = "e")
public void exceptionService(Throwable e){
System.out.println("异常通知"+e.getMessage());
}
//最终通知
@After("execution(* com.yzl.spring.dao.AdminDao.save(..))")
public void finalAdvic(){
System.out.println("最终通知");
}
//环绕通知
@Around("execution(* com.yzl.spring.dao.AdminDao.save(..))")
public void aroundAdvice(ProceedingJoinPoint point){
try {
System.out.println("前置通知");
point.proceed();
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知"+throwable.getMessage());
}
System.out.println("最终通知");
}