Fork me on GitHub

Spring笔记

写此系列的目的是做一个学习笔记管理,方便以后复习用。

Spring是轻代码,重配置的框架。

Spring是分层的 Java SE/EE应用 full-stack 轻量级(重量级)开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

提供了展现层 SpringMVC 和持久层 Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

Spring框架的核心功能:

创建管理所有的Java对象,这些对象被称为Bean。

参考文档

参考视频

Spring快速入门

  1. 导入Spring开发的基本包坐标

    pom.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <properties>  
    <spring.version>5.0.5.RELEASE</spring.version>
    </properties>

    <dependencies>
    <!--导入spring的context坐标,context依赖core、beans、expression-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
    </dependency>
    </dependencies>
  1. 编写Dao接口和实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface UserDao {
    public void save();
    }

    public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
    System.out.println("UserDao save method running....");
    }
    }
  1. 创建Spring核心配置文件

    在类路径下(resources)创建applicationContext.xml配置文件

    1
    2
    3
    4
    5
    6
    7
    <?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">
    </beans>
  2. 在Spring配置文件中配置UserDaoImpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?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="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
    </beans>
  1. 使用Spring的API获得Bean实例

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void test1(){
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) applicationContext.getBean("userDao");
    userDao.save();
    }

Spring配置文件

重要配置标签

1
2
3
4
5
6
7
8
9
10
11
12
13
<bean>标签
id属性:在容器中Bean实例的唯一标识,不允许重复
class属性:要实例化的Bean的全限定名
scope属性:Bean的作用范围,常用是Singleton(默认)和prototype
<property>标签:属性注入
name属性:属性名称
value属性:注入的普通属性值
ref属性:注入的对象引用值
<list>标签
<map>标签
<properties>标签
<constructor-arg>标签
<import>标签:导入其他的Spring的分文件

Bean实例化的三种方式

无参构造方法实例化

1
<bean id = "userDao" class = "com.itheima.dao.impl.UserDaoImpl"></bean>

工厂静态方法实例化

工厂实例方法实例化

Bean的依赖注入

依赖注入(Dependency Injection):它是 Spring 框架核心 IOC(反转控制) 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

用依赖注入的方式管理Bean之间的依赖关系。依赖注入可以让Bean以配置文件组织在一起,是一种优秀的解耦方式

依赖注入有两种方式,set注入和构造注入。(设计模式解耦合)

set注入和构造注入

1
2
3
4
5
6
7
8
9
10
11
12
<!--set方式注入-->
<bean id="userDao" class="com.tsuki.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.tsuki.service.impl.UserServiceImpl">
<!--set方法后的名字是name,ref是指向注入的UserDao的id-->
<property name="userDao" ref="userDao"></property>
</bean>

<!--构造器方式注入-->
<bean id="userDao" class="com.tsuki.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.tsuki.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

相关API

applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象

ApplicationContext的实现类

1)ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种
2)FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3)AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

1
2
3
ApplicationContext app = new ClasspathXmlApplicationContext("xml文件");
app.getBean("id");
app.getBean(Class);

Spring配置数据源(连接池)

druid步骤:

  1. 导入依赖 pom.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
    <!--mysql驱动-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.26</version>
    <scope>compile</scope>
    </dependency>

    <!--druid连接池-->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.9</version>
    </dependency>

    <!--导入Junit-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <!--版本要求高于4.11-->
    <version>4.13</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
    </dependency>
  2. 创建DataSource 在Spring中采用注入容器的方法 applicationContext.xml

    1
    2
    3
    4
    5
    6
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/db1"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    </bean>
    • 将参数提取到properties文件中后如何注入容器

      1.需要引入context的命名空间和约束路径

      • 命名空间 xmlns:context="http://www.springframework.org/schema/context"
      • 约束路径 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
      • applicationContext.xml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <!--加载外部配置文件,classpath是从类加载文件resources中加载-->
      <context:property-placeholder location="classpath:jdbc.properties"/>

      <!--注入datasource-->
      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
      <!--spl表达式,类似el表达式的用法 value="${key}"-->
      <property name="driverClassName" value="${jdbc.driverClassName}"></property>
      <property name="url" value="${jdbc.url}"></property>
      <property name="username" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
      </bean>
  3. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void test2() throws SQLException {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");//加载Spring的配置文件
    //从容器中获取对象
    DataSource ds = (DataSource) applicationContext.getBean("dataSource");
    Connection conn = ds.getConnection();
    System.out.println(conn);
    conn.close();
    }

注解开发

简化和舍弃Spring的配置文件,方便快捷

  • 原始注解

    1. @Component <bean id="userDao" class="com.tsuki.dao.impl.UserDaoImpl"></bean>

      @Controller @Service @Repository

    2. @Autowired @Qualifier---@Resource按名称注入

      1
      2
      3
      <bean id="userService" class="com.tsuki.service.impl.UserServiceImpl">
      <property name="userDao" ref="userDao"></property>
      </bean>
    3. @Value(普通属性) @Scope

  • 配置组件扫描

    1
    2
    <!--配置组件扫描-->
    <context:component-scan base-package="com.tsuki" />
  • 新注解开发—全注解开发

    旧注解有几方面不能满足

    @Configuration Spring的核心配置类,用类的方式替代xml文件, 注解的方式替代标签

    1. 非自定义的bean @Bean("dataSource")
    2. 配置文件properties @PropertySource("classpath:jdbc.properties")
    3. 组件扫描 @ComponentScan("com.tsuki")
    4. 引入import @Import

Junit的集成

  1. 导入spring集成Junit坐标 pom.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!--导入Junit-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <!--版本要求高于4.11-->
    <version>4.13</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
    </dependency>
    <!--导入spring的test坐标-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
    </dependency>
  2. @Runwith代替原来运行周期 @RunWith(SpringJUnit4ClassRunner.class)

  3. @ContextConfiguration指定配置文件或配置类 @ContextConfiguration("classpath:applicationContext.xml")

  4. @Autowired注入需要测试的对象

  5. 创建测试方法进行测试

AOP

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

用到了代理模式,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。

常见的动态代理技术有JDK代理和cglib代理,代码上的区别就是需不需要接口。

动态代理代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Target target = new Target(); //创建目标对象
Enhancer enhancer = new Enhancer(); //创建增强器
enhancer.setSuperclass(Target.class); //设置父类
enhancer.setCallback(new MethodInterceptor() { //设置回调
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("前置代码增强....");
Object invoke = method.invoke(target, objects);
System.out.println("后置代码增强....");
return invoke;
}
});
Target proxy = (Target) enhancer.create(); //创建代理对象

//测试,当调用接口的任何方法时,代理对象的代码都无序修改
proxy.method();

基于xml

快速入门

  1. 导入坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!--导入spring的context坐标,context依赖aop-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
    </dependency>
    <!-- aspectj的织入 -->
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
    </dependency>
  2. 创建目标接口和目标类(内部有切点)

  3. 创建切面类(内部有增强方法)

  4. 将目标类和切面类的对象创造权交给spring

  5. 在applicationContext.xml配置织入关系

  6. 测试

重要标签

切点表达式的写法

execution(public void com.tsuki.aop.Target.method())

通知的类型: aop:before aop:after-returning aop:around aop:throwing aop:after

织入

1
2
3
4
5
<aop:config>
<aop:aspect ref=“切面类”>
<aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>
</aop:aspect>
</aop:config>

基于注解

快速入门

  1. 创建目标接口和目标类

  2. 创建切面类

  3. 将目标类和切面类的对象创建交给spring

  4. 在切面类中使用注解配置织入关系

    1
    2
    3
    4
    5
    6
    7
    8
    @Component("myAspect")//步骤3
    @Aspect
    public class MyAspect {
    @Before("execution(* com.itheima.aop.*.*(..))")
    public void before(){
    System.out.println("前置代码增强.....");
    }
    }
  5. 在配置文件中开启组件扫描和AOP自动代理

    1
    2
    3
    4
    5
    <!--组件扫描-->
    <context:component-scan base-package="com.itheima.aop"/>

    <!--aop的自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  6. 测试

重要标签

注解通知常见的类型

@Before @AfterReturning @Around @AfterThrowing @After

事务控制

编程式事务控制三大对象

  • PlatformTransactionManager
  • TransactionDefinition
  • TransactionStatus

声明式事务控制

Spring 声明式事务控制底层就是AOP。

Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

基于注解和xml方式实现。

事务隔离级别

  • ISOLATION_DEFAULT

  • ISOLATION_READ_UNCOMMITTED 未提交读

  • ISOLATION_READ_COMMITTED 已提交读

  • ISOLATION_REPEATABLE_READ 可重复读

  • ISOLATION_SERIALIZABLE 串行化(通过加锁来实现)

事务传播行为

参考对Spring事务传播行为的理解

  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

    通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

    以上试验结果我们证明在外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。

  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

  • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。

    通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

    在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。

  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作

    通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.NESTEDPropagation.REQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰

    以上试验结果我们证明在外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务



本文标题:Spring笔记

文章作者:tsuki

发布时间:2021.12.24 - 16:27

最后更新:2022.10.13 - 20:36

原始链接:https://tsuki419.github.io/Spring.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------THE END-------------
0%