1、简介
Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。支持对事务的处理,各种框架的整合。
2、七大模块
核心容器(Spring core)
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。BeanFactory使用依赖注入的方式提供给组件依赖。
Spring上下文(Spring context)
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子
邮件、国际化、校验和调度功能。
Spring面向切面编程(Spring AOP)
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使
Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO模块
DAO模式主要目的是将持久层相关问题与一般的的业务规则和工作流隔离开来。Spring 中的DAO提供一致的方式
访问数据库,不管采用何种持久化技术,Spring都提供一直的编程模型。Spring还对不同的持久层技术提供一致的
DAO方式的异常层次结构。
Spring ORM模块
Spring 与所有的主要的ORM映射框架都集成的很好,包括Hibernate、JDO实现、TopLink和IBatis SQL Map等。
Spring为所有的这些框架提供了模板之类的辅助类,达成了一致的编程风格。
Spring Web模块
Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。Web层使用Web层框
架,可选的,可以是Spring自己的MVC框架,或者提供的Web框架,如Struts、Webwork、tapestry和jsf。
Spring MVC框架(Spring WebMVC)
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。Spring
的MVC框架提供清晰的角色划分:控制器、验证器、命令对象、表单对象和模型对象、分发器、处理器映射和视图解析器。Spring支持多种视图技术。
3、IOC
3.1、控制反转
- 它不是一种技术,而是一种思想
- 在之前的业务中,我们都是自己new对象,是程序员来控制对象的创建
- Spring中若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,我们不用去管对象的创建,只关注代码的实现,也就是把创建对象的控制权反转给了Spring框架
控制反转是一种通过描述(XML或注解)通过第三方去生产或获取指定对象的方式。在Spring中实现控制反转的时IOC容器,实现方式是DI(依赖注入)
3.2、DI(依赖注入)
实现IOC的一种方法,创建对象时自动为其配置属性。
Spring容器在初始化时先读取配置文件,根据配置文件将对象创建到容器中,程序使用时再从IOC容器中取出需要的对象。
maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
构造器注入
<bean id="goldilocks" class="com.xinzhi.entity.Dog">
<constructor-arg name="name" value="goldilocks"/>
<constructor-arg name="age" value="11"/>
</bean>
set注入
先配置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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--bean就是new出来的对象,id变量名,class类对象-->
<bean id="address" class="com.xinzhi.entity.Address">
<!--为对象属性复制-->
<property name="name" value="南十方锅炉房"/>
</bean>
<!--别名-->
<alias name="address" alias="地址"/>
<!--导入多个beans,内容相同会合并-->
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
</beans>
测试
//获取spring获取上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//直接获取bean
JdbcTemplate bean = context.getBean(JdbcTemplate.class);
各种类型的注入方式
<!--数组-->
<property name="salary">
<array>
<value>2000</value>
<value>4000</value>
<value>8000</value>
<value>16000</value>
</array>
</property>
<!--list-->
<property name="duties">
<list>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</list>
</property>
<!--map-->
<property name="familyTies">
<map>
<entry key="爸爸" value="王先生"/>
<entry key="麻麻" value="张先生"/>
</map>
</property>
<!--set-->
<property name="carts">
<set>
<value>北极星</value>
<value>奇瑞QQ</value>
</set>
</property>
<!--Properties-->
<property name="workExperience">
<props>
<prop key="2020年">秃头</prop>
</props>
</property>
<!--null-->
<property name="daughter">
<null/>
</property>
3.3、Bean作用域
Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,作用域包括singleton(单例模式)、prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)
Singleton
bean的默认作用域就是Singleton。这个是时候在Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例,每次获取到的对象都是同一个对象。
Prototype
Prototype是原型类型,当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。
每次对该bean请求时都会创建一个新的bean实例。它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
<bean id="account" class="com.xinzhi.entity.User" scope="prototype"/>
Request
web专用
<bean id="account" class="com.xinzhi.entity.User" scope="request"/>
session
web专用
<bean id="account" class="com.xinzhi.entity.User" scope="session"/>
3.4、自动装配
只要我们的Beans满足bean之间的依赖,且这些bean都存在于容器,且唯一,那么我么就可以按照约定进行bean
的自动装配。同时还能大量的减少配置文件的数量
ByName
<bean id="user" class="com.xinzhi.entity.User" autowire="byName"/>
ByType
只能有一个值不然会报错
<bean id="user" class="com.xinzhi.entity.User" autowire="byType"/>
3.5、注解开发
在类的字段上添加注解,注意一定要扫包
<?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-3.0.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--扫包-->
<context:component-scan base-package="com.xinzhi"/>
3.5.1、自动
@Autowired
//按照类型装配
@Autowired
private Address address;
@Qualifier()
//两个对象时就要用Qualifier指定对象名字
@Autowired
@Qualifier("newAddress")
private Address address;
@Resource()
//可以直接指定名字
@Resource(name = "newAddress")
private Address address;
@Autowired与@Resource异同
1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null
值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合
@Qualifier注解进行使用
3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定
name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进
行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只
会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName
3.5.2、分层开发
注解 | 作用与 |
---|---|
@Controller | Controller层 |
@Service | Service层 |
@Repository | dao层 |
@Autowired | 字段 |
3.5.3、组件开发
只能赋一个值
@Component
public class Student {
@Value("马先生")
private String name;
@Resource
private Address address;
}
3.5.4、配置文件
@Configuration
public class TestConfig {
@Bean
public Mouse mouse() {
return new Mouse("shu");
}
@Bean
public Cat cat() {
return new Cat("mao");
}
}
3.6、集成spring测试环境
<!--集成spring测试环境-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beans.xml")
public class TestSpring {
@Resource
private Cat cat;
@Test
public void test() {
String name = cat.getName();
System.out.println(name);
System.out.println(cat);
}
}
3.7、Bean的生命周期
1.创建对象,调用构造方法
2.为对象输入属性
3.初始化前,可以自己定义,继承BeanPostProcessor接口,重写Befor
4.初始化
5.初始化后,可以自己定义,继承BeanPostProcessor接口,重写After
6.使用
7.容器关闭时销毁
3.8、jdbcTemplate
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?
useSSL=false&useUnicode=true&characterEncoding=utf8"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
4、AOP
面向切面编程,本质就是代理模式,在不改变原有代码的情况下添加各种功能,比如日志、事务等。
4.1、Aop名词
-
通知(Advice):在切面的某个特定的连接点(Join point)上执行的动作。
-
切面(Aspect):被抽取的公共模块,可能会横切多个对象。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普
通类中以 @AspectJ 注解来实现。
-
连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。
-
切入点(Pointcut):切入点是指 我们要对哪些Join point进行拦截的定义。通过切入点表达式,指定拦截的方法,比如指定拦截
add、search。
-
引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。Spring允许
引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化
缓存机制。
-
目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做被通知(adviced) 对象。
既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied)对象。
-
织入(Weaving):指把增强应用到目标对象来创建新的代理对象的过程。Spring是在运行时完成织入。
4.2、Spring实现Aop
织入的依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
头文件
xmlns:aop="http://www.springframework.org/schema/aop“
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
4.2.1、注解实现
定义切面
@Component
@Aspect//标注这个类是切面
public class AnnotationAOP {
//springAop自动的5种aop这里全部列出
//执行前
@Before("execution(* com.xinzhi.service.impl.*.*(..))")
public void before() {
System.out.println("---------方法执行前before()---------");
}
//执行后
@After("execution(* com.xinzhi.service.impl.*.*(..))")
public void after() {
System.out.println("---------方法执行后after()---------");
}
//返回后
@AfterReturning("execution(* com.xinzhi.service.impl.*.*(..))")
public void afterReturning() {
System.out.println("---------方法返回后afterReturning()---------");
}
//环绕
@Around("execution(* com.xinzhi.service.impl.*.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("-------环绕前-------");
System.out.println("签名(拿到方法名):"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("-------环绕后------");
System.out.println(proceed);
}
//异常时
@AfterThrowing("execution(* com.xinzhi.service.impl.*.*(..))")
public void afterThrow() {
}
}
<!--在配置文件注册-->
<aop:aspectj-autoproxy/>
4.2.2、xml实现
自定义
public class MyAop {
public void before(){
System.out.println("执行方法之前打印一条日志! -- 自定义形式");
}
public void after(){
System.out.println("执行方法之后打印一条日志! -- 自定义形式");
}
}
<!--注册bean-->
<bean id="myAop" class="com.xinzhi.aop.MyAop"/>
<!--aop的配置-->
<aop:config>
<!--使用AOP的标签实现-->
<aop:aspect ref="myAop">
<aop:pointcut id="pointcut" expression="execution(* com.xinzhi.service.impl.*.*(..))"/>
<aop:before pointcut-ref="pointcut" method="before"/>
<aop:after pointcut-ref="pointcut" method="after"/>
</aop:aspect>
</aop:config>
内置接口
//继承接口
public class LogAfter implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1)throws Throwable {
System.out.println(method.getName()+":之后打印一条日志! -- 内置接口形式");
}
}
public class LogBefore implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(method.getName()+":之前打印一条日志!-- 内置接口形式");
}
}
<bean id="logBefore" class="com.xinzhi.aop.LogBefore"/>
<bean id="logAfter" class="com.xinzhi.aop.LogAfter"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.xinzhi.service.impl.*.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>
</aop:config>
4.3、整合MyBatis
配置数据库文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
https://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:property-placeholder location="db.properties"/>
<!-- Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描 cn.wmyskxz.mapper 包下的组件 -->
<property name="basePackage" value="com.xinzhi.dao"/>
</bean>
<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
</bean>
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.xinzhi.dao.impl.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="userservice" class="com.xinzhi.service.impl.UserServiceImpl">
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.xinzhi.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
4.4、添加事务
头文件
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
spring配置文件
<!-- 开启事务注解,并配置一个事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
添加注解
//加在类上所有方法都有
@Transactional
public class UserServiceImpl implements UserService {
@Transactional
public void getUser() {
}
}
事务的传播性
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务
中,这是最常见的选择。 - propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与
propagation_required类似的操作