Spring4实战

Spring致力于全方位的简化Java开发,采取了4种策略:

  1. 基于POJO的轻量级和最小侵入性编程;
  2. 通过依赖注入和面向接口实现松耦合;
  3. 基于切面和惯例进行声明式编程;
  4. 通过切面和模板减少样板式代码。

01.Spring-Core

下载地址:https://repo.spring.io/ui/native/libs-release/org/springframework/spring

IoC(控制反转)

IOC

目的:降低耦合性。

控制反转是软件工程中的一项原则,它将对对象或程序部分的控制转移到容器或框架中。我们最常在面向对象编程的上下文中使用它。

与我们的自定义代码调用库的传统编程相比,IoC 使框架能够控制程序的流程并调用我们的自定义代码。为了实现这一点,框架使用内置附加行为的抽象。如果我们想添加自己的行为,我们需要扩展框架的类或插入我们自己的类。

我们可以通过各种机制来实现控制反转,例如:策略设计模式、服务定位器模式、工厂模式和依赖注入(DI)。

IOC操作Bean管理(FactoryBean)

Spring里的Bean分为普通Bean和工厂Bean(FactoryBean),普通Bean在配置文件中配置的类即是Bean实例类型;工厂Bean返回的类型可以和配置文件中定义的类型不同。

工厂Bean实现:

DI (依赖注入)

依赖注入是我们可以用来实现 IoC 的一种模式,其中被反转的控制是设置对象的依赖关系。

将对象与其他对象连接起来,或将对象“注入”到其他对象中,是由汇编程序完成的,而不是由对象本身完成的。

AOP (切面编程)

不改变源码的前提下,增强源码功能。实现手段有JDK动态代理cglib动态代理

优势

  • 方便解耦、简化开发
  • AOP的编程支持
  • 声明式事务的支持
  • 方便程序的测试
  • 方便继承各种优秀的框架
  • 降低Java EE API的使用难度
  • Java源码是经典学习范例

实现手段

接口的JDK动态代理

UserDao.java:接口类

1
2
3
4
5
public interface UserDao {
int add(int a, int b);

void update(String id);
}

UserDaoImpl.java:接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add()方法执行..");
return a + b;
}
@Override
public void update(String id) {
System.out.println("update()方法执行..");
System.out.println("id = " + id);
}
}

JDKProxy.java:动态代理增强例子

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class JDKProxy {

public static void main(String[] args) {
//要增强的接口类
Class[] interfaces = {UserDao.class};
//接口实现类实例
UserDaoImpl userDao = new UserDaoImpl();
//获取接口的增强实现类
UserDao dao = (UserDao)Proxy.newProxyInstance
(JDKProxy.class.getClassLoader()
, interfaces
, new UserDaoProxy(userDao));
//调用增强实现类后的add方法
int res = dao.add(1, 2);
//调用增强实现类后的update方法
dao.update("update....");
System.out.println("res = " + res);
}
}

//对接口实现类做一个动态代理增强。
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 {
//before
System.out.println("方法执行之前..." + method.getName() + "传递的参数: " + Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj, args);
//after
System.out.println("方法执行之后..." + obj);
return res;
}
}

OUTPUT:

1
2
3
4
5
6
7
8
方法执行之前...add传递的参数: [1, 2]
add()方法执行..
方法执行之后...io.ainexur.spring5.UserDaoImpl@79fc0f2f
方法执行之前...update传递的参数: [update....]
update()方法执行..
id = update....
方法执行之后...io.ainexur.spring5.UserDaoImpl@79fc0f2f
res = 3

无接口的cglib动态代理

术语

  1. 连接点:类里面哪些方法可以被增强,这些方法称为连接点
  2. 切入点:实际被真正增强的方法,称为切入点
  3. 通知(增强):
    • 实际增强的逻辑部分称为通知(增强)
    • 通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知
  4. 切面:是动作,把通知应用到切入点的过程称为切面。

AOP操作(准备)

Spring框架一般基于AspectJ实现AOP操作

1. 什么是AspectJ

不是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作

2. 基于AspectJ实现AOP操作

基于XML配置文件实现

*基于注解方式实现(使用)

3. 导包

4. 切入点表达式

作用:知道对哪个类里面的那个方法进行增强

语法结构:

1
execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])

举例1:对io.ainexur.dao.BookDao类里面的add进行增强

1
execution(* io.ainexur.dao.BookDao.add(..))

举例2:对io.ainexur.dao.BookDao类里面的所有方法进行增强

1
execution(* io.ainexur.dao.BookDao.*(..))

举例3:对io.ainexur.dao包里的所有类里面的所有方法进行增强

1
execution(* io.ainexur.dao.*.*(..))

5. AspectJ注解实现

创建被增强类User.class

1
2
3
4
5
6
7
8
9
10
import org.springframework.stereotype.Component;

//被增强的类
@Component
public class User {
public void add() {
System.out.println("add()........");
}
}

创建增强类UserProxy.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//增强类
@Component
@Aspect //生成代理对象
public class UserProxy {

//@before表示前置通知
@Before(value = "execution(* io.ainexur.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before().......");
}
}

进行通知文件的配置:

  • 在Spring配置文件中,开启注解扫描
  • 使用注解创建User和UserProxy对象:@Component
  • 在增强类上面添加注解:@Aspect
  • 在Spring配置文件中开启生成代理对象

6.AspectJ XML配置文件实现

JdbcTemplate

Spring对jdbc的封装, 使用JdbcTemplate方便实现对数据库操作。

准备工作

1.导包

image-20220314074220155

2. 在Spring配置文件中配置连接池

1
2
3
4
5
6
7
<!--    数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql:///user_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>

3.配置JdbcTemplate对象

1
2
3
4
5
 <!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入DataSource属性-->
<property name="dataSource" ref="dataSource"/>
</bean>

4. 创建Service类,创建Dao类,在Dao注入jdbcTemplate对象

组件扫描

1
2
<!--开启组件扫描-->
<context:component-scan base-package="io.ainexur.spring5"/>

Dao

1
2
3
4
5
6
@Repository
public class BookDao {
//注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}

Service

1
2
3
4
5
6
@Service
public class BookService {

@Autowired
private BookDao bookDao;
}

事务操作

事务添加到JavaEE三层结构里的Service层

在Spring进行事务管理操作:

  1. 有两种方式:编程式事务管理(不经常用)和声明式事务管理(使用)
  2. 声明式事务管理:
    • 基于注解方式(简单,常用)
    • 基于XML配置文件方式

在Spring进行声明式事务管理,底层使用AOP

Spring事务管理API:提供一个接口,代表事务管理,这个接口针对不同的框架提供不同的实现类。

image-20220316103821056

基于注解&XML的声明式事务管理

xml配置事务管理器

1
2
3
4
<!--    创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

xml开启事务管理

  • 引入tx命名空间

    1
    2
    <beans xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
  • 开启事务注解

    1
    2
    <!--    开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  • 在Service类上(或者Service类里面方法上)添加注解

    1
    2
    3
    4
    5
    @Service
    @Transactional
    public class BookService {
    //...
    }
    1. @Transactional该注解添加到类上,则类里所有方法都添加事务
    2. @Transactional该注解添加到方法上,则只为该方法添加事务

声明式事务管理参数配置(注解配置相关参数)

image-20220316233206706

propagation

事务传播行为

  1. 多事务方法直接进行调用,这个过程事务是如何进行管理的

    • 事务方法:对数据库表数据进行变化的操作(增删改是,查不是)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Transactional
    public void add() {
    update();
    }

    public void upadte() {

    }
    //通过add()调用upadte(),事务是怎么处理的,这个过程就叫传播行为
  2. Spring框架事务传播有七种传播行为

    image-20220317003631476

  3. 主要使用前两种,其他了解有个印象就行

配置举例:

1
@Transactional(propagation = Propagation.REQUIRED)

ioslation

事务隔离级别

  1. 事务有特性称为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题:

  2. 三个读问题

    • 脏读:一个未提交事务读取到另一个未提交事务的数据(问题)
    • 不可重复读:一个未提交事务读到一个已经提交事务的数据(现象)
    • 幻读(虚读):一个未提交事务读到另一个提交事务添加数据()
  3. 通过设置事务隔离性,解决读问题

    四个隔离级别:

    image-20220317024524526

配置举例:

1
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)

MySQL中默认的隔离级别

timeout

超时时间:

  1. 事务需要在一定的时间内进行提交,如果不提交进行回滚
  2. 默认为-1(即不超时),设置时间以秒为单位进行计算

配置举例

1
@Transactional(timeout = 100)

readOnly

是否只读:

  1. 读:查询操作,写:添加修改删除操作
  2. readOnly默认值是False,表示可以增删改查
  3. 设置readOnly为True时,只能使用查询

配置举例

1
@Transactional(readOnly = true)

rollbackFor

回滚:

  1. 设置出现哪些异常进行事务回滚

noRollbackFor

不回滚:

  1. 设置出现哪些异常不进行事务回滚

基于XML配置的方式声明式事务管理

  • 配置事务管理器
  • 配置通知(增强的类)
  • 配置切入点和切面(要增强的类,即要使用事务的类)

XML配置例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

<!--1, 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!--2。配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!-- 指定哪种方法上添加事务-->
<tx:method name="acountMoney" propagation="REQUIRED"/>
<!--<tx:method name="acount*"/>-->
</tx:attributes>
</tx:advice>

<!--3.配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* io.ainexur.spring5.service.UserService.* (..)"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

完全注解方式的声明式事务管理

创建配置类TxConfig.java

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

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@ComponentScan(basePackages = "io.ainexur")
@EnableTransactionManagement //开启事务
public class TxConfig {

//创建配置类的连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///user_db?allowPublicKeyRetrieval=true&useSSL=false");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");

return druidDataSource;
}

//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) { //到IOC容器里自动寻找DataSource对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}

//创建事务管理容器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager =
new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}

测试

1
2
3
4
5
6
7
8
@Test
public void testTransactional2() {
ApplicationContext context =
new AnnotationConfigApplicationContext(TxConfig.class);

UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}

结果

配置类代理XML配置,和XML配置运行结果一致。

Spring5新功能和特性

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

  2. Spring5框架自带了日志的封装

    • Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2
    • Spring5框架整合Log4j2
    • 整合Log4j2
      • 引入jar包
      • 创建Log4j2.xml配置文件,log4j2.xml文件名字是固定的。
  3. Spring5框架核心容器支持@Nullable注解

    @Nullable注解可以用在方法、属性、参数上

    • 用在方法上,表示返回值可以为空
    • 用在属性上,表示属性值可以为空
    • 用在参数上,表示参数可以为空
  4. Spring5核心容器支持函数式风格GenericApplicationContext

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    public void testGenericApplicationContext() {
    //创建GenericApplicationContext对象
    GenericApplicationContext context =
    new GenericApplicationContext();
    //调用context方法对象注册
    context.refresh();
    context.registerBean(User.class, () -> new User());
    //获取在Spring中注册的对象
    User user = context.getBean(User.class);
    System.out.println(user);
    }
  5. Spring5支持整合JUnit5

  1. SpringWebFlux

Spring单元测试

Spring整合JUnit4

  1. 引入Spring相关依赖spring-test-5.3.16.jarhamcrest-core-1.3.jarjunit-4.13.1.jar

  2. 创建测试类JTest4.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
    @ContextConfiguration("classpath:bean1.xml") //加载配置文件
    public class JTest4 {

    @Autowired
    private UserService userService;

    @Test
    public void test1() {
    userService.accountMoney();
    }
    }
    //通过注解自动加载配置文件,省去每个测试方法都需要手动加载配置文件。
    //自动加载配置文件后,可以通过注解注入属性

Spring5整合JUnit5

  1. 引入相关依赖

    image-20220317174135264
    1
    2
    3
    4
    5
    6
    7
    8
    apiguardian-api-1.1.2.jar
    junit-jupiter-5.8.1.jar
    junit-jupiter-api-5.8.1.jar
    junit-jupiter-engine-5.8.1.jar
    junit-jupiter-params-5.8.1.jar
    junit-platform-commons-1.8.1.jar
    junit-platform-engine-1.8.1.jar
    opentest4j-1.2.0.jar
  2. 创建测试类JTest5.java:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //@ExtendWith(SpringExtension.class)
    //@ContextConfiguration("classpath:bean1.xml")
    @SpringJUnitConfig(locations = "classpath:bean1.xml") //复合注解,代替上面两个,简化配置
    public class JTest5 {

    @Autowired
    private UserService userService;


    @Test
    public void test1() {
    userService.accountMoney();
    }
    }

*SpringWebFlux

1.什么是SpringWebFlux

  • Spring5添加的新模块,用于Web开发,功能和SpringMVC类似, WebFlux为当前一种比较流行的响应式编程出现的框架。
  • 使用传统Web框架,比如SpringMVC,这些都基于Servlet容器;WebFlux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持。核心基于Reactor的相关API实现
    • 异步非阻塞:

2.响应式编程

3.WebFlux执行流程和API

4. SpringWebFlux(基于注解编程模型)

5.SpringWebFlux(基于注解编程模型)

开发步骤

  1. maven导入Spring包
  2. 创建Dao接口和实现
  3. 创建Spring核心配置文件
  4. 在Spring配置文件中配置UserDaoImpl
  5. 使用Spring的API获得Beans实例

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!