Spring
1 概述
1.1 Spring优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
1.2 Spring七大模块
- Spring Core:基础,可以说Spring其他所有的功能都需要依赖于该类库。主要提供IOC依赖注入功能
- Spring Aspects:该模块为与AspectJ的集成提供 支持
- Spring AOP:提供了面向切面的编程实现
- Spring JDBC:Java数据库连接
- Spring JMS:Java消息服务
- Spring ORM:用于支持Hibernate等ORM工具
- Spring Web:为创建Web应用程序提供支持
- Spring Test:提供了对JUnit和TestNG测试的支持
2 IOC控制反转
IOC是一种编程思想,由主动的编程变成被动的接收,核心是set方法注入
- 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
- 反转:程序本身不创建对象,而变成被动的接收对象
案例:
<?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的实例化对象容器,代替了java实例化新对象代码
1、原来的是 User user = new User();
2、id = 对象名
class = 实例化类的路径
3、<property>标签
value = 基本数据类型对象
ref = 创建的对象
-->
<bean id="user" class="com.falsche.pojo.User">
<property name="name" value="HelloSpring"/>
</bean>
<bean id="userDaoImpl" class="com.falsche.dao.UserDaoImpl"/>
<bean id="userDaoMysql" class="com.falsche.dao.UserDaoMysql"/>
<bean id="userDaoOracle" class="com.falsche.dao.UserDaoOracle"/>
<bean id="userServiceImpl" class="com.falsche.service.UserServiceImpl">
<property name="userDao" ref="userDaoOracle"/>
</bean>
</beans>
创建ApplicationContext对象,获取Spring容器对象,即获取xml配置文件对象
//创建ApplicationContext对象,获取beans.xml配置文件中,Spring容器的对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//通过context.getBean()方法,获取User对象
User user = context.getBean("user", User.class);
3 Spring配置
3.1 别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew">
3.2 Bean的配置
<!--bean标签,是spring的实例化对象容器,代替了java实例化新对象代码
1、原来的是 User user = new User();
2、id = 对象名
class = 实例化类的路径:包名 + 类型
3、name:也是别名,而且name可以同时取多个别名
3、<property>标签
value = 基本数据类型对象
ref = 创建的对象
-->
<bean id="user" class="com.falsche.pojo.User" name="user2 u2, u3, u4">
<property name="name" value="HelloSpring"/>
</bean>
<bean id="userServiceImpl" class="com.falsche.service.UserServiceImpl">
<property name="userDao" ref="userDaoOracle"/>
</bean>
3.3 import
import一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
使用总的xml配置文件即可,例如:
applicationContext.xml
<import resource="beans.xml">
<import resource="beans1.xml">
<import resource="beans2.xml">
4 依赖注入DI
4.1 构造器注入
<bean id="user3" class="com.falsche.pojo.User">
<constructor-arg name="name" value="Azoe"/>
</bean>
4.2 Set方法注入(核心)
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 id="address" class="com.falsche.pojo.Address">
<property name="address" value="珠海"/>
</bean>
<bean id="student" class="com.falsche.pojo.Student" name="stu">
<!-- 普通值注入,value -->
<property name="name" value="Falsche"/>
<!-- Bean对象值注入,ref -->
<property name="address" ref="address"/>
<!-- 数组注入,使用<array>标签 -->
<property name="books">
<array>
<value>三国演义</value>
<value>水浒传</value>
<value>西游记</value>
<value>红楼梦</value>
</array>
</property>
<!-- List注入,使用<list>标签 -->
<property name="hobbies">
<list>
<value>篮球</value>
<value>羽毛球</value>
<value>乒乓球</value>
</list>
</property>
<!-- Map注入,使用<map>标签,其中<entry>标签是给key,value赋值 -->
<property name="card">
<map>
<entry key="学生证" value="1712402702"/>
<entry key="银行卡" value="54877984651"/>
</map>
</property>
<!-- Set集合注入,使用<set>标签 -->
<property name="games">
<set>
<value>LOL</value>
<value>WOW</value>
</set>
</property>
<!-- Properties注入 -->
<property name="info">
<props>
<prop key="driver">MySQL</prop>
<prop key="url">a</prop>
<prop key="name">root</prop>
<prop key="password">Falsche</prop>
</props>
</property>
</bean>
</beans>
4.3 c命名空间和p命名空间注入
使用c和p命名空间需要导入头文件
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
c命名空间和p命名空间注入方式:
也就是简化了标签和标签
<bean id="user" class="com.falsche.pojo.User" p:name="Falsche"/>
<bean id="user2" class="com.falsche.pojo.User" c:name="IDEA"/>
4.4 bean的作用域
单例模式singleton(Spring默认机制)
<bean id="user" class="com.falsche.pojo.User" scope="singleton"/>
原型模式prototype:每次从容器中获取的时候,都会产生一个新对象
<bean id="user" class="com.falsche.pojo.User" scope="prototype"/>
其余的request、session、application、这些只能在web开发中使用
5 Bean的自动装配
5.1 使用注解实现自动装配
使用注解须知:
- 导入约束:context约束
配置注解的支持:<context:annotation-config/>
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解 -->
<context:annotation-config/>
</beans>
@Autowired:Spring的注解
- 在set方法上使用
- 直接在属性上使用,若属性在IOC(Spring)容器中存在,则可以不用编写set方法
@Qualifier:配合@Autowired使用,可以指定一个唯一的bean对象注入
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Resource:java原生注解,@Autowired和@Qualifier的整合版
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 通过byType的方式实现,而且必须要求这个对象存在
- @Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现
6 使用注解开发
- 使用注解,需要导入约束
- 需要配置注解支持
<!-- 开启注解 -->
<context:annotation-config/>
<!-- 指定要扫描的包,这个包下的注解会生效 -->
<context:component-scan base-package="com.falsche"/>
bean和属性注入
// @Component 相当于 <bean id="user" class="com.falsche.pojo.User"/>
@Component
public class User {
// @Value 相当于 <property name="name" value="Falsche"/>
@Value("Falsche")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
衍生的注解
@Component有几个衍生注解,衍生注解功能与@Component一样,web开发中,会用MVC三层架构- dao(@Repository)
- service(@Service)
- controller(@Controller)
- 自动装配
详情见第五章 - 作用域
@Scope
7 代理模式
7.1 静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,会做一些附属操作
- 客户:访问代理对象的人
静态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色:代码量会翻倍,开发效率会贬低
7.2 动态代理(AOP底层)
- 底层是反射
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
- java字节码实现:javasist
需要了解一个类和一个接口:
- 类Proxy:代理
- 接口InvocationHandler:调用处理程序
动态代理的好处:
- 一个动态代理类代理的是一个接口,一般对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一接口即可
具体实现:
动态代理工具类
/**
* 使用这个工具类,自动生成动态代理类
*/
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object object;
public void setObject(Object object) {
this.object = object;
}
/**
* 得到生成的代理类
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
object.getClass().getInterfaces(), this);
}
/**
* 处理代理实例,并返回结果
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质,就是反射机制
// invoke():执行方法
Object result = method.invoke(object, args);
return result;
}
}
实现类
public class Client {
public static void main(String[] args) {
// 真实角色
UserService service = new UserServiceImpl();
// 代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 需要代理的接口
pih.setObject(service);
// 生成代理类
UserService proxy = (UserService) pih.getProxy();
proxy.insert();
}
}
8 AOP(重点)
8.1 概念
AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP的优点:
- 对业务逻辑的各个部分进行隔离,使各个部分之间的耦合度降低
- 提高程序的可重用性
- 提高了开发效率
8.2 AOP在Spring中的作用
作用:提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志、安全、缓存、事务等等
- 切面(Aspect):横切关注点被模块化的特殊对象。即,一个类
- 通知(Advice):切面必须要完成的工作。即,类中的一个方法
- 目标(Target):被通知的对象
- 代理(Proxy):向目标对象应用通知之后创建的对象
- 切入点(PointCut):切入通知执行的地点
- 连接点(JoinPoint):与切入点匹配的执行点
Spring AOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
即AOP在不改变原有代码的情况下,增加新的功能
8.3 用Spring实现AOP
使用AOP织入,需要导包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
配置AOP约束
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
方法一:使用Spring API接口
编写前置和后置增强类public class LogBefore implements MethodBeforeAdvice {
/**
* method:要执行的目标对象的方法
* args:被调用的方法的参数
* target:目标对象
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class LogAfter implements AfterReturningAdvice {
/**
* returnValue:返回值
* method:要执行的目标对象的方法
* args:被调用的方法的参数
* target:目标对象
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+ "的" + method.getName() + "方法," + "返回值为:" + returnValue);
}
}
配置AOP
<!-- 配置AOP -->
<aop:config>
<!-- 切入点:expression:表达式匹配要执行的方法,execution(* * * * *) -->
<aop:pointcut id="pointcut" expression="execution(* com.falsche.service.UserServiceImpl.* (..))"/>
<!-- 执行环绕:advice-ref表示执行方法,pointcut-ref表示切入点名称 -->
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
</aop:config>
测试类
public class MyTest {
@Test
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
方法二:自定义类来实现AOP
先写一个自己的切入点类:DiyPointcutpublic class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
配置AOP
<bean id="diyPointcut" class="com.falsche.diy.DiyPointcut"/>
<aop:config>
<!-- 自定义切面,ref:要引用的类 -->
<aop:aspect ref="diyPointcut">
<!-- 切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.falsche.service.UserServiceImpl.* (..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
方法三:使用注解实现AOP
配置注解所需要的约束,context和aop
开启Bean注解支持<context:annotation-config/>
<context:component-scan base-package="com.falsche"/>
开启aop注解支持
<!-- 开启AOP注解支持 -->
<aop:aspectj-autoproxy/>
定义一个切面类
@Component()
@Aspect //标记这是一个切面类
public class AnnotationPointcut {
@Before("execution(* com.falsche.service.UserServiceImpl.* (..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.falsche.service.UserServiceImpl.* (..))")
public void after(){
System.out.println("---------方法执行后---------");
}
}
9 整合MyBatis
步骤:
- 导入相关jar包
- junit
- mybatis
- mysql
- jdbc,druid或者spring-jdbc
- spring-mvc
- aop织入,aspect
- mybatis-spring
- 编写配置文件
- 测试
9.1 第一种SqlSessionTemplate
spring配置文件:
<!-- DataSource:使用Spring的数据源替代Mybatis的环境配置 -->
<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/db_mybatis?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="Falsche"/>
</bean>
<!-- sqlSessionFactory注入 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定Mybatis配置文件,可以代替mybatis-config.xml中的所有配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/falsche/mapper/*.xml"/>
</bean>
<!-- sqlSessionTemplate:就是sqlSession,只能使用构造器注入,因为他没有set方法-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
SqlSessionTemplate需要被手动注入:
public class UserMapperImpl implements UserMapper {
// 手动实现sqlSessionTemplate注入
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<User> queryUser() {
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
return mapper.queryUser();
}
}
注入配置文件:
<bean id="userMapper" class="com.falsche.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
9.2 第二种SqlSessionDaoSupport
省略注入sqlSessionTemplate,用SqlSessionDaoSupport抽象类来代替。SqlSessionDaoSupport类会自动创建sqlSession。
spring配置文件:
<!-- DataSource:使用Spring的数据源替代Mybatis的环境配置 -->
<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/db_mybatis?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="Falsche"/>
</bean>
<!-- sqlSessionFactory注入 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定Mybatis配置文件,可以代替mybatis-config.xml中的所有配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/falsche/mapper/*.xml"/>
</bean>
用一个实现类来继承SqlSessionDaoSupport类
/**
* SqlSessionDaoSupport提供了sqlSession
*/
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> queryUser() {
return getSqlSession().getMapper(UserMapper.class).queryUser();
}
}
注入配置文件:
<bean id="userMapper2" class="com.falsche.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
9.3 关联数据库文件
<!-- 关联数据库文件 -->
<context:property-placeholder location="classpath:druid.properties"/>
9.4 配置dao接口扫描包
使用dao接口扫描包,就不用在dao层写实现类,代替了实现类
<!-- 配置dao接口扫描包,动态的实现了Dao接口可以注入到Spring容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 要扫描的dao包 -->
<property name="basePackage" value="com.falsche.dao"/>
</bean>
10 声明式事务
10.1 事务
事务就是把一系列动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
一般事务都是放在service层
- 事务在项目开发过程中非常重要,涉及到数据的一致性的问题。
- 事务管理式企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。
事务的四个属性ACID
- 原子性(atomicity)
- 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
- 一致性(consistency)
- 一旦所有事务动作完成,事务就要被提交,数据和资源处于一种满足业务规则的一致性状态中
- 隔离性(isolation)
- 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开,防止数据损坏
- 持久性(durability)
- 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
10.2 Spring中的事务管理
Spring支持编程式事务管理和声明式事务管理。
编程式事务管理:
- 将事务管理代码嵌到业务方法中来控制事务的提交commit和回滚rollback
- 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
声明式事务管理:(重点)
- 一般情况下比编程式事务好用
- 将事务管理代码从业务方法中分离出来,以声明的方式实现事务管理
- 将事务管理作为横切关注点,通过AOP方法模块化。Spring中通过Spring AOP框架支持声明式事务管理
10.3 使用声明式事务管理(原理)
- 使用Spring管理事务,需要导入两个约束:tx、aop ```xml xmlns:aop=”http://www.springframework.org/schema/aop“ xmlns:tx=”http://www.springframework.org/schema/tx“
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd“
2. 配置声明式事务
```java
<!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
配置事务通知,即给哪些方法配置事务
<!-- 结合AOP实现事务的织入,配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给方法配置事务,默认事务是REQUIRED -->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
配置AOP织入事务
<!-- 配置AOP织入事务 -->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.falsche.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
10.4 注解版声明式事务
提供事务管理器
<!-- spring提供的事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
在spring容器中开启事务控制注解支持:
<!-- 开启事务控制注解支持 ;
transaction-manager属性告诉spring事务控制器是哪一个;
false为JDK方式实现默认可不写,而非CGLIB方式-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
在类上面加上@Transactional, 那么这个类所有方法都会开启事务控制
Bean的作用域
- singleton——唯一bean实例(默认)
当一个bean的作用域为singleton,那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean相匹配,则只会返回bean的同一实例。 - prototype——每次请求都会创建一个新的bean实例
当一个bean的作用域为prototype,表示一个bean定义对应多个对象实例。prototype作用域的bean会导致在每次对该bean请求时,都会创建一个新的bean实例。 - request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效
request只适用于Web程序,每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。 - session——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效
session只适用于Web程序,session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。 - globalSession
global session作用域类似于标准的HTTP session作用域。