概述

  • Spring是轻量级的开源的JavaEE框架,可以解决企业应用开发的复杂性
  • Spring有两个核心部分:IOC和AOP
    • IOC:控制反转,把创建对象过程交给Spring管理
    • AOP:面向切面,不修改源代码进行功能增强
  • 特点
    • 方便解耦,简化开发
    • AOP编程支持
    • 方便程序测试
    • 方便和其他框架进行整合
    • 方便进行事务操作
    • 降低API开发

简单示例
首先需要引入这些依赖
image.png

  • 新建一个类MyUser

    1. public class MyUser {
    2. public void add(){
    3. System.out.println("1111111111111");
    4. }
    5. }
  • 写一个bean文件bean1.xml

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    5. <!--配置user对象-->
    6. <bean id="user" class="com.example.MyUser"/>
    7. </beans>
  • Test一下该类,新建TestUser

    1. public class TestUser {
    2. public static void main(String[] args) {
    3. //加载spring配置文件
    4. ApplicationContext context = new ClassPathXmlApplicationContext("file:E:\\_LB\\spring-5-demo\\src\\main\\java\\bean1.xml");
    5. //获取创建的对象
    6. MyUser user = context.getBean("user", MyUser.class);
    7. user.add();
    8. System.out.println(user);
    9. }
    10. }

    文件目录如下
    image.png
    程序打印结果
    image.png

IOC容器

IOC,控制反转,把对象创建和对象之间的调用过程交给Spring进行管理
使用IOC的目的,是为了耦合度降低

底层原理:xml解析、工厂模式、反射

  • IOC的过程

image.png

  • IOC接口

IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
Spring提供IOC容器实现的两种方式(两个接口):

  • BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,不提供开发人员进行使用。在加载配置文件的时候不会创建对象,在获取对象(使用)才去创建对象
  • ApplicationContext:是BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。在加载配置文件的时候就会把对象进行创建

ApplicationContext接口有两个实现类
image.png

IOC操作Bean管理(基于xml方式)

  • 什么是Bean管理

Bean管理指的是两个操作:Spring创建对象、Spring注入属性

  • Bean管理操作有两种方式

    • 基于xml配置文件方式实现
    • 基于注解方式实现

      xml基本使用

  • 基于xml方式创建对象

在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

  1. <!--配置user对象-->
  2. <bean id="user" class="com.example.MyUser"/>

常用属性

  • id:唯一标识
  • class:类的全路径
  • name:与id类似,区别是id中不能有特殊符号,而name中可以有特殊符号(/等)

创建对象的时候,默认也是执行无参构造方法

  • 基于xml方式注入属性

DI:依赖注入,就是注入属性
有这么几种注入方式:
1、set方法注入

  1. <!--配置user对象-->
  2. <bean id="user" class="com.example.MyUser">
  3. <property name="userName" value="Jack"></property>
  4. <property name="userDesc" value="a man"></property>
  5. </bean>
  1. public class MyUser {
  2. private String userName;
  3. private String userDesc;
  4. public void setUserName(String userName) {
  5. this.userName = userName;
  6. }
  7. public void setUserDesc(String userDesc) {
  8. this.userDesc = userDesc;
  9. }
  10. public void test(){
  11. System.out.println("1111111111111");
  12. System.out.println(userName+"--"+userDesc);
  13. }
  14. }

测试代码

  1. public static void main(String[] args) {
  2. //加载spring配置文件
  3. ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
  4. //获取创建的对象
  5. MyUser user = context.getBean("user", MyUser.class);
  6. user.test();
  7. }

2、有参构造注入

  1. <!--配置user对象-->
  2. <bean id="anotherUser" class="com.example.AnotherUser">
  3. <constructor-arg name="userName" value="Rose"></constructor-arg>
  4. <constructor-arg name="userDesc" value="a woman"></constructor-arg>
  5. </bean>
  1. public class AnotherUser {
  2. private String userName;
  3. private String userDesc;
  4. public AnotherUser(String userName, String userDesc){
  5. this.userName = userName;
  6. this.userDesc = userDesc;
  7. }
  8. public void test(){
  9. System.out.println(userName+"--"+userDesc);
  10. }
  11. }

测试一下

  1. ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
  2. AnotherUser anotherUser = context.getBean("anotherUser", AnotherUser.class);
  3. anotherUser.test();

3、还有一种p名称空间注入,这个用的不多。使用p名称空间注入,简化基于xml配置方式
先要添加p名称空间在配置文件中
image.png
再进行属性注入,在bean标签里面进行操作

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="user" class="com.example.MyUser" p:userName="jack" p:userDesc="boy">
  7. </bean>
  8. </beans>

xml注入其他类型属性

字面量

  • null值

    1. <bean id="user" class="com.example.MyUser">
    2. <property name="userName" value="Jack"></property>
    3. <property name="userDesc">
    4. <null></null>
    5. </property>
    6. </bean>
  • 属性值包含特殊符号

例如属性值含有特殊符号<``>,可以进行转义&lt;``&gt;,也可以把带特殊符号内容写到CDATA

  1. <bean id="user" class="com.example.MyUser">
  2. <property name="userName" value="Jack"></property>
  3. <property name="userDesc">
  4. <value><![CDATA[<<boy>>]]></value>
  5. </property>
  6. </bean>

image.png

外部bean

例如,在service中引入Dao实现类
image.png

内部bean和级联赋值

  • 一对多关系,例如一个部门有多个员工

    image.png

  • 级联赋值

级联赋值有这两种写法
image.png
还有另一种
image.png
image.png

集合属性

  • 数组类型属性

    1. <bean id="stu" class="com.example.Stu">
    2. <!-- 数组类型属性注入 -->
    3. <property name="courses">
    4. <array>
    5. <value>java</value>
    6. <value>mysql</value>
    7. </array>
    8. </property>
    9. </bean>
  • List集合

简单的list

  1. <bean id="stu" class="com.example.Stu">
  2. <!-- list类型属性注入 -->
  3. <property name="courses">
  4. <list>
  5. <value>java</value>
  6. <value>mysql</value>
  7. </list>
  8. </property>
  9. </bean>

复杂的list,list中属性是对象

  1. <bean id="stu" class="com.example.Stu">
  2. <!-- list类型属性注入 -->
  3. <property name="courses">
  4. <list>
  5. <ref bean="course1">java</ref>
  6. <ref bean="course2">java</ref>
  7. </list>
  8. </property>
  9. </bean>
  10. <!-- 创建多个course对象 -->
  11. <bean id="course1" class="com.example.Course">
  12. <property name="cname" value="java"></property>
  13. </bean>
  14. <bean id="course2" class="com.example.Course">
  15. <property name="cname" value="mysql"></property>
  16. </bean>

还可以将list集合抽取出来使用,这里就需要引入名称空间util

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:util="http://www.springframework.org/schema/util"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
  7. <!-- 提取list集合类型属性注入 -->
  8. <util:list id="bookList">
  9. <value>java</value>
  10. <value>mysql</value>
  11. <value>redis</value>
  12. </util:list>
  13. <!-- list集合类型属性注入使用 -->
  14. <bean id="book" class="com.example.Book">
  15. <property name="list" ref="bookList"></property>
  16. </bean>
  17. </beans>
  • Map集合

    1. <bean id="stu" class="com.example.Stu">
    2. <!-- map类型属性注入 -->
    3. <property name="courses">
    4. <map>
    5. <entry key="java" value="Java"></entry>
    6. <entry key="mysql" value="MySQL"></entry>
    7. </map>
    8. </property>
    9. </bean>
  • Set集合

    1. <bean id="stu" class="com.example.Stu">
    2. <!-- set类型属性注入 -->
    3. <property name="courses">
    4. <set>
    5. <value>java</value>
    6. <value>mysql</value>
    7. </set>
    8. </property>
    9. </bean>

IOC操作Bean管理(FactoryBean)

Spring有两种类型Bean,一种普通Bean,另一种工厂Bean(FactoryBean)。

  • 普通Bean:在配置文件中定义的Bean类型就是返回类型
  • 工厂Bean:在配置文件定义的Bean类型可以和返回类型不一样

创建工厂Bean的步骤:

  • 创建类,让这个类作为工厂Bean,实现接口FactoryBean
  • 实现接口里面的方法,在实现的方法中定义返回的Bean类型

代码示例

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="myBean" class="com.example.MyBean"></bean>
  6. </beans>

MyBean实现Factory接口,这里使用泛型,重写getObject方法

  1. public class MyBean implements FactoryBean<MyUser> {
  2. @Override
  3. public MyUser getObject() {
  4. MyUser myUser = new MyUser();
  5. myUser.setUserName("111");
  6. return myUser;
  7. }
  8. @Override
  9. public Class<?> getObjectType() {
  10. return null;
  11. }
  12. @Override
  13. public boolean isSingleton() {
  14. return false;
  15. }
  16. }

接的时候,拿MyUser类型来接

  1. ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
  2. MyUser myBean = context.getBean("myBean", MyUser.class);
  3. System.out.println(myBean);

运行,拿到的就是Myuser
image.png

Bean作用域

在Spring里,设置创建Bean实例是单实例还是多实例,默认情况下Bean是单实例对象

  • 在Spring配置文件Bean标签里面有属性(scope)用于设置单实例还是多实例
    • 默认值singleton,表示单例。加载Spring配置文件的时候就会创建单实例对象
    • prototype,多实例对象。不在加载Spring配置文件时创建对象,而是在调用getBean方法时创建多实例对象
    • 此外scope还有属性值requestsession,用于Web请求

Bean生命周期

指从对象创建到对象销毁的过程

  • 通过构造器创建Bean实例(无构造参数)
  • 为Bean的属性设置值和对其他Bean引用(调用set方法)
  • 调用Bean的初始化方法(需要进行配置)

Bean标签中配置init-method属性,属性值为初始化的方法名

  • Bean可以进行使用,被对象获取
  • 当容器关闭的时候,调用Bean的销毁方法(需要进行配置销毁的方法)

Bean标签中配置destroy-method属性,属性值为销毁的方法名,并且在需要销毁时调用close方法

xml自动装配

根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
通过Bean标签属性autowire,配置自动装配

  • autowire属性常用两个值
    • byName:根据属性名称注入,注入Bean的id值要和类属性名称一样

image.png

  • byType:根据属性类型注入

image.png
名称可以随便取,但是Bean文件中只能有一个Dept类

外部属性文件(Druid连接池)

这里举一个引入Druid数据库连接池的例子
先配置连接池文件jdbc.properties

  1. prop.driverClassName=com.mysql.jdbc.Driver
  2. prop.url=jdbc:mysql://localhost:3306/example
  3. prop.username=root
  4. prop.password=root

然后修改xml文件的命名空间

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--配置连接池-->
  8. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  9. <property name="driverClassName" value="${prop.driverClassName}"/>
  10. <property name="url" value="${prop.url}"/>
  11. <property name="username" value="${prop.username}"/>
  12. <property name="password" value="${prop.password}"/>
  13. </bean>
  14. <!-- 引入外部属性文价-->
  15. <context:property-placeholder location="jdbc.properties"/>
  16. </beans>

IOC操作Bean管理(基于注解方式)

注解是代码里的特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值……)
注解可以作用在类、方法、属性上面
使用注解目的:简化xml配置

Spring针对Bean管理中创建对象提供了下面的注解:

  • @Component
  • @Service
  • @Controller
  • @Repository

上面四个注解功能都是一样的,都可以用来创建Bean实例

基本使用

  • 首先要引入依赖,除了上面用到的三个依赖,还需要增加spring-aop

image.png

  • 开启组件扫描,如果扫描多个包可以用逗号隔开

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    7. <context:component-scan base-package="com.example"/>
    8. </beans>

    这里插一嘴,还有其他用法,可以添加过滤器,下面的写法表示只扫描com.example包下的@Controller注解

    1. <context:component-scan base-package="com.example" use-default-filters="false">
    2. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    3. </context:component-scan>

    还可以设置不去扫描哪些注解,下面的写法表示扫描com.example包中除了@Controller之外的其他注解

    1. <context:component-scan base-package="com.example">
    2. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    3. </context:component-scan>
  • 创建类,在类上面添加创建对象注解

注解里面的value属性值可以省略不写,默认值是类名称,首字母小写

注入属性

  • @AutoWired:根据属性类型进行自动装配
  • @Qualifier:根据属性名称进行注入,需要和@AutoWired一起使用,这里的属性名要在其他接口先声明

    1. @AutoWired
    2. @Qualifier(value = "userDaoImpl")
    3. private UserDao userDao;
  • @Resource:可以根据类型注入;也可以根据名称注入@Resource(name="")

但是这里注意了,
image.png
注意这个包名,@Resource是javax下的属于Java的扩展包,@AutoWired@Qualifier都是Spring官方的,因此Spring推荐使用前两个注解

  • @Value:注入普通类型属性
    1. @Value(value = "jack")
    2. private String name;
    这里的name属性就已经通过注解注入了属性值“jack”

完全注解开发

完全注解开发就舍弃了xml文件,不需要在xml文件中配置Bean相关内容,这就需要用配置类来替代,这里配置类是SpringConfig.java

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.example"})
  3. public class SpringConfig {}

相应的调用也有变化

  1. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
  2. UserService userService = context.getBean("userService", UserService.class);
  3. userService.test();

但是一般这种开发都会使用SpringBoot

AOP

AOP(Aspect Oriented Programming)面向切面编程,是OOP的延续。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率

AOP底层使用动态代理:
有接口的情况,使用JDK动态代理,创建接口实现类的代理对象;没有接口的情况,使用CGLIB动态代理,创建当前类子类的代理对象增强类的方法

JDK动态代理

JDK动态代理,使用Proxy(java.lang.reflect.Proxy)类里面的方法创建代理对象,调用newProxyInstance _(_ClassLoader loader, Class_<_?_>[] _interfaces, InvocationHandler h_)_方法。该方法有三个参数,分别是类加载器、增强方法所在的接口(支持多个接口)、增强的方法所在的接口。
对于第三个参数,要实现InvocationHandler接口,创建代理对象,写增强的方法

下面有个简单的示例
有个接口UserDao.java

  1. public interface UserDao {
  2. Integer add(Integer a, Integer b);
  3. String update(String id);
  4. }
  1. 接口的实现类 `UserDaoImpl.java`
  1. public class UserDaoImpl implements UserDao {
  2. public Integer add(Integer a, Integer b) {
  3. return a+b;
  4. }
  5. public String update(String id) {
  6. return id;
  7. }
  8. }
  1. 代理对象 `UserInvocationHandler.java`
  1. public class UserInvocationHandler implements InvocationHandler {
  2. public Object object;
  3. public UserInvocationHandler(Object object){
  4. this.object = object;
  5. }
  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  7. System.out.println("方法执行前");
  8. System.out.println(method.getName());
  9. System.out.println(Arrays.toString(args));
  10. Object res = method.invoke(object, args);
  11. System.out.println("方法执行后");
  12. System.out.println(object);
  13. return res;
  14. }
  15. }
  1. 调用该代理
  1. public class JDKProxy {
  2. public static void main(String[] args) {
  3. Class[] interfaces = {UserDao.class};
  4. // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
  5. // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  6. // return null;
  7. // }
  8. // });
  9. UserDao instance = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserInvocationHandler(new UserDaoImpl()));
  10. Integer add = instance.add(1, 2);
  11. System.out.println(add);
  12. }
  13. }
  1. 调用代理的时候有两种方法,一种是直接写个内部类,另一种是把代理拿出来新写一个类,这里是后者<br />打印结果<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2961643/1635996953143-fff756ac-dab7-45cf-89e4-f7cfeb3cd31e.png#clientId=ue65d068a-1f32-4&from=paste&height=165&id=ud73a64e1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=165&originWidth=355&originalType=binary&ratio=1&size=6797&status=done&style=none&taskId=ua99445a7-9f72-4d85-bc12-db3d418d93e&width=355)

相关术语

  • 连接点:指类里面可以被增强的方法。上述例子中的连接点为UserDaoImpladd()update()方法
  • 切入点:实际真正被增强的方法。UserDaoImpladd()方法
  • 通知(增强):实际增强的逻辑部分

通知有以下几种类型:

  • 前置通知:在切入点之前
  • 后置通知:在切入点之后
  • 环绕通知:在切入点前后都有
  • 异常通知:切入点出现异常时通知
  • 最终通知:无论如何都会通知,类似于try、catch、finallyfinally
    • 切面:把通知应用到切入点的过程

AOP操作

Spring框架一般基于AspectJ实现AOP操作。AspectJ不是Spring组成部分,它独立于AOP框架,一般把AspectJ和Spring框架一起使用进行AOP操作
有两种基于AspectJ实现AOP的操作,基于xml配置文件实现、基于注解方式实现,一般都是使用后者

先引入相关依赖
image.png

再写切入表达式
切入表达式表明对哪个类里面的哪个方法进行增强,结构:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])。例如对com.example.UserDaoadd()方法进行增强,则切入表达式为execution(* com.example.UserDao.add(..)),其中*表示任意权限修饰符都行

创建被增强的类UserDao.java和增强类UserProxy.java,并在类上添加注解

  1. @Component
  2. public class UserDao {
  3. public void add(){
  4. System.out.println("run add");
  5. }
  6. }
  1. @Component
  2. @Aspect
  3. public class UserProxy {
  4. public void before(){
  5. System.out.println("before");
  6. }
  7. }
  1. xml文件( `bean1.xml`)中配置开启注解扫描、开启AspectJ生成代理对象
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  8. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <!--开启注解扫描-->
  10. <context:component-scan base-package="com.example"/>
  11. <!--开启AspectJ生成代理对象-->
  12. <aop:aspectj-autoproxy/>
  13. </beans>
  1. 配置不同类型的通知,这里是前置通知
  1. @Component
  2. @Aspect
  3. public class UserProxy {
  4. //@Before表示前置通知
  5. @Before(value = "execution(* com.example.UserDao.add(..))")
  6. public void before(){
  7. System.out.println("before");
  8. }
  9. }
  1. 测试一下
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
  2. UserDao userDao = context.getBean("userDao", UserDao.class);
  3. userDao.add();
  1. 结果如下<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2961643/1636016741153-8f21914b-33d0-4c3b-913d-3d0ec29ac286.png#clientId=u593b8ab2-9222-4&from=paste&height=63&id=u5dae071f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=63&originWidth=181&originalType=binary&ratio=1&size=1267&status=done&style=none&taskId=u4dc4e525-c608-4447-9cb7-d914a1a104b&width=181)<br />增强的注解有`@Before`、`@After`、`@AfterReturning`、`@AfterThrowing`、`@Around`,`@After`无论如何最后都会通知,`@AfterReturning`是有返回结果后才通知<br />环绕通知`@Around`注解的使用方法不太一样,这里演示一下
  1. @Around(value = "execution(* com.example.UserDao.add(..))")
  2. public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
  3. System.out.println("before");
  4. proceedingJoinPoint.proceed();
  5. System.out.println("after");
  6. }

image.png
此外,还可以把切入点抽取出来

  1. @Pointcut(value = "execution(* com.example.UserDao.add(..))")
  2. public void demoTest(){}
  3. @Before(value = "demoTest()")
  4. public void before(){
  5. System.out.println("before");
  6. }
  1. <br />有多个类对同一个方法进行增强时,可以设置增强类的优先级。在增强类上面添加注解`@Order(数值)`,数值越小优先级越高

除了使用注解的方式,还可以使用xml配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  8. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <bean id="userDao" class="com.example.UserDao"/>
  10. <bean id="userProxy" class="com.example.UserProxy"/>
  11. <aop:config>
  12. <aop:pointcut id="p" expression="execution(* com.example.UserDao.add(..))"/>
  13. <aop:aspect ref="userProxy">
  14. <aop:before method="before" pointcut-ref="p"/>
  15. </aop:aspect>
  16. </aop:config>
  17. </beans>

当然,也可以不写任何配置文件,完全使用注解开发,这就需要写配置类

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.example"})
  3. @EnableAspectJAutoProxy(proxyTargetClass = true)
  4. public class ProxyConfig {}

JdbcTemplate

Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库的操作

首先要引入依赖,在前面的基础之上在加几个依赖
image.png
配置好数据库连接池Druid,详细步骤见IOC容器

配置JdbcTemplate对象,注入DataSource。同时,在配置文件开启注解扫描

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--配置连接池-->
  8. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  9. <property name="driverClassName" value="${prop.driverClassName}"/>
  10. <property name="url" value="${prop.url}"/>
  11. <property name="username" value="${prop.username}"/>
  12. <property name="password" value="${prop.password}"/>
  13. </bean>
  14. <!-- 引入外部属性文价-->
  15. <context:property-placeholder location="jdbc.properties"/>
  16. <!--JdbcTemplate对象-->
  17. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  18. <!--注入dataSource-->
  19. <property name="dataSource" ref="dataSource"/>
  20. </bean>
  21. <context:component-scan base-package="com.example"/>
  22. </beans>
  1. UserDao里面简单调用一下方法
  1. @Repository
  2. public class UserDao {
  3. @Autowired
  4. private JdbcTemplate jdbcTemplate;
  5. public void addUser(People people){
  6. int update = jdbcTemplate.update("insert into people values(?,?,?,?)", people.getId(), people.getName(), people.getAddress(), people.getAge());
  7. System.out.println(update);
  8. }
  9. }

事务

事务是数据库操作最基本单元,逻辑上一组操作要么都成功,要么都失败

事务四个特性(ACID):

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

在Spring进行事务管理操作有两种方式:编程式事务管理(类似上面的示例)和声明式事务管理,一般使用后者

编程式事务管理

基本做法:

  1. try {
  2. //开启事务
  3. //进行业务操作
  4. //没有发生异常,提交事务
  5. }catch (Exception e){
  6. //出现异常,事务回滚
  7. }

声明式事务管理

声明式事务管理有基于注解的方式和基于xml配置文件的方式。在Spring进行声明式事务管理,底层使用AOP
Spring提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
image.png
JDBC和MyBatis框架使用DataSourceTransactionManager,Hibernate框架使用HibernateTransactionManager

基于注解方式

先配置,开启事务注解的时候要引入tx空间

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  8. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
  9. <!--配置连接池-->
  10. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  11. <property name="driverClassName" value="${prop.driverClassName}"/>
  12. <property name="url" value="${prop.url}"/>
  13. <property name="username" value="${prop.username}"/>
  14. <property name="password" value="${prop.password}"/>
  15. </bean>
  16. <!-- 引入外部属性文价-->
  17. <context:property-placeholder location="jdbc.properties"/>
  18. <!--JdbcTemplate对象-->
  19. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  20. <!--注入dataSource-->
  21. <property name="dataSource" ref="dataSource"/>
  22. </bean>
  23. <context:component-scan base-package="com.example"/>
  24. <!--创建事务管理器-->
  25. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  26. <!--注入dataSource-->
  27. <property name="dataSource" ref="dataSource"/>
  28. </bean>
  29. <!--开启事务注解-->
  30. <tx:annotation-driven transaction-manager="transactionManager"/>
  31. </beans>
  1. 在需要开启事务的 **类**或**方法**上开启注解`@Transactional`,该注解里面有很多参数可以配置(也可以不配置)

主要有这些参数:

  • propagation:事务传播行为
    • 多事务方法(对数据库的增删改查)直接进行调用,这个过程中事务是如何进行管理的
    • Spring框架事务传播行为有7种,默认是REQUIRED
    • image.png
  • ioslation:事务隔离级别

    • 事务有隔离性,不考虑隔离性会产生脏读、不可重复读、虚(幻)读
      • 脏读:一个未提交事务读取到另一个未提交事务的数据
      • 不可重复读:一个未提交事务读取到另一提交事务的修改数据
      • 幻读:一个未提交事务读取到另一提交事务的添加数据 | | 脏读 | 不可重复读 | 幻读 | | —- | —- | —- | —- | | READ_UNCOMMITTED
        (读未提交) | 有 | 有 | 有 | | READ_COMMITTED
        (读已提交) | 无 | 有 | 有 | | REPEATABLE_READ
        (可重复读) | 无 | 无 | 有 | | SERIALIZABLE
        (串行化) | 无 | 无 | 无 |
  • timeout:超时时间

    • 事务需要在一定时间内进行提交,如果不提交就进行回滚
    • 默认值是 -1,表示永不超时,设置时间以秒为单位
  • readOnly:是否只读
    • 读:查询操作;写:添加修改删除操作
    • 默认值false,表示增删改查均可;设置为true则只能查询
  • rollbackFor:回滚
    • 设置出现哪些异常进行事务回滚
    • 默认值是UncheckedException,包括了RuntimeExceptionError,不指定值时Exception及其子类都不会触发回滚
  • noRollBackFor:不回滚
    • 设置出现哪些异常不进行事务回滚

基于xml方式

配置事务管理器、配置通知、配置切入点和切面

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  8. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <!--配置连接池-->
  10. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  11. <property name="driverClassName" value="${prop.driverClassName}"/>
  12. <property name="url" value="${prop.url}"/>
  13. <property name="username" value="${prop.username}"/>
  14. <property name="password" value="${prop.password}"/>
  15. </bean>
  16. <!-- 引入外部属性文价-->
  17. <context:property-placeholder location="jdbc.properties"/>
  18. <!--JdbcTemplate对象-->
  19. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  20. <!--注入dataSource-->
  21. <property name="dataSource" ref="dataSource"/>
  22. </bean>
  23. <!--创建事务管理器-->
  24. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  25. <!--注入dataSource-->
  26. <property name="dataSource" ref="dataSource"/>
  27. </bean>
  28. <!--配置通知-->
  29. <tx:advice id="advice">
  30. <tx:attributes>
  31. <tx:method name="test" propagation="REQUIRED"/>
  32. </tx:attributes>
  33. </tx:advice>
  34. <!--配置切入点和切面-->
  35. <aop:config>
  36. <!--配置切入点-->
  37. <aop:pointcut id="pt" expression="execution(* com.example.UserDao.*(..))"/>
  38. <!--配置切面-->
  39. <aop:advisor advice-ref="advice" pointcut-ref="pt"/>
  40. </aop:config>
  41. </beans>

完全注解开发

下面使用完全注解开发来进行声明式事务管理
创建配置类,使用配置类替代xml配置文件

  1. package com.example;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.ComponentScan;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.jdbc.core.JdbcTemplate;
  7. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  8. import org.springframework.transaction.annotation.EnableTransactionManagement;
  9. import javax.sql.DataSource;
  10. @Configuration
  11. @ComponentScan(basePackages = "com.example")
  12. @EnableTransactionManagement
  13. public class TransactionConfig {
  14. /**
  15. * 创建数据库连接池
  16. * @return 数据库连接池
  17. */
  18. @Bean
  19. public DruidDataSource getDataSource(){
  20. DruidDataSource druidDataSource = new DruidDataSource();
  21. druidDataSource.setDriverClassName("${prop.driverClassName}");
  22. druidDataSource.setUrl("${prop.url}");
  23. druidDataSource.setUsername("${prop.username}");
  24. druidDataSource.setPassword("${prop.password}");
  25. return druidDataSource;
  26. }
  27. /**
  28. * 创建JdbcTemplate对象
  29. * @param dataSource 数据源
  30. * @return JdbcTemplate对象
  31. */
  32. @Bean
  33. public JdbcTemplate getJdbcTemplate(DataSource dataSource){
  34. JdbcTemplate jdbcTemplate = new JdbcTemplate();
  35. jdbcTemplate.setDataSource(dataSource);
  36. return jdbcTemplate;
  37. }
  38. /**
  39. * 创建事务管理器
  40. * @param dataSource 数据源
  41. * @return 事务管理器
  42. */
  43. @Bean
  44. public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
  45. DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
  46. dataSourceTransactionManager.setDataSource(dataSource);
  47. return dataSourceTransactionManager;
  48. }
  49. }

Spring5新功能

整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除

整合Log4j2框架

Spring5框架自带了通用的日志封装,移除了Log4jConfigListener,官方建议使用Log4j2

引入jar包
image.png

创建log4j2.xml配置文件,文件名是固定的

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!--日志级别优先级:OFF->FATAL->ERROR->WARN->INFO->DEBUG->TRACE->ALL,约往右优先级越高-->
  3. <configuration status="INFO">
  4. <appenders>
  5. <console name="Console" target="SYSTEM_OUT">
  6. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
  7. </console>
  8. </appenders>
  9. <loggers>
  10. <root level="info">
  11. <appender-ref ref="Console"/>
  12. </root>
  13. </loggers>
  14. </configuration>

@Nullable注解

Spring5框架核心容器支持@Nullable注解
@Nullable可以用在方法上面,表示方法返回可以为空;可以用在属性上面,表示属性值可以为空;可以用在参数上面,表示参数值可以为空

函数式风格

Spring5核心容器支持函数式风格GenericApplicationContext
image.png

SpringWebFlux

Spring5添加的新模块,一个基于reactive的spring-webmvc,完全的异步非阻塞,旨在使用enent-loop执行模型和传统的线程池模型。
传统的Web框架,比如SpringMVC,基于Servlet容器。Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。

异步和同步:针对调用者。调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等对方回应就去做其他事情就是异步。 阻塞和非阻塞:针对被调用者。被调用者收到请求后,做完请求任务之后才给出反馈就是阻塞,马上给出反馈然后再去做事情就是非阻塞。

WebFlux特点:

  • 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现相应式编程
  • 函数式编程:Spring5框架基于Java8,WebFlux使用函数式编程方式实现路由请求

SpringMVC采用命令式编程,Webflux采用异步响应式编程

响应式编程:响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播 Java8及其之前版本,提供观察者模式两个类 ObserverObservable ,Java9及之后采用 Flow

Reactor实现响应式编程

响应式编程中,Reactor满足Reactive规范框架
Reactive有两个核心类 MonoFlux,这两个类实现接口Publisher,提供丰富的操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或1个元素
FluxMono都是数据流的发布者,使用FluxMono都可以发出三种数据信号:元素值、错误信号、完成信号,错误信号、完成信号代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流的同时把错误信息传递给订阅者

三种信号特点:

  • 错误信号和完成信号都是终止信号,但是不能共存
  • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
  • 如果没有错误信号和完成信号,表示是无限数据流
  1. 调用 `just`或其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生<br />下面代码演示了`Flux`对数组、List集合、stream流的数据流发布
  1. Flux<Integer> flux = Flux.just(1, 2, 3);
  2. flux.subscribe(System.out::println);
  3. Integer[] arr = {1,2,3,4,5};
  4. Flux.fromArray(arr).subscribe(System.out::println);
  5. Flux.fromIterable(Arrays.asList(arr)).subscribe(System.out::println);
  6. Flux.fromStream(Arrays.stream(arr)).subscribe(System.out::println);

SpringWebFlux基于注解编程模型

SpringWebFlux基于Reactor,默认使用的容器是Netty,Netty是高性能同步非阻塞NIO框架
SpringWebFlux核心控制器DispatchHandler,实现接口WebHandler
实现函数式编程,两个接口:RouterFunction(路由处理)、HandlerFunction(处理函数)

先引入Webflux依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-webflux</artifactId>
  4. </dependency>
  1. 示例
  1. public class TestService {
  2. private final Map<Integer, People> peopleMap = new HashMap<>();
  3. public TestService() {
  4. this.peopleMap.put(1, new People("Jack",18));
  5. this.peopleMap.put(2, new People("Rose",20));
  6. }
  7. public Mono<People> getPeopleById(Integer id) {
  8. return Mono.justOrEmpty(this.peopleMap.get(id));
  9. }
  10. public Flux<People> getPeopleList(){
  11. return Flux.fromIterable(this.peopleMap.values());
  12. }
  13. public Mono<Void> addPeople(Mono<People> peopleMono){
  14. return peopleMono.doOnNext(p -> this.peopleMap.put(this.peopleMap.size()+1, p)).thenEmpty(Mono.empty());
  15. }
  16. }
  1. <br />SpringMVC方式,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat;<br />SpringWebflux方式,异步非阻塞方式,基于SpringWebflux+Reactor+Netty