Spring框架
Spring是轻量级的开源的JavaEE框架,可以解决企业应用开发的复杂性
Spring 有两个核心部分:IOC 和 Aop
1.IOC:控制反转,把创建对象过程交给Spring进行管理
2.Aop:面向切面,不修改源代码进行功能增强
1 2 3 4 5 6 7 8 9 10
| @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); User user = context.getBean("user", User.class); System.out.println(user); user.add(); }
|
IOC
概念:IOC(Inversion Of Control) 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
作用:可以降低耦合度
底层原理:xml解析、工厂模式、反射
- IoC是一种设计思想
- IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
IOC(接口)
1.IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2.Spring提供IOC容器实现的两种方式:(两个接口)
①BeanFactory:Spring内部使用接口,一般开发不使用
特点:在加载配置文件时不会同时创建对象,仅在获取或使用对象时才会创建对象
②ApplicationContext:BeanFactory接口的子接口,功能更多,开发通常使用这个接口
特点:在加载配置文件时就同时创建对象
ApplicationContext的实现类:
Bean管理
- bean管理是指两个操作,即Spring创建对象,Spring注入属性
bean管理操作的两种方式:
基于xml配置文件实现
1.实现方式
①基于xml方式创建对象
在spring配置文件中使用bean标签,标签里面添加对应属性即可
- 注意一点:创建对象时默认执行的是无参构造器方法,缺少无参构造器将报错
1 2 3 4
|
<bean id="user" class="spring.User"></bean>
|
②基于xml方式注入属性
DI(Dependency Injection) 依赖注入就是注入属性
方式一:set方法注入 必须先创建对象后才能调用set方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class Book { private String name; private String author;
public void setName(String name) { this.name = name; } public void setAuthor(String author) { this.author = author; } public void test(){ System.out.println(name + "=" + author); } }
@Test public void testBook(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("book", Book.class); book.test(); }
|
spring文件配置:
1 2 3 4 5 6 7 8 9 10
| <bean id="book" class="spring.Book" >
<property name="name" value="test"></property> <property name="author" value="test"></property> </bean>
|
方式二:有参构造注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Order { private String orderId; private String orderName;
public Order(String orderId, String orderName) { this.orderId = orderId; this.orderName = orderName; }
public void test(){ System.out.println(orderId + "=" + orderName); } }
@Test public void testOrder(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Order order = context.getBean("order", Order.class); order.test(); }
|
spring文件配置:
1 2 3 4 5 6 7 8 9 10
| <bean id="order" class="spring.Order">
<constructor-arg name="orderId" value="test1"></constructor-arg> <constructor-arg name="orderName" value="test1"></constructor-arg> </bean>
|
2.xml注入其他类型的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <property name="address"> <null /> </property>
<property name="address" > <value><![CDATA[<<广东>>]]></value> </property> </bean>
<bean id="userservice" class="spring.service.UserService">
<property name="userDao" ref="userdaoimpl"></property> </bean> <bean id="userdaoimpl" class="spring.dao.UserDaoImpl"></bean>
<bean id="emp" class="spring.bean.Emp"> <property name="name" value="test1"></property> <property name="sex" value="boy"></property>
<property name="dept" > <bean id="dept" class="spring.bean.Dept"> <property name="deptName" value="dept1"></property> </bean> </property> </bean>
<bean id="emp" class="spring.bean.Emp"> <property name="name" value="test1"></property> <property name="sex" value="boy"></property> <property name="dept" ref="dept"></property> <property name="dept.deptName" value="dept2"></property> </bean> <bean id="dept" class="spring.bean.Dept"></bean> </beans>
|
3.xml注入集合属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| <bean id="student" class="collecttype.Student">
<property name="course"> <array> <value> java</value> <value> C++</value> <value> JS</value> </array> </property>
<property name="list" > <list> <value>s1</value> <value>s2</value> <value>s3</value> </list> </property>
<property name="map"> <map> <entry key="t1key" value="t1value"></entry> <entry key="t2key" value="t2value"></entry> </map> </property>
<property name="set"> <set> <value>1</value> <value>2</value> </set> </property> <property name="courseList"> <list> <ref bean="coures1"></ref> <ref bean="coures2"></ref> </list> </property>
<bean id="coures1" class="collecttype.Course"> <property name="className" value="c++"></property> </bean> <bean id="coures2" class="collecttype.Course"> <property name="className" value="java"></property> </bean> <util:list id="booklist"> <value >test1</value> <value >test2</value> <value >test3</value> </util:list>
<bean id="book" class="collecttype.Book"> <property name="list" ref="booklist"></property> </bean>
|
Bean
此处的FactoryBean与最上面的BeanFactory并不一样,FactoryBean是一个接口,而BeanFactory是spring内置的工厂bean
Spring 有两种类型 bean:
①普通 bean:在配置文件中定义 bean 类型就是返回类型
②工厂 bean(FactoryBean):在配置文件定义 bean 类型可以和返回类型不一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class TestBean implements FactoryBean<Course>{ @Override public Course getObject() throws Exception { Course course = new Course(); course.setClassName("test"); return course; }
@Override public Class<?> getObjectType() { return null; }
@Override public boolean isSingleton() { return false; } }
|
bean的作用域
作用域:在 Spring 里面,设置创建 bean 实例是单实例还是多实例
- 在 Spring 里面,默认情况下,bean 是单实例对象
1.设置单实例还是多实例的方法
①在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
②scope 属性值 第一个值 默认值,singleton,表示是单实例对象 第二个值 prototype,表示是多实例对象
2.singleton 和 prototype 区别
① singleton 单实例,prototype 多实例
② scope 值是 singleton ,加载 spring 配置文件时候就会创建单实例对象
scope 值是 prototype,不是在加载 spring 配置文件时候创建 对象,在调用 getBean 方法时候创建多实例对象
bean的生命周期
1.生命周期:对象创建到对象销毁的过程
2、bean 生命周期
①通过构造器创建 bean 实例(无参数构造)
②为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
③调用 bean 的初始化的方法(需要进行配置初始化的方法)
④bean 可以使用了(对象获取到了)
⑤当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Orders { public Orders() { System.out.println("第一步 执行无参数构造创建 bean 实例"); } private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("第二步 调用 set 方法设置属性值"); } public void initMethod() { System.out.println("第三步 执行初始化的方法"); } public void destroyMethod() { System.out.println("第五步 执行销毁的方法"); } }
@Test public void testBean3() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 获取创建 bean 实例对象"); System.out.println(orders); context.close(); }
|
- 执行的init初始化方法和销毁方法需要在spring的xml文件中进行配置
1 2 3
| <bean id="orders" class="com.atguigu.spring5.bean.Orders" initmethod="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean>
|
3.bean 的后置处理器,bean 生命周期有七步
①通过构造器创建 bean 实例(无参数构造)
②为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
③把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
④调用 bean 的初始化的方法(需要进行配置初始化的方法)
⑤把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
⑥bean 可以使用了(对象获取到了)
⑦当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法"); return bean; } }
<!--配置后置处理器--> <bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
|
xml自动装配
自动装配:根据指定装配规则(属性名称或属性类型),spring自动将匹配的属性值进行注入
1 2 3 4 5 6 7 8 9 10 11
|
<bean id="emp" class="autowire.Emp" autowire="byType"> </bean> <bean id="dept" class="autowire.Dept"></bean>
|
引入外部属性
①创建外部属性文件,properties 格式文件,写数据库信息
②把外部 properties 属性文件引入到 spring 配置文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?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:util="http://www.springframework.org/schema/util"//util 名称空间 xmlns:context="http://www.springframework.org/schema/context"//context 名称空间 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.user}"></property> <property name="password" value="${prop.password}"></property> </bean> </beans>
|
基于注解方式实现
1.注解的概念
- 注解:是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)
注解可以作用在类上面,方法上面,属性上面,注解可以简化xml配置
2.spring提供的注解
bean管理中创建对象提供注解:
①@Component 普通组件
②@Service 业务层组件
③@Controller 控制层组件
④@Repository 持久层组件
- 这四个注解的功能都是一样的,都可以用来创建bean的对象实例
bean管理中提供的属性注入的注解:
①@Autowired:根据属性类型进行自动装配
②@Qualifier:根据名称进行注入,常用@autowired搭配使用
- PS:当出现接口的实现类不止这一个,就不能单独使用@Autowired,因为如果根据类型获取系统就不知道你想获取具体的实现类对象,
所以要根据名获取,@Autowired 和@Qualifier搭配一起用
③@Resource:可以根据类型注入,可以根据名称注入
④@Value:注入普通类型属性
3.实现方式
①引入jar包
②在xml文件中利用名称空间和开启组件扫描
③创建类,在类上添加创建对象的注解
3.1实现对象的创建
1 2 3 4 5 6 7 8 9
|
@Controller(value = "userService") public class UserService { public void test(){ System.out.println("test success!"); } }
|
spring的xml配置文件开启组件扫描
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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:util="http://www.springframework.org/schema/util" 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:component-scan base-package="dao,service "></context:component-scan> </beans>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
<context:component-scan base-package="com.atguigu" use-defaultfilters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
<context:component-scan base-package="com.atguigu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
|
3.2实现在对象创建后注入属性
第一步:把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
第二步:在 service 注入dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Service public interface UserDao { void test(); }
@Repository public class UserDaoImpl implements UserDao{ @Override public void test() { System.out.println("this is UserDaoImpl"); } }
public class UserService { @Value(value = "test") private String name;
@Resource(name = "userDaoImpl1") private UserDao userDao; public void test(){ System.out.println("this is UserService," + name); userDao.test(); } }
|
完全注解开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration @ComponentScan(basePackages = {"com.atguigu"}) public class SpringConfig { }
@Test public void testService2() { ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
|
Aop
aop的概念:Aspect oriented programming 面向切面编程,AOP 是 OOP(面向对象编程)的一种延续。
作用:在不改变源代码的情况下,添加其他模块的代码,根本上解耦合,避免横切逻辑代码重复。
aop的底层原理是:动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class JDKProxy {
public static void main(String[] args) { Class[] interfaces = {UserDao.class}; UserDaoImpl userDaoImpl = new UserDaoImpl(); UserDao userDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImpl)); int add = userDao.add(1, 2); System.out.println(add); } }
class UserDaoProxy implements InvocationHandler{ private Object obj;
public UserDaoProxy(Object obj){ this.obj = obj; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法之前 ..." + method.getName() + "传递的参数" + Arrays.toString(args));
Object invoke = method.invoke(obj, args);
System.out.println("方法之后" + obj); return invoke;
} }
|
操作术语
1.连接点:类里面哪些方法可以被增强,这些方法成为连接点
2.切入点:实际被增强的方法,这些方法称为切入点
3.通知(增强):实际上在被增强的方法中的逻辑部分(代码) ,称为通知
通知有多种类型:
①前置通知:在增强方法前执行
②后置通知:在增强方法后执行
③环绕通知:在增强方法前后分别执行
④异常通知:在增强方法出现异常后执行
⑤最终通知:无论增强方法怎么样,都会执行
4.切面:它是个动作,把通知应用到切入点的过程
AOP操作
- spring框架一般都是基于AspectJ实现AOP操作
1.AspectJ 的概念:它不是Spring的组成部分,它是独立AOP框架,一般把AspectJ经常与Spring框架一起使用,进行AOP操作
2.切入点表达式
作用:指定对哪个类里面的哪个方法进行增强
语法结构:execution([ 权限修饰符] -[返回类型]- [类全路径]-[方法名称]-(参数列表))
实现AOP操作
1.基于xml配置文件实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
<aop:config> <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/> <aop:aspect ref="bookProxy"> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
|
2.基于注解方法时实现(使用)
①创建类,在类里面定义方法
②创建增强类(编写增强逻辑)
③进行通知的配置
④配置不同类型的通知(在增强的类中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| @Component public class Student {
public void add(){
System.out.println("test success!"); }
}
@Aspect @Service public class StudentProxy {
@Pointcut(value = "execution(void aop.aopanno.Student.add(..))") public void point(){
}
@Before("point()") public void before(){ System.out.println("before"); }
@After("execution(void aop.aopanno.Student.add(..))") public void after(){ System.out.println("after"); }
@AfterReturning("execution(void aop.aopanno.Student.add(..))") public void afterReturning(){ System.out.println("afterReturning"); }
@AfterThrowing("execution(void aop.aopanno.Student.add(..))") public void afterThrowing(){ System.out.println("afterThrowing"); }
@Around("execution(void aop.aopanno.Student.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("around 之前");
proceedingJoinPoint.proceed();
System.out.println("around 之后"); } }
|
通知的xml文件配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns: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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="ioc,aop.aopanno"></context:component-scan>
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy> </beans>
|
1 2 3 4 5 6 7 8 9 10 11 12
| @Pointcut(value = "execution(void aop.aopanno.Student.add(..))") public void point(){
}
@Before("point()") public void before(){ System.out.println("before"); }
|
1 2 3 4 5
| @Order(1) public class PersonProxy { @Order(3) public class StudentProxy {
|
jdbcTemplate
①引入相关 jar 包
druid-1.1.10.jar
mysql-connector-java-8.0.11.jar
spring-orm-5.2.6.RELEASE.jar
spring-tx-5.2.6.RELEASE.jar
spring-jdbc-5.2.6.RELEASE.jar
②在 spring 配置文件配置数据库连接池
③配置 JdbcTemplate 对象,注入 DataSource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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:component-scan base-package="jdbctemplate"/> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.user}"></property> <property name="password" value="${prop.password}"></property> </bean>
<bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
|
④创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Service public class BookService {
@Autowired private BookDao bookDao; }
@Repository public class BookDaoImpl implements BookDao{
@Autowired private JdbcTemplate jdbcTemplate; }
|
- 具体方法的增删改查操作与自己封装的jdbcutils相差不多,不在此浪费篇幅说明 具体看spring下的jdbctemplate包
spring事务管理
1.事务即数据库的一组逻辑操作单元由一种状态到另一种状态
2.事务一般添加在JavaEE的service层
3.spring事务管理分为了编程式事务管理和声明式事务管理(推荐)
- Spring使用的声明式事务管理,其底层使用了Aop技术
- Spring提供了一个事务管理API
基于注解方式实现事务管理
实现步骤:
1.在 spring 配置文件配置事务管理器
2.在 spring 配置文件,开启事务注解
①在 spring 配置文件引入名称空间 tx
②开启事务注解
3.在 service 类上面(或者 service 类里面方法上面)添加事务注解
- @Transactional,这个注解添加到类上面,也可以添加方法上面
- 如果把这个注解添加类上面,这个类里面所有的方法都添加事务
- 如果把这个注解添加方法上面,为这个方法添加事务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="jdbctemplate"/> <context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${prop.url}"/> <property name="driverClassName" value="${prop.driverClass}"/> <property name="username" value="${prop.user}"/> <property name="password" value="${prop.password}"/> </bean>
<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Service @Transactional public class AccountService {
@Autowired private AccountDao accountDao;
public void moneyIn(Account account){ accountDao.changeMoney(account); }
|
声明式事务管理参数配置
- 在注解@Transacional里面可以配置事务相关参数
1.propagation:事务传播行为
概念:事务传播行为用来描述由某一个事务方法被嵌套进另一个普通方法的时侯事务如何传播。
事务方法:对数据库表数据进行变化的操作,如增删改等操作,但不包括查询。
1 2 3 4 5 6 7 8 9
| public void methodA(){ methodB();
}
@Transaction(Propagation=XXX) public void methodB(){
}
|
事务传播行为类型 |
说明 |
PROPAGATION_REQUIRED(默认) |
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的选择。 |
PROPAGATION_REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起,以新的事务进行管理 |
PROPAGATION_SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY |
使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
2.ioslation:事务隔离级别
- 事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
数据库并发问题:脏读、不可重复读、幻读
隔离级别:读未提交、读已提交、可重复读、串行化
3.timeout:超时时间
①事务需要在一定时间内进行提交,如果不提交进行回滚
②默认值是 -1 ,设置时间以秒单位进行计算
4.readOnly:是否只读
①读:查询操作,写:添加修改删除操作
②readOnly 默认值 false,表示可以查询,可以添加修改删除操作
③设置 readOnly 值是 true,设置成 true 之后,只能查询
5.rollbackFor:回滚
①设置出现哪些异常进行事务回滚
6.noRollbackFor:不回滚
①设置出现哪些异常不进行事务回滚
基于xml配置文件实现
实现步骤
在 spring 配置文件中进行配置
①配置事务管理器
②配置通知
③配置切入点和切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
<tx:advice id="txadvice"> <tx:attributes> <tx:method name="accountMoney" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
<aop:config> <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
|
完全注解开发
- @Component:表示这是一个组件。IOC会托管这个类的对象。
- @bean:方法返回的对象交给IOC管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Configuration @ComponentScan(basePackages = "com.atguigu") @EnableTransactionManagement public class TxConfig { @Bean public DruidDataSource getDruidDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///user_db"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } }
|
Spring5 新功能
@Nullable注解
Spring5框架的核心容器支持Nullable注解
- 此注解可以使用在方法、属性、参数上面,表示以上的返回值都可以为空,避免报空指针异常
函数式风格
Spring5 核心容器支持函数式风格 GenericApplicationContext
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Test public void testGenericApplicationContext() { GenericApplicationContext context = new GenericApplicationContext(); context.refresh(); context.registerBean("user1",User.class,() -> new User()); User user = (User)context.getBean("user1"); System.out.println(user); }
|
SpringWebFlux
1.SpringWebFlux的介绍
①在spring5版本中添加的新模块,功能与SpringMVC相似,都可用于WEB开发
WebFlux使用的是一种当前比较流程响应式编程的框架
②传统web框架,例如SpringMVC都是基于Servlet容器,而WebFlux是一种异步非阻塞的框架,
这种异步非阻塞框架在Servlet3.1以后的版本才支持,核心是基于Reactor的相关API实现的
所谓的异步和同步都是针对调用者来说的,如调用者发请求,同步则需等待请求的响应,
异步则发完请求不需要等待就可以去做其他事情了
阻塞和非阻塞都是针对被调用者来说的,如被调用者收到请求后,非阻塞则代表马上做出响应后
再去处理对应请求,而阻塞则代表收到请求后,处理完成对应请求后再去做出响应
③Webfiux特点
Ⅰ.非阻塞式:在有限资源下,提高系统吞吐量和伸缩性
Ⅱ.函数式编程:基于java8,可以使用Java8函数式编程方式实现路由请求
④与SpringMVC的区别
Ⅰ.两者都可以使用注解的方式,且都可以运行在Tomcat等容器中
Ⅱ.SpringMVC采用的是命令式编程(即代码一行行的书写和执行)
而WebFlux采用异步响应式编程
2.响应式编程(Java实现)
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便
地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似”=B1+C1”的公
式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
除此之外,也类似于Vue中动态绑定
- Java8 及其之前版本 提供的观察者模式两个类 Observer 和 Observable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ObserverDemo extends Observable { public static void main(String[] args) { ObserverDemo observerDemo = new ObserverDemo(); observerDemo.addObserver((o,arg) -> { System.out.println("数据发送变化"); }); observerDemo.addObserver((o,arg) -> { System.out.println("收到被观察者的通知,数据准备变化"); });
observerDemo.setChanged(); observerDemo.notifyObservers();
} }
|
3.响应式编程(Reactor实现)
①响应式编程操作中,Reactor是满Reactive规范框架
②Reactor有两个核心类,Mono 和 Flux,这两个类实现接口Publisher,提供丰富操作符。
Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素
③Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:
元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉
订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者
④代码演示 Flux和Mono
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class TestReactor { public static void main(String[] args) { Flux.just(1,2,3,4); Mono.just(1); Integer[] array = {1, 2, 3, 4}; Flux.fromArray(array);
List<Integer> list = Arrays.asList(array); Flux.fromIterable(list);
Stream<Integer> stream = list.stream(); Flux.fromStream(stream); } }
|
⑤三种信号特点
错误信号和完成信号都是终止信号,不能共存的
如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
如果没有错误信号,没有完成信号,表示是无限数据流
⑥调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发
数据流,不订阅什么都不会发生的
1 2 3
| Flux.just(1,2,3,4).subscribe(System.out :: print); Mono.just(1).subscribe(System.out :: print);
|
⑦操作符
对数据流进行一道道操作,成为操作符,比如工厂流水线
第一 map 元素映射为新元素
第二 flatMap 元素映射为流,把每个元素转换流,把转换之后多个流合并大的流
4.SpringWebflux执行流程和核心API
SpringWebflux 基于 Reactor,默认使用容器是 Netty,Netty 是高性能的 NIO框架,
异步非阻塞的框架
BIO是同步阻塞的
NIO同步非阻塞
2.SpringWebflux 执行过程和 SpringMVC 相似的
①SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler
函数式接口 WebHandler 有一个方法
1 2 3
| public interface WebHandler { Mono<Void> handle(ServerWebExchange exchange); }
|
- 函数式接口 WebHandler的实现类DispatcherHandler
1 2 3 4 5 6 7 8 9 10 11 12 13
| public Mono<Void> handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return this.createNotFoundError(); } else { return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? this.handlePreFlight(exchange) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { return mapping.getHandler(exchange); }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { return this.invokeHandler(exchange, handler); }).flatMap((result) -> { return this.handleResult(exchange, result); }); } }
|
②SpringWebflux 里面 DispatcherHandler,负责请求的处理(跟SpringMVC这一套差不多)
HandlerMapping:请求查询到处理的方法
HandlerAdapter:真正负责请求处理
HandlerResultHandler:响应结果处理
③SpringWebflux 实现函数式编程,两个接口:
Ⅰ.RouterFunction(路由处理):将请求转发到对应的控制器
Ⅱ.HandlerFunction(处理函数):执行控制器方法
3.SpringWebflux(基于注解编程模型)
①添加依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
|
②创建entity层和实体类
1 2 3 4 5 6
| @lombok public class User { private String name; private String gender; private Integer age; }
|
③创建service层和实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public interface UserService { Mono<User> getUserById(Integer id);
Flux<User> getAllUser();
Mono<Void> addUser(Mono<User> user); } @Service public class UserServiceImpl implements UserService { Map<Integer,User> userMap = new HashMap<>();
public UserServiceImpl(){ this.userMap.put(1,new User("test1","男",21)); this.userMap.put(2,new User("test2","女",22)); this.userMap.put(3,new User("test3","男",23)); }
@Override public Mono<User> getUserById(Integer id) { return Mono.just(this.userMap.get(1)); }
@Override public Flux<User> getAllUser() { return Flux.fromIterable(this.userMap.values()); }
@Override public Mono<Void> addUser(Mono<User> user) { return user.doOnNext(person ->{ Integer id = userMap.size()+1; userMap.put(id,person); }).thenEmpty(Mono.empty()); } }
|
④创建Controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService;
@GetMapping("/query/{id}") public Mono<User> queryUserById(@PathVariable("id") Integer id){ return userService.getUserById(id); }
@GetMapping("/queryAll") public Flux<User> queryAllUser(){ return userService.getAllUser(); }
@PostMapping("/add") public Mono<Void> addUser(User user){ Mono<User> userMono = Mono.just(user); return userService.addUser(userMono); } }
|
SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC+Servlet+Tomcat
SpringWebflux 方式实现,异步非阻塞 方式,基于 SpringWebflux+Reactor+Netty
4.SpringWebflux(基于函数式编程模型)
①在使用函数式编程模型操作时候,需要自己初始化服务器
②基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发
给对应的 handler)和 HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数
式接口的实现并且启动需要的服务器。
③ SpringWebflux 请 求 和 响 应 不 再 是 ServletRequest 和 ServletResponse ,而是
ServerRequest 和 ServerResponse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class UserHandler { private final UserService userService;
public UserHandler(UserService userService){ this.userService = userService; } public Mono<ServerResponse> queryById(ServerRequest serverRequest){ Integer id = Integer.valueOf(serverRequest.pathVariable("id")); Mono<ServerResponse> notFound = ServerResponse.notFound().build(); Mono<User> user = userService.getUserById(id); return user.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON) .bodyValue(person) .switchIfEmpty(notFound));
} public Mono<ServerResponse> queryAll(ServerRequest serverRequest){ Flux<User> users = userService.getAllUser(); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class); } public Mono<ServerResponse> addUser(ServerRequest serverRequest){ Mono<User> userMono = serverRequest.bodyToMono(User.class); return ServerResponse.ok().build(userService.addUser(userMono)); } }
|
- 创建server完成router、适配器和服务器的设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Server { public static void main(String[] args) throws IOException { Server server = new Server(); server.createReactorServer(); System.out.println("方法已执行完毕!正在退出"); System.in.read(); }
public RouterFunction<ServerResponse> routingFunction(){ UserService userService = new UserServiceImpl(); UserHandler userHandler = new UserHandler(userService); return RouterFunctions.route( GET("/users/{id}").and(accept(APPLICATION_JSON)),userHandler::queryById) .andRoute(GET("/users").and(accept(APPLICATION_JSON)),userHandler::queryAll); }
public void createReactorServer(){ RouterFunction<ServerResponse> routerFunction = routingFunction(); HttpHandler httpHandler = toHttpHandler(routerFunction); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); HttpServer httpServer = HttpServer.create(); httpServer.handle(adapter).bindNow(); } }
|