文章目录
- spring
- 一、概述
- 二、入门案例
- 三、IOC
- 3.1、什么是IOC
- 3.2、IOC底层原理
- 3.3、IOC接口
- 3.4、Xml中Set属性注入
- 3.5、Xml中有参构造注入
- 3.6、P名称空间注入
- 3.7、Xml中注入null和特殊字符
- 3.8、Xml中外部bean注入
- 3.9、Xml中内部bean注入,和级联赋值
- 4.0、Xml集合属性注入 (二)
- 4.1、Xml集合属性注入(二)
- 4.2、Bean管理(工厂Bean)
- 4.3、Bean的作用域
- 4.4、Bean的生命周期 (从对象创建到销毁的过程)
- 4.5、bean的后置处理器--bean的生命周期变为七步
- 4.6、bean管理(xml自动装配)
- 4.7、Bean管理(xml 外部属性注入方式)
- 4.8、注解方式(创建对象 )
- 4.9、开启组件扫描配置(了解)
- 5.0、注解方式(属性注入)
- 5.1、注解方式(配置类)
- 四、AOP
- 五、JdbcTemplate
- 六、事务操作
- 七、Spring5框架新功能
spring
一、概述
- spring是轻量级的开源的JavaEE框架
- spring可以解决企业应用开发的复杂性
- spring有两个核心 : IOC 和 AOP
- IOC : 控制翻转,把创建对象过程交给spring进行管理(反射,工厂)
- AOP: 面向切面,不修改源代码进行功能增强(JDK动态代理)
- spring 特点:
- 方便解耦,简化开发
- Aop编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低API开发难度
二、入门案例
-
新建maven项目,引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.2.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0-b01</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency>
-
新建配置 xml
```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"> <!--配置Student对象创建--> <bean id="student" class="com.wen.Student"/> </beans> ```
-
新建一个类
import lombok.Data; @Data public class Student { private Integer id; private String name; private Integer age; public void add(){ System.out.println("add"); } }
-
测试使用配置bean的方式创建对象效果
@Test public void test1(){ // 1.加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //获取配置创建的对象 Student student = context.getBean("student",Student.class); student.add(); }
三、IOC
3.1、什么是IOC
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC的目的:降低耦合度
- 入门案例就是IOC实现的
3.2、IOC底层原理
- xml解析、工厂模式、反射
- IOC实现图解
3.3、IOC接口
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
- Spring提供IOC容器实现两种方式:(两个接口)
- BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用
- 加载配置文件时候不会创建对象,在获取对象(使用)才会创建对象
- ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
- 加载配置文件时候会把配置文件对象进行创建
- BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用
- ApplicationContext接口实现类
- FileSystemXmlApplicationContext 指定系统路径
- ClassPathXmlApplicationContext src下java类路径
3.4、Xml中Set属性注入
@Data
public class Student {
private String name;
private Integer age;
public void add(){
System.out.println(name+":::"+age);
}
}
<!--set属性注入-->
<bean id="student" class="com.wen.Student">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = context.getBean("student",Student.class);
student.add(); // 张三:::18
3.5、Xml中有参构造注入
<!--有参构造注入-->
<bean id="student" class="com.wen.Student">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="18"/>
</bean>
3.6、P名称空间注入
-
使用p名称空间注入,可以简化基于xml配置方式
-
一、添加p 名称空间在配置文件中
-
二、进行属性注入
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p 命名空间注入--> <bean id="student" class="com.wen.Student" p:age="18" p:name="张三"/> </beans>
-
3.7、Xml中注入null和特殊字符
- 注入null
<bean id="student" class="com.wen.Student">
<property name="name">
<null/>
</property>
</bean>
- 特殊字符
- 可以使用转义字符 <> > <
- 使用CDATA
<bean id="student" class="com.wen.Student">
<property name="name">
<value><![CDATA[<<南京>>]]></value>
</property>
</bean>
3.8、Xml中外部bean注入
- 创建两个类service类和mapper
- 在service中调mapper
- 在配置文件中进行配置
//service
public class UserService {
private UserMapper userMapper;
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public void add(){
userMapper.add();
}
}
//mapper
public interface UserMapper {
void add();
}
public class UserMapperImpl implements UserMapper{
@Override
public void add() {
System.out.println("大家伙");
}
}
<bean id="userService" class="com.wen.service.UserService">
<property name="userMapper" ref="userMapperImpl"/>
</bean>
<bean id="userMapperImpl" class="com.wen.mapper.UserMapperImpl"/>
3.9、Xml中内部bean注入,和级联赋值
<bean id="school" class="com.wen.School">
<property name="address" value="中国"/>
<property name="student" ref="student"/>
<property name="student.name" value="张三"/>
</bean>
<bean id="student" class="com.wen.Student">
<property name="name" value="张三"/>
</bean>
4.0、Xml集合属性注入 (二)
@Data
public class User {
String[] name;
List<String> names;
Map<String, Object> nameMaps;
Set nameSet;
public void print(){
System.out.println(Arrays.toString(name));
System.out.println(names);
System.out.println(nameMaps);
System.out.println(nameSet);
}
}
<bean id="user" class="com.wen.User">
<!--数组注入-->
<property name="name">
<array>
<value>张三</value>
<value>李四</value>
</array>
</property>
<!--list属性注入-->
<property name="names">
<list>
<value>张三</value>
<value>李四</value>
</list>
</property>
<!--map类型属性注入-->
<property name="nameMaps">
<map>
<entry key="user" value="张三"/>
<entry key="password" value="12345"/>
</map>
</property>
<!--set属性注入-->
<property name="nameSet">
<set>
<value>张三</value>
<value>李四</value>
</set>
</property>
</bean>
4.1、Xml集合属性注入(二)
- 使用util 命名空间
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util" <!-- 这里-->
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-4.0.xsd">
<!--提取list集合类型属性注入-->
<util:list id="userList">
<value>张三</value>
<value>李四</value>
</util:list>
<util:map id="userMap">
<entry key="user" value="张三"/>
<entry key="password" value="12345"/>
</util:map>
<util:set id="userSet">
<value>张三</value>
<value>李四</value>
</util:set>
<!--注入类-->
<bean id="user" class="com.wen.User">
<property name="names" ref="userList"/>
<property name="nameMaps" ref="userMap"/>
<property name="nameSet" ref="userSet"/>
</bean>
</beans>
4.2、Bean管理(工厂Bean)
- Spring有两种类型Bean,一种普通bean,另外一种工厂bean(FactoryBean)
- 普通bean:在配置文件中定义bean类型就是返回类型 (bean标签的class指定的)
- 工厂bean:在配置文件定义bean类型可以和返回类型不一样
- 创建类,让这个类作为工厂bean,实现接口FactoryBean
- 实习接口里面的方法,在实现方法中定义返回的bean类型
//自定义类,实现FactoryBean,自定义回参类型为User
public class MyBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user = new User();
String[] name = {"张三", "李四"};
user.setName(name);
return user;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
//测试
@Test
public void test1() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
User myBean = context.getBean("myBean", User.class);
System.out.println(myBean);
}
<bean id="myBean" class="com.wen.MyBean"/>
4.3、Bean的作用域
- 在spring里面,设置创建bean实例是单实例还是多实例
- 在spring里面,默认情况下bean是单实例对象
- singleton和prototype区别
- 设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象
- 设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象
- scope默认singleton
com.wen.Student@56aac163<bean id="student" class="com.wen.Student" scope="prototype"/>
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = context.getBean("student", Student.class);
Student student1 = context.getBean("student", Student.class);
System.out.println(student); // com.wen.Student@56aac163
System.out.println(student1); // com.wen.Student@1f7030a6
4.4、Bean的生命周期 (从对象创建到销毁的过程)
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- bean可以使用了
- 当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
public class User {
public User() {
System.out.println("第一步:通过无参构造创建bean实例");
}
private String name;
public void setName(String name) {
this.name = name;
System.out.println("第二步:为bean的属性赋值");
}
public void initMethod(){
System.out.println("第三步:调用bean的初始化方法");
}
public void destroyMethod(){
System.out.println("第五步:调用bean销毁方法");
}
}
<bean id="user" class="com.wen.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="张三"/>
</bean>
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
System.out.println("第四步:bean可以使用了");
User user = context.getBean("user", User.class);
System.out.println(user);
//关闭容器
context.close();
4.5、bean的后置处理器–bean的生命周期变为七步
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 把bean实例传递bean后置处理器方法postProcessBeforeInitialization
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- 把bean实例传递bean后置处理器方法postProcessAfterInitialization
- bean可以使用了
- 当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
- 实现bean后置处理器:创建类,实现接口BeanPostProcessor,创建后置处理器
public class MyBean implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("bean实例传递bean后置处理器前");
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("bean实例传递bean后置处理器后");
return o;
}
}
<bean id="user" class="com.wen.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="张三"/>
</bean>
<!--这个xml中所有的bean都走后置处理器-->
<!--配置后置处理器-->
<bean id="myBean" class="com.wen.MyBean"/>
4.6、bean管理(xml自动装配)
- 根据指定装配规则(属性名称或者属性类型),spring自动将匹配的属性值进行注入
- bean标签属性 autowire,配置自动装配
- byName:更具属性名称注入,注入bean的id值和类属性名称一样
- byType根据属性类型注入
@Data
public class Student {
private String name;
private Integer age;
}
@Data
public class School {
private Student student;
}
<bean id="school" class="com.wen.School" autowire="byType">
<!--<property name="student" ref="student"/>-->
</bean>
<bean id="student" class="com.wen.Student"></bean>
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
School school = context.getBean("school", School.class);
System.out.println(school); // School(student=Student(name=null, age=null))
4.7、Bean管理(xml 外部属性注入方式)
- 直接配置
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mysqlOne"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
-
使用外部属性方式
- 一、创建properties属性文件
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mysqlOne jdbc.username=root jdbc.password=root
- 二、xml中引入context空间,然后注入外部属性
<!--1、引入context命名空间--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--2、引入外部属性文件--> <context:property-placeholder location="jdbc.properties"/> <!--3、配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
4.8、注解方式(创建对象 )
- Spring针对Bean管理中创建对象提供注解
- @Component
- @Service
- @Controller
- @Repository
- 上面四个注解功能是一样的,都可以用来创建bean实例
- 实现
- 引入aop
- xml中开启组件扫描
- 类上加注解
<!--开启组件扫描
1.如果扫描多个包,多个包使用逗号隔开
2.扫描包上层路径
-->
<context:component-scan base-package="com.wen"/>
@Component
public class Order {
public void add(){
System.out.println("add............");
}
}
@Test
public void test1() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Order order = context.getBean("order", Order.class);
System.out.println(order);
order.add();
}
4.9、开启组件扫描配置(了解)
<!--示例一、
use-default-filters=“false” 表示不使用默认filter自己配置filter
context:include-filter,指定扫描哪些内容(只扫描controller注解)
-->
<context:component-scan base-package="com.wen" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例二丶
context:exclude-filter: 不扫描哪些内容
-->
<context:component-scan base-package="com.wen">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
5.0、注解方式(属性注入)
- @Autowired: 根据属性类型进行自动装配
- @Qualifier: 根据属性名称进行注入(需要配合@Autowired使用)
- @Resource: 可以根据类型注入,可以根据名称注入
- @Value: 注入普通类型属性
@Autowired:
//@Autowired的使用
public interface UserMapper {
void add();
}
@Repository
public class UserMapperImpl implements UserMapper{
@Override
public void add() {
System.out.println("大家伙");
}
}
@Service
public class UserService {
//不需要添加set方法
@Autowired
private UserMapper userMapper;
public void add(){
userMapper.add();
}
}
@Qualifier的使用需要配合@Autowired一起来使用
- 当一个mapper有多个实现类时,使用@Autowired就不能明确调用的是哪个实现类了
@Service
public class UserService {
@Autowired
@Qualifier(value = "userMapperImpl")
private UserMapper userMapper;
public void add(){
userMapper.add();
}
}
@Resource:Resource是javax包下的,autowired和qualifier都是spring的,所以推荐使用spring包下的
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
@Service
public class UserService {
//@Resource //按照类型注入
@Resource(name = "userMapperImpl") //按名称注入
private UserMapper userMapper;
public void add(){
userMapper.add();
}
}
**@Value: **普通属性赋值
@Repository
public class UserMapperImpl implements UserMapper{
@Value(value = "张三")
private String name;
@Override
public void add() {
System.out.println("大家伙..."+name);
}
}
5.1、注解方式(配置类)
@Configuration //标注为配置类
@ComponentScan(basePackages = {"com.wen"}) //开启包扫描
public class SpringConfig {
}
@Test
public void test2() throws Exception {
//使用AnnotationConfigApplicationContext来获取配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
四、AOP
4.1、什么是aop
- 面向切面编程
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重复行,提高开发效率
- 通俗描述: 不通过修改源代码方式,在主干功能里面添加新功能
4.2、AOP底层原理
AOP底层使用动态代理
- 第一种:有接口的情况 使用 JDK动态代理
- 创建接口实现类代理对象,增强类的方法
- 第二种:在没有接口的情况 使用CGLIB动态代理
- 创建子类的代理对象,增强类的方法
4.3、JDK动态代理实现
- 使用 Proxy类实现
- 调用期中的newProxyInstance 获得指定接口的代理类的实例
//接口
public interface UserService {
int sum(int a, int b);
int update(int a);
}
//实现类
public class UserServiceImpl implements UserService{
@Override
public int sum(int a, int b) {
System.out.println("sum方法执行了.........");
return a+b;
}
@Override
public int update(int a) {
System.out.println("update方法执行了.......");
return a;
}
}
//实现jdk动态代理
public class JDKProxy {
public static void main(String[] args) {
/**
* Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* loader: 类加载器
* interfaces: 增强方法所在的类,这个类实现的接口,支持多个接口
* h : 实现这个接口InvocationHandler,创建代理对象,写增强的方法
*
*/
Class[] interfaces = {UserService.class};
UserService userService = new UserServiceImpl();
UserService us = (UserService) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new MyHandler(userService));
int sum = us.sum(1, 2);
System.out.println(sum);
}
}
//代理对象创建
class MyHandler implements InvocationHandler{
//通过有参构造,将要代理增强的对象传递过来
private Object obj;
public MyHandler(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("增强之后::"+method.getName()+"回参::"+invoke);
return invoke;
}
}
4.4、AOP术语
- 连接点
- 类里面有哪些方法可以被增强,这些方法称为连接点
- 切入点
- 实际被真正增强的方法,称为切入点
- 通知(增强)
- 实际增强的逻辑部分称为通知
- 通知有多种类型
- 前置通知(方法之前执行)
- 后置通知(方法之后执行)
- 环绕通知(前后都执行)
- 异常通知(方法出异常了才通知)
- 最终通知(finally一定会执行)
- 切面
- 是动作
- 把通知应用到切入点的过程
4.5、AOP操作准备
-
Spring框架一般都是基于AspectJ实现AOP操作
- AspectJ不是Spring组成部分,是独立的AOP框架,一般把AspectJ和Spring框架一起使用进行AOP操作
-
基于AspectJ实现AOP操作
- 基于xml配置方式实现
- 基于注解方式实现(使用)
-
切入点表达式
- 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
- 语法结构:
- execution([权限修饰符][ 返回类型][类全路径][方法名称]([参数列表]))
//举例一: 对com.wen.dao.UserDao类里面的add进行增强 execution(* com.wen.dao.UserDao.add(..)) //举例二: 对com.wen.dao.UserDao类里面所有方法进行增强 execution(* com.wen.dao.UserDao.*(..)) //举例三: 对com.wen.dao包里所有类,类里面所有方法进行增强 execution(* com.wen.dao.*.*(..))
4.6、基于AspectJ注解方式实现AOP操作
- 创建类,在类里面定义方法
- 创建增强类(编写增强逻辑)
- 进行通知的配置
- 开启注解扫描
- 使用注解创建类对象
- 在增强类上面添加注解@Aspect
- 在配置文件中开启生成代理对象
- 配置不同类型的通知
- 在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//配置类
@Configuration //标注为配置类
@ComponentScan(value = {"com.wen"}) // 开启包扫描
@EnableAspectJAutoProxy //开启生成代理对象
public class SpringConfig {
}
//被增强类
@Component
public class User {
public void add(){
System.out.println("add...........");
}
}
//增强类
@Component
@Aspect
public class UserProxy {
//前置通知
@Before(value = "execution(* com.wen.User.add(..))")
public void before(){
System.out.println("before....");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.wen.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning....");
}
//最终通知
@After(value = "execution(* com.wen.User.add(..))")
public void after(){
System.out.println("after....");
}
//异常通知
@AfterThrowing(value = "execution(* com.wen.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing....");
}
//环绕通知
@Around(value = "execution(* com.wen.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前around....");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后around....");
}}
//测试
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean("user", User.class);
user.add();
}
4.7、抽取相同的切入点
@Component
@Aspect
public class UserProxy {
//抽取相同的切入点
@Pointcut(value = "execution(* com.wen.User.add(..))")
public void pointDemo(){
}
//前置通知
@Before(value = "pointDemo()")
public void before(){
System.out.println("before....");
}
//后置通知(返回通知)
@AfterReturning(value = "pointDemo()")
public void afterReturning(){
System.out.println("afterReturning....");
}}
4.8、有多个增强类对同一个方法增强,设置执行优先级
- 在增强类上面添加@Order(数字类型值),值越小优先级越高
@Component
@Aspect
@Order(1) //设置优先级
public class UserProxy {}
五、JdbcTemplate
5.1、什么是JdbcTemplate
- Spring框架对Jdbc进行的封装,使用JdbcTemplate方便实现对数据库操作
5.2、准备工作
-
依赖
- 配置数据库连接池
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/my_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
3. 配置JdbcTemple对象,注入DataSource
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
5.3、使用JdbcTemplate操作数据库
@Repository
public class UserMapperImpl implements UserMapper{
@Autowired
private JdbcTemplate jdbcTemplate;
//新增
@Override
public int add(User user) {
String sql = "insert into user(name,password) values(?,?)";
Object[] args = {user.getName(), user.getPassword()};
int update = jdbcTemplate.update(sql, args);
return update;
}
//修改
@Override
public int update(User user) {
String sql = "update user set name = ?,password = ? where id = ?";
Object[] args = {user.getName(), user.getPassword(),user.getId()};
int update = jdbcTemplate.update(sql, args);
return update;
}
//删除
@Override
public int delete(Integer id) {
String sql = "delete from user where id = ?";
int update = jdbcTemplate.update(sql, id);
return update;
}
//查询记录数
@Override
public int selectCount() {
String sql = "select count(*) from user";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
return integer;
}
//根据id查询
@Override
public User selectById(Integer id) {
String sql = "select * from user where id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
//查询所有记录
@Override
public List<User> selectAll() {
String sql = "select * from user";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
return users;
}
}
六、事务操作
6.1、什么是事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败,所有操作都失败
- 事务的四大特性 (ACID)
- 原子性 atomicity
- 指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做
- 一致性 consistency
- 事务的执行不能破坏数据库数据的完整性和一致性
- 隔离性 isolation
- 多事务操作,之间不产生影响
- 持久性 durability
- 事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中
- 原子性 atomicity
6.2、Spring事务管理说明
-
spring中有两种方式实现事务管理
- 编程式事务管理
- 声明式事务管理(使用)
-
声明式事务管理的两种实现方式
- 基于注解方式 (使用)
- 基于xml配置文件方式
-
spring进行声明式事务管理,底层使用的是AOP原理
-
Spring事务管理API
- 提供了PlatformTransactionManager接口,这个接口针对不同的框架提供不同的实现类
- mybatis和jdbc:DataSourceTransactionManager(使用)
- 提供了PlatformTransactionManager接口,这个接口针对不同的框架提供不同的实现类
6.3、编程式事务
//转账操作
public void transfer() {
try {
//1.开启事务
//2.进行业务操作
//加钱
userMapper.add();
//自定义异常
int i = 10 / 0;
//减钱
userMapper.reduce();
//3.未出现异常提交事务
} catch (Exception e) {
e.printStackTrace();
//4.出现异常回滚事务
}
}
6.4、声明式事务管理(注解方式)
- 在spring配置文件中配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
2. 在配置文件中引入名称空间tx
<beans
xmlns="http://www.springframework.org/schema/beans"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
3. 开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
4. 在业务层类上面或者方法上面加**@Transactional** 注解开启事务
//转账操作
@Transactional
public void transfer() {
//加钱
userMapper.add();
//自定义异常
int i = 10 / 0;
//减钱
userMapper.reduce();
}
6.5、@Transactional 注解参数详情
- propagation: 事务传播行为
- 多事务方法之间进行调用,这个过程中事务是如何进行管理的
- spring提供七种事务传播行为
- isolation: 事务隔离级别
- 事务隔离性,多事务操作之间不会产生影响,不考虑隔离性会产生很多问题
- 脏读
- 一个未提交事务读取到另一个未提交事务的数据
- 不可重复读
- 一个未提交事务读取到另一个已提交事务修改的数据
- 幻读
- 一个未提交事务读取到另一个提交事务添加的数据
- 脏读
- 事务隔离级别设置
- READ_UNCOMMITTED (读未提交) : 啥也未解决
- READ_COMMITTED (读已提交) :解决了脏读 (oracle)
- REPEATABLE_READ (可重读读): 解决了脏读,不可重复读 (mysql默认)
- SERIALIZABLE (串行化): 三种问题全解决
- 事务隔离性,多事务操作之间不会产生影响,不考虑隔离性会产生很多问题
- timeout: 超时时间
- 事务需要在一定时间内进行提交,不提交就进行回滚
- 默认值-1 (表示不超时), 设置时间以秒为单位
- readOnly: 是否只读
- 默认false,设置为true,只能对数据库做查询操作
- rollbackFor: 回滚
- 设置出现哪些异常进行回滚
- noRollbackFor: 不回滚
- 设置出现哪些异常不进行事务回滚
6.6、使用xml配置声明式事务
<!--1.创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.配置通知-->
<tx:advice id="advice">
<!--配置事务参数-->
<tx:attributes>
<!--指定那种规则的方法上面添加事务-->
<tx:method name="add" propagation="REQUIRED"/>
<!--<tx:method name="a*"/>-->
</tx:attributes>
</tx:advice>
<!--3.配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.wen.service.*.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="advice" pointcut-ref="pt"/>
</aop:config>
6.7、声明式事务(全注解)
@Configuration //标注为配置类
@ComponentScan(basePackages = {"com.wen"}) //开启包扫描
@EnableTransactionManagement //开启事务
public class SpringConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/my_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//到ioc容器中根据类型找到dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
6.8、七种事务传播行为
- REQUIRED
- 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,是Spring默认的事务传播行为
- REQUIRES_NEW
- 创建新事务,无论当前存不存在事务,都创建新事务
- SUPPORTS
- 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行
- MANDATORY
- 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常
- NOT_SUPPORTED
- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER
- 以非事务方式执行,如果当前存在事务,则抛出异常
- NESTED
- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行
七、Spring5框架新功能
7.1、Spring5概述
- Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除
7.2、Spring5 框架自带了通用的日志封装
- Spring5已经移除Log4jConfigListener,官方建议使用Log4j2 (要使用Log4j需要降低版本)
- Spring5框架整合Log4j2
- 依赖
- log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以优先级排序: OFF>FATAL>ERROR>WARN>INFO>DEBUG>TRACE>ALL-->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<Configuration status="DEBUG">
<!--先定义所有的appender-->
<Appenders>
<!--输出日志到控制台-->
<Console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<!--然后定义logger,只有定义了logger并引入了appender,appender才会生效-->
<!--root: 用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<Loggers>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
private static final Logger log = LoggerFactory.getLogger(UserService.class); //使用
7.3、Spring5框架核心容器支持@Nullable注解
- @Nullable : 表示可为空
- 用在方法上:表示回参可为空
- 用在属性上:表示属性可为空
- 用在方法参数上:表示参数可为空
7.4、Spring5核心容器支持函数式风格创建bean
- 函数式风格创建对象,交给spring进行管理,GenericApplicationContext类
//创建GenericApplicationContext对象
GenericApplicationContext context = new GenericApplicationContext();
//调用context方法进行注册
context.refresh();
// user名称可指定可不指定
context.registerBean("user",User.class,()->new User());
//获取注册的对象
//不指定注册的对象名称
Object bean = context.getBean("com.wen.entity.User");
//指定对象名称
Object user = context.getBean("user");
7.5、Spring5支持整合Junit5
- 整合Junit4
@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
@ContextConfiguration(classes = SpringConfig.class) //加载配置类
//@ContextConfiguration(locations = {"classpath*:/*.xml"}) //加载配置文件
//@ContextConfiguration(locations = {"classpath*:spring-config.xml"})
public class SpringTest {
@Autowired
private UserService userService;
- 整合junit5
@SpringJUnitConfig(classes = SpringConfig.class)
public class SpringTest {
@Autowired
private UserService userService;
7.6、Spring5新功能 Webflux (响应式编程)
-
Webflux介绍
- Webflux是Spring5新添加的模块,用于web开发,功能和SpringMVC类似,Webflux是当前比较流行的响应式编程
- 使用传统web框架,比如SpringMVC,这些基于Servlet容器,webflux是一种异步非阻塞的框架,异步非阻塞的框架在servlet3.1以后才支持,核心是基于Reactor的相关API实现的
-
异步非阻塞
- 同步和异步 (针对调用者)
- 调用者发送请求,如果等着对方回应之后才继续做其他事情.这就是同步
- 调用者发送请求,不等着对方回应就继续做其他事情,这就是异步
- 阻塞和非阻塞(针对被调用者)
- 被调用者收到请求后,做完请求的任务才给出反馈…这就是阻塞
- 被调用者收到请求后,马山给出反馈,然后再去做请求的任务…这就是非阻塞
- 同步和异步 (针对调用者)
-
Webflux特点:
- 非阻塞式: 在有限资源下,提高系统吞吐量和伸缩性(就是能完成更多的请求),以Reactor为基础实现响应式编程
- 函数式编程: Spring5框架基于java8,Webflux使用Java8函数式编程方式实现路由请求
-
和SpringMVC对比:
- 两个框架都可以使用注解方式开发,都运行在Tomcat等容器中
- SpringMVC才用命令式编程(一行一行执行), Webflux采用异步响应式编程
-
响应式编程
- 响应式编程是一种面向数据流和变化传播的编程范式,这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播
- 例: excel表中,某栏是"=b1+c1" ,这个单元格的值会随其他单元格的值变化而变化
7.7、观察者模式
- 响应式编程底层就是观察者模式
- 观察者模式: 就是对在意的数据观察,如果发生变化,就发出通知
- Java8及其之前实现:
- 提供了两个类: Observer 和 Observable
- Java9之后实现
- Flow类
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer = new ObserverDemo();
//添加观察者
observer.addObserver((o, arg) -> {
System.out.println("观察数据发生变法,发送通知");
});
observer.addObserver((o, arg) -> {
System.out.println("接收到通知,改变..");
});
//将此Observable对象标记为已更改; hasChanged方法现在将返回true
// 意思就是数据改变
observer.setChanged();
//发送通知
observer.notifyObservers();
}
}
7.8、响应式编程( Reactor实现)
-
Reactor 是满足 Reactive规范的框架
-
Reactor两个核心类 , 这两个类实现接口Publisher
- Mono
- 实现发布者,返回0或1个元素
- Flux
- 返回N个元素
- Mono
-
Flux和Mono都是数据流的发布者, 可以发送三种数据信号
- 元素值
- 错误信号 (终止信号)
- 完成信号 (终止信号)
- 终止信号告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者
-
三种信号特点
- 错误信号和完成信号都是终止信号,不能共存
- 如果没有发送任何元素值,而是直接发送错误或者完成信号表示是空数据流
- 如果没有错误信号,没有完成信号,表示无限数据流
-
代码演示Flux和Mono 需要引入依赖
-
reactor-core
//just方法直接声明 Flux.just(1, 2, 3); Mono.just(1); //其他方法 Integer[] arr = {1, 2, 3}; Flux.fromArray(arr); List<Integer> list = Arrays.asList(arr); Flux.fromIterable(list); Stream<Integer> stream = list.stream(); Flux.fromStream(stream);
-
-
调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生
//just方法直接声明 //subscribe 订阅 Flux.just(1, 2, 3).subscribe(System.out::println); Mono.just(1).subscribe(System.out::println);
-
操作符
- 对数据流进行操作
- map 元素映射为新元素
- flatMap 元素映射为流
- 对数据流进行操作
7.9、SpringWebflux执行流程和核心API
- SpringWebflux基于Reactor,默认使用容器是Netty,Netty是高性能的NIO框架,异步非阻塞框架
- BIO 阻塞
- NIO 非阻塞
- SpringWebflux执行过程和SpringMVC相似
- SpringWebflux核心控制器 DispatchHandler,实现接口WebHandler
- DispatchHandler : 负责请求的处理
- HandlerMapper: 请求查询到处理的方法
- HandlerAdapter: 真正负责请求处理
- HandlerResultHandler: 响应结果处理
- SpringWebflux 实现函数式编程,两个接口 : RouteFunction (路由处理) 和 HandlerFunction(处理函数)
8.0、SpringWebFlux 基本实现(基于注解)
- 依赖 springboot版本2.0以上
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
- entity 实体类
@Data
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
- **service **
public interface UserService {
//根据id查询用户
Mono<User> getUserById(Integer id);
//查询所有用户
Flux<User> getAll();
//添加用户
Mono<Void> addUser(Mono<User> userMono);
}
@Service
public class UserServiceImpl implements UserService{
//模拟数据库数据
private final Map<Integer, User> map = new HashMap<>();
public UserServiceImpl() {
this.map.put(1, new User("张三", 20));
this.map.put(2, new User("李四", 20));
this.map.put(3, new User("王五", 20));
}
@Override
public Mono<User> getUserById(Integer id) {
return Mono.justOrEmpty(this.map.get(id));
}
@Override
public Flux<User> getAll() {
return Flux.fromIterable(this.map.values());
}
@Override
public Mono<Void> addUser(Mono<User> userMono) {
return userMono.doOnNext(user -> {
int i = map.size() + 1;
map.put(i, user);
}).thenEmpty(Mono.empty());
}
}
- controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public Mono<User> getUserById(@PathVariable Integer id){
return userService.getUserById(id);
}
@GetMapping("/user")
public Flux<User> getAll(){
return userService.getAll();
}
@PostMapping("/add")
public Mono<Void> add(@RequestBody User user){
Mono<User> userMono = Mono.just(user);
return userService.addUser(userMono);
}
}
8.1、SpringWebFlux 函数式编程实现
-
说明
- SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
- SpringWebFlux方式实现,异步非阻塞,基于SpringWebFlux+Reactor+Netty
-
SpringWebFlux (基于函数式编程模型)
- 使用函数式编程模型操作时候,需要自己初始化服务器
- 基于函数式编程模型时候,有两个核心接口: RouterFunction (实现路由功能,请求转发个对应的handler) 和 HandlerFunction(处理请求生成响应的函数),核心任务定义两个函数式接口的实现并且启动需要的服务器
- SpringWebFlux请求和响应不在是ServletRequest和ServletResponse ,而是 ServerRequest和ServerResponse
-
SpringWebFlux实现步骤
- 1、注解模式的service和mapper保存,删除controller
- 2、创建Handler (具体实现方法)
public class UserHandler { private UserService userService; //利用有参构造传输业务层 public UserHandler(UserService userService) { this.userService = userService; } //根据id查询 public Mono<ServerResponse> getUserById(ServerRequest request){ //获取id值 Integer id = Integer.valueOf(request.pathVariable("id")); // 空值处理 Mono<ServerResponse> notFound = ServerResponse.notFound().build(); //调用service方法得到数据 Mono<User> userMono = userService.getUserById(id); //把userMono进行转换返回 //使用Reactor操作符 flatMap return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(userMono, User.class) .switchIfEmpty(notFound); } //查询所有 public Mono<ServerResponse> getAll(ServerRequest request){ Flux<User> userFlux = userService.getAll(); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON) .body(userFlux, User.class); } //添加 public Mono<ServerResponse> add(ServerRequest request){ Mono<User> userMono = request.bodyToMono(User.class); return ServerResponse.ok().build(this.userService.addUser(userMono)); } }
- 初始化服务器,编写Router (路由)
public class Server { //1. 创建Router路由 public RouterFunction<ServerResponse> routerFunction(){ //创建hanler对象 UserService userService = new UserServiceImpl(); UserHandler userHandler = new UserHandler(userService); //设置路由 return RouterFunctions.route( GET("/user/{id}").and(accept(APPLICATION_JSON)),userHandler::getUserById) .andRoute(GET("/user").and(accept(APPLICATION_JSON)),userHandler::getAll); } //2. 创建服务器完成适配 public void createReactorServer(){ //路由和handler适配 RouterFunction<ServerResponse> route = routerFunction(); HttpHandler httpHandler = toHttpHandler(route); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); //创建服务器 HttpServer httpServer = HttpServer.create(); httpServer.handle(adapter).bindNow(); } //最终调用 public static void main(String[] args) throws IOException { Server server = new Server(); server.createReactorServer(); System.out.println("enter to exit"); System.in.read(); } }
- 使用WebClient调用测试 端口号是启动最终调用后获取
public class Client { public static void main(String[] args) { //调用服务器地址 端口号是启动最终调用后获取 WebClient webClient = WebClient.create("http://127.0.0.1:54152"); //根据id查询 String id = "1"; User user = webClient.get().uri("/user/{id}", id) .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class) .block(); System.out.println(user); } }