这里写目录标题
1. Spring介绍
Spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题–解耦
- 轻量级:体积小,对代码没有入侵性
- 控制反转:IoC,把创建对象的工作交给Spring完成,Spring在创建对象的同时可以完成对对象属性赋值(DI,依赖注入)
- 面向切面:AOP,面向切面编程,是OOP(面向对象编程)的升级,可以在不改变原有的业务逻辑的情况下实现对业务的增强
- 容器:实例的容器,管理创建的对象
2. SrpingIOC-基于XML
2.1 创建Maven项目
java工程
web工程
2.2 添加SpringIoC依赖
- core
- beans
- aop
- expression
- context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
2.3创建Spring的配置文件
- 在resources下创建applicationContext.xml(文件名可以自定义)
- 通过配置文件“告诉”spring容器该创建什么对象,给对象赋什么值
resources–右键new XML Configuration File–Spring Config 创建文件即可
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通常一个框架为了让开发者正确的配置 都会提供xml的规范文档(dtd/xsd)-->
</beans>
3.SpringIoC的使用
使用SpringIoC组件创建并管理对象
3.1创建一个实体类(暂时不使用lombok,可以看原里)
public class Strudent {
private String num;
private String name;
private String gender;
private int age;
private Date birth;
}
3.2使用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.xsd">
<!--通常一个框架为了让开发者正确的配置 都会提供xml的规范文档(dtd/xsd)-->
<!--通过bean标签,将实体类配置给Spring容器管理,id实体类的唯一标识(不可以重复)-->
<bean id="stu1" class="com.qf.pojo.Strudent">
<!--DI依赖注入,使用spring容器对对象的属性进行赋值-->
<property name="num" value="10000"></property>
<property name="name" value="tom"></property>
<property name="age" value="17"></property>
<property name="gender" value="男"></property>
</bean>
</beans>
3.3初始化 Spring对象工厂,获取对象
ClassPathXmlApplicationContext
@Test
public void test01(){
//1.初始化Spring容器,加载 spring配置文件
ClassPathXmlApplicationContext cxa = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过Spring容器获取其内部管理的对象
Strudent stu1 =(Strudent) cxa.getBean("stu1");
//3.使用获取到的对象
System.out.println(stu1);
System.out.println(stu1.getName());
}
4.IoC和DI
- IoC控制反转,通过Spring工厂完成对象的创建
- DI依赖注入,Spring完成对象创建的同时,可以完成属性进行赋值
4.1IoC
Spring管理创建的对象,Spring会在第一次创建时,创建内部的所有bean的对象
4.2DI
通过Spring容器为对象属性赋值,SpringDI是默认调用了对象的set方法为属性赋值的
5.DI依赖注入
Spring容器加载配置文件后,通过反射创建类的对象,并给属性赋值的
Spring容器通过反射实现属性注入的方式:
- set方法注入
- 构造方法注入
5.1set方法注入
使用set方法进行注入
注入类型
简单类型和String类型
<property name="num" value="10000"></property>
<property name="name" value="tom"></property>
除String外的其他引用类型 使用ref进行引入
-
除String外的其他引用类型 使用ref进行引入
- 方式一
- 方式二
使用的子标签来指定对象
<property name="birth"> <bean class="java.util.Date"></bean> </property>
集合类型:需要util命名空间配合使用(标签)
<util:可以添加命名空间
list集合
<!--注入person-->
<bean id="p1" class="com.qf.pojo.Person">
<property name="name" value="卡了"></property>
<property name="age" value="20"></property>
<!--引入car-->
<property name="cars" ref="cars"></property>
</bean>
<!--注入car-->
<bean id="car" class="com.qf.pojo.Car">
<property name="brand" value="BMW"></property>
<property name="price" value="20"></property>
</bean>
<!--注入list-->
<util:list id="cars">
<!--引入已知的bean-->
<ref bean="car"></ref>
<ref bean="car"></ref>
<ref bean="car"></ref>
<ref bean="car"></ref>
</util:list>
<bean id="p2" class="com.qf.pojo.Person">
<property name="name" value="你卡了"></property>
<property name="age" value="20"></property>
<!--引入car-->
<property name="cars">
<!--直接使用list标签-->
<list>
<ref bean="car"></ref>
<ref bean="car"></ref>
<ref bean="car"></ref>
<ref bean="car"></ref>
</list>
</property>
</bean>
set集合类似与list
map集合
<bean id="p3" class="com.qf.pojo.PersonMap">
<property name="name" value="你卡了"></property>
<property name="age" value="20"></property>
<property name="cars" ref="cars2"></property>
</bean>
<util:map id="cars2">
<entry key="aa" value-ref="car"></entry>
<entry key="bb" value-ref="car"></entry>
<entry key="cc" value-ref="car"></entry>
</util:map>
p:命名空间
简化属性注入的方式
<bean id="xx" class="xx" p:xx="" p:xx-ref=""></bean>
文档约束:
xmlns:p=“http://www.springframework.org/schema/p”
<bean id="p1" class="com.qf.pojo.Person"
p:name="tom" p:age="29" p:cars-ref="cars">
</bean>
5.2构造注入
使用构造方法,进行注入
<!--使用构造注入 注入时就是按照构造的参数顺序进行注入-->
<!--调用Car中的对应构造方法-->
<bean id="c1" class="com.qf.pojo.Car">
<constructor-arg value="byd"></constructor-arg>
<constructor-arg value="20"></constructor-arg>
</bean>
<!--可以使用index指定参数下标 使用index控制参数的顺序-->
<bean id="s1" class="com.qf.pojo.Strudent">
<constructor-arg value="29" index="3"></constructor-arg>
<constructor-arg value="10000" index="0"></constructor-arg>
<constructor-arg value="tom" index="1"></constructor-arg>
<constructor-arg value="男" index="2"></constructor-arg>
<constructor-arg index="4">
<bean class="java.util.Date"></bean>
</constructor-arg>
</bean>
<!--使用type控制参数的类型-->
<bean id="s2" class="com.qf.pojo.Strudent">
<constructor-arg value="tom"></constructor-arg>
<constructor-arg value="29" type="int"></constructor-arg>
</bean>
5.3工厂注入
使用工厂模式进行注入
工厂模式:静态工厂和实例工厂
静态工厂创建
package com.qf.pojo;
import java.util.HashMap;
import java.util.Map;
public class CarStaticFactory {
public static Map<String,Car> cars = new HashMap<String,Car>();
static {
cars.put("audi",new Car("audi",20));
cars.put("audi",new Car("audi",20));
cars.put("audi",new Car("audi",20));
cars.put("audi",new Car("audi",20));
}
public static Car getCar(String brand){
return cars.get(brand);
}
}
静态工厂注入
<!--静态工厂 factory-method声明工厂方法-->
<bean id="c1" class="com.qf.pojo.CarStaticFactory" factory-method="getCar">
<!--为方法参数传值-->
<constructor-arg value="audi"></constructor-arg>
</bean>
实例工厂创建
package com.qf.pojo;
import java.util.HashMap;
import java.util.Map;
public class CarInstanceFactory {
public Map<String,Car> cars = new HashMap<>();
public CarInstanceFactory(){
cars.put("audi",new Car("audi",20));
cars.put("audi",new Car("audi",20));
cars.put("audi",new Car("audi",20));
cars.put("audi",new Car("audi",20));
}
public Car getCar(String brand){
return cars.get(brand);
}
}
实例工厂注入
<!--实例工作-->
<!--配置实例工厂对象-->
<bean id="carInstanceFactory" class="com.qf.pojo.CarInstanceFactory"></bean>
<!--factory-bean:指向工厂bean -->
<bean id="c2" factory-bean="carInstanceFactory" factory-method="getCar">
<constructor-arg value="audi"></constructor-arg>
</bean>
6.Bean的作用域
在bean标签可以通过scope属性指定对象的作用域
-
scope=“singleton” 表示当前bean为单例模式(默认是饿汉式,设置lazy-init="true"可以变为懒汉式)
- bean默认是饿汉的单例模式
<!--默认为单例的,容器加载时创建,所有引用公用一个地址-->
<bean id="b1" class="com.qf.pojo.Book">
<property name="name" value="java从门到放弃"></property>
<property name="type" value="玄学"></property>
</bean>
- scope=“prototype” 表示当前bean为原型的,每次通过Spring容器获取bean时都会创建一个新的对象(和new的形式类似)
<!--使用scope="prototype"设置bean是每次获取时创建,每个引用都一个新的对象-->
<bean id="b2" class="com.qf.pojo.Book" scope="prototype">
<property name="name" value="mysql从删库到跑路"></property>
<property name="type" value="神学"></property>
</bean>
7.Bean的生命周期方法
在bean中通过init-method属性指定当前bean的初始化方法,初始化方法在构造方法之后执行,通过destory-method属性指定当前bean的销毁方法,在对象销毁前执行
public void init(){
System.out.println("init...");
}
public void destory(){
System.out.println("destory");
}
<bean id="b1" class="com.qf.pojo.Book" init-method="init" destroy-method="destory">
<property name="name" value="java从门到放弃"></property>
<property name="type" value="玄学"></property>
</bean>
8.自动装配
自动装配:Spring在实例化当前bean的时候,从Spring容器中找到与之匹配的实例赋值给当前bean的属性
不能自动装配的数据类型:Object、基本数据类型(Date、CharSequence、Number、URI、URL、Class、int)等
自动装配共有两种方式
- byName:根据当前bean属性的名字在spring容器中寻找匹配的对象,若根据name找到了bean但类型不匹配则抛出异常
<bean id="car" class="com.qf.pojo.Car">
<property name="brand" value="aa"></property>
<property name="price" value="20"></property>
</bean>
<bean id="car111" class="com.qf.pojo.Book"></bean>
<!--byName:匹配和Person中car属性名字一致的bean
没有对应的bean,则赋值为null
有,但类型不对应,则抛出异常
-->
<bean id="p1" class="com.qf.pojo.Person22" autowire="byName">
<property name="name" value="tom"></property>
<property name="age" value="19"></property>
</bean>
- byType:根据当前bean属性的类型在spring容器中寻找匹配的对象,若类型找到了多个则抛出异常
<!--
byType:匹配和Person中的car为一个类型
同一个类型不能有多个bean在容器中
-->
<bean id="p1" class="com.qf.pojo.Person22" autowire="byType">
<property name="name" value="tom"></property>
<property name="age" value="19"></property>
</bean>
9.SpringIoC工作原理
Bean的获取有两种方式
- cta.getBean(“id”);
- 通过id去获取,需要向下造型
- cta.getBean(xxx.class);
- 通过类型获取,要求一个类型只能有一个bean
10.SpringIoC-基于注解
创建工程,引入依赖,创建配置文件
SpringIoC的使用,需要通过配置的形式或者注解的形式
使用注解简化开发步骤
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 声明使用注解配置 -->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围 -->
<context:component-scan base-package="com.qf"/>
</beans>
10.1常用注解
@Component
- 使用此注解声明的类默认添加到springIoC容器中
- id默认为类名首字母小写
- 可以使用注解的value属性自定义id,若使用默认id则可以省略
- 可以使用@Value对属性进行注入
@Service @Controller @Repository
- 这三个注解与@Component没有任何的差别,语义上的不同
- @Service服务层,处理业务逻辑的
- @Controller控制层,类似Servlet
- @Repository持久层,用于处理数据的
- 处理控制器,service,dao外都使用@Component
@Scope
- 用于声明当前类为单例模式还是非单例的
@Lazy
- 当前单例的bean为懒汉式还是饿汉式(默认为饿汉式)
@PostConstruct
- 方法注解,相当于bean标签的 init-method 属性
- 声明当前类的初始化方法
@PreDestory
- 方法注解,相当于bean标签的 destory-method 属性
- 声明当前类的销毁方法
@Autowired
- 自动装配,默认是byType
@Resource
- 自动装配,默认是byName
11.代理设计模式
将通用性的工作交给代理对象完成,被代理的对象只专业做自己的核心业务
11.1静态代理
代理类只能为特定的类产生代理,不能代理任意类,局限性特别大
使用代理的好处
- 被代理的类只用关注核心业务实现,将通过的管理逻辑和业务逻辑分开
- 将通过的的代理放在代理类中,提高了代码的复用率
- 通过代理类添加业务逻辑,可以实现对原有业务逻辑的扩展
11.2动态代理
几乎可以为所有的类产生代理对象
动态代理的实现方式有2种:
- JDK动态代理
- CGLib动态代理
11.2.1JDK动态代理
- 创建一个类,实现InvocationHandler接口,重写invoke方法
- 在类中定义一个Object类型的变量,并提供这个变量的有参构造方法,用于将被代理对象传递进来
- 定义getProxy方法,用于创建并返回代理对象
创建代理
package com.qf;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* - 创建一个类,实现InvocationHandler接口,重写invoke方法
* - 在类中定义一个Object类型的变量,并提供这个变量的有参构造方法,用于将被代理对象传递进来
* - 定义getProxy方法,用于创建并返回代理对象
*/
public class JDKDynamicProxy implements InvocationHandler {
//被代理的对象
private Object obj;
public JDKDynamicProxy(Object obj){
this.obj = obj;
}
//生成代理对象,返回值为代理对象
public Object getProxy(){
//1.获取被代理对象的类加载器
ClassLoader classLoader = obj.getClass().getClassLoader();
//2.获取被代理对象的类实现的接口
Class<?>[] interfaces = obj.getClass().getInterfaces();
//3.产生代理对象(通过被代理对象的类加载器以及实现的接口)
//参数1:被代理对象的类加载器
//参数2:被代理对象的类实现的接口
//参数3:使用产生代理对象调用方法时,用于拦截执行方法的处理器
Object proxy = Proxy.newProxyInstance(classLoader,interfaces,this);
return proxy;
}
/**
* invoke方法的方向执行,有handler自行调用
* proxy:代理对象
* method:被代理者调用的方法
* args:正在执行的方法,的方法参数
* @return Object是当前方法的返回值
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
begin();
Object returnValue = method.invoke(obj,args);//执行method的方法
commit();
return returnValue;
}
public void begin(){
System.out.println("...开启事务");
}
public void commit(){
System.out.println("...事务提交");
}
}
测试
package com.qf;
public class Test02 {
public static void main(String[] args) {
//被代理者
UserDaoImpl udi = new UserDaoImpl();
//使用动态代理
JDKDynamicProxy jDKDynamicProxy = new JDKDynamicProxy(udi);/*将被代理者传入*/
//获取代理对象
UserDao proxy = (UserDao)jDKDynamicProxy.getProxy();
//com.sun.proxy.$Proxy0
System.out.println(proxy.getClass().getName());
//使用代理执行方法
proxy.delete();
proxy.update();
proxy.insert();
}
}
11.2.2CGLib动态代理
- 添加CGLib依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
- 创建一个类,实现MethodInterceptor接口,重写intercept方法
- 在类中定义一个Object类型的变量,并提供这个变量的有参构造方法,用于将被代理对象传递进来
- 定义getProxy方法,用于创建并返回代理对象
package com.qf;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CGLibDynamicProxy implements MethodInterceptor {
//被代理的对象
private Object obj;
public CGLibDynamicProxy(Object obj) {
this.obj = obj;
}
//生成代理对象,返回值为代理对象
public Object getProxy() {
Enhancer enhancer = new Enhancer();
//传入当前类的类型
enhancer.setSuperclass(obj.getClass());
//传入this
enhancer.setCallback(this);
//创建代理对象
Object o = enhancer.create();
return o;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
begin();
//执行method的方法
Object invoke = method.invoke(obj, objects);
commit();
return invoke;
}
public void begin() {
System.out.println("事务开启");
}
public void commit() {
System.out.println("事务提交");
}
}
测试
package com.qf;
public class Test03 {
public static void main(String[] args) {
//被代理者
UserDaoImpl udi = new UserDaoImpl();
//使用动态代理
CGLibDynamicProxy cGLibDynamicProxy = new CGLibDynamicProxy(udi);
//获取代理对象
UserDao proxy = (UserDao) cGLibDynamicProxy.getProxy();
// com.qf.UserDaoImpl$$EnhancerByCGLIB$$1f20b26d
System.out.println(proxy.getClass().getName());
//使用代理执行方法
proxy.insert();
proxy.delete();
proxy.update();
}
}
12.SpringAOP
AOP:面向切面编程,是一种利用"横切"的技术(底层实现就是动态代理).对原有的业务逻辑进行拦截,并且可以在这个拦截的切面上添加特定的业务逻辑,对原业务进行增强。
基于动态代理模式的,在不改变原有的业务逻辑的情况下对代码进行增强.
相关名词:
- 连接点(JoinPoint):程序中的方法
- 切入点(PointCut):被Spring横切的方法
- 通知/增强:配置新增的业务的配置方式
- 切点:添加到切入点新增的方法
- 切面:定义切点方法的类
12.1SpringAOP部署
创建Maven项目
添加依赖
- context
- aspects
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
创建spring的配置配置文件
- 需要引入aop命名空间
<?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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config></aop:config>
</beans>
12.2AOP配置基于xml
AOP开发流程:
- 创建切面类,在切面类中定义切点方法
- 将切面类配置给SpringIoC容器
- 声明切入点
- 配置aop的通知策略
创建一个Dao及其实现
package com.qf.dao;
public interface UserDao {
void insert();
void update();
void delete();
}
package com.qf.dao.impl;
import com.qf.dao.UserDao;
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("insert user");
}
@Override
public void update() {
System.out.println("update user");
}
@Override
public void delete() {
System.out.println("delete user");
}
}
创建一个类,定义要添加的业务逻辑
package com.qf.aspect;
public class TxMangerAspect {
public void begin(){
System.out.println("....开启事务");
}
public void commit(){
System.out.println("....提交事务");
}
}
配置aop
<?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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用xml的形式配置aop-->
<!--配置实现到ioc容器中-->
<!-- pointcut:切谁,expression:切入的方法是啥-->
<bean id="userDaoImpl" class="com.qf.dao.impl.UserDaoImpl"></bean>
<!--配置切面到ioc容器中-->
<bean id="txMangerAspect" class="com.qf.aspect.TxMangerAspect"></bean>
<!--配置aop-->
<aop:config>
<!--声明切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.qf.dao.impl.UserDaoImpl.*())"/>
<!--声明切面-->
<aop:aspect ref="txMangerAspect">
<!--配置通知/增强-->
<aop:before method="begin" pointcut-ref="pointcut"></aop:before>
<aop:after method="commit" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
测试
@Test
public void test01()
{
ClassPathXmlApplicationContext cxa = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao udi = (UserDao)cxa.getBean("userDaoImpl");
// com.sun.proxy.$Proxy4
System.out.println(udi.getClass().getName());
udi.delete();
udi.insert();
udi.update();
}
12.3切入点的声明(切点表达式)
<!--声明切入点-->
<!--execution:表达式-->
<!--使用aop:pointcut标签声明切入点 :切入点可以是一个方法-->
<!-- <aop:pointcut id="pointcut" expression="execution(* com.qf.dao.impl.UserDaoImpl.update())"/>-->
<!--UserDaoImpl无参无返回值的方法-->
<!-- <aop:pointcut id="pointcut" expression="execution(void com.qf.dao.impl.UserDaoImpl.*())"/>-->
<!--具体返回值类型 *表示任意返回值-->
<!--<aop:pointcut id="pointcut" expression="execution(int com.qf.dao.impl.UserDaoImpl.*())"/>-->
<!--任意类型-->
<!-- <aop:pointcut id="pointcut" expression="execution(void com.qf.dao.impl.*.*())"/>-->
<!--参数:单参的int类型-->
<!-- <aop:pointcut id="pointcut" expression="execution(void com.qf.dao.impl.*.*(int))"/>-->
<!--参数:两个参数都为int int-->
<!--<aop:pointcut id="pointcut" expression="execution(void com.qf.dao.impl.*.*(int,double))"/>-->
<!--任意参数.. 大于等于0个参数-->
<!--<aop:pointcut id="pointcut" expression="execution(void com.qf.dao.impl.*.*(..))"/>-->
<!--匹配所有的方法-->
<aop:pointcut id="pointcut" expression="execution(* *(..))"/>
12.4AOP通知策略
AOP通知:就是声明切面中的切点方法如何织入到切入点
-
before:前置通知 方法执行之前
-
after:后置通知 方法执行之后
-
after-throwing:异常通知 方法执行时产生的异常
-
after-returning:返回通知 方法正常的结果
-
around:环绕通知 包含上面的四个通知 (同一个切面中around 不要和其他的四个一起使用)
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--前置通知-->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--后置通知:无论是否有异常都会执行的通知-->
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
<!--异常通知,产生的异常,赋值给afterThrowing()方法变量e-->
<aop:after-throwing method="afterThrowing" pointcut="execution(* *(..))" throwing="e"></aop:after-throwing>
<!--返回通知,将返回的结果赋值给result-->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
package com.qf.aspect;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
public class MyAspect {
//JoinPoint封装了该执行方法的信息
public void before(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("当前执行的方法是:"+methodName);
Object[] args = joinPoint.getArgs();
System.out.println("方法执行参数为:"+ Arrays.asList(args));
System.out.println("前置通知");
}
public void after(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"执行完成...");
System.out.println("后置通知");
}
//在异常通知中,得到异常信息
public void afterThrowing(JoinPoint joinPoint,Throwable e){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"异常...为:"+e);
System.out.println("异常通知");
}
public void afterReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"正常执行,结果为:"+result);
System.out.println("返回通知");
}
/**
* 1.必须携带一个ProceedingJoinPoint类型参数
* 2.必须有Object类型的返回值
* 3.在前后增强的业务逻辑之间必须执行Object v = point.proceed();
* 4.方法最后的返回值必须是v
*/
public Object around(ProceedingJoinPoint point) {
Object v = null;
System.out.println("===="+point.getSignature().getName()+"方法执行,参数:"+Arrays.asList(point.getArgs()));//before
try {
v = point.proceed();
System.out.println("===="+point.getSignature().getName()+"方法正常执行,结果为:"+v);//after-returning
} catch (Throwable e) {
// e.printStackTrace();
System.out.println("===="+point.getSignature().getName()+"方法异常执行,异常为:"+e);//after-throwing
} finally {
System.out.println("===="+point.getSignature().getName()+"方法执行完毕");//after
}
return v;
}
}
12.5多切面的执行顺序
使用order属性 order的值越低优先级越高
<!--声明切面-->
<aop:aspect ref="txMangerAspect" order="0">
<!--配置通知/增强-->
<aop:before method="begin" pointcut-ref="pointcut"></aop:before>
<!-- <aop:after method="commit" pointcut-ref="pointcut"></aop:after>-->
</aop:aspect>
<!--声明切面-->
<aop:aspect ref="myAspect" order="1">
<!--前置通知-->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
</aop:aspect>
12.6AOP配置基于注解
创建Maven工程
略
添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
Spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用注解-->
<context:annotation-config></context:annotation-config>
<!--包扫描,将添加注解的类注入到容器中-->
<context:component-scan base-package="com.qf"></context:component-scan>
<!--基于注解配置的aop代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
aop注解配置的案例
package com.qf.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
//将切面添加到容器中
@Component
//声明为切面
@Aspect
//切面的优先级
@Order(0)
public class MyAspect {
//切点表达式的定义
@Pointcut("execution(* com.qf.dao.impl.UserDaoImpl.*(..))")
public void pointcut(){
}
//前置通知
@Before("pointcut()")
public void before(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.print("当前执行的方法是:"+methodName);
Object[] args = joinPoint.getArgs();
System.out.println("方法执行参数为:"+ Arrays.asList(args));
}
//后置通知
@After("pointcut()")
public void after(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"执行完成...");
}
//异常通知
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Throwable e){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"异常...为:"+e);
}
//返回通知
@AfterReturning(value = "pointcut()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"正常执行,结果为:"+result);
}
//环绕通知
// @Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
Object v = null;
System.out.println("===="+point.getSignature().getName()+"方法执行,参数:"+Arrays.asList(point.getArgs()));//before
try {
v = point.proceed();
System.out.println("===="+point.getSignature().getName()+"方法正常执行,结果为:"+v);//after-returning
} catch (Throwable e) {
// e.printStackTrace();
System.out.println("===="+point.getSignature().getName()+"方法异常执行,异常为:"+e);//after-throwing
} finally {
System.out.println("===="+point.getSignature().getName()+"方法执行完毕");//after
}
return v;
}
}
注意:
注解使用起来方便,但需要添加到源码上,因此自定义的切面提倡使用注解,若使用第三方提供的类则需要使用xml
13.Spring整合MyBatis
Spring两大核心:SpringIoC 和SpringAOP
IoC:控制反转 spring容器可以完成对对象的创建 属性注入 对象管理等工作
AOP:面向切面编程,在不修改原有业务逻辑的情况下,进行业务的增强
13.1Spring可以对MyBatis提供哪些支持?
IoC支持:SpringIoC可以为MyBatis完成DataSource,SqlSessionFactory,SqlSession以及DAO对象的创建
帮助管理MyBatis所需的bean
AOP支持:使用Spring提供的事务管理切面类完成对MyBatis的数据库操作中的事务管理
进行事务管理
13.2Spring整合MyBatis的准备工作
创建Maven项目
略
部署MyBatis框架
- 添加依赖
- MySQL依赖
- MyBatis依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
创建MyBatis的配置文件(创建文件之后无需任何配置)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
部署Spring框架
- 添加依赖
- spring-context
- spring-aspects
- spring-jdbc
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
创建Spring配置文件:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
添加Spring整合MyBatis的依赖
mybatis-spring就是mybatis提供兼容spring补丁
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
13.3Spring整合MyBatis的IoC配置
整合Druid连接池(数据源)
- 添加druid的依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.11</version>
</dependency>
- 添加druid.properties属性文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mysql?characterEncoding=utf-8
username=root
password=root
- 在applicationContext.xml中配置DruidDataSource
<!--加载druid.properties属性文件-->
<context:property-placeholder location="druid.properties"></context:property-placeholder>
<!--依赖Spring容器完成对数据源DruidDataSource的注入-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
- 使用SpringIoC完成SqlSessionFactory的注入
<!--依赖Spring容器完成对SqlSessionFactory的注入-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="druidDataSource"></property>
<!--管理所有的映射文件:类路径下mappers文件夹中所有一Mapper.xml结尾的文件-->
<property name="mapperLocations" value="classpath:mappers/*Mapper.xml"></property>
<!--配置MyBatis的主配置文件,可选-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--配置需要定义的实体类的别名,可选-->
<property name="typeAliasesPackage" value="com.qfedu.pojo"></property>
</bean>
13.4创建Mapper
<!--加载dao包中所有DAO接口 ,通过SqlSessionFactory获取SqlSession,然后创建所有DAO接口对象,存在于Spring容器中-->
<!--将Mybatis的代理"实现"交给Spring容器管理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!--dao的bean的id名字为XxxDao类名首字母小写-->
<property name="basePackage" value="com.qfedu.dao"></property>
</bean>
13.5Spring整合MyBatis的AOP配置
使用Spring提供的事务管理切面类,完成对DAO中增删改的事务管理
13.5.1事务的隔离级别
ACID:原子性 一致性 隔离性 持久性
隔离级别 | 英文 | 描述 |
---|---|---|
串行化 | SERIALIZABLE | T1在执行的过程中,T2不能读也不能写(安全性最高,效率是最低) |
可重复读 | REPEATABLE_READ | T1在执行的过程中,T2能读不能改,T2可以添加的数据(幻读/虚读)(实际开发中使用最多的) |
读已提交 | READ_COMMITTED | T1在执行的过程中,T2可以读也可以写,但T1只能读到T2以提交的数据(不可重复读)(幻读) |
读未提交 | READ_UNCOMMITTED | T1在执行的过程中,T2可以读也可以写,T1可以读取到T2未提交的数据不可重复读)(幻读)(脏读) |
13.5.2事务的传播机制
propagation设置事务的传播机制
传播机制 | 解释 |
---|---|
REQUIRED(默认) | 支持当前事务,当前事务不存在,创建一个新的事务(常用于增删改,因情况而定) |
SUPPORTS | 支持当前事务,若当前事务不存在,则不使用事务(常用于查询,因情况而定) |
REQUIRES_NEW | 创建一个新的事务,若当前事务存在,则挂起当前事务 |
NOT_SUPPORTED | 无事务执行,若当前事务存在,则挂起 |
NEVER | 无事务执行,若当前事务存在,则抛出异常 |
MANDATORY | 支持当前事务,若当前事务不存在,则抛出异常 |
NESTED | 嵌套事务,若当前事务存在,则嵌套在事务中执行,若不存在和REQUIRED是一样的 |
13.5.3SpringAOP事管理配置-xml配置
<!--1.将spring中提供的事务管理切面类注入到spring容器中-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--2.通过SpringJDBC提供的tx标签,声明事务管理策略-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--事务管理策略-->
<tx:attributes>
<!--配置事务的隔离级别和传播机制-->
<!--REPEATABLE_READ:隔离级别为重复读-->
<!--REQUIRED:没有事务新建一个事务 ,若有事务 则加入-->
<tx:method name="insert*" isolation="READ_COMMITTED" propagation="REQUIRED"/>
<tx:method name="delete*" isolation="READ_COMMITTED" propagation="REQUIRED"/>
<tx:method name="update*" isolation="READ_COMMITTED" propagation="REQUIRED"/>
<tx:method name="query*" isolation="READ_COMMITTED" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--3.将事务管理策略以AOP配置的形式应用于DAO操作方法-->
<!--每个dao方法只做一个操作 业务逻辑在service包中编写-->
<aop:config>
<aop:pointcut id="crud" expression="execution(* com.qfedu.service.impl.*.*(..))"/>
<!--应用策略在对应的方法之上-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="crud"></aop:advisor>
</aop:config>
13.5.3SpringAOP事管理配置-注解配置
- 在applicationContext.xml中配置事务管理,声明使用注解的方式进行事务配置
<!--1.将spring中提供的事务管理切面类注入到spring容器中-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--2.声明使用注解完成事务配置-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
- 在需要Spring进行事务管理的方法之上添加@Transactional注解即可
//可以在注解内设置隔离级别和传播机制
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
public void updateBook() {
//更新用户的余额
//更新库存
}
14事务管理案例
买书管理
所需表
用户表:t_user(u_id,u_name,u_blance)
图书表:t_book(b_id,b_name,b_price,b_store)
买书的过程(默认买一本)
public boolean buyBook(b_id,u_id);//业务:书是什么用户是谁
事务
正常的事务
用户的余额更新
仓库的库存更新
事务回滚
余额不足
库存不足
数据表的创建
CREATE TABLE `t_book` (
`b_id` int(11) NOT NULL,
`b_name` varchar(50) DEFAULT NULL,
`b_price` double DEFAULT NULL,
`b_store` int(11) DEFAULT NULL,
PRIMARY KEY (`b_id`)
)
CREATE TABLE `t_user` (
`u_id` int(11) NOT NULL,
`u_name` varchar(10) DEFAULT NULL,
`u_blance` double DEFAULT NULL,
PRIMARY KEY (`u_id`)
)
实体类的创建
public class Book {
private int bId;
private String bName;
private double bPrice;
private int bStore;
}
public class User {
private int uId;
private String uName;
private double uBlance;
}
接口的创建
public interface UserDao {
//根据用户id查询余额
double queryById(int uId);
//根据id和价格(书的价格)修改余额
void updateUser(@Param("uId") int uId, @Param("price") double price);
}
public interface BookDao {
//根据编号查询价格
double queryById(int bId);
//查询库存
int queryStore(int bId);
//根据书的编号改变库存-1
void updateStroe( int bId);
}
Mapper的创建
<mapper namespace="com.qfedu.dao.UserDao">
<update id="updateUser">
update t_user set u_blance = u_blance - #{price} where u_id = #{uId}
</update>
<select id="queryById" resultType="java.lang.Double">
select u_blance from t_user where u_id = #{uId}
</select>
</mapper>
<mapper namespace="com.qfedu.dao.BookDao">
<update id="updateStroe">
update t_book set b_store = b_store - 1 where b_id = #{bId}
</update>
<select id="queryById" resultType="java.lang.Double">
select b_price from t_book where b_id = #{bId}
</select>
<select id="queryStore" resultType="java.lang.Integer">
select b_store from t_book where b_id = #{bId}
</select>
</mapper>
service的创建
package com.qfedu.service.impl;
import com.qfedu.dao.BookDao;
import com.qfedu.dao.UserDao;
import com.qfedu.exception.BlanceException;
import com.qfedu.exception.StoreException;
import com.qfedu.service.BuyBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
//将service添加到容器中
@Service
public class BuyBookServiceImpl implements BuyBookService {
@Autowired
private BookDao bookDao;
@Autowired
private UserDao userDao;
/**
*
* @param uId 用户编号
* @param bId 图书编号
*/
@Override
@Transactional
public void buyBook(int uId, int bId) {
//1.通过用户的id查询余额
double uBlance = userDao.queryById(uId);
//2.通过图书的id查询价格
double bPrice = bookDao.queryById(bId);
//3.通过图书的id查询库存
int store = bookDao.queryStore(bId);
//4.做一系列限定 库存不足 余额不足
//余额限定
if(uBlance < bPrice){
throw new BlanceException("余额不足");
}
//5.更新余额
userDao.updateUser(uId,bPrice);
//库存不足
if(store <= 0){
throw new StoreException("库存不足");
}
//6.更新库存
bookDao.updateStroe(bId);
}
}
测试
@Test
public void test04(){
ClassPathXmlApplicationContext cax = new ClassPathXmlApplicationContext("applicationContext.xml");
BuyBookService buyBookService = (BuyBookService) cax.getBean("buyBookServiceImpl");
buyBookService.buyBook(1,1);
}