Java Spring 学习记录

本文最后更新于 2022年5月9日 上午

Spring

相关概念

优点

  • 轻量化
  • IOC
  • AOP

IOC(Inversion of Control):通过容器实现对象的创建,属性赋值,依赖的管理。

DI(Dependency Injection):调用一个对象时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器自动创建后传递给程序

Spring使用DI实现IOC


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
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>

Spring配置文件applicationContext.xml :

1
2
3
4
5
6
7
8
9
10
11
<?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">


<bean id="service01" class="com.example.service.impl.ServiceImpl"/>
<bean id="service02" class="com.example.service.impl.ServiceImpl"/>

<bean id="date01" class="java.util.Date"/>
</beans>

此时,程序运行时自动一次性创建好两个ServiceImpl对象,一个非自定义的Data对象

使用容器中的对象:

1
2
3
4
5
6
7
8
9
10
public void testSpringCreateObject() {
String config = "spring.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config); //创建对象,默认调用无参构造方法(所有对象)
Services service = (Services) applicationContext.getBean("service01"); //s: id
service.doSome();

Date date = (Date) applicationContext.getBean("date01");
System.out.println(date.getTime());
System.out.println(date);
}

基于XML的DI

  • set注入

类中必须要有set方法

applicationContext:

1
2
3
4
5
6
7
8
9
<bean id="student01" class="com.example.bean01.Student">
<property name="name" value="Azuas"/> <!--Call SetXXX();-->
<property name="age" value="20"/>
<!-- Call Setter-->
</bean>

<bean id="date01" class="java.util.Date">
<property name="time" value="114514"/> <!-- Call setTime()-->
</bean>
  • 引用注入

即一个bean时另一个bean的参数,使用ref=bean_id

applicationContext:

1
2
3
4
5
6
7
8
9
10
<bean id="student01" class="com.example.bean02.Student">
<property name="name" value="Azuas"/>
<property name="age" value="20"/>
<property name="school" ref="school01"/> <!--setSchool();-->
</bean>

<bean id="school01" class="com.example.bean02.School">
<property name="name" value="HUFE"/>
<property name="address" value="ChangSha"/>
</bean>
  • 构造注入

实体类中构造器:

1
2
3
4
5
6
7
public Student(String name, int age, School school) {
System.out.println("有参构造");
this.name = name;
this.age = age;
this.school = school;

}

applicationContext:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<bean id="student01" class="com.example.bean03.Student"> <!--有参构造-->
<!-- 参数名 -->
<constructor-arg name="name" value="Azusa1"/>
<constructor-arg name="age" value="20"/>
<constructor-arg name="school" ref="school01"/>
</bean>

<bean id="student02" class="com.example.bean03.Student"> <!--有参构造-->
<!-- 参数位置 -->
<constructor-arg index="0" value="Azusa1"/>
<constructor-arg index="1" value="20"/>
<constructor-arg index="2" ref="school01"/>
</bean>

<bean id="school01" class="com.example.bean03.School">
<property name="name" value="HUFE"/>
<property name="address" value="ChangSha"/>
</bean>

<bean id="file01" class="java.io.File">
<!-- 构造内置对象 -->
<constructor-arg name="parent" value="D:\Java\Spring_Lesson\Lesson2"/>
<constructor-arg name="child" value="pom.xml"/>
</bean>
  • 引用类型自动注入 - ByName

注入school:

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="student01" class="com.example.bean04.Student" autowire="byName"> <!--有参构造--> <!-- 自动注入(ref) setSchool()-->
<!-- 参数名 -->
<property name="name" value="Azusa1"/>
<property name="age" value="20"/>

<!-- <constructor-arg name="school" ref="school01"/> -->
</bean>

<bean id="school" class="com.example.bean04.School"> <!--自动注入 id与参数名一致-->
<property name="name" value="HUFE"/>
<property name="address" value="ChangSha"/>
</bean>
  • 引用类型自动注入 - ByType
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="student01" class="com.example.bean04.Student" autowire="byType"> <!--有参构造--> <!-- 自动注入(ref) setSchool()-->
<!--
byType: 1.同一class 2.父子类 3.接口 (三选一)
-->
<property name="name" value="Azusa1"/>
<property name="age" value="20"/>

<!-- <constructor-arg name="school" ref="school01"/> -->
</bean>

<bean id="midschool01" class="com.example.bean04.MidSchool">
<property name="name" value="HengYang MidSchool"/>
<property name="address" value="HengYang"/>
</bean>

基于注解的DI

applicationContext配置组件扫描器:

1
2
3
4
5
6
7
8
<context:component-scan base-package="com.example.bean01"/>
<context:component-scan base-package="com.example.bean02"/>

<!-- 导入多个包 -->
<context:component-scan base-package="com.example.bean01;com.example.bean02"/>

<!-- 导入父包 -->
<!-- <context:component-scan base-package="com.example"/> -->

在类上直接使用注解@Component,@Value

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
@Component("student01")
public class Student {

/**
* @Value: 属性赋值,放在定义或者set方法上
* @Value(value="")
*/
@Value("ChenXi")
private String name;
@Value("22")
private Integer age;

public Student() {
System.out.println("无参构造");
}

public void setName(String name) {
this.name = name;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

对引用类型使用Autowire注入

默认ByType

1
2
3
4
5
6
7
8
9
10
@Component("student01")
public class Student {

@Value("Azusa")
private String name;
@Value("20")
private int age;

@Autowired //ByType
private School school;
1
2
3
4
5
6
@Component("school01")
public class School {
@Value("HUFE")
private String name;
@Value("ChangSha")
private String address;

强制使用ByName,需设置@Qualifier注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component("student01")
public class Student {

@Value("aaa")
private String name;
@Value("20")
private int age;

/**
* @Autowired(required=true) Default
* @Autowired(required=flase) 赋值失败 继续执行
*/
@Autowired
@Qualifier("school01") //ByName
private School school;
1
2
3
4
5
6
@Component("school01")
public class School {
@Value("HUFE")
private String name;
@Value("ChangSha")
private String address;

对引用类型使用JDK的@Resource注入

1
2
3
4
5
6
7
8
@Value("aaa")
private String name;
@Value("20")
private int age;

//@Resource //先ByName 后ByType
@Resource(name = "school01") //强行ByName
private School school;

AOP编程

在不修改主业务逻辑的前提下,扩展和增强其功能。Spring采用了JDK的动态代理,CGLIB动态代理

概念

  • 切面(Aspect)

事务处理和日志处理可以理解为两个切面

  • 连接点(JoinPoint)

程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:

  1. 方法(表示程序执行点,即在哪个目标方法)

  2. 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)

  • 切入点(PointCut)

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。

  • 目标对象(Target)

目标对象指将要被增强的对象,即包含主业务逻辑的类对象。

  • 通知(Advice)

通知表示切面的执行时间, Advice 也叫增强。通知定义了增强代码切入到目标代码的时间点,是目标方
法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。

切入点表达式

1
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)  
1
execution(访问权限 方法返回值 方法声明(参数) 异常类型)

modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分

AspectJ使用

  1. @Aspect注解

Aspect类上面使用

  1. @Before前置通知

在目标方法执行之前执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// @Before("execution(void *..ServicesImpl.*(..))")
// @@Before("execution(* *..ServicesImpl.*(..))")
@Before("execution(public void com.example.bean01.ServicesImpl.anyService(String, Integer))")
public void beforeOperation(JoinPoint joinPoint) {
//JoinPoint(非必须): 获取方法的信息
System.out.println("Signature: " + joinPoint.getSignature());
System.out.println("Function Name: " + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("Arg: " + arg);
}

System.out.println("Before Aspect...");
}
  1. @AfterReturning后置通知

在目标方法执行之后执行,可以获取到目标方法的返回值。

1
2
3
4
5
6
@AfterReturning(value = "execution(boolean com.example.bean02.ServicesImpl.isService(String))", returning = "res")
public void afterRetOperation(Object res) {
System.out.println("Return: " + res);
//if(res==false)
System.out.println("After Return Aspect...");
}
  1. @Around环绕通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Around("execution(* com.example.bean03.ServicesImpl.isService(..))")
public Object aroundOperation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object res = null;
System.out.println("Around: Before Aspect...");

Object[] args = proceedingJoinPoint.getArgs();
String name = "";
if(args != null && args.length > 0){
Object arg = args[0];
name = (String) arg;
}

if("ChenXi".equals(name))
res = proceedingJoinPoint.proceed();
else
res = false;

System.out.println("Around: Return Aspect...");

return res;
}

proceedingJoinPoint.proceed(); 表示目标方法执行,可以获取返回值。故可以在目标方法前后添加想要的功能。

  • @AfterThrowing异常通知

在目标方法抛出异常后执行

1
2
3
4
@AfterThrowing(value = "execution(* com.example.bean04.ServicesImpl.anyService(..))", throwing = "exception")
public void afterThrowingOperation(Exception exception){
System.out.println("Aspect: AfterThrowing..." + exception.getMessage());
}
  • @After最终通知

最后一定会执行,不管遇到异常

1
2
3
4
5
6
7
@After("aspect01()")
public void afterOperation(JoinPoint joinPoint) {
System.out.println("After Aspect...");
}

@Pointcut("execution(public void com.example.bean01.ServicesImpl.anyService(String, Integer))") //复用
public void aspect01(){}

整合MyBatis

加入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

配置druid以及声明MapperScannerConfigurer,不用手动写工具类,自动读取Mapper文件创建对象,

applicationContext.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<context:property-placeholder location="classpath:jdbc.properties"/>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_user}"/>
<property name="password" value="${jdbc_password}"/>

<property name="maxActive" value="${jdbc_maxActive}"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.example.dao"/>
</bean>

事务

applicationContext.xml声明事务管理器,加入注解驱动:

1
2
3
4
5
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

在ServiceImpl中添加事务注解

1
2
3
4
5
6
7
8
9
/*@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {NullPointerException.class, NotEnoughException.class} //一定回滚
) 等效*/
@Transactional
@Override
public void buyGoods(Integer goodsId, Integer count) {

使用AOP来管理事务

只需要在applicationContext.xml配置即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="buyAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="buyGoods" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException, com.example.except.NotEnoughException"/>
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="servicePointCut" expression="execution(* *..service.impl.*.*(..))"/>
<aop:advisor advice-ref="buyAdvice" pointcut-ref="servicePointCut"/>
</aop:config>

Java Spring 学习记录
https://nanami.run/2022/02/14/java-spring/
作者
Nanami
发布于
2022年2月14日
许可协议