文章目录
Spring
1、Spring框架的引言
spring(春天),生于在2002年,由Rod Johnson创作。Spring框架是一个集众多设计模式于一身的开源的、轻量级的项目管理框架。致力于JAVAEE 轻量级解决方案。相对于原来学过的框架而言,spring框架和之前学习的 struts2 、 mybatis 框架有了本质的区别,不是替换原来的某个框架,而是对其进行整合管理。
轻量级解决方案:提供一个以简单的、统一的、高效的方式构造整个应用,并且可以将单层框架以最佳的组合揉和在一起建立一个连贯的体系。
2、Spring框架的核心作用
Spring 框架用来 管理 [创建|使用|销毁] 项目中的组件,由于spring 框架可以帮我们生产项目中组件对象,因此也习惯称spring是一
个工厂|容器。
组件:项目中的service,dao,controller,都是项目中的组件
注意:spring框架通常不管理对实体类对象(entry)创建
3、spring第一个程序
1、引入依赖
<!--spring核心以及相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
2、创建组件
public interface UserDAO{
void save(String name);
}
public class UserDAOImpl implements UserDAO{
@Override
public void save(String name){
System.out.println("姓名:"+name);
}
}
3、添加配置文件
配置文件名称: 任意名称 (默认 spring.xml)
配置文件位置: 本项目中resources目录下
<?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-3.2.xsd ">
<!-- 通过spring管理组件
bean: 用来管理组件对象的创建
class: 用来指定管理组件对象的全限定名 包.类
id: 用来指定spring框架创建的当前组件对象在spring(容器|工厂)中唯一标识 全局唯一
-->
<bean class="init.UserDAOImpl" id="aa"></bean>
</beans>
4、启动工厂测试
public class TestSpring{
public static void main(String[] args) {
//启动工厂
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//获取工厂中创建好的对象 参数:获取工厂中指定对应的唯一标识
UserDAO userDAO = (UserDAO) context.getBean("aa");
userDAO.save("小黑");
}
}
4、Spring框架中的核心思想
4.1、IOC
Inversion of Controll 控制反转
将对象的创建由原来(new)的方式转移到配置文件中,交给spring ,通过在配置文件中配置bean标签形式创建对象,由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
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd ">
<bean class="init.UserDAOImpl" id="aa"></bean>
<bean class="init.CityDAOImpl" id="cityDAO"></bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserDAO userDAO = (UserDAO) context.getBean("aa");
userDAO.save("小黑");
CityDAO cityDAO = (CityDAO) context.getBean("aa");
ciryDAO.delete("1");
}
当bean对象多了之后,ioc的好处变体现出来了
问题来了:我们可以把service对象也通过bean管理,但是在service中需要调用dao层对象,怎么办呢???
**DI(dependcy Injection)**依赖注入
定义: 为组件中成员变量完成赋值过程 这个过程称之为 依赖注入
public interface DeptService{
void save(String name);
}
public class DeptServiceImpl implements DeptService{
// 需要的组件
private DeptDAO deptDAO;
public void setDeptDAO(DeptDAO deptDAO){
this.deptDAO=deptDAO;
}
@Override
public void save(String name){
System.out.println("deptService 姓名:"+name);
deptDAO.save(name);
}
}
<?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-3.2.xsd ">
<!--管理DAO组件-->
<bean class="init.DeptDAO" id="aa"></bean>
<!--管理service组件-->
<bean class="init.DeptServiceImpl" id="deptService">
<!--依赖注入
property: 用来给组件中的属性进行复制操作
name: 用来指定给组件中那个属性名惊醒赋值
ref: 用来指定赋值对象在工厂中唯一标识 bean的id
-->
<property name="deptDAO" ref="aa"/>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
DeptService deptService = (DeptService) context.getBean("deptService");
deptService.save("小黑");
}
**IOC 全部概述:**控制反转 就是通过原来手动通过new关键字创建对象的权力交给spring,由spring工厂创建对象的过程,当然spring不仅要创建对象还要在创建对象的同时通过DI的方式维护组件与组件的调用关系
spring中注入方式
- set 注入 使用成员变量set方式形式进行赋值
- 构造注入 使用构造方法形式进行属性的赋值
- 自动注入 就是通过在配置文件中完成类中属性自动赋值
spring中set方式的注入语法
- set注入: 使用类中的属性set方法为属性完成赋值的过程
- set注入相关语法
public interface DeptService{
void save(String name);
}
public class DeptServiceImpl implements DeptService{
// 需要的组件
private DeptDAO deptDAO;
private String name;
private Integer age;
public void setName(String name){
this.name=name;
}
public void setDeptDAO(DeptDAO deptDAO){
this.deptDAO=deptDAO;
}
@Override
public void save(String name){
System.out.println("deptService 姓名:"+name);
deptDAO.save(name);
System.out.println("Name:"+ this.name);
}
}
<?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-3.2.xsd ">
<bean class="init.DeptDAO" id="aa"></bean>
<bean class="init.DeptServiceImpl" id="deptService">
<!--set注入相关语法, 八种基本类型 + String 类型注入使用+Date类型 value属性进行赋值-->
<property name="deptDAO" ref="aa"/>
<property name="name" value="小陈123"/>
<property name="age" value="23"/>
<!--注意:在spring中日期格式默认为yyyy/MM/dd HH:mm:ss-->
<property name="bir" value="2121/12/12"/>
<!--注入数组类型-->
<property name="qqs">
<array>
<value>小陈</value>
<value>大</value>
</array>
</property>
<property name="clazzDAOS">
<array>
<ref bean="clazzDAO"/>
<ref bean="clazzDAO"/>
</array>
</property>
<!-- 注入list、set、map-->
<property name="habby">
<list>
<value>看书</value>
<value>睡觉</value>
<value>打游戏</value>
</list>
</property>
<property name="map">
<map>
<!-- 基本类型-->
<entry key="" value=""></entry>
<!-- 引用类型-->
<entry key-ref="" value-ref=""></entry>
</map>
</property>
<!-- 注入properties-->
<property name="propertis">
<props>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/test</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
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
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd ">
<!-- 管理DAO组件
1. set方式注入 注入时使用property标签
2. 构造方法注入 注入时使用constructor-arg标签
-->
<bean class="cdi.EmpDAOImpl" id="empDAO">
<!--使用构造注入-->
<constructor-arg index="0" name="name" value="小黑"/>
<constructor-arg index="1" name="age" value="23"/>
<constructor-arg index="2" name="userDao" ref=""/>
<!-- 注入数组-->
<constructor-arg>
<array>
<value></value>
<value></value>
<value></value>
</array>
</constructor-arg>
<constructor-arg>
<list>
<value></value>
<value></value>
<value></value>
</list>
</constructor-arg>
</bean>
</beans>
缺点:需要有对应的构造方法
spring中自动注入
在spring工厂配置文件中通过制定自动注入方式 开启组件属性的自动赋值
注意:
- 底层使用原理也是set方式注入
- 自动注入需要在对应组件标签开启才能使用
- 只用于引用类型的注入|对应类型|组件类型的注入
自动注入语法
- 需要谁将谁声明为成员变量,并提供set方法
- 在对应组件标签汇总加入autowired 属性并制定自动注入方式即可完成注入
<?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-3.2.xsd ">
<bean class="adi.StudentADOImpl" id="studentDAO"></bean>
<!-- 管理service组件
autowire: 用来给组件中成员变量完成自动赋值操作
byType: 根据类型完成自动注入 根据成员变量类型去工厂找 找到对应类型完成赋值
注意:如果工厂中存在多个类型一致的组件,使用类型自动注入会报错
byName: 根据名称完成自动注入 根据成员变量名去工厂中获取与之一致名字,找到对应的赋值
-->
<bean class="adi.StudentADOImpl" id="studentService" autowire="byName"></bean>
</beans>
4.2、spring工厂的相关特性
Spring中工厂创建对象的模式
- 默认spring在管理组件对象是 单例创建 singleton
注意:工厂默认在管理对象都是单例模式,单例方式无论在工厂获取多少次始终获取的是同一个对象
- 如何修改工厂创建组件对象为多例
<bean class="xxx" id="xxx" scope="prototype"></bean>
<?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-3.2.xsd ">
<!-- 管理组件
scope:用来指定工厂创建对象的模式
默认值:singleton 单例
prototype 多例
-->
<!-- <bean class="scope.TagDAOImpl" id="tagDAO" scope="singleton"></bean>-->
<bean class="scope.TagDAOImpl" id="tagDAO" scope="prototype"></bean>
</beans>
spring工厂创建对象的原理
原理:反射 + 构造方法
// 工厂原理
TagDAO tt = (TagDAO) Class.forName("scope.TagDAOImpl").newInstance();
System.out.println(tt);
spring工厂管理组件生命周期
- 组件对象什么时候创建
- 组件对象什么时候销毁
单例对象:工厂启动工厂中所有单例的对象随之创建 ,工厂销毁工厂中所有单例随之销毁(工厂销毁必须在工厂正常关闭后)
多例对象:每次在工厂中使用时创建,工厂不负责多例对象的销毁
bean工厂的好处
- 解耦合——使用配置文件管理java类,再生产环境中更换类的实现时不需要重新部署,修改文件即可
- 减少jvm内存占用——spring默认使用单例的模式创建bean,减少内存的占用
- 通过依赖注入建立了类与类之间的关系(使java之间关系更为清晰,方便了维护与管理)
5、代理
现有业务层开发存在问题
public interface UserService {
void save(String name);
void delete(String id);
}
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
}
}
}
问题: 从上面可以看出,现有业务层中控制事务代码出现了大量的冗余,如何解决现有业务层出现的冗余问题?
代理引言
为什么需要代理
- 很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能
代理的作用
- 代理对象可以在客户和目标对象之间起到中介作用,从而为目标对象提供额外的功能
静态代理的开发
目标类|对象(target):被代理类称之为目标类|或者,被代理的对象的 称之为目标对象
开发代理的原则:代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象
//静态代理类
//注意:这里为每一个业务层通过手动开发一个代理对象的过程称之为 静态代理对象
//代理对象:保证原始功能不变情况下,完成业务逻辑中附加操作
public class UserServiceStaticProxy implements UserService {
//依赖原始业务逻辑对象
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void save(String name) {
try {
System.out.println("开启事务");
userService.save(name);//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
userService.delete(id);//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
}
// 更改目标实现类
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
@Override
public void delete(String id) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
}
<!--配置目标类-->
<bean id="userService" class="staticproxy.UserServiceImpl"/>
<!--配置代理类-->
<bean id="userServiceStaticProxy" class="staticproxy.UserServiceStaticProxy">
<!--注入目标对象-->
<property name="userService" ref="userService"/>
</bean>
// 调用代理方法
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceStaticProxy = (UserService) context.getBean("userServiceStaticProxy");
userServiceStaticProxy.save("小黑");
新的问题:往往在开发我们书写的不仅仅是一个业务层,两个业务层,而我们的业务层会有很多,如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,甚至让我们的工作量多了一倍不止怎么解决以上这个问题呢?
解决方案: 为业务层在运行过程中动态创建代理类,通过动态代理类去解决我们现有业务层中代码冗余的问题。
动态代理
通过内部类方式生成动态代理对象
public class TestDynamicProxy {
public static void main(String[] args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class[] classes = {UserService.class};
/**
* 参数1:classLoader 类加载器
* 参数2:Class[] 目标对象的接口的类型的数组
* 参数3:InvocationHandler接口 invoke方法 用来书写额外功能 附加操作
*/
UserService proxyInstance = (UserService) Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
/**
* 通过动态代理对象调用自己里面代理方法时会优先指定invokcationHandler类中invoke
*
* @param o 当前创建好的代理对象
* @param method 当前dialing对象执行的方法对象
* @param objects 当前代理对象执行方法的参数
* @return 创建好的动态搭理对象
*/
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("当前执行的方法:" + method.getName());
System.out.println("当前执行的方法的参数:" + objects[0]);
try {
System.out.println("开启事务"); //附加操作
Object invoke = method.invoke(new UserServiceImpl(), objects);
System.out.println("关闭事务!"); //附加操作
return invoke;
} catch (Exception e) {
System.out.println("回滚事务!"); //附加操作
}
return null;
}
});
System.out.println(proxyInstance.getClass());
String result = proxyInstance.findAll("小明");
System.out.println(result);
}
}
通过代理类方式创建
public class ProxyInvocationHandler implements InvocationHandler {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
public Object getProxy() {
return Proxy.newProxyInstance(getClass().getClassLoader(),
userService.getClass().getInterfaces(), this);
}
// proxy : 代理类 method : 代理类的调用处理程序的方法对象.
// 处理代理实例上的方法调用并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//动态代理的本质就是利用反射实现!
Object result = method.invoke(userService, args);
after();
return result;
}
//附加操作
public void before() {
System.out.println("开启事务");
}
//附加操作
public void after() {
System.out.println("关闭事务");
}
}
public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setUserService(userService);
UserService proxy = (UserService) pih.getProxy();
String result = proxy.findAll("小明");
System.out.println(result);
}
}
6、AOP
6.1、概念
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
底层原理:动态代理
好处:在保证原始业务功能不变情况下,通过代理对象完成业务中附加操作(事务),将业务中核心操作放在目标对象中执行,实现了附加操作和核心操作解耦
通知(Advice):除了目标方法以外的操作都称之为通知
切入点(Pointcut):指定开发好的通知应用于项目中哪些组件中哪些方法
切面(Aspect):通知 + 切入点
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
6.2、第一个AOP开发
引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
public interface EmpService {
void save(String name);
String find(String name);
}
public class EmpServiceImpl implements EmpService {
@Override
public void save(String name) {
System.out.println("EmpServiceImpl save: " + name);
}
@Override
public String find(String name) {
System.out.println("EmpServiceImpl find: " + name);
return name;
}
}
/**
* 自定义记录业务方法名称前置通知
* 前置通知:目标方法执行之前先执行的额外操作
*/
public class MyBeforeAdvice implements MethodBeforeAdvice {
/**
* @param method 当前执行方法对象
* @param objects 当前执行方法的参数
* @param o 目标对象
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("当前执行方法名称:" + method.getName());
System.out.println("当前执行方法参数:" + objects[0]);
System.out.println("目标对象:" + o);
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="empService" class="aop.EmpServiceImpl"/>
<bean id="myBeforeAdvice" class="aop.MyBeforeAdvice"/>
<!--组装切面-->
<aop:config>
<!--配置切入点pointcut
id:切入点在工厂中唯一标识
expression:用来指定初入项目中哪些组件中哪些方法
execution(返回值 包.类名.*(..))
-->
<aop:pointcut id="pc" expression="execution(* aop.EmpServiceImpl.*(..))"/>
<!--配置切面
advice-ref:工厂中通知id
pointcut-ref:工厂中切入点唯一标识
-->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc"/>
</aop:config>
</beans>
6.3、环绕通知
public class MethodInvokeTimeAdvice implements MethodInterceptor {
/**
* @param methodInvocation 获取当前执行方法、获取当前执行方法参数、获取目标对象
*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("进入环绕通知");
System.out.println("当前执行方法:" + methodInvocation.getMethod().getName());
System.out.println("方法的参数:" + methodInvocation.getArguments()[0]);
System.out.println("获取当前的目标对象" + methodInvocation.getThis());
try {
long start = System.currentTimeMillis();
Object proceed = methodInvocation.proceed(); // 继续处理
long end = System.currentTimeMillis();
System.out.println("方法:" + methodInvocation.getMethod().getName() + ",执行了" + (end - start) + "ms!");
return proceed;
} catch (Exception e) {
e.printStackTrace();
System.out.println("出现异常时业务处理");
}
return null;
}
}
6.4、切入点表达式
# exacution 切入点表达式 -->> 方法级别的切入点表达式 控制粒度:方法级别 效率低
完整语法:
1. execution(访问权限修饰符 返回值 报名.类名.方法名(参数类型))
2. execution(返回值 报名.类名.方法名(参数类型))
# within -->> 类级别的切入点表达式 控制粒度:类级别 效率高
完整语法:
1. within(包名.类名)
7、spring工厂创建复杂对象
/**
* 通过实现 FactoryBean接口,用来在工厂直供创建复杂对象
*/
public class CalendarFactoryBean implements FactoryBean<Calendar> {
// 指定复杂对象的创建方式
@Override
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
// 指定创建的对象的类型
@Override
public Class<?> getObjectType() {
return Calendar.class;
}
// 指定创建对象的模式 true 单例 | false 多例
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="calendar" class="factorybean.CalendarFactoryBean"/>
8、spring整合mybatis
思路分析
整合思路:通过spring接管mybatis中核心对象SqlSessionFactory的创建
SqlSessionFactory 复杂对象 ===> SqlSessionFactoryBean创建 ===> Mybatis mybatis-spring (SqlSessionFactoryBean)
1、引入依赖
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!--spring相关-->
<!--mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--数据库相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
2、配置spring.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: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">
<!--dataSource: 使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource.DriverManagerDataSource
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSl=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSession-->
<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:com/susu/mapper/*.xml"/>
</bean>
<!--SqlSessionTemplate: 就是我们使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--将自己写的实现类,注入到Spring中-->
<bean id="userMapper" class="com.susu.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
3、从工厂中获取sqlSessionFactory对象
dao层开发
# 1. 引入依赖
spring mybatis mysql mybatis-spring druid
# 2. 建表
# 3. 实体类
# 4. Dao接口
# 5. Mapper配置文件
# 6. 编写spring.xml整合mybatis
- a. 创建DataSource 注入driverClassName url username password
- b. 创建SqlSessionFactory SqlsessionFactoryBean注入 DataSource mapperLocations 配置文件位置
- c. 创建DAO mapperFactoryBean 注入
# 7. 启动工厂获取DAO调用方法测试
9、事务控制
9.1、编程式事务处理
通过在业务层中注入事务管理器对象,然后通过编码的方式进行事务控制
# 1. mybatis框架汇总事务控制
- sqlsession 提交:sqlsession.commit(); 回滚:sqlsession.rollback()
- mybatis 是对 原始jdbc 技术封装 ===> Connection 对象 java.sql.Connection
- 项目中真正负责数据库事务控制的对象: Connection对象
# 2. 用来实现事务控制的核心对象是
Connection
# 3. 如何在现有项目中获取Connection对象
- 注意: 现有项目中DruidDataSource 连接池
Connection conn = DruidDataSource().getConnection();
# 4. 在spring和mybatis框架中提供了一个类
- DataSourceTransactionManger 数据源事务管理器
作用:
1. 在全局创建一个事务管理器,用来统一调度 业务层当前线程使用连接对象和DAO实现连接对象一致 ThreadLocal
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
# 5. 给现有业务添加事务控制
public class UserServiceImpl implements UserService {、
private PlatformTransactionManager platformTransactionManager;
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
this.platformTransactionManager = platformTransactionManager;
}
@Override
public void save(User user) {
// 创建事务配置对象
TransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
// 获取事务状态
TransactionStatus transaction = platformTransactionManager.getTransaction(defaultTransactionDefinition);
try {
user.setId(UUID.randomUUID().toString());
userDao.save(user);
int i = 1 / 0;
platformTransactionManager.commit(transaction);
} catch (Exception e) {
e.printStackTrace();
platformTransactionManager.rollback(transaction);
}
}
}
9.2、声明式事务处理
通过利用AOP切面编程进行事务控制,并对事务属性在配置文件中完成细粒度配置
好处:
- 通过 减少了代码冗余 更加专注于业务开发
<!-- 配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 结合AOP实现事务的织入-->
<!-- 配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给那些方法配置事务-->
<!-- 配置事务的传播特性: -->
<!--
propagation: 事务传播属性
REQUIRED: 需要事务 如果外层没有事务,则开启新的事务 如果外层存在事务,则融入当前事务
SUPPORTS: 支持事务 如果外层没有事务,不会开启新的事务 如果外层存在事务,则融入当前事务
REQUIRES_NEW: 每次开启新的事务 如果外层存在事务,外层事务挂起,自己开启新的事务,执行完成,恢复外层事务继续执行
NOT_SUPPORTED: 不支持事务 如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外层事务
NEVER: 不能有事务 存在事务报错
MANDATORY: 强制事务 没有事务报错
NESTED: 嵌套事务 事务之间可以嵌套运行 oracle,mysql 不支持
isolation: 事务隔离级别
DEFAULT: 使用数据库默认的隔离级别 (推荐)
READ_UNCOMMITTED: 读未提交 一个客户端读到了另一个客户端没有提交的数据 脏读现象
READ_COMMITTED: 读提交 一个客户端只能读到另一个客户端提交的数据 避免脏读现象 oracle
REPEATABLE_READ: 可重复读 主要是用来避免不可重复读现象出现 行锁 mysql
SERIALIZABLE: 序列化读 主要是用来避免幻影读现象出现 表锁
注意:隔离级别越高,查询效率越低,一遍推荐使用数据库默认隔离级别
read-only: 事务读写性 true 只读 不能执行增删改操作 false:可读可写(mysql支持, oralce不支持)
rollback-for: 出现什么类型异常回滚 默认出现RuntimeException及其子类异常 回滚
no-rollback-for: 出现什么异常不会回滚
timeout: 事务超时性 -1 用不超时
设置>0 代表设置超时时间 单位 秒
-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.susu.mapper.UserMapper.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
事务传播:就是在多个业务层之间互相调用时传递事务的过程称之为事务传播
<!--
propagation: 事务传播属性
REQUIRED: 需要事务 如果外层没有事务,则开启新的事务 如果外层存在事务,则融入当前事务
SUPPORTS: 支持事务 如果外层没有事务,不会开启新的事务 如果外层存在事务,则融入当前事务
REQUIRES_NEW: 每次开启新的事务 如果外层存在事务,外层事务挂起,自己开启新的事务,执行完成,恢复外层事务继续执行
NOT_SUPPORTED: 不支持事务 如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外层事务
NEVER: 不能有事务 存在事务报错
MANDATORY: 强制事务 没有事务报错
NESTED: 嵌套事务 事务之间可以嵌套运行 oracle,mysql 不支持
isolation: 事务隔离级别
DEFAULT: 使用数据库默认的隔离级别 (推荐)
READ_UNCOMMITTED: 读未提交 一个客户端读到了另一个客户端没有提交的数据 脏读现象
READ_COMMITTED: 读提交 一个客户端只能读到另一个客户端提交的数据 避免脏读现象 oracle
REPEATABLE_READ: 可重复读 主要是用来避免不可重复读现象出现 行锁 mysql
SERIALIZABLE: 序列化读 主要是用来避免幻影读现象出现 表锁
注意:隔离级别越高,查询效率越低,一遍推荐使用数据库默认隔离级别
read-only: 事务读写性 true 只读 不能执行增删改操作 false:可读可写(mysql支持, oralce不支持)
rollback-for: 出现什么类型异常回滚 默认出现RuntimeException及其子类异常 回滚
no-rollback-for: 出现什么异常不会回滚
timeout: 事务超时性 -1 用不超时
设置>0 代表设置超时时间 单位 秒
-->
10、log4j日志使用
# 作用:用来展示项目中的运行气质
# 日志分类:
- 项目根日志(全局日志)
- 项目子日志(指定包级别日志)
# 日志级别:
ERROE(高) > WARN > INFO > DEBUG(低)
如何使用
1、引入依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
2、配置log4j.properties
# 根日志
log4j.rootLogger=ERROR,bb
log4j.appender.bb=org.apache.log4j.ConsoleAppender
log4j.appender.bb.layout=org.apache.log4j.PatternLayout
log4j.appender.bb.layout.conversionPattern=[%p] %d{yyyy-MM-dd} %m%n
# 包日志
log4j.logger.com.su.dao=debugger
log4j.logger.org.springframework=debugger
11、注解式开发
定义:通过spring框架中提供的一系列相关注解完成项目中快速开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入context约束,增加注解的支持
<?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/>
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.susu"/>
</beans>
# 1).创建对象相关注解
- @Component
作用:用来负责对象的创建 ====> <bean id="" class=""/>
修饰范围:只能用在类上
注意:默认使用这个注解在工厂中创建的对象的唯一标识为 类名首字母小写 UserDaoImpl类 ==> userD0aoImpl
- dao层 【@Repository】
- service层 【@Service】
- controller层 【@Controller】
# 2).控制对象在工厂中创建次数
- a. 配置文件修改 <bean id="" class=""/>
b. 注解如何控制
@Scope
- 作用:用来指定对象的创建次数 默认为单例
- 修饰范围:只能加在类上
# 3). 属性注入的相关注解
- a. spring框架提供的 @Autowire 默认根据类型注入
如果Autowired不能唯一自动装配属性,则需要通过@Qualifier(value="xxx")
- b. JavaEE中本身就有的 @Resource 默认根据名字注入,找不到再通过类型注入
修饰范围:用在类中的成员变量,或者是勒种成员变量的SET方法上
作用: 用来完成成员变量的赋值 | 注入操作
- @Nullable : 字段标记了这个注解,说明这个字段可以为null
# 4). 控制事务的注解
- @Transactional
修改范围: 用在类上主要用在业务层组件类上或者是方法上
注解作用: 用来给类中方法加入事务,当类上和方法上同时存在该注解时 局部优先
注解属性:
- propagation 用来控制传播属性
- isolation 用来控制隔离级别
- timeout 用来设置超时性
- readOnly 用来设置事务读写性
- rollbackFor 用来设置什么异常回滚
- noRollbackFor 同来设置什么异常不会回滚
注意:要使这个注解生效,需要加入如下配置
<tx:annotation-driven transaction-manager="transactionManager"/>