Spring框架
spring是一个轻量级的,非侵入式的,集成了IOC和AOP功能的一站式框架,在spring框架中,存在解耦思想,既包括业务代码间的解耦,还包括业务代码和框架之间的解耦,当某一部分代码出现问题时,只需要将那一部分代码提取出来并进行修改,不会影响其他部分,类似于衣服破洞了一样我们不需要将全身的衣服脱下来进行修改,而只是哪里出现问题了就补哪里。
轻量级
spring框架使用的核心jar包较小,所以spring框架运行占用资源小,运行效率高。
非侵入式
业务代码类不会实现或继承spring框架中的类或接口。
IOC
Insersion of control(控制反转或反转控制),主要是将生成对象的权力交给spring框架,但是需要在spring框架中进行注入。
AOP
面向切面变成,是OOP面向对象编程的拓展,在不影响原来业务代码的情况下隐式的调用某些非业务代码。
一站式框架
不仅对IOC和AOP进行管理,还对jdbc,web,事务管理等等进行封装。
IOC
spring框架中存在bean(交给spring管理的对象),当我们将创建对象的权力交给spring框架后,我们就无需手动创建对象而是直接通过某个方法获取对象。
将创建对象的权力交给spring框架的方式有两种:
1、通过配置文件
2、通过注解标签
配置文件(IOC)
首先我们需要声明一个对象
public class Admin {
private int id;
private String account;
private String password;
public Admin() {
System.out.println("Admin无参构造方法");
}
public Admin(int id, String account) {
this.id = id;
this.account = account;
}
}
我们将通过在配置文件中书写标签的方式进行注入(加入IOC容器),id为自定义的名字,class为该类的全类名,singleton为单例bean表示该对象只会存在一个,prototype为原型bean 每次通过getBean方法获取bean对象时都会创建一个新的bean对象,这样就将创建该类的对象的方式交给了spring框架。
<bean id="admin" class="com.whn.spring.model.Admin" scope="singleton"></bean>
我们还可以给该对象的属性在创建对象时赋初值(属性注入),属性注入也有两种方式:
1、通过set方法给属性赋值
2、通过带参数的构造方法赋值
两种方式对应着标签的两种不同的标签
set方法进行属性注入前提要保证该类中有对应属性的set方法,name为属性名,value为赋的值
<property name="id" value="1"></property>
构造方法进行属性注入有三种方式:使用构造方法对属性赋值时,要确保存在该属性为参数列表的构造方法,所以一般通过构造方法给属性赋值的方法很少使用。
1、使用构造方法的参数名称进行注入值
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="account" value="111"></constructor-arg>
2、使用构造方法参数的下标注入值
<bean id="stu" class="com.ffyc.main.Student"><!--创建学生对象,使用构造方法参数的下标注入值-->
<constructor-arg index="0" value="张三"></constructor-arg>
<constructor-arg index="1" value="22"></constructor-arg>
<constructor-arg index="2" ref="school"></constructor-arg>
</bean>
3、使用默认的构造方法的参数的循序注入值
<bean id="stuSequence" class="com.ffyc.main.Student"><!--创建学生对象,使用构造方法的默认参数顺序注入值-->
<construct-arg value="张三"></construct-arg>
<construct-arg value="22"></construct-arg>
<construct-arg ref="school"></construct-arg>
</bean>
当对象的属性中出现自定义类型的属性时,我们可以通过标签加上name和ref属性将自定义类型的属性注入,ref为自定义属性的全类名,或者也可以先将该自定义属性加入IOC容器中。
<bean id="adminDao" class="com.ffyc.spring.dao.AdminDao"></bean>
<bean id="adminService" class="com.ffyc.spring.service.AdminService">
<property name="adminDao" ref="adminDao"></property>
</bean>
通过注解标签的方式将创建对象的权力交给spring框架,但是需要先开启spring的注解扫描,通过设置扫描的包将注解扫描出来。base-package表示需要扫描的包路径。
<context:component-scan base-package="com.whn.spring"></context:component-scan>
创建对象的注解标签:
@Component:可以创建任意对象,创建对象的默认名称是类名的驼峰命名法,也可以指定对象的名称@Component(“指定名称”)
@Controller:专门用来创建控制器的对象(Servlet),这种对象可以接受用户的请求,可以返回处理结果给客户端
@Service:专门用来创建业务逻辑层的对象,负责向下访问数据访问层,处理完毕后的结果返回给界面层
@Repository:专门用来创建数据访问层的对象,负责数据库中的增删改查所有操作
需要注意的是spring框架中在同一个类中不能调用自己的方法,这样会导致方法出错
通过注解的方式将类加入IOC容器,我们只需要在对应类前加入注解即可,也可给valeu中起别名,如果不加名称那么该类的默认对象名就为该类的驼峰命名表达式,例如Admin类的对象名就是admin
@Component(value = "admin")//等同于<bean id="admin" class=""></bean>,也可添加@Scope注解
@Scope(value = "singleton")
public class Admin {
private int id;
private String account;
private String password;
}
如果类中存在自定义类型的属性,我们需要使用自动注入@Autowired注解标签进行依赖注入,@Autowired注解有两种注入属性的方式,@Autowired先通过成员变量的类型在spring容器中查找对象,当存在多个相同类型的对象时再根据成员变量的名称进行查找。还可以使用jdk提供的注解标签@Resource通过名字或者类型查找,如果给name赋值就会根据名称查找,如果没有则根据类型进行查找。
@Service(value = "adminService")
@Transactional
public class AdminService {
/*@Autowired自动注入要求对象必须存在
*加入自动注入标签时可以删除get和set方法
* @Autowired是spring框架自己的注解标签有两种方式查找
* 1、通过对象名称查找,需要配合@Qualifier("name")查找
* 2、通过类型查找*/
@Autowired
@Qualifier("adminDao")//标签存在,通过对象名称查找;标签不存在,通过属性的类型查找
private AdminDao adminDao;// @Resource或者@Resource(name="adminDao")
}
@Repository(value = "adminDao")
public class AdminDao {
}
spring-jdbc
spring框架也提供了和数据库连接的功能,首先需要下载spring中的Jdbc包以及导入数据源,这里选择的是阿里的数据源因为其存在数据库连接池,然后在spring容器中设置数据源和数据库连接的属性。
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!-- 阿里数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><!-- 设置数据库连接属性 -->
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${uname}"></property>
<property name="password" value="${pwd}"></property>
<property name="initialSize" value="${initialSize}"></property>
<property name="maxActive" value="${maxActive}"></property>
<property name="maxWait" value="${maxWait}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!-- 设置数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
然后在自定义类中使用@Autowired对JdbcTemplate进行依赖注入就可以使用对数据库进行的增删改查操作。
AOP
AOP为 Aspect Oriented Programming的缩写,意思为面向切面的编程,在业务开发中,总有一些功能与业务代码耦合度不强(例如:保存日志,提交事务,权限验证,异常处理),可以公共的,通用的,重复的代码提取到一个工具类中单独开发,调用某些方法时通过动态代理调用即可,减少了代码的复用性,可以在不修改原来代码的情况下,额外的添加其他公共功能。
解决方法:AOP面向切面编程,底层使用代理对象(动态代理模式)需要将实现的功能配置(AOP不是spring框架特有的设计思想)
连接点:类中可以被增强的所有方法都叫做连接点
切入点:类中真正被增强的方法叫做切入点,并不是类中所有的方法都需要增强
通知:是指切面在某个特定的连接点要做的事情(增强的功能),通知一般分为前置通知,后置通知,异常通知和环绕通知等
切面:把通知添加到切入点的过程
目标:代理的目标对象(连接点,切入点所在的类)
代理:向目标对象应用通知时创建的代理对象
<dependency><!--导入springAOP相关jar包-->
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
spring中的AOP也有两种实现方式:配置方式和注解方式
配置方式:
<bean id="aopdemo" class="com.ff.spring.aop.AopDemo"></bean><!--在spring容器中创建通知类对象-->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(*com.ff.spring.service.UserService.adduser(..))" id="adduser"/><!--具体到方法-->
<aop:pointcut expression="execution(*com.ff.spring.service.UserService.*(..))" id="allmethod"/>
<!--给切入点配置通知-->
<aop:aspect ref="aopdemo"><!--当某个切入点有前置通知或后置通知或异常通知时不能再给该切入点添加环绕通知-->
<aop:before method="savelog" pointcut-ref="adduser"/>
<aop:after method="savelog" pointcut-ref="adduser"/>
<aop:around method="aroundAdvice" pointcut-ref="adduser"/>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="allmethod"throwing="e" />
</aop:aspect>
</aop:config>
AspectJ 中常用的通知有五种类型:
前置通知,后置通知,环绕通知,异常通知,最终通知.
注解方式:
首先需要在spring核心配置文件中启动AspectJ 支持:
<aop:aspectj-autoproxy /><!--启动AspectJ支持-->
使用@Component注解标签在spring容器中创建通知类对象,再使用@Aspect标签定义通知,最后根据@Before,@After,@Around等注解标签定义通知的具体类型
@Component
@Aspect
public class AopDemo {
@Before("execution(* com.ffyc.spring.dao.UserDemo.*(..))")//定义连接点
public void before() {
System.out.println("before");
}
@After("execution(* com.ffyc.spring.dao.UserDemo.*(..))")
public void after() {
System.out.println("after");
}
@Around("execution(* com.ffyc.spring.dao.UserDemo.*(..))")
public void around() throws Throwable {
System.out.println("arround");
}
@AfterThrowing(value = "execution(* com.ffyc.spring.dao.UserDemo.*(..))",throwing = "e")
public void afterthrow(Throwable){
System.out.println("afterthrow");
}
@AfterReturning("execution(* com.ffyc.spring.dao.UserDemo.*(..))")
public void afterreturn(){
System.out.println("afterreturn");
}
}
Spring事务管理
事务:事务是由一个或若干个数据库操作所构成,在进行业务处理时一个业务可能会有多个对数据库的操作,这些操作可能会出现异常在执行过程中产生中断,为了确保用户对数据库数据的操作具有一致性,需要通过事务对这些操作进行管理,被事物进行管理的操作在执行后不会立即提交给数据库,而是等待被同一个事务管理的所有数据库操作执行完后,在确保所有的操作都不出异常的情况下,再进行事务的提交,如果这些操作中存在了异常情况,事务会进行回滚撤销之前已经执行的操作,这样就可以确保数据库在一次业务处理时数据库数据的一致性。
@Service(value = "adminService")
@Transactional
public class AdminService {
@Autowired
private AdminDao adminDao;
public void saveAdmin(Admin admin) {
this.adminDao.saveAdmin();
this.adminDao.updateAdmin();
}
public void zhuanZhang() {
adminDao.add();
System.out.println(10 / 0);//出现异常事务进行回滚,撤销上一步的操作
adminDao.sub();
}
}
事务有两种:一种是编程式事务像一些事务提交,事务回滚等操作需要程序员自己手写;第二种是声明式事务是建立在AOP基础上的,本质是对方法前后进行拦截,所以声明式事务是方法级别的。
通过注解方式实现事务管理
首先需要在spring核心配置文件中配置事务管理器,并开启注解事务管理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property><!--注入数据源-->
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/><!--开启注解事务管理-->
然后在需要添加事务管理的方法所在的类上添加@Transactional
声明式事务不生效的场景
如果方法不为public方法就不会生效
方法中出现的异常被程序员主动地进行捕获,认为方法没有出现异常事务正常提交
默认情况下方法中出现编译时异常
@Transactional事务传播行为设置错误
数据库引擎不支持事务管理
在同一个类中一个非事务方法中,通过this调用事务方法,此时是通过非代理对象调用
Spring事务传播行为
事务传播行为是指当一个事务方法调用另一个事务方法时被调用的方法中的事务是如何进行管理的。事务传播行为是Spring框架独有的事务增强属性,他不属于事务提供方数据库行为。
REQUIRED
当一个有事务的方法,调用了传播行为为REQUIRED的方法,那么被调用的方法会合并到调用它的方法事务中
当一个没有事务的方法,那么被调用的方法会自己开启一个事务执行
SUPPORTS
当一个有事务的方法,调用了传播行为为SUPPORTS的方法,那么被调用的方法会合并到调用它的方法事务中
当一个没有事务的方法,那么被调用的方法就会以非事务方式执行
REQUIRES_NEW
当一个有事务的方法,调用了传播行为为REQUIRES_NEW的方法,那么被调用的方法会新建一个事务,当被调用方法执行完后再执行调用方法中的事务
当一个没有事务的方法,那么被调用的方法还是会新建一个事务并执行
Spring集成Mybatis
Spring集成Mybatis的核心是将Mybatis中的SqlSessionFactory交给Spring管理,并由Spring生成对dao接口的代理对象
首先需要导入mybatis-spring jar包,在spring容器中配置sqlSessionFactory的属性
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property><!--配置数据源-->
<property name="configLocation" value="mybatis-config.xml"></property><!--配置连接数据库的信息,用户名、密码-->
<property name="mapperLocations" value="com/ffyc/*Mapper.xml"></property><!--配置映射器所在地址-->
</bean>
指定生成接口代理
<bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ffyc.ssm.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
最后再在service中的类中注入Dao代理接口就可以在spring中使用mybatis管理数据库