1、认识Spring
Spring全家桶:Spring SpringMVC SpringBoot SpringCloud SpringCloud-alibaba SpringSecurity SpringData ……
Spring框架,被称为胶水框架,本身没有发明出新的技术,使现有的java技术,如何更好用。
现有的web开发中遇到的问题:
1、耦合度高,依赖所创建的对象
在业务逻辑层中
DeptDao deptDao=new DeptDaoImpl();
在servlet中
DeptService deptService=new DeptServiceImpl();
2、java原生的API比较复杂、开发效率低 JDBC
Spring框架的特点:
1、提供了很多技术的解决方案---IoC AOP 事务管理等
2、没有发明新技术,使现有技术更好用---胶水框架
3、优秀的框架,使用了很多设计模式:单例模式、简单工厂设计模式、代理设计模式、观察者、模板、策略等
2、Spring的体系结构

Test:
测试,用来测试程序
CoreContainer:
核心容器
Beans: 对象,也称为bean IoC
Core: 其他模块所依赖的核心模块
Context: 上下文,包括调度和远程抽象
SpEL:Spring提供的支持的表达式语言 提供了很多标签
Aop:
面向切面编程
Spring-Aop:基于代理的Aop的支持
Aspects:基于AspectsJ的切面
Instrument :JVM调用的一个用于监测目的代理
messaging: 对消息框架的支持
数据访问部分 :
Data Access
将Mybatis和Spring融合在一起
jdbc: spring-data jdbc-template 对JDBC封装 简化jdbc的操作
orm:整合第三方的ORM框架,mybatis hibernate等 简化mybaits等ORM框架的使用
Transactions: 整合了事务---声明式事务,简化事务的操作,无需编程式事务中,手动提交、回滚事务
web支持:
servlet:使用
web: web网页
MVC:整合MVC框架
| GroupId | ArtifactId | 说明 |
|---|---|---|
| org.springframework | spring-beans | Beans 支持,包含 Groovy |
| org.springframework | spring-aop | 基于代理的AOP支持 |
| org.springframework | spring-aspects | 基于AspectJ 的切面 |
| org.springframework | spring-context | 应用上下文运行时,包括调度和远程抽象 |
| org.springframework | spring-context-support | 支持将常见的第三方类库集成到 Spring 应用上下文 |
| org.springframework | spring-core | 其他模块所依赖的核心模块 |
| org.springframework | spring-expression | Spring 表达式语言,SpEL |
| org.springframework | spring-instrument | JVM 引导的仪表(监测器)代理 |
| org.springframework | spring-instrument-tomcat | Tomcat 的仪表(监测器)代理 |
| org.springframework | spring-jdbc | 支持包括数据源设置和 JDBC 访问支持 |
| org.springframework | spring-jms | 支持包括发送/接收JMS消息的助手类 |
| org.springframework | spring-messaging | 对消息架构和协议的支持 |
| org.springframework | spring-orm | 对象/关系映射,包括对 JPA 和 Hibernate 的支持 |
| org.springframework | spring-oxm | 对象/XML 映射(Object/XML Mapping,OXM) |
| org.springframework | spring-test | 单元测试和集成测试支持组件 |
| org.springframework | spring-tx | 事务基础组件,包括对 DAO 的支持及 JCA 的集成 |
| org.springframework | spring-web | web支持包,包括客户端及web远程调用 |
| org.springframework | spring-webmvc | REST web 服务及 web 应用的 MVC 实现 |
| org.springframework | spring-webmvc-portlet | 用于 Portlet 环境的MVC实现 |
| org.springframework | spring-websocket | WebSocket 和 SockJS 实现,包括对 STOMP 的支持 |
| org.springframework | spring-jcl | Jakarta Commons Logging 日志系统 |
3、IoC( Inverse Of Controll:控制反转 )
将new对象交由Spring来创建而不是自己创建
Bean:咖啡豆,转义为 java对象
自己创建对象,例如在业务逻辑层,需要创建dao的对象,从而调用dao中的方法,依赖于创建对象的类
把对象的创建、管理交给spring来进行,降低了对类的依赖,这样耦合度低
自定义框架:
实现创建对象的功能,当程序员需要对象时,通过框架获取即可
1、新建maven项目
2、创建实体类
3、创建配置文件,配置要创建的对象信息---bean的定义信息
properties文件
zhangsan=com.qf.springpro2105.pojo.Student
dev=com.qf.springpro2105.pojo.Dept
4、编写工厂类,创建声明的对象
4.1:先读取bean的定义信息
4.2:根据读取到的bean的定义信息,通过反射方式创建对象
package com.qf.springpro2105.util;
import java.io.IOException;
import java.util.Properties;
public class MyBeanFactory {
//创建Properties对象
private Properties properties=new Properties();
public MyBeanFactory() {
}
//参数就是配置文件的名字
public MyBeanFactory(String configName) {
try {
//加载配置文件信息
properties.load(MyBeanFactory.class.getResourceAsStream(configName));
} catch (IOException e) {
e.printStackTrace();
}
}
//获取对象
public Object getBean(String beanName){
//1、从配置文件中读取到要创建的对象的类名
String className=properties.getProperty(beanName);
if(className!=null){
Class clazz=null;
try {
//2、通过反射创建对象
clazz=Class.forName(className);
return clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
return null;
}
}
5、测试
package com.qf.springpro2105.test;
import com.qf.springpro2105.pojo.Dept;
import com.qf.springpro2105.pojo.Student;
import com.qf.springpro2105.util.MyBeanFactory;
public class TestMyBeanFactory {
public static void main(String[] args) {
//创建工厂类对象
// /beans.properties 中/的含义是项目的根目录
MyBeanFactory beanFactory=new MyBeanFactory("/beans.properties");
//获取对象
Object object=beanFactory.getBean("zhangsan");
if(object!=null){
Student jiaBao=(Student)object;
System.out.println(jiaBao);
}
Dept dev=(Dept)beanFactory.getBean("dev");
System.out.println(dev);
}
}
spring中bean的功能就相当于我们自定义的工厂
IoC内部框架:
为了降低耦合度,把对象的创建、管理的权利交给spring进行
1、搭建spring环境
1.1 添加依赖
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
1.2 编写spring的配置文件,名字任意,有意义即可
spring-context.xml
【说明】spring支持bean的声明/定义方式有很多
1、xml方式
2、注解方式
3、properties方式等 --- zhangsan = 类地址(包名)
<?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.xsd">
<bean id="jiaBao" class="com.qf.springpro2105.pojo.Student"></bean>
<bean id="dev" class="com.qf.springpro2105.pojo.Dept"></bean>
</beans>
1.3 获取bean
System.out.println("----------------------------------------------");
System.out.println("spring获取bean");
ApplicationContext beanFactory2=new ClassPathXmlApplicationContext("spring-context.xml");
Student jiaBao2= (Student) beanFactory2.getBean("zhangsan");
System.out.println(jiaBao2);
2、给对象的属性赋值
DI: Dependency Injection 依赖注入,在创建对象的过程中,给属性赋值的过程
也就是说给属性赋值的过程叫注入
<?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.xsd">
<bean id="jiaBao" class="com.qf.springpro2105.pojo.Student">
<!--
每一个property就是class里指定类的一个属性
其中name代表属性名,value是属性值
默认情况下:调用getBean()时,才创建对象
如果设置了property,则在创建对象后,调用setXXX()方法,给属性赋值
-->
<property name="stuName" value="张三"></property>
<property name="stuNo" value="qf001"></property>
</bean>
<bean id="dev" class="com.qf.springpro2105.pojo.Dept">
<property name="dName" value="软件研发2105部"></property>
<property name="deptNo" value="1001"></property>
<property name="loc" value="北京市海淀区"></property>
</bean>
</beans>
bean的创建过程:---简化的过程
1、声明bean (xml 文件 注解 properties文件)
2、读取配置文件 (声明的方式不同,读取的方式也不同)
3、通过反射创建对象
4、通过setXXX()方法给属性赋值
DI:不同类型的属性值如何注入
//User类
public class User {
//不同类型的属性如何注入值
private Integer id;
private String password;
private String sex;
private Date bornDate;
private String[] hobbys;
private Set<String> phones;
private List<String> names;
private Map<String,String> countries;
private Properties files;
private Student[] stus;
}
<!--set注入-->
<?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.xsd">
<bean id="jiaBao" class="com.qf.springpro2105.pojo.Student">
<!--
每一个property就是class里指定类的一个属性
其中name代表属性名,value是属性值
默认情况下:调用getBean()时,才创建对象
如果设置了property,则在创建对象后,调用setXXX()方法,给属性赋值
-->
<property name="stuName" value="王佳保"></property>
<property name="stuNo" value="qf001"></property>
</bean>
<bean id="yinZuEn" class="com.qf.springpro2105.pojo.Student">
</bean>
<bean id="dev" class="com.qf.springpro2105.pojo.Dept">
<property name="dName" value="软件研发2105部"></property>
<property name="deptNo" value="1001"></property>
<property name="loc" value="设计城906"></property>
</bean>
<bean id="mingYuan" class="com.qf.springpro2105.pojo.Emp">
<!--DI:给对象的属性赋值-->
<property name="empNo" value="1001"></property>
<property name="eName" value="江明源"></property>
<!--dept属性是一个对象类型,如何注入?
属性如果是对象类型,通过ref注入对象的属性值
-->
<property name="dept" ref="dev"></property>
</bean>
<!--给不同类型的属性注入值-->
<bean id="haoTian" class="com.qf.springpro2105.pojo.User">
<property name="id" value="1001"></property>
<property name="password" value="123abc"></property>
<property name="sex" value="mail"></property>
<property name="bornDate" value="2000/04/08"></property><!--注意格式/-->
<!--可以存多个值的类型:容器类型-->
<property name="hobbys">
<array>
<value>Run</value>
<value>Code</value>
<value>sleep</value>
</array>
</property>
<property name="phones">
<set>
<value>13888888888</value>
<value>13888888886</value>
</set>
</property>
<property name="names">
<list>
<value>tom</value>
<value>harry</value>
<value>jack</value>
</list>
</property>
<property name="countries">
<map>
<entry key="CN" value="中国"></entry>
<entry key="US" value="美国"></entry>
<entry key="KR" value="韩国"></entry>
</map>
</property>
<property name="files">
<props>
<prop key="first">第一个文档</prop>
<prop key="second">第二个文档</prop>
<prop key="third">第三个文档</prop>
</props>
</property>
<property name="stus">
<array>
<!--内部bean-->
<bean id="tao" class="com.qf.springpro2105.pojo.Student"></bean>
<!--可以引入外部bean-->
<ref bean="jiaBao"></ref>
<ref bean="yinZuEn"></ref>
</array>
</property>
</bean>
</beans>
注入方式
Spring给对象的属性注入值的方式有很多:
1、设值注入,通过setXXX()方法来给属性赋值
通过bean的property方式,使用的就是设值注入(如上)
2、构造注入,必须提供有参构造方法(如下)

<!--构造注入-->
<bean id="zeYang" class="com.qf.springpro2105.pojo.Student">
<constructor-arg name="stuNo" value="qf003"></constructor-arg>
<constructor-arg name="stuName" value="王泽洋"></constructor-arg>
</bean>
3、自动注入,自动装配
无需配置赋值的代码,根据名字或类型自动赋值
package com.qf.springpro2105.pojo;
public class Teacher {
private String teaName;
private Student stu;
public String getTeaName() {
return teaName;
}
@Override
public String toString() {
return "Teacher{" +
"teaName='" + teaName + '\'' +
", stu=" + stu +
'}';
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
}
<bean id="stu" class="com.qf.springpro2105.pojo.Student">
<property name="stuNo" value="qf007"></property>
<property name="stuName" value="王冰"></property>
</bean>
<!--使用自动注入
autowire="byName" 根据名字的自动注入,找bean的id值和Teacher类的属性名一致的bean进行赋值
byType:要求和属性同类型的bean只能有一个
-->
<bean id="liu" class="com.qf.springpro2105.pojo.Teacher" autowire="byName">
<property name="teaName" value="刘老师"></property>
</bean>

Bean的作用域
【说明】SpringIoC创建Bean 是使用工厂模式
默认情况下都是单例Bean
作用域共有5个:
1、单例singleton:此bean默认为单例模式
优势:节省内存,不管getBean()获取多少次,就创建一个对象
缺点:多线程情况下,产生线程安全问题(springMVC框架时讲解)
2、多例/原型prototype :每次通过beanfactory的getBean()方法都会创建一个新的对象
3、request: bean在同一次请求内有效
4、session: bean在同一次会话内有效
5、global session: 所有会话都有效
FactoryBean
在spring中bean分两种类型:
1、普通bean ,之前使用的bean 特点:能够通过反射方式直接创建出来
2、FactoryBean,工厂Bean/复杂Bean, 实现了FactoryBean接口的bean,不能通过反射直接创建出来,需要若干步骤才能被创建
例如:mybatis中的SqlSession对象,不能通过反射直接创建

创建FactoryBean
package com.qf.springpro2105.pojo;
import org.springframework.beans.factory.FactoryBean;
import java.io.Serializable;
public class Book implements Serializable, FactoryBean {
private String bookName;
private float price;
private String isbn;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public Object getObject() throws Exception {
//编码,实现多个步骤创建对象
//创建SqlSessionBuilder
//SqlSessionFactory
//SqlSession
//return SqlSession对象
Publisher publisher=new Publisher();
publisher.setBook(this);
publisher.setPublisherName("人民教育出版社");
return publisher;
}
public Class<?> getObjectType() {
//得到对象的类型
return Publisher.class;
}
public boolean isSingleton() {
//是否是单例
return false;
}
}
package com.qf.springpro2105.pojo;
public class Publisher {
private String PublisherName;
private Book book;
public String getPublisherName() {
return PublisherName;
}
public void setPublisherName(String publisherName) {
PublisherName = publisherName;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
//这样的话只需要给book赋值就可以了,调用当前方法,因为xml给book赋值了
//直接得到book的各种属性名了
public void say(){
System.out.println("本出版社"+this.PublisherName+"出版了图书《"
+this.book.getBookName()+"》价格是"+this.book.getPrice()
+"ISBN条形码是"+this.book.getIsbn());
}
}


BeanFactroy的特性
饿汉模式:占内存
当工厂创建的时候,配置文件中配置的bean就一并被创建

懒汉模式:创建bean工厂时,并不创建bean,当通过beanfactroy的getBean()获取bean的时候,才会创建bean 效率低


【面试题】如何设置不让工厂创建某个类的对象?
1、bean 标签中 lazy-init="true", 不统一创建,在通过getBean()获取时创建
2、bean 标签中 abstract="true", 不会被spring的工厂创建
【说明】通常都默认
Bean的生命周期

生命周期方法的执行顺序
1、构造方法 2、setXXX() 3、初始化方法 4、销毁方法
当初始化执行完毕后,对象才创建结束,可以使用
如何设置生命周期里的初始化方法和销毁方法呢?
有两种方式:
方式一:自定义方法名,通过bean标签的init-method方法指定初始化方法
通过bean标签的destroy-method方法指定销毁方法
方式二:实现spring里的初始化、销毁接口,自动执行实现的方法

package com.qf.springpro2105.pojo;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import java.io.Serializable;
public class Animal2 implements Serializable, InitializingBean, DisposableBean {
public Animal2() {
System.out.println("Animal2的构造方法");
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("执行了Animal的setName方法");
this.name = name;
}
public void afterPropertiesSet() throws Exception {
//初始化方法,由于实现接口,会自动执行
System.out.println("执行了Animal2类中的初始化方法");
}
public void destroy() throws Exception {
//销毁时执行的方法
System.out.println("执行了Animal类中的销毁方法");
}
}
<bean id="wangCai2" class="com.qf.springpro2105.pojo.Animal2">
<property name="name" value="旺财2"></property>
</bean>
生命周期的总结:
单例bean: 默认是饿汉模式的bean,随着工厂的创建而创建--->构造方法--->setXXX()--->初始化方法--->对象创建完毕--->随着工厂的销毁而销毁
多例bean:通过beanfactory的getBean()获取时而创建(懒汉模式的bean)-->构造方法--->setXXX()--->初始化方法--->对象创建完毕--->JVM的GC来回收、销毁
【说明】bean由工厂创建完毕后,会存入map集合中
代理设计模式
做一件事情,可以设置代理,让代理去做,简化编程。
案例:
静态代理:只能为某个接口或某个类做代理
接口
package com.qf.springpro2105.proxy;
import com.qf.springpro2105.pojo.Dept;
public interface DeptDao {
int addDept(Dept dept);
}
接口的实现类
package com.qf.springpro2105.proxy;
import com.qf.springpro2105.pojo.Dept;
public class DeptDaoImpl implements DeptDao {
public int addDept(Dept dept) {
System.out.println("添加部门"+dept.getDeptNo()+"到数据库中");
return 1;
}
}
代理类
package com.qf.springpro2105.proxy;
import com.qf.springpro2105.pojo.Dept;
//代理类---实现哪个接口,就给谁做代理
//给固定类型做代理---静态代理
public class DeptProxy implements DeptDao {
//创建实现类的对象
private DeptDao deptDao=new DeptDaoImpl();
public int addDept(Dept dept) {
//设置公共的代码
//通过代理去调用方法
System.out.println("即将执行添加方法");
int result=deptDao.addDept(dept);
System.out.println("执行添加方法结束");
return result;
}
}
测试类:直接使用代理即可
package com.qf.springpro2105.proxy;
import com.qf.springpro2105.pojo.Dept;
import javax.crypto.spec.DESedeKeySpec;
public class Test {
public static void main(String[] args) {
//创建部门对象
Dept dept=new Dept();
dept.setDeptNo(9118);
dept.setdName("研发部");
dept.setLoc("设计城906");
//创建代理
DeptDao deptDao=new DeptProxy();
int result=deptDao.addDept(dept);
if(result==1){
System.out.println("添加部门成功");
}else{
System.out.println("添加部门失败");
}
}
}
动态代理设计模式:可以给任意类型做代理,灵活 (jdk动态代理,cglib动态代理)
jdk动态代理
package com.qf.springpro2105.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//jdk动态代理
public class DyProxy<T> implements InvocationHandler {
public DyProxy() {
}
public DyProxy(T proxy) {
this.proxy = proxy;
}
//声明属性:接收所代理的对象
private T proxy;
public T getProxy() {
return proxy;
}
public void setProxy(T proxy) {
this.proxy = proxy;
}
//调用invoke方法,就是在执行/调用代理对象对象的方法
//参数1:proxy 代理对象
//参数2:执行的代理对象中的方法
//参数3:执行代理对象方法所需要的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//声明变量,接收执行代理对象方法后的返回值
Object result=null;
//执行代理对象方法前,编写业务代码
System.out.println("动态代理开始执行……");
try{
result=method.invoke(this.proxy,args);
//执行代理方法后,编写业务代码
System.out.println("动态代理方法执行结束");
}catch (Exception ex){
System.out.println("执行代理对象的方法,发生异常,异常信息如下:"+ex.getMessage());
}finally {
System.out.println("执行了代理对象的finally方法");
}
return result;
}
}
测试
package com.qf.springpro2105.proxy;
import com.qf.springpro2105.pojo.Dept;
import com.qf.springpro2105.pojo.Emp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class TestDyProxy {
public static void main(String[] args) {
//使用jdk动态代理
//不直接调用DeptDaoImpl EmpDaoImpl中的方法
//使用动态代理对象去调
//1、创建被代理的对象
DeptDao deptDao=new DeptDaoImpl();
EmpDao empDao=new EmpDaoImpl();
//2、设置代理
InvocationHandler handler=new DyProxy<DeptDao>(deptDao);
//3、绑定---创建动态代理对象的过程
DeptDao proxy= (DeptDao) Proxy.newProxyInstance(DeptDao.class.getClassLoader(),
new Class[]{DeptDao.class},handler);
//执行代理方法
Dept dept=new Dept();
dept.setDeptNo(9099);
proxy.addDept(dept);
System.out.println("--------------------------------------------------------");
//设置代理
InvocationHandler handler1=new DyProxy<EmpDao>(empDao);
//创建代理对象
EmpDao proxy1=(EmpDao)Proxy.newProxyInstance(EmpDao.class.getClassLoader(),
new Class[]{EmpDao.class},handler1);
Emp emp=new Emp();
emp.setEmpNo(1001);
proxy1.addEmp(emp);
}
}
【说明】JDK动态代理有要求:接口+接口的实现类
通过反射方式实现
【面试题】:反射在程序中的应用场景有哪些?
1、SpringIoC:用反射来创建bean
2、动态代理:代理对象的创建
3、自己:优化servlet
cglib动态代理
1、添加依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
2、创建代理类
package com.qf.springpro2105.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//cglib动态代理
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer=new Enhancer();
//获取代理独享
public Object getProxy(Class calzz){
//设置代理类的父类,给谁做代理,谁就是父类
enhancer.setSuperclass(calzz);
enhancer.setCallback(this);
//JVM动态生成所代理对象的子类(字节码文件)
//动态生成的代理类,给谁做代理继承谁
return enhancer.create();
}
//o:代理对象
//objects:参数
//methodProxy 方法的代理对象
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行代理对象方法之前的逻辑代码
System.out.println("cglib动态代理开始执行……");
Object obj=null;
try{
obj=methodProxy.invokeSuper(o,objects);
System.out.println("cglib懂爱代理执行结束");
}catch (Exception ex){
System.out.println("cglib动态代理执行时发生异常,异常信息如下:"+ex.getMessage());
}finally {
System.out.println("执行了cglib动态代理的finally");
}
return obj;
}
}
测试
package com.qf.springpro2105.cglib;
public class test {
public static void main(String[] args) {
CglibProxy proxy=new CglibProxy();
//获取JVM生成的代理对象
DoSomething doSomething=(DoSomething)proxy.getProxy(DoSomething.class);
//doSomethis里存储的是DoSomething子类对象,子类对象就是DoSomething类的代理对象
doSomething.doSomeThing();
}
}
【说明】cglib代理不要求代理对象实现接口
cglib原理是通过动态生产代理类对象的子类作为代理对象的方式
【面试题】jdk动态代理和cglib动态代理的区别?
mybatis使用的是jdbc动态代理
spring里两种代理方式都使用,优先使用jdk动态代理,如果类没有实现接口,才是用cglib动态代理
如果不理解代理模式,将无法理解Aop
【总结】
动态代理:
jdk:
类中:
类名设置泛型T 实现InvocationHandler --- 重写invok方法(利用反射)
添加属性 类型为T类型 --- 该属性代表的是动态代理的对象
设置Constructor和setget方法
重写invok方法(可在该方法中编写业务代码)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
try-catch中 写入 method.invok(this.proxy,args) --- 用Object类型参数来接收
return Object类型的参数
测试:
实现被代理的对象 --- newDaoImpl
设置代理对象 --- InvocationHandler handler=new DyProxy<接口>(接口的变量名);
绑定动态代理的对象 --- 接口类型 变量名1= (接口类型) Proxy.newProxyInstance(接 口.class.getClassLoader(), new Class[]{接口.class},handler);
此时变量名1可调用该接口所存在的方法
cglib:
类中:
类名实现MethodInterceptor接口 --- 重写intercept方法
new Enhancer对象
创建代理独享的方法 --- 参数传Class
方法中实现 enhancer.setSuperclass(参数名); enhancer.setCallback(this);return enhancer.create();
重写intercept方法(可在该方法中编写业务代码)
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {}
try-catch中 写入 methodProxy).invokSuper(o,objects) --- 用Object类型参数来接收
return obj
测试:
new 动态代理的类(上边的类)
调用类中创建代理独享的方法 --- 参数传被动态代理类的对象类型
就可以调用被动态代理中的方法
【区别】 jdk本质是反射
cglib本质是设置父类的方式将代理的类作为父类
jdk运用接口+接口的实现类的方式 调用的方法所在的类要实现接口
cglib无需创建接口可直接创建类并将其作为父类进行调用
jdk实现InvocationHandler接口 重写invoke方法 该接口是jdk所自带的
cglib实现MethodInterceptor接口 重写intercept方法 该接口需导入cglib依赖
jdk创建T类型的属性 将其声明为代理对象
cglib创建代理独享的方法 创建Enhancer 将其setSuperclass setCallback create传进该方法中
4、Aop
AOP:Aspect Oriented Programming 面向切面编程
桌子:OOP 面向对象编程 桌面对象、桌腿对象、螺丝钉对象、抽屉对象等
航天飞机:比较复杂,用OOP设计程序的话,对象非常多,不好管理,辅助使用Aop来设计程序
问题:注册用户功能,只需要添加到数据库吗?显然不是,在执行核心功能的同时,还要执行辅助功能
核心功能:把用户添加到数据库中
辅助功能/通用功能:异常处理
事务处理
记录日志等
Aop的思想,让程序员只关注(编写)核心功能,辅助功能通过配置后,自动执行
5、Aop的核心思想

-
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
-
切入点(Pointcut):被Spring切入连接点。
-
通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
-
目标对象(Target):代理的目标对象
-
引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。
-
织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
-
代理(Proxy):被AOP织入通知后,产生的结果类。
-
切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。
6、搭建SpringAop开发环境
添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
修改spring配置文件的头信息,增加了aop部分的xsd的限制
<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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>
7、Aop的实现
编写核心方法
package com.qf.springpro2105.service.impl;
import com.qf.springpro2105.pojo.Dept;
import com.qf.springpro2105.service.DeptService;
public class DeptImpl implements DeptService {
public boolean addDept(Dept dept) {
System.out.println("添加部门"+dept.getDeptNo()+"成功!");
return true;
}
}
编写通用方法
记录日志:记录谁、在什么时间、在哪台电脑、做了什么事
异常日志:记录什么时间、发生什么异常,为了调试使用
添加log4j的依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
添加log4j的配置文件
名字必须为:log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout,logfile
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=qf.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n
编写增强方法
注意:增强方法,可以写到一个类中,也可以写到多个类中
记录日志的增强方法
package com.qf.springpro2105.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
public class OperatorAdvice {
//声明Log4j对象,用于记录日志
private final Logger logger=Logger.getLogger(OperatorAdvice.class);
//编写通用方法
//前置增强方法:在核心方法之前执行---记录日志
//获取执行核心方法的信息
//JoinPoint连接点对象:可以获取PointCut的信息
//增强的执行顺序并不是按照自上而下的顺序,而是将切入点放在将被定义的增强当中
public void beforeOper(JoinPoint joinPoint){
//记录日志
logger.debug("即将执行"+joinPoint.getTarget()+"类里的"+
joinPoint.getSignature()+"方法,方法传参如下:"+
Arrays.toString(joinPoint.getArgs()));
}
//后置增强方法:在执行核心方法之后执行---记录日志
public void afterOper(JoinPoint joinPoint,Object result){
//记录日志
logger.debug("执行"+joinPoint.getTarget()+"类中的方法"+
joinPoint.getSignature()+"结束,方法的返回值如下:"+result);
}
}
异常处理增强、最终增强
package com.qf.springpro2105.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
public class ExceptionAdvice {
//在catch finally 记录日志
Logger logger=Logger.getLogger(ExceptionAdvice.class);
//异常处理增强:发生异常时所执行的通用方法
//参数1:spring传递过来的连接点对象,封装了切入点核心方法的信息
//参数2:spring在执行核心方法发生异常时,传递过来的异常对象
public void catchException(JoinPoint joinPoint,RuntimeException ex){
System.out.println("发生异常,异常信息如下:"+ex.getMessage());
//error,写到控制台或日志文件,红色文字打印
logger.error(joinPoint.getSignature()+"类中的方法"+
joinPoint.getSignature()+"发生异常,异常信息如下:"+ex.getMessage());
}
//最终增强
public void finallyException(JoinPoint joinPoint){
logger.debug(joinPoint.getSignature()+"类中的方法"+
joinPoint.getSignature()+"执行finally结束");
}
}
xml文件方式配置Aop
切入点(核心方法)的配置有两种:
1、精确配置,某个特定的方法
2、通用表达式配置,代表一类方法

<!--定义核心方法bean信息-->
<bean id="deptService" class="com.qf.springpro2105.service.impl.DeptServiceImpl"></bean>
<!--定义增强方法的bean-->
<bean id="operAdvice" class="com.qf.springpro2105.aop.OperatorAdvice"></bean>
<bean id="exceptionAdvicee" class="com.qf.springpro2105.aop.ExceptionAdvice"></bean>
<!--Aop配置-->
<aop:config>
<!--告知spring核心方法是谁,也称为切入点
切入点的配置有两种:
方式一:配置固定方法(特定某个方法)
方式二:表达式配置,配置一类方法(多个方法)
.. 参数类型任意
-->
<!--方式一:配置固定方法(特定某个方法)-->
<!--<aop:pointcut id="addDeptPointCut" expression="execution(public boolean addDept(Dept))"/>-->
<!--方式二:表达式配置,配置一类方法(多个方法)-->
<aop:pointcut id="aopPointCut" expression="execution(public * com.qf.springpro2105.service..*.*(..))"/>
<aop:pointcut id="aopPointCut2" expression="execution(public String com.qf.springpro2105.service..*.*(..))"/>
<aop:pointcut id="aopPointCut3" expression="execution(public String com.qf.springpro2105.service..*.*(String,int))"/>
<!--配置切面-->
<aop:aspect ref="operAdvice">
<!--前置增强-->
<aop:before method="beforeOper" pointcut-ref="aopPointCut"></aop:before>
<!--后置增强-->
<aop:after-returning method="afterOper" pointcut-ref="aopPointCut" returning="result"></aop:after-returning>
</aop:aspect>
<aop:aspect ref="exceptionAdvicee">
<!--异常处理增强-->
<aop:after-throwing method="catchException" pointcut-ref="aopPointCut" throwing="ex"></aop:after-throwing>
<!--最终增强-->
<aop:after method="finallyException" pointcut-ref="aopPointCut"></aop:after>
</aop:aspect>
</aop:config>
环绕增强
声明环绕增强
package com.qf.springpro2105.aop;
//环绕增强
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Arrays;
public class ArroundAdvice {
//创建日志对象
Logger logger=Logger.getLogger(ArroundAdvice.class);
public Object doArround(ProceedingJoinPoint joinPoint){
//声明存放返回值的变量
Object result=null;
//相当于前置增强的代码
logger.debug("环绕增强---执行了"+joinPoint.getTarget()+"里的方法"+
joinPoint.getSignature()+"方法的参数如下:"+
Arrays.toString(joinPoint.getArgs()));
try{
//执行切入点方法---核心方法
result=joinPoint.proceed();
//相当于后置增强
logger.debug("环绕增强---执行"+joinPoint.getSignature()+"方法结束,"+
"方法的返回值如下:"+result);
}catch (Throwable throwable) {
//throwable.printStackTrace();
//相当于异常处理增强
logger.error("环绕增强---执行"+joinPoint.getSignature()+"发生异常,异常信息如下:"+
throwable.getMessage());
} finally {
//相当于最终增强
logger.debug("环绕增强---执行"+joinPoint.getSignature()+"finally结束");
}
return result;
}
}
配置环绕增强
<!--定义环绕增强的bean-->
<bean id="arround" class="com.qf.springpro2105.aop.ArroundAdvice"></bean>
<aop:aspect ref="arround">
<aop:around method="doArround" pointcut-ref="aopPointCut"></aop:around>
</aop:aspect>
【说明】切入点如果使用了接口,则spring使用jdk动态代理(效率较高)
如果没有使用接口,则spring使用cglib动态代理
注解方式配置aop
//注解方式配置aop
@Component//创建类的对象存储在ioc容器中
@Aspect//声明切面,相当于配置文件里的<aop:config>
public class Advice {
private final Logger logger=Logger.getLogger(Advice.class);
//声明切入点的pointCut,execution设置的是对哪些方法进行拦截增强
@Pointcut("execution(* com.qf.java2105.service.impl..*.*(..))")
public void method(){};
//配置前置增强
@Before("method()")
public void doBefore(JoinPoint joinPoint){
logger.debug("正在执行"+joinPoint.getTarget()+"类里的"+
joinPoint.getSignature()+"方法");
}
//配置后置增强
@AfterReturning(value = "method()",returning = "result")
public void doAfter(JoinPoint joinPoint,Object result){
logger.debug("正在执行"+joinPoint.getTarget()+"类里的"+
joinPoint.getSignature()+"方法,返回值为"+result);
}
//异常处理增强
@AfterThrowing(value = "method()",throwing = "ex")
public void doThrowing(JoinPoint joinPoint,Exception ex){
logger.error("正在执行"+joinPoint.getTarget()+"类里的"+
joinPoint.getSignature()+"方法,异常信息为"+ex.getMessage());
}
@After("method()")
public void doFinally(JoinPoint joinPoint){
logger.debug("正在执行"+joinPoint.getTarget()+"类里的"+
joinPoint.getSignature()+"方法");
}
}
8、后处理器
是bean的生命周期里的一个阶段
1、构造方法 2、setXXX() 3、后处理器前置阶段 4、初始化方法 5、 后处理器后置阶段 创建完成 6、销毁
package com.qf.springpro2105.aop;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
//后处理器:对象已经被创建
//执行分两个阶段:
//1、在初始化方法之前执行,称为后处理器的前置阶段
//2、在初始化方法之和执行,称为后处理器的后置阶段
public class MyBenPostProcessor implements BeanPostProcessor {
//后处理器的前置阶段 所执行的方法
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器 在init之前执行,"+bean.getClass());
return bean;
}
//后处理器的后置阶段 所执行的方法
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器 在init之后执行,"+bean.getClass());
return bean;
}
}
<!--配置后处理器,将对bean工厂中的所有bean的生命周期进行干预-->
<bean class="com.qf.springpro2105.aop.MyBenPostProcessor"></bean>
9、spring整合mybatis
为什么要整合?
使mybatis更易用。
【面试题】spring都整合了mybais的哪些组件?
1、mybatis工具类里的对象 SqlSessionFactoryBuilder SqlSessionFactory SqlSession
使用Spring-IoC功能实现的
2、整合了事务---spring自动提交、回滚事务,声明式事务
使用Spring-Aop功能实现的
3、Spring自动获取Dao接口的实现类对象(代理类对象),放在容器中
两个版本的整合:
版本一:配置文件方式 xml
版本 二:注解方式(主要使用)
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- Spring整合mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql驱动 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
<scope>runtime</scope>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<!-- 更改maven编译规则 -->
<resources>
<resource>
<!-- 资源目录 -->
<directory>src/main/java</directory>
<includes>
<include>*.xml</include><!-- 默认(新添加自定义则失效) -->
<include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
编写配置文件
数据库配置文件
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3
xml整合
引入外部文件
<!--
引入外部文件
引入外部文件用 context:property-placeholder
引入文件需要用文件的类路径 + properties文件名
-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
配置连接池信息
<!--
配置连接池信息
id:自己起的名
class : 引入连接池信息
init-method : 初始化方法 init : 连接池里的初始化方法 - 方法自己就初始化了
destroy-method :销毁方法 close :连接池里的销毁方法 - 结束之后自己就销毁了
-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--
property是Druid连接池自带的名
value是db.properties里的名
-->
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!--配置连接池的其他信息-->
<!--
配置超时时间 设定时间范围之外就不等待了
maxWait : 配置等待时间的
value : 是以毫秒为单位的
maxActive 最大活跃数量
minIdle 最小空闲 数量
initialSize 初始化数量
-->
<property name="maxWait" value="5000"></property>
<property name="initialSize" value="${jdbc.init}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
</bean>
创建sqlSessionFactory
<!--
说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
然后创建 SqlSessionFactory对象
在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可
"org.mybatis.spring.SqlSessionFactoryBean" : Spring和Mybatis整合依赖里的
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--
注入数据源属性
name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
相当于就是把原先的mybatis连接池替换成Druid连接池
-->
<property name="dataSource" ref="druidDataSource"></property>
<!--
配置映射文件的位置
typeAliasesPackage : 配置别名
value写实体类的别名位置
mapperLocations : 映射文件位置 是一个集合类型
-->
<property name="typeAliasesPackage" value="com.qf.springandmybatis.pojo"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/qf/springandmybatis/dao/*.xml</value>
</list>
</property>
</bean>
将接口类对象放在bean工厂中
<!--
获取接口的代理类对象,放在bean工厂中
配置使用sqlSessionFactory里的sqlSession来执行
org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--
dao接口的位置
basePackage : 基本包 value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
-->
<property name="basePackage" value="com.qf.springandmybatis.dao"></property>
<!--
配置:到哪里获取sqlSession
value中放入的是sqlSessionFactory
因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
配置ServiceImpl层的对象
<!--
配置deptServiceImpl对象
autowire: 自动装配 byType类型 byName名字
id给测试的getBean用
-->
<bean id="deptService" class="com.qf.springandmybatis.service.impl.DeptServiceImpl" >
<!--
到IoC的bean工厂中(集合)获取对象,注入给deptDao属性
ref="deptDao"中的deptDao,是mybatis创建的接口的代理类对象,接口名的字母小写
name是Impl中set的名
ref是dao层的类名
这么写是将dao层的接口与service的Impl绑定在一起
-->
<property name="deptDao" ref="deptDao"></property>
</bean>
xml整体

依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qf</groupId>
<artifactId>springandmybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- Spring整合mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql驱动 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
<scope>runtime</scope>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<!-- 更改maven编译规则 -->
<resources>
<resource>
<!-- 资源目录 -->
<directory>src/main/java</directory>
<includes>
<include>*.xml</include><!-- 默认(新添加自定义则失效) -->
<include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
spring-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
">
<!--
引入外部文件
引入外部文件用 context:property-placeholder
引入文件需要用文件的类路径
-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--
配置连接池信息
id:自己起的名
class : 引入连接池信息
init-method : 初始化方法 init : 连接池里的初始化方法 - 方法自己就初始化了
destroy-method :销毁方法 close :连接池里的销毁方法 - 结束之后自己就销毁了
-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--
property是Druid连接池自带的名
value是db.properties里的名
-->
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!--配置连接池的其他信息-->
<!--
配置超时时间 设定时间范围之外就不等待了
maxWait : 配置等待时间的
value : 是以毫秒为单位的
maxActive 最大活跃数量
minIdle 最小空闲 数量
initialSize 初始化数量
-->
<property name="maxWait" value="5000"></property>
<property name="initialSize" value="${jdbc.init}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
</bean>
<!--
说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
然后创建 SqlSessionFactory对象
在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可
"org.mybatis.spring.SqlSessionFactoryBean" : Spring和Mybatis整合依赖里的
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--
注入数据源属性
name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
相当于就是把原先的mybatis连接池替换成Druid连接池
-->
<property name="dataSource" ref="druidDataSource"></property>
<!--
配置映射文件的位置
typeAliasesPackage : 配置别名
value写实体类的别名位置
mapperLocations : 映射文件位置 是一个集合类型
-->
<property name="typeAliasesPackage" value="com.qf.springandmybatis.pojo"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/qf/springandmybatis/dao/*.xml</value>
</list>
</property>
</bean>
<!--
获取接口的代理类对象,放在bean工厂中
配置使用sqlSessionFactory里的sqlSession来执行
org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--
dao接口的位置
basePackage : 基本包 value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
-->
<property name="basePackage" value="com.qf.springandmybatis.dao"></property>
<!--
配置:到哪里获取sqlSession
value中放入的是sqlSessionFactory
因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!--
配置deptServiceImpl对象
autowire: 自动装配 byType类型 byName名字
id给测试的getBean用
-->
<bean id="deptService" class="com.qf.springandmybatis.service.impl.DeptServiceImpl" >
<!--
到IoC的bean工厂中(集合)获取对象,注入给deptDao属性
ref="deptDao"中的deptDao,是mybatis创建的接口的代理类对象,接口名的字母小写
name是Impl中set的名
ref是dao层的类名
这么写是将dao层的接口与service的Impl绑定在一起
-->
<property name="deptDao" ref="deptDao"></property>
</bean>
</beans>
数据库properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3
pojo
package com.qf.springandmybatis.pojo;
import java.io.Serializable;
public class Dept implements Serializable {
private int deptNo;
private String dName;
private String loc;
public Dept() {
}
public Dept(int deptNo, String dName, String loc) {
this.deptNo = deptNo;
this.dName = dName;
this.loc = loc;
}
public int getDeptNo() {
return deptNo;
}
public void setDeptNo(int deptNo) {
this.deptNo = deptNo;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public String toString() {
return "Dept{" +
"deptNo=" + deptNo +
", dName='" + dName + '\'' +
", loc='" + loc + '\'' +
'}';
}
}
dao---接口
package com.qf.springandmybatis.dao;
import com.qf.springandmybatis.pojo.Dept;
public interface DeptDao {
int addDept(Dept dept);
}
dao---Mapper
<?xml version="1.0" encoding="UTF8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.springandmybatis.dao.DeptDao">
<insert id="addDept" parameterType="Dept">
insert into dept(deptno,dname,loc)
values
(#{deptNo},#{dName},#{loc})
</insert>
</mapper>
Service---接口
package com.qf.springandmybatis.service;
import com.qf.springandmybatis.pojo.Dept;
public interface DeptService {
boolean addDept(Dept dept);
}
Service---Impl
package com.qf.springandmybatis.service.impl;
import com.qf.springandmybatis.dao.DeptDao;
import com.qf.springandmybatis.pojo.Dept;
import com.qf.springandmybatis.service.DeptService;
public class DeptServiceImpl implements DeptService {
//需要调用dao 创建DeptDao对象
//不需要自己new,springIoC中已经存储了mybatis创建的该接口类型的代理类对象
//需要注入即可
private DeptDao deptDao;
//供设置注入时调用
public void setDeptDao(DeptDao deptDao){
this.deptDao=deptDao;
}
@Override
public boolean addDept(Dept dept) {
int result = deptDao.addDept(dept);
return result==1;
}
}
Test
package com.qf.springandmybatis.test;
import com.qf.springandmybatis.pojo.Dept;
import com.qf.springandmybatis.service.DeptService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
DeptService deptService = (DeptService) context.getBean("deptService");
Dept dept = new Dept();
dept.setDeptNo(9020);
dept.setdName("9020");
dept.setLoc("9020");
boolean result = deptService.addDept(dept);
if(result){
System.out.println("添加部门成功");
}else{
System.out.println("添加部门失败");
}
}
}
注解整合
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tr="http://www.springframework.org/schema/cache"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!--
引入外部文件
引入外部文件用 context:property-placeholder
引入文件需要用文件的类路径
-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--
配置连接池信息
id:自己起的名
class : 引入连接池信息
init-method : 初始化方法 init : 连接池里的初始化方法 - 方法自己就初始化了
destroy-method :销毁方法 close :连接池里的销毁方法 - 结束之后自己就销毁了
-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--
property是Druid连接池自带的名
value是db.properties里的名
-->
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!--配置连接池的其他信息-->
<!--
配置超时时间 设定时间范围之外就不等待了
maxWait : 配置等待时间的
value : 是以毫秒为单位的
maxActive 最大活跃数量
minIdle 最小空闲 数量
initialSize 初始化数量
-->
<property name="maxWait" value="5000"></property>
<property name="initialSize" value="${jdbc.init}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
</bean>
<!--
说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
然后创建 SqlSessionFactory对象
在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可
"org.mybatis.spring.SqlSessionFactoryBean" : Spring和Mybatis整合依赖里的
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--
注入数据源属性
name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
相当于就是把原先的mybatis连接池替换成Druid连接池
-->
<property name="dataSource" ref="druidDataSource"></property>
<!--
配置映射文件的位置
typeAliasesPackage : 配置别名
value写实体类的别名位置
mapperLocations : 映射文件位置 是一个集合类型
-->
<property name="typeAliasesPackage" value="com.qf.springandmybatispro2.pojo"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/qf/springandmybatispro2/dao/*.xml</value>
</list>
</property>
</bean>
<!--
1)获取接口的代理类对象,放在bean工厂中
2)配置使用sqlSessionFactory里的sqlSession来执行
org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--
dao接口的位置
basePackage : 基本包 value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
-->
<property name="basePackage" value="com.qf.springandmybatispro2.dao"></property>
<!--
配置:到哪里获取sqlSession
value中放入的是sqlSessionFactory
因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!--
如果使用注解方式,通过配置告知spring,扫描指定包下的类,按类中的注解执行
通过base-package设置父包即可 告知所有注解的父包就可以了
spring会扫描父包及子包下的所有类
-->
<context:component-scan base-package="com.qf.springandmybatispro2"></context:component-scan>
<!--开启Aop的注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--创建事务管理器-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--
指定数据源使用该事务
告诉德鲁伊使用创建的tx管理器了
每当使用一个需要用到连接池的就要将他替换成Druid
-->
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--
配置事务管理器来控制事务
利用的就是aop的方式生成一个额外的处理
-->
<!--配置事务管理器来控制事务-->
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<!--
<tx:method name=""/> name里写的方法名就是需要添加事物的方法名
add*代表着以add开头的方法
<tx:method name="add*"/> 意思指 add开头的所有方法都添加事务
-->
<tx:method name="add*"/>
<tx:method name="update*"/>
<tx:method name="delete"></tx:method>
<!--所有方法都使用事务-->
<tx:method name="*"></tx:method>
</tx:attributes>
</tx:advice>
</beans>
注解 整体

依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qf</groupId>
<artifactId>springandmybatispro2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- Spring整合mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mysql驱动 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
<scope>runtime</scope>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<!-- 更改maven编译规则 -->
<resources>
<resource>
<!-- 资源目录 -->
<directory>src/main/java</directory>
<includes>
<include>*.xml</include><!-- 默认(新添加自定义则失效) -->
<include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
spring-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tr="http://www.springframework.org/schema/cache"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!--
引入外部文件
引入外部文件用 context:property-placeholder
引入文件需要用文件的类路径
-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--
配置连接池信息
id:自己起的名
class : 引入连接池信息
init-method : 初始化方法 init : 连接池里的初始化方法 - 方法自己就初始化了
destroy-method :销毁方法 close :连接池里的销毁方法 - 结束之后自己就销毁了
-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--
property是Druid连接池自带的名
value是db.properties里的名
-->
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<!--配置连接池的其他信息-->
<!--
配置超时时间 设定时间范围之外就不等待了
maxWait : 配置等待时间的
value : 是以毫秒为单位的
maxActive 最大活跃数量
minIdle 最小空闲 数量
initialSize 初始化数量
-->
<property name="maxWait" value="5000"></property>
<property name="initialSize" value="${jdbc.init}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
</bean>
<!--
说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
然后创建 SqlSessionFactory对象
在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可
"org.mybatis.spring.SqlSessionFactoryBean" : Spring和Mybatis整合依赖里的
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--
注入数据源属性
name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
相当于就是把原先的mybatis连接池替换成Druid连接池
-->
<property name="dataSource" ref="druidDataSource"></property>
<!--
配置映射文件的位置
typeAliasesPackage : 配置别名
value写实体类的别名位置
mapperLocations : 映射文件位置 是一个集合类型
-->
<property name="typeAliasesPackage" value="com.qf.springandmybatispro2.pojo"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/qf/springandmybatispro2/dao/*.xml</value>
</list>
</property>
</bean>
<!--
1)获取接口的代理类对象,放在bean工厂中
2)配置使用sqlSessionFactory里的sqlSession来执行
org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--
dao接口的位置
basePackage : 基本包 value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
-->
<property name="basePackage" value="com.qf.springandmybatispro2.dao"></property>
<!--
配置:到哪里获取sqlSession
value中放入的是sqlSessionFactory
因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!--
如果使用注解方式,通过配置告知spring,扫描指定包下的类,按类中的注解执行
通过base-package设置父包即可 告知所有注解的父包就可以了
spring会扫描父包及子包下的所有类
-->
<context:component-scan base-package="com.qf.springandmybatispro2"></context:component-scan>
<!--开启Aop的注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--创建事务管理器-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--
指定数据源使用该事务
告诉德鲁伊使用创建的tx管理器了
每当使用一个需要用到连接池的就要将他替换成Druid
-->
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--
配置事务管理器来控制事务
利用的就是aop的方式生成一个额外的处理
-->
<!--配置事务管理器来控制事务-->
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<!--
<tx:method name=""/> name里写的方法名就是需要添加事物的方法名
add*代表着以add开头的方法
<tx:method name="add*"/> 意思指 add开头的所有方法都添加事务
-->
<tx:method name="add*"/>
<tx:method name="update*"/>
<tx:method name="delete"></tx:method>
<!--所有方法都使用事务-->
<tx:method name="*"></tx:method>
</tx:attributes>
</tx:advice>
</beans>
db.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3
pojo
package com.qf.springandmybatispro2.pojo;
import java.io.Serializable;
public class Dept implements Serializable {
private int deptNo;
private String dName;
private String loc;
public Dept() {
}
public Dept(int deptNo, String dName, String loc) {
this.deptNo = deptNo;
this.dName = dName;
this.loc = loc;
}
public int getDeptNo() {
return deptNo;
}
public void setDeptNo(int deptNo) {
this.deptNo = deptNo;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public String toString() {
return "Dept{" +
"deptNo=" + deptNo +
", dName='" + dName + '\'' +
", loc='" + loc + '\'' +
'}';
}
}
aop---Advice
package com.qf.springandmybatispro2.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//注解方式配置aop
@Aspect //身份:是一个切面 相当于配置文件里的<aop:config>
@Component //创建类的对象,然后把对象放在IoC容器中 相当于<bean id="" class="">
public class Advice {
//声明切入点PointCut
@Pointcut("execution(public * com.qf.springandmybatispro2.service..*.*(..))")
public void method(){} //方法的声明,其中方法的名字就是切入点的通用表达式所代表的方法
//配置前置增强
@Before("method()")
public void doBefore(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature() + "方法将被执行");
}
//配置后置增强
@AfterReturning(value = "method()",returning = "result")
public void doAfter(JoinPoint joinPoint,Object result){
System.out.println(joinPoint.getSignature() + "方法执行结束,返回值是:" +result);
}
//异常处理增强
@AfterThrowing(value = "method()",throwing = "ex")
public void doThrowing(JoinPoint joinPoint,Throwable ex){
System.out.println("执行"+joinPoint.getSignature()+"发生异常,异常信息如下:" + ex.getMessage());
}
//最终增强
@After("method()")
public void doFinally(JoinPoint joinPoint){
System.out.println("执行" + joinPoint.getSignature()+"的finally");
}
// //了解
// //环绕增强
// @Around("method()")
// public void around(ProceedingJoinPoint joinPoint,Throwable e){
// Object result = null;
// //前置增强
// System.out.println("执行了前置增强");
// }
}
dao---接口
package com.qf.springandmybatispro2.dao;
import com.qf.springandmybatispro2.pojo.Dept;
public interface DeptDao {
int addDept(Dept dept);
}
dao---Mapper
<?xml version="1.0" encoding="UTF8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.springandmybatispro2.dao.DeptDao">
<insert id="addDept" parameterType="Dept">
insert into dept(deptno,dname,loc)
values
(#{deptNo},#{dName},#{loc})
</insert>
</mapper>
service---接口
package com.qf.springandmybatispro2.service;
import com.qf.springandmybatispro2.pojo.Dept;
public interface DeptService {
boolean addDept(Dept dept);
}
service---Impl
package com.qf.springandmybatispro2.service.Impl;
import com.qf.springandmybatispro2.dao.DeptDao;
import com.qf.springandmybatispro2.pojo.Dept;
import com.qf.springandmybatispro2.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service("deptService") //创建对象,放于bean容器中 相当于<bean id="" class=""> 对象的默认名字是接口名字的首字母小写
//@Scope("prototype") 设置bean的作用域 默认为单例 prototype为多例
@Transactional //类中的每一个方法都是独立的一个事务 这里也可以全局控制
public class DeptServiceImpl implements DeptService {
@Autowired //按照类型自动装配,到bean容器中,找该类型的bean 然后赋值 如果找不到或找到多个就报异常
private DeptDao deptDao; //直接注入,无需封装setXXX()
//在每一个方法的上方 指定当前方法如何使用事务
//NEVER 从不使用事务
//SUPPORTS 用事务就用没事务我就不用 因为在类上边已经写了Transactional 所以现在每个都是独立的 不整体加事务了
// 就可以是使用编程式事务,自己编写以及提交 如果额外自己写了 那么就使用事务 没写就不用
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public boolean addDept(Dept dept) {
int result = deptDao.addDept(dept);
return result==1;
}
}
Test
package com.qf.springandmybatispro2.test;
import com.qf.springandmybatispro2.pojo.Dept;
import com.qf.springandmybatispro2.service.DeptService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
//getBean中的名字是Service中的@Service("deptService")这个名字 如果不写这个名字,那么对象的默认名字是接口名字的首字母小写
//例如接口名字为DeptService 那么生成的名字为deptService
DeptService deptService = (DeptService) context.getBean("deptService");
Dept dept = new Dept();
dept.setDeptNo(6531);
dept.setdName("6528");
dept.setLoc("6528");
boolean result = deptService.addDept(dept);
if(result){
System.out.println("部门添加成功");
}else{
System.out.println("部门添加失败");
}
}
}
spring测试
额外增加依赖(上边案例的pom中已添加)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
spring-context.xml中的映射文件进行更改

Test文件夹下建包进行测试

package com.qf.springandmybatispro.dao;
import com.qf.springandmybatispro2.dao.DeptDao;
import com.qf.springandmybatispro2.pojo.Dept;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) //运行器,Spring运行环境 用来运行Test 就可以获取bean工厂中的bean了
@ContextConfiguration("classpath:spring-context.xml") //读取配置文件里的内容
public class TestDept {
@Autowired
private DeptDao deptDao;
@Test //无返回值就可以
public void testAddDept(){
Dept dept = new Dept();
dept.setDeptNo(9000);
dept.setdName("9000");
dept.setLoc("9000");
int result = deptDao.addDept(dept);
if(result == 1){
System.out.println("测试成功");
}else{
System.out.println("测试失败");
}
}
}
10、Spring的事务处理机制
Spring把mybatis中的事务进行了整合,利用Aop的思想进行整合,所以自动提交、回滚事务
声明式事务:使用Aop思想实现,简化事务的控制
在配置文件中创建事务管理器,控制事务
<!--创建事务管理器-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源使用该事务-->
<property name="dataSource" ref="druidDataSource"></property>
</bean>
【注意】事务管理器里的dataSource一定要和SqlSessionFactoryBean里是同一个dataSource
【说明】spring整合事务后,所有的方法都使用了事务,都会自动提交、回滚事务
事务管理器的作用:哪些方法使用事务,哪些方法不用事务
事务管理器的配置有两种方式:配置方式、注解方式
配置方式 了解---用的少
<!--配置事务管理器来控制事务-->
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<tx:method name="add*"/>
<tx:method name="update*"></tx:method>
<tx:method name="delete*"></tx:method>
<!--所有方法都使用事务-->
<tx:method name="*"></tx:method>
</tx:attributes>
</tx:advice>
注解方式使用事务管理器

【说明】1、事务用在业务罗层
2、事务使用Aop实现---代理对象
3、根据异常情况决定是提交事务还是回滚事务,不要在使用事务的业务逻辑代码里进行异常处理
事务的隔离级别
数据库是并发的,多个进程(线程)同时访问
例如:有两个事务同时去操作数据库,其中一个事务是修改id是1的这条数据,另一个事务是读取id是1的这个数据
执行情况:一个事务开启后,执行了update语句,但是没有提交,也没有回滚
这时另外一个事务来读取数据,如果读取了一个事务中未提交的数据,如果这个事务回滚了,那么当前事务读取到的就是脏数据,称为脏读。
事务的隔离级别:如何在并发环境中,如何控制多个事务之间的操作,由于数据库的锁机制造成的
写、写 互斥
读、读 不互斥
读、写 隔离级别来控制
通过4个值来控制事务的隔离级别:
| 名称 | 描述 |
|---|---|
| default | (默认值)(采用数据库的默认的设置) (建议) |
| read-uncommited | 读未提交 |
| read-commited | 读提交 (Oracle数据库默认的隔离级别) |
| repeatable-read | 可重复读 (MySQL数据库默认的隔离级别) |
| serialized-read | 序列化读 |
read-uncommited:读未提交,允许一个事务读取另一个事务中未提交的数据,造成脏读
read-commited:读取已提交,一个事务只能读取另一个事务中已经提交的数据,如果没有提交,则不允许另一个事务读取数据
repeatable-read:可重复读,同时存在多个读事务,多个写事务,读取已提交的事务的数据。
事务 多次读取,由于有多个写事务,第一次读取的数据(A事务提交的数据),和第二次读取的数据(B事务提交的数据),造成两次读取会不一致,造成幻读
serialized-read:序列化读 不允许读写事务之间同时进行,要串行,效率最低
由低到高:read-uncommited < read-commited < repeatable-read < serialized-read
【说明】级别越低,效率越高,越不安全


事务的传播行为/机制
传播行为/传播机制:每个业务逻辑层的方法都是一个事务,如果一个方法调用了另一个方法,这里两个事务如何共存的问题就称为事务的传播行为。
isolation:指定事务的隔离级别
propagation:指定事务的传播行为
传播行为有7个值
REQUIRED:必须有事务,当前方法必须执行事务里,如果没有事务就创建一个事务
如果已经存在事务则加入到该事务中
SUPPORTS: 可有可无,如果当前方法有事务,则使用当前事务,如果没有事务,就以非事务方式运行
MANDATORY:强制使用事务,使用当前的事务,如果没有事务,就抛异常
REQUIRES_NEW: 必须使用新的事务,如果当前方法已经有事务则挂起,创建新的事务
NOT_SUPPORTED: 以非事务方式运行,如果当前方法有事务,则挂起
NEVER: 以非事务方式运行,如果当前方法有事务,则抛出异常
NESTED: 如果当前方法存在事务,则事务进行嵌套,如果没有事务就新建事务
使用场景:主从表操作
业务逻辑层:方法1---插入订单主表,主键回调
方法2---插入订单从表
方法1 调用 方法2:REQUIRED
事务的其他属性
readOnly 设置为true: 只允许其他的读事务并行运行
默认为false: 允许其他的增删改查事务都并行运行
timeout 当前事务所操作的数据被其他事务占用,等待的时间
-1 由数据库决定
可以自行设置等待时间,以秒为单位
rollbackFor 回滚属性
如果事务中抛出 RuntimeException ,则事务自动回滚
如果事务中抛出 CheckException, 不自动回滚,则默认提交事务
处理方案,将CheckException转换成RuntimeException 往上抛 设置为Exception
【注意】不要在业务逻辑层捕获异常,如果捕获了,事务则接受不到异常,无法回滚
注解开发
定义Bean的注解:使用注解 相当于使用bean标签
-
@Service 业务类专用
-
@Repository dao实现类专用
-
@Controller web层专用
-
@Component 通用
-
@Scope 用户控制bean的创建模式
注入值的注解:
@Autowired //按类型自动装配,到bean容器中,找该类型的bean然后赋值,如果找不到,或找到多个就报异常
private DeptDao deptDao; //直接注入,无需封装setXXX()
@Resource //按名称自动装配,到bean工厂中(集合)找名字是deptDao2的bean,赋值给下面的属性
private DeptDao deptDao2;
@Resource(name = "deptDao4") //到bean工厂中找名字是deptDao4的对象,赋值给下面的属性
private DeptDao getDeptDao4;
@Autowired
@Qualifier("dd") //当@Autowired按类型在工厂中找到多个同类型的Bean时,则使用名字为dd的Bean来注入给该属性
private DeptDao deptDao3;
@Value("设计城")//给8大基本类型+String 进行注入
private String defaultLoc;
事务的注解
可以用在类的上方,对类中的方法进行统一的事务配置
也可以用在某个方法的商法,对该方法进行单独配置
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.NESTED,readOnly = true,timeout = -1,
rollbackFor = Exception.class)
【注意】如果事务使用了注解的方式进行了配置,需要在spring的配置文件中开启事务
<!--开始事务的注解方式,并且制定事务管理器-->
<tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>
Aop的注解
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {
// 定义切入点
@Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")
public void pc(){}
@Before("pc()") // 前置通知
public void mybefore(JoinPoint a) {
System.out.println("target:"+a.getTarget());
System.out.println("args:"+a.getArgs());
System.out.println("method's name:"+a.getSignature().getName());
System.out.println("before~~~~");
}
@AfterReturning(value="pc()",returning="ret") // 后置通知
public void myAfterReturning(JoinPoint a,Object ret){
System.out.println("after~~~~:"+ret);
}
@Around("pc()") // 环绕通知
public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
System.out.println("interceptor1~~~~");
Object ret = p.proceed();
System.out.println("interceptor2~~~~");
return ret;
}
@AfterThrowing(value="pc()",throwing="ex") // 异常通知
public void myThrows(JoinPoint jp,Exception ex){
System.out.println("throws");
System.out.println("===="+ex.getMessage());
}
}
在配置文件中开启注解的方式
<!--开启Aop的注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
xml头文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--suppress SpringFacetInspection -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
</beans>

SqlSessionFactoryBean的主要作用便是用来创建SqlSessionFactory,在它的构建过程中会解析MyBatis所需要的配置文件,将所有配置都封装到Configuration类中,最后返回SqlSessionFactory实例。(Spring中的sqlSessionFactoryBean中包含了SqlsessionFactoryBuilder方法和SqlsessionFactory方法)
sqlSessionFactory是mybatis中的一个重要的对象,通俗讲它是用来不断创建sqlSession对象的,每一个sqlsession对象对应一个mapper
sqlSession用来操作数据库的,通过SqlSession可以实现增删改查
当一个类没有被spring管理的时候,想在这个类中调用spring管理的bean以及其中的方法
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public final class SpringBeanTool implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(Class clazz) {
return getApplicationContext().getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringBeanTool.applicationContext == null) {
SpringBeanTool.applicationContext = applicationContext;
}
}
}
public class QuestionnaireData {
public static boolean questionnaireDataState(String smId){
HomePointsFaceBiz homePointsFaceBiz = (HomePointsFaceBiz) SpringBeanTool.getBean(HomePointsFaceBiz.class);
HomePointsFace homePointsFace = new HomePointsFace();
homePointsFace.setSmId(smId);
HomePointsFace homePointsFaceDate = homePointsFaceBiz.selectOne(homePointsFace);
if (Objects.isNull(homePointsFaceDate.getInvestigateType())) {
return true;
}
throw new BizException("抛出异常");
}
}
167万+

被折叠的 条评论
为什么被折叠?



