Spring
一、 什么是Spring?
-
Spring是一个优秀的开源的企业级框架。
-
Spring和其他框架不同,其他框架大多是专注于某一领域的框架(如MyBatis专注于持久层的框架),而Spring是一个包罗万象的框架。
-
Spring是一个管理框架的框架,尽管Spring包罗万象,但它又不具体做任何事情,Spring奉行一个“不重复制造轮子”的原则,如已经存在了MyBatis这样的持久化框架,Spring就不会重复的开发一个持久化框架,而是使用MyBatis等持久化框架,实现持久化功能,Spring是通过集成其他框架实现各领域的管理和协作。
-
Spring通过管理和整合软件开发中使用到的各种组件和框架,实现各组件和框架的搭配组合使用,并让各组件和框架的使用更简洁、更方便,Spring在整合其他框架的过程中,将一些通用的功能、各组件间的依赖关系自动管理起来,让开发者从这些繁琐重复的代码中解脱出来,从而实现“优雅编程”(开发者只需编写个性化和业务相关的代码),让开发变得更简单、更方便。
-
Spring的作用:
(1) 对系统中的各个组件进行有效的管理让其能协调工作
(2) 使各个组件之间的耦合度降低,提高各个组件的可维护性(高内聚低耦合)
-
Spring中使用面向接口编程
-
Spring提供两大核心技术,其他技术都是在这两项技术的基础上搭建起来的:IOC(***控制反转和AOP(***面向切面编程)
(1) IOC:称为控制反转,将对象的创建及对象间依赖关系的维护权由对象自身交给***外部容器***管理,这种管理权的转移我们称为控制反转,实现各组件间的解耦;IOC的实现依赖DI(依赖******注入)
(2) AOP:面向切面编程, 将系统中多处都要使用的通用功能,从各个部分分离出来,单独开发和维护,不再各个部分代码中调用。而是在系统运行时由外部容器将这些功能嵌入到各部分代码中。(过滤器、拦截器都属于面向切面思想)
- Spring体系结构
二、 使用Spring框架
- Spring框架提供了两种使用方式:
(1) 基于XML实现
使用步骤:
- 在Maven中引入spring-context依赖包,此时Maven会将spring的相关包如core、aop、beans、expression包同步下载
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
2.在项目中resource文件夹下配置spring的核心配置文件(applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
配置bean标签,将对象交给spring容器管理
此时class指定的类的对象由spring容器来创建并管理
id:对象名
-->
<bean id="userDao" class="com.jiazhong.spring.dao.impl.UserDaoImpl"/>
</beans>
3.使用spring容器创建并使用对象
/**
* 启动spring环境并初始化spring容器
* ClassPathXmlApplicationContext是ApplicationContext接口基于XML的一个实现类
*/
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获得对象,根据对象名获得
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.addUser();
(2) 基于注解编程式实现
-
创建一个配置类,任意一个类,但要使用@Configuration
-
在配置类中定义方法,用于创建Bean对象,在方法上要加入@Bean注解,@Bean注解的作用,将方法的返回值对象交给spring进行管理,对象名默认为方法名,我们可以通过注解设置对象名
@Configuration//加入此注解后该就成了spring的一个配置类
public class ApplicationContextConfig {
//该注解只能在配置类中的方法中使用,表示将方法的返回值交给spring进行管理的一个Bean对象
//Bean注解默认的对象名为方法名,我们可以设置对象名@Bean("userDao")
@Bean()
public UserDao userDaoImpl(){
return new UserDaoImpl();
}
}
//使用时
public class Test1 {
public static void main(String[] args) {
//初始化Spring环境并指定核心配置类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
UserDao userDao = (UserDao) applicationContext.getBean("userDao1");
userDao.queryUser();
}
}
Spring对象管理
(1) Spring管理对象的创建时机
a) 默认情况下,spring容器一创建,由spring管理的对象同步创建
b) 可以通过相关配置修改Spring受管对象的创建时机
c) 配置lazy属性可以让spring的受管对象进行懒加载创建(在使用时创建)
设置lazy的方式有两种:
1.设置全局对象lazy(Spring的所有收管对象使用懒加载机制)
在XML的beans标签中配置:
default-lazy-init=“true”:设置全局受管对象懒加载,当第一次使用该对象时才创建
2.设置某个对象使用懒加载机制创建
在bean标签中加入lazy-init=“true”
(默认情况下,spring容器管理的对象是单例的
受管对象的实例只有一个
我们可以通过相应的配置设置spring收管对象的实例
通过在bean标签中配置scope="prototype"来设置对象每次使用都创建一个新对象**
Scope设置为prototype后容器启动时将不会创建类的对象,而是每次使用时创建该类的对象,此时对象并不在spring容器中存放)
(2 )对象初始化和销毁方法
基于XML配置中的init-method属性指定初始化方法(构造方法执行后自动调用),destory-method方法指定销毁方法(对象销毁前自动调用)
- Spring的依赖注入(DI)
在Spring中所有对象的创建及管理都由spring容器负责,如果一个对象中要使用其他由spring容器管理的对象时,就需要spring通过依赖注入将对象注入到使用的对象中;”简言之依赖注入指向对象的属性赋值”
(1) 基于XML的依赖注入
- Set注入:通过set方法注入依赖对象,为依赖对象提供set方法
<?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">
<!-- default-lazy-init="true":设置全局延迟创建Bean对象-->
<!--
Spring默认情况下,在启动时会创建受管对象
默认情况下,Spring的对象时单例的
bean标签用于将类交给Spring进行管理
name:设置对象名
class:设置对象的类
lazy-init:延迟初始化,当使用该对象Spring才创建该对象
scope="prototype":设置Spring对象是单例存在还是每使用一次创建一个(singleton<单例>|prototype<非单例>)
面试题:Spring中一个Bean设置scope属性的为"prototype",该对象是否(受Spring管理)存在于spirng容器中
-->
<bean name="userDao"
class="com.jiazhong.dao.impl.UserDaoImpl"
lazy-init="false"
scope="singleton"
init-method="init"
destroy-method="destory"></bean>
<!-- 当service对象需要userDao对象时-->
<bean name="userService" class="com.jiazhong.service.impl.UserServiceImpl">
<!--
向userService对象注入userDao对象
property表示set注入,属性必须提供set方法
ref:指定的是一个由spring受管的对象
value:基本类型
-->
<property name="userDao" ref="userDao"></property>
//基本类型注入
<!-- <property name="name" value="小明"></property>
//集合类型注入
<property name="myset">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="mymap">
<map>
<entry key="aaa" value="bbb"></entry>
<entry key="bbb" value="ccc"></entry>
</map>
</property>
<property name="myprops">
<props>
<prop key="aaa">aaa</prop>
<prop key="bbb">bbb</prop>
</props>
</property>
//构造器注入
<constructor-arg index="0" value="1"></constructor-arg>
<constructor-arg index="1" value="小明"></constructor-arg>
<constructor-arg index="2" >
<list>
<value>1111</value>
</list>
</constructor-arg>
-->
</bean>
Aop 面向切面
AOP(面向切面编程)全称Aspect Oriented Programming
AOP就是把系统中重复的代码抽取出来,单独开发 ,在系统需要时,使用动态代理技术,在不修改源码的基础上,将单独开发的功能通知织入(应用)到系统中的过程,完成完整的代码逻辑
优点:减少重复代码、提高开发效率、提高维护性
AOP相关术语:
1.Joinpoint(连接点):连接点是指那些被拦截到的点。在spring中这些点指的是方法,spring只支持方法类型的连接点。
指切面在系统中要应用的位置,spring中能够使用切面的位置为方法,所以连接指方法
2.Pointcut(切入点):指我们要对那些Joinpoint进行拦截的定义,spring中使用切入点表达式定义(切入点表达式)
简单理解,切入点是对连接点的描述,它将多个连接点进行概括的定义
3.Advice(通知):指拦截到joinpoint之后所要做的事情就是通知
通知:前置通知、后置通知、异常通知、最终通知、环绕通知
4.Target(目标对象):代理的目标对象
5.Weaving(织入):将通知应用到切入点的过程
6.Proxy(代理):代理对象
7.Aspect(切面):切入点和通知的总称
简单说AOP是,在系统执行某个方法时,在执行这个方法前或方法后去执行另一段程序(另一段程序就是AOP中的通知),而拦截这个方法的定义可以称为切入点
SpringAOP的使用步骤(基于注解):
1.引入spring依赖包和aop植入依赖包AspectJ Weaver包
2.创建切面类
3.将切面类交个spring进行管理,使用@Component注解
4.将切面类设置为一个切面,使用@Aspect注解
5.编写切入点poincut,使用切入点表达式定义连接点(方法)。
Execution(表达式)
表达式写法:访问修饰符 返回类型 包名.包名.类名.方法名(参数列表)//指定那些方法做为切入点(拦截那些方法)
使用具体方法方式:void com.jiazhong.service.impl.UserServiceImpl.addUser()
使用通配符方式:* ….*(…)
6.编写相关通知
/**
流程:
1.第一步
@Component//将切面类交给Spring管理
@Aspect//表示该类为一个切面类
@EnableAspectJAutoProxy//开启AOP的动态代理
2.第二🙅
定义切入点,使用切入点表达式定义要拦截的方法的连接点
@Pointcut:该注解用于定义切入点表达式
3.第三步
@Before("myPointcut()") 前置通知
@AfterReturning("myPointcut()")后置通知
@AfterThrowing("myPointcut()")异常通知
@After("myPointcut()")
/**
* 环绕通知,它可以在方法调用前、后、异常中、最终执行
* 在环绕通知内部必须主动调用目标方法
* @param proceedingJoinPoint*/
@Around("myPointcut()")
*/
//---------------------------------------------------------
@Component//将切面类交给Spring管理
@Aspect//表示该类为一个切面类
@EnableAspectJAutoProxy//开启AOP的动态代理
public class MyAspect {
/**
* 定义切入点,使用切入点表达式定义要拦截的方法的连接点
* @Pointcut:该注解用于定义切入点表达式
* myPointcut():切入点的名字
*
* 切入点表达式:
* (1)void com.jiazhong.service.impl.UserServiceImpl.addUser():连接点是 void addUser()方法
* //拦截UserServiceImpl类中的所有方法,*表示任意,..表示任意参数
* (2)* com.jiazhong.service.impl.UserServiceImpl.*(..)
* //拦截impl包及其子包中的类的所有方法
* (3)* com.jiazhong.service.impl..*.*(..)
*
*/
//定义切点
@Pointcut("execution(* com.jiazhong.service.impl.UserServiceImpl.*(..))")
private void myPointcut(){}//声明切入点和方法的声明一样,但没有方法体
/***
* 定义通知
* 1.前置通知:在目标方法调用前执行的通知
@Before("myPointcut()")
public void beforeAdvice(){
System.out.println("执行前置通知...");
}*/
/**
* 后置通知
@AfterReturning("myPointcut()")
public void afterReturnning(){
System.out.println("执行后置通知...");
}*/
/**
* 异常通知
@AfterThrowing("myPointcut()")
public void afterThrowing(){
System.out.println("异常通知...");
} */
/***
* 最终通知
@After("myPointcut()")
public void after(){
System.out.println("最终通知...");
}*/
/**
* 环绕通知,它可以在方法调用前、后、异常中、最终执行
* 在环绕通知内部必须主动调用目标方法
*/
@Around("myPointcut()")
public Object round(ProceedingJoinPoint proceedingJoinPoint){
try {
//前置通知
System.out.println("开始调用目标方法...");
//获得目标方法签名
Signature signature = proceedingJoinPoint.getSignature();
System.out.println(signature.getName());
Object obj = proceedingJoinPoint.proceed();//调用目标方法
//后置通知
System.out.println("目标方法调用结束...");
return obj;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知....出现异常了");
}finally {
System.out.println("最终通知...");
}
return null;
}
}