文章目录
一、Spring概念
1、概述
为了简化企业应用程序的开发而创建。
有以下优势:
- 分层架构(MVC模式分层架构)
- 为JavaEE程序开发集成各种框架(比如MyBatis)
- 遵守软件设计的五大原则
- 非侵入式框架(Servlet是侵入式组件):我们编写的业务不用继承或实现spring的任何组件;方便将当前业务剥离spring,移植到其他容器。
2、软件设计的五大规则
- 开闭原则:对源代码的修改关闭,对系统的升级维护开放
- 李斯特替代原则
- 依赖倒置原则:高层模块(业务)不依赖于底层模块(具体实现细节),都依赖于抽象。面向接口编程是这个原则的具体实现方式,在业务中,依赖的是接口,而不是具体实现,在运行时由容器注入具体依赖的实现,有利于降低耦合度。
- 组合组装原则
- 迪米特原则:组件独立,降低类之间的耦合性
3、Spring的组件构成
- Spring Core
创建组件的容器,Spring核心,最基础的部分,包括Spring运行期间需要的工具类库。提供IoC特性和DI依赖注入特性。 - Spring AOP
保证系统正常运行,记录系统运行时的状态,保证系统的事务和异常处理功能的组件。 - Spring ORM
对象映射组件(实体化组件),用于不同系统的数据之间转换,支持流行的ORM框架整合,比如MyBatis等组件 - Spring Web
提供对web工具的支持,包括:HttpSession、HttpServletRequest、HttpServletResponse、HttpServletContext - Spring MVC
把所有的用户请求转发给不同的控制器组件去处理,DispatcherServlet是SpringMVC的核心组件。替代Servlet,开发人员去关注业务处理,而不是数据传输。 - Spring DAO
Spring数据持久化组件,用于Spring的JPA框架支持,springdata框架使用这个组件实现数据持久化。 - Spring Context
Spring上下文容器,用于处理调用者和spring容器之间的会话。
ApplicationContext是SpringContext的核心组件。
用于加载applicationContext.xml文件(注入的类或者调用者依赖的组件类都要在这个文件中配置)
二、Spring核心——IoC
先导入spring依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
1、控制反转定义
对象创建的控制权由代码转换到外部容器,这种思想称为控制反转。
传统的编程以硬编码的方式直接在代码中创建对象,替换对象时就得修改调用方,耦合度高;控制反转的方式是在运行时,容器会根据配置信息,自动创建并注入依赖关系。
2、依赖注入DI
2.1 定义
外部容器为应用程序提供依赖项,而不是对象自己创建所需的依赖,从而达到解耦,依赖注入是Spring控制反转思想的具体实现。
无参注入对象是提供应用程序运行所需的依赖,为对象赋值是提供对象调用所需的依赖,都是依赖注入
2.2 依赖注入的方式
2.2.1 构造器注入
- 无参构造注入:容器创建对象的时候不传参,就是无参构造器注入;注入了对象
- 带参构造注入:容器创建对象的时候传参就是使用带参构造器注入;即给容器中注入了对象,又为对象注入了属性
index表示带参构造参数的索引
为第一个属性品牌赋值:<constructor-arg index="0" value="罗技"/>
为第二个属性颜色赋值:<constructor-arg index="1" value="紫色"/>
2.2.2 setter注入
是给对象注入属性
容器无参创建对象,创建完之后调用set方法为属性赋值
<property name="属性" value="属性值"/>
2.2.2.1注入集合
<bean id="usbService" class="com.service.impl.UsbMouseService">
<property name="devName" value="罗技鼠标"/>
<property name="pingJiaList">
<list>
<value>非常棒,手感很好</value>
<value>鼠标质量很好</value>
<value>鼠标很丝滑</value>
</list>
</property>
<property name="liShiPriceSet">
<set>
<value>199</value>
<value>129</value>
<value>99</value>
</set>
</property>
<property name="lingMinMap">
<map>
<entry key="600" value="慢"/>
<entry key="800" value="中"/>
<entry key="1000" value="快"/>
</map>
</property>
</bean>
2.2.3 FactoryBean工厂注入
- 写一个工厂类,实现FactoryBean<>接口
- 重写接口方法
public class BookServiceFactory implements FactoryBean<BookService> {
@Override
public BookService getObject() throws Exception {
return new BookServiceImp();//返回你要的对象
}
@Override
public Class<?> getObjectType() {
return BookService.class;//返回接口的字节码文件
}
@Override
public boolean isSingleton() {
return true;//true是单例,false是非单例
}
}
- 将工厂对象注入容器
<bean id="bookServiceFactory" class="com.jz.fanctory.BookServiceFactory"/>
- 使用的时候获取工厂对象
BookService bookService = (BookService) ctx.getBean("bookServiceFactory");
2.2.4 接口注入
非常不常用。需要依赖的类实现指定接口,重写方法,做赋值操作就是接口注入
3、配置Bean
3.1 常用属性
- scope:不设置scope属性,默认单例,即创建的是同一个对象。单例:singleton;非单例:prototype
- init-method:bean初始化执行的方法
- destroy-method:bean销毁时(容器正常关闭)执行的方法
<bean id="bookService" class="com.jz.service.imp.BookServiceImp" scope="singleton" init-method="init" destroy-method="destory" />
3.2 控制bean的生命周期的第二种方式
spring容器提供了两个接口:InitializingBean、DisposableBean
指定的Bean实现这两个接口,重写两个方法就是初始化和销毁执行的方法。
3.3 正常关闭容器
一般情况下,Bean销毁之前的方法不会被调用执行的。因为spring容器不负责主动执行销毁方法,只有容器正常关闭时才会执行。
3.3.1 注册关闭钩子
可以放在任何位置
3.3.2 close直接关闭容器
只能放在最后
3.3.3 演示
两个方法都在ClassPathXmlApplicationContext类中
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();//注册关闭钩子,可以放在任何位置
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
//ctx.close();直接关闭容器,只能放在最后
}
4、Spring的装配模式
spring容器为属性赋值的方式,装配是依赖注入的具体实现。
4.1 xml配置
可以在xml的<bean/>
中配置属性autowire的值,默认为no、byType、byName、constructor
- 按类型装配
- 按名称装配
- 构造器装配
4.2 注解配置
@Resource
:@Resource(name=“类名”, type=类.class)更加精准,是jdk提供的,jdk11后就没有了,再由jacket提供@Autowired
:默认按类型装配;无此类型时,或者依赖的接口不止一个实现类,容器就不知道装配哪个就报错@Autowired和@Qualifier("name")
:如果容器中接口有多个类型,容器不知道装配哪个,就用第二个注解配合指定名称,联合装配
5、注入第三方Bean
比如我们要注入数据库连接池Druid对象
5.1 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.32</version>
</dependency>
5.2 编写jdbc.properties配置文件
用jdbc.
开头,避免和系统环境变量名冲突
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bankdb
jdbc.username=root
jdbc.password=123456
5.3 在xml中开启命名空间,加载properties文件
5.4 注入依赖
在xml中注入DruidDataSource对象,
并通过${}占位符为其属性注入文件中的值
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- 加载命名空间,读取配置文件 -->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
6、使用注解与配置文件实现注入
不需要在配置文件中注入bean类。只用在配置文件的头文件引入命名空间和注解扫描器
6.1 配置文件的头部引入命名空间和注解扫描器
6.1.1 引入代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 注入注解的支持描述 -->
<context:annotation-config></context:annotation-config>
<!-- 注入注解扫描器 -->
<context:component-scan base-package="com.dao"/>
<context:component-scan base-package="com.service"/>
</beans>
6.1.2 可能报错:
解决
6.2 注入Bean的注解
都用于描述bean的注入方式,替代<bean id="" class=""/>
。
@Component:声明bean的基础注解,若有类不属于mvc三层时;比如工具类就用此注解
@Controller:标注在控制器类上
@Service:标注在业务类上
@Repository:标注在数据访问类上,由于MyBatis整合了,用的少
@Bean:表示当前方法的返回值是一个Bean
@Configuration:配置类
三、Spring核心——Aop
1、定义
面向切面编程:横向操作,在不惊动原始设计的情况下为其增加功能
2、作用
对业务逻辑各部分进行隔离,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性、维护性,同时提高了开发的效率。
3、应用场景
在开发中将公共的事务、日志、安全、异常处理等组件独立出来,作为公共模块作用于系统的所有业务组件,不纠缠在业务代码中,开发者就不关心这些了。
4、AOP的底层实现——动态代理
通过代理对象和拦截器去实现的,这些代理对象可以拦截和增强目标对象的方法调用。
5、使用步骤
5.1 编写拦截器,并注入spring容器。这也是通知类
<bean id="serviceInterceptor" class="com.interceptor.ServiceMethodInterceptor"/>
实现了MethodInterceptor接口的类就是拦截器,重写invoke方法。在里面可以控制访问原方法
Object obj = invocation.proceed();就是放行对原方法的访问,有返回值的话要将返回值返回出去。
5.2 注入代理工厂,并绑定拦截器
Spring容器处理Aop的核心组件——ProxyFactoryBean:负责创建接口的代理对象,而不是普通对象,只有代理对象访问才会被拦截,增强效果。
有JDK动态代理工厂和CGLIB动态代理工厂
proxyTargetClass
:指定true使用cglib代理,false或不写这个属性都是jdk
proxyInterfaces
:指定要代理的接口,只有jdk需要写
target
:指定代理的具体类
interceptorNames
:指定拦截器
<!-- 注入代理bean工厂,代理业务接口类 -->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定使用cglib代理,值为false或不写这行默认就是jdk代理 -->
<property name="proxyTargetClass" value="false"/>
<!-- 描述将要代理的接口(jdk代理才需要写) -->
<property name="proxyInterfaces" value="com.service.EmpService"/>
<!-- 指定接口的实现目标类 -->
<property name="target" ref="empService"/>
<!-- 指定拦截器 -->
<property name="interceptorNames" value="serviceInterceptor"/>
</bean>
5.2.3 二者区别
- jdk只能代理实现了接口的类,cglib可以代理单独的类
- jdk指定代理的类,cglib接口的全部类都代理
- jdk通过反射机制实现,cglib直接在使用cglib库生成生成字节码,性能比jdk好得多
- jdk用于接口较多的场景,如果有单独类的话只能用cglib
6、相关名词
- 连接点:程序执行过程中能够插入切面的一个点,这个点可以是任何位置(调用方法,抛出异常,修改打印等等)
- 切入点:匹配通知要植入的连接点的式子
- 通知:切入点要执行的操作,也就是共性的功能
- 通知类:包含通知方法的类
- 切面:描述通知与切入点的对应关系
四、Spring的事务处理
1、事务处理的方式
1.1 编程式事务
使用TransactionTemplate对象调用方法,显式管理事务,比如,需要显示调用commit或者rollback方法
1.2 声明式事务
建立在aop基础上的,通过aop对注解的方法拦截,在方法执行前开启事务,方法执行完后根据情况提交或回滚事务。通过配置文件和注解实现自动开启提交回滚。
1.2.1 优缺点
优点:不需要在业务代码中掺杂事务管理,减少代码污染
缺点:只能做到方法级别,不能做到像编程式事务那样的代码块级别
2、事务管理器的分类
PlatformTransactionManager是所有事务管理器的父类
- JDBC的事务管理器:DataSourceTrancationManager,MyBatis使用这个
- Hibernate的事务管理器:HibernateTrancationManager
- JPA的事务管理器:JtaTrancationManager,SpringData使用这个
3、声明式事务管理器使用步骤
- 在配置文件中配置数据源,链接数据库
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bankdb?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
- 注入事务管理器,并指定事务管理器用于哪个数据源
<!-- 注入事务管理器,指定用于哪个数据源 -->
<bean id="txtransaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
- 启用基于注解的事务管理功能,指定要使用的事务管理器
这个标签内部还配置了aop功能用于拦截配置了@Transactional的方法
<!-- 启用基于注解的事务管理功能,属性指定用哪个事务管理器,隐式配置了aop功能 -->
<tx:annotation-driven transaction-manager="txtransaction"/>
- 在要使用事务的类或方法前加注解@Transactional
五、SpringMVC
1、概念
MVC是一种设计模式,将应用程序分为模型、视图、控制器三个独立的组件。
SpringMVC是基于Servlet实现了MVC模式的轻量级的Web框架,是Spring的子框架,替换servlet做控制层开发。
2、作用
- 用于替代Servlet构建控制器层:接收请求参数,产生响应结果。
- 减少开发者对数据传输细节的处理,省去了request.getParameter(…)
- 非侵入组件技术,方便代码的移植和维护升级
3、SpringMVC的构成
- DispatcherServlet
是SpringMVC的核心组件,它实现了Servlet接口 - 配置文件
dispatcher-servlet.xml - 拦截器组件
Bean的注入拦截器
参数拦截器
文件上传拦截器
安全验证和类型转换拦截器
请求链接的拦截器:哪些请求归mvc处理
4、使用步骤
4.1 引入依赖:spring-webmvc、javax.servlet.api,创建目录结构包
4.2 编写两个配置文件,修改web.xml
- 编写applicationContext.xml文件,注入注解扫描器,扫描除过控制器之外的包
- 编写SpringMVC的配置文件dispatcher-servlet.xml,扫描控制器类所在的包
- 修改web.xml文件,注入spring和springmvc容器的启动