阶段六模块二 Spring 学习笔记

Spring概述

Spring是什么

Spring是分层的 Java SE/EE应用 full-stack(全栈式) 轻量级开源框架。

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

两大核心:以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

Spring优势

1)方便解耦,简化开发
  Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理
  什么是耦合度?对象之间的关系,通常说当一个模块(对象)更改时也需要更改其他模块(对象),这就是耦合,耦合度过高会使代码的维护成本增加。要尽量解耦
2)AOP编程的支持
  Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能。
3)声明式事务的支持
  通过配置完成事务的管理,无需手动编程
4)方便测试,降低JavaEE API的使用
  Spring对Junit4支持,可以使用注解测试
5)方便集成各种优秀框架
  不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持

Spring体系结构

 IOC

控制反转(Inverse Of Control)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。

控制:在java中指的是对象的控制权限(创建、销毁)
反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制

Spring基于的开发步骤

1. 导入坐标
2. 创建Bean
3. 创建applicationContext.xml
4. 在配置文件中进行Bean配置
5. 创建ApplicationContext对象,执行getBean

Spring API继承体系介绍

 

 BeanFactory

BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能。

特点:在第一次调用getBean()方法时,创建指定对象的实例

ApplicationContext

代表应用上下文对象,可以获得spring中IOC容器的Bean对象。

特点:在spring容器启动时,加载并创建所有对象的实例

常用实现类

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

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

常用方法

1. Object getBean(String name);
  根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。
2. <T> T getBean(Class<T> requiredType);
  根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。
3. <T> T getBean(String name,Class<T> requiredType);
  根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况。

Bean标签基本配置

<bean id="" class=""></bean>
* 用于配置对象交由Spring来创建。
* 基本属性:
  id:Bean实例在Spring容器中的唯一标识
  class:Bean的全限定名
* 默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功。

Bean标签范围配置

<bean id="" class="" scope=""></bean>

scope属性指对象的作用范围,取值如下:

 

1. 当scope的取值为singleton时
   Bean的实例化个数:1个
      Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
      Bean的生命周期:
        对象创建:当应用加载,创建容器时,对象就被创建了
        对象运行:只要容器在,对象一直活着
        对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2. 当scope的取值为prototype时
   Bean的实例化个数:多个
      Bean的实例化时机:当调用getBean()方法时实例化Bean
      Bean的生命周期:
         对象创建:当使用对象时,创建新的对象实例
         对象运行:只要对象在使用中,就一直活着
         对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了

Bean生命周期配置

<bean id="" class="" scope="" init-method="" destroy-method=""></bean>
* init-method:指定类中的初始化方法名称
* destroy-method:指定类中销毁方法名称

Bean实例化三种方式

无参构造方法实例化

它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败

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

工厂静态方法实例化

<bean id="userDao" class="com.lagou.factory.StaticFactoryBean" factory-method="createUserDao" />

工厂普通方法实例化

<bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="dynamicFactoryBean" factory-
method="createUserDao"/>

Bean依赖注入概述

依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是通过框架把持久层对象传入业务层,而不用我们自己去获取。

Bean依赖注入方式

构造方法

public class UserServiceImpl implements UserService {
  private UserDao userDao;
  public UserServiceImpl(UserDao userDao) {
    this.userDao = userDao;
 }
  @Override
  public void save() {
    userDao.save();
 }
}

配置Spring容器调用有参构造时进行注入

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
  <!--<constructor-arg index="0" type="com.lagou.dao.UserDao" ref="userDao"/>-
->
  <constructor-arg name="userDao" ref="userDao"/>
</bean>

set方法

在UserServiceImpl中创建set方法

public class UserServiceImpl implements UserService {
  private UserDao userDao;
  public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
 }
  @Override
  public void save() {
    userDao.save();
 }
}

配置Spring容器调用set方法进行注入

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
  <property name="userDao" ref="userDao"/>
</bean>

P命名空间注入

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:

首先,需要引入P命名空间:

xmlns:p="http://www.springframework.org/schema/p"

其次,需要修改注入方式:

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>

Bean依赖注入的数据类型

1. 普通数据类型

2. 引用数据类型

3. 集合数据类型

注入普通数据类型

<bean id="user" class="com.lagou.domain.User">
  <property name="username" value="jack"/>
  <property name="age" value="18"/>
</bean>

注入集合数据类型

1)List集合注入

<bean id="user" class="com.lagou.domain.User">
  <property name="username" value="jack"/>
  <property name="age" value="18"/>
</bean>
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
  <property name="list">
    <list>
      <value>aaa</value>
      <ref bean="user"></ref>
    </list>
  </property>
</bean>

2)Set集合注入

<bean id="user" class="com.lagou.domain.User">
  <property name="username" value="jack"/>
  <property name="age" value="18"/>
</bean>
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
  <property name="set">
    <list>
      <value>bbb</value>
      <ref bean="user"></ref>
    </list>
  </property>
</bean>

3)Array数组注入

<bean id="user" class="com.lagou.domain.User">
  <property name="username" value="jack"/>
  <property name="age" value="18"/>
</bean>
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
  <property name="array">
    <array>
      <value>ccc</value>
      <ref bean="user"></ref>
    </array>
  </property>
</bean>

4)Map集合注入

<bean id="user" class="com.lagou.domain.User">
  <property name="username" value="jack"/>
  <property name="age" value="18"/>
</bean>
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
  <property name="map">
    <map>
      <entry key="k1" value="ddd"/>
      <entry key="k2" value-ref="user"></entry>
    </map>
  </property>
</bean>

5)Properties配置注入

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
  <property name="properties">
    <props>
      <prop key="k1">v1</prop>
      <prop key="k2">v2</prop>
      <prop key="k3">v3</prop>
    </props>
  </property>
</bean>

配置文件模块化

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化

1)并列的多个配置文件

ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");

2)主从配置文件

<import resource="applicationContext-xxx.xml"/>

注意:

  • 同一个xml中不能出现相同名称的bean,如果出现会报错
  • 多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean

知识小结

Spring的重点配置

<bean>标签:创建对象并放到spring的IOC容器
   id属性:在容器中Bean实例的唯一标识,不允许重复
   class属性:要实例化的Bean的全限定名
   scope属性:Bean的作用范围,常用是Singleton(默认)和prototype
 
<constructor-arg>标签:属性注入
  name属性:属性名称
   value属性:注入的普通属性值
   ref属性:注入的对象引用值

<property>标签:属性注入
   name属性:属性名称
   value属性:注入的普通属性值
   ref属性:注入的对象引用值
  <list>
     <set>
     <array>
        <map>
        <props>
<import>标签:导入其他的Spring的分文件

Spring注解开发

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率

Spring常用注解

 说明:

JDK11以后完全移除了javax扩展导致不能使用@resource注解。

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

<!--注解的组件扫描-->
<context:component-scan base-package="com.lagou"></context:component-scan>

Spring新注解

* 非自定义的Bean的配置:<bean>

* 加载properties文件的配置:<context:property-placeholder>

* 组件扫描的配置:<context:component-scan>

* 引入其他文件:<import>

 Spring整合Junit

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class SpringJunitTest {
 
 @Autowired
  private AccountService accountService;
 
  //测试查询
  @Test
  public void testFindById() {
    Account account = accountService.findById(3);
    System.out.println(account);
 }
}

AOP

常用的动态代理技术

  • JDK 代理 : 基于接口的动态代理技术·:利用拦截器(必须实现invocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,从而实现方法增强
  • CGLIB代理:基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行增强

 JDK动态代理方式

@Component
    public class JdkProxyFactory {
        @Autowired
        private AccountService accountService;

        @Autowired
        private TransactionManager transactionManager;

        public AccountService createAccountServiceJdkProxy() {
            AccountService accountServiceProxy = null;
            accountServiceProxy = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                    accountService.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            Object result = null;
                            try {
                                // 1.开启事务
                                transactionManager.beginTransaction();
                                // 2.业务操作
                                result = method.invoke(accountService, args);
                                // 3.提交事务 transactionManager.commit(); 
                            } catch (Exception e) {
                                e.printStackTrace();
                                // 4.回滚事务 
                                transactionManager.rollback();
                            }
                        }
                    }
        }
    }

CGLIB动态代理方式

@Component
    public class CglibProxyFactory {
        @Autowired
        private AccountService accountService;

        @Autowired
        private TransactionManager transactionManager;

        public AccountService createAccountServiceCglibProxy() {
            AccountService accountServiceProxy = null;
            /**
             * 参数一:目标对象的字节码对象
             * 参数二:动作类,实现增强功能
             */
            accountServiceProxy = (AccountService)
                    Enhancer.create(accountService.getClass(), new MethodInterceptor() {
                        @Override
                        public Object intercept(Object o, Method method, Object[] objects,
                                                MethodProxy methodProxy) throws Throwable {
                            Object result = null;
                            try {
                                // 1.开启事务
                                transactionManager.beginTransaction();
                                // 2.业务操作
                                result = method.invoke(accountService, objects);
                                // 3.提交事务
                                transactionManager.commit();
                            } catch (Exception e) {
                                e.printStackTrace();
                                // 4.回滚事务
                                transactionManager.rollback();
                            } finally {
                                // 5.释放资源
                                transactionManager.release();
                            }
                            return result;
                        }
                    });
            return accountServiceProxy;
        }
    }

 什么是AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程。

 AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

这样做的好处是:

1. 在程序运行期间,在不修改源码的情况下对方法进行功能增强
2. 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
3. 减少重复代码,提高开发效率,便于后期维护

AOP底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强。

* Target(目标对象):代理的目标对象
* Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
* Joinpoint(连接点):所谓连接点是指那些可以被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
* Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
* Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
  分类:前置通知、后置通知、异常通知、最终通知、环绕通知
* Aspect(切面):是切入点和通知(引介)的结合
* Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织
入,而AspectJ采用编译期织入和类装载期织入

AOP开发明确事项

开发阶段(我们做的)

1. 编写核心业务代码(目标类的目标方法) 切入点
2. 把公用代码抽取出来,制作成通知(增强功能方法) 通知
3. 在配置文件中,声明切入点与通知间的关系,即切面

运行阶段(Spring框架完成的)

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

底层代理实现

在 Spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
当bean实现接口时,会用JDK代理模式
当bean没有实现接口,用cglib实现( 可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)

基于XML的AOP开发

XML配置AOP详解

切点表达式

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

切点表达式抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替pointcut 属性来引用抽取后的切点表达式。

<aop:config>
  <!--抽取的切点表达式-->
<aop:pointcut id="myPointcut" expression="execution(* com.lagou.service..*.*
(..))"> </aop:pointcut>
  <aop:aspect ref="myAdvice">
    <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
  </aop:aspect>
</aop:config>

通知类型

通知的配置语法:

<aop:通知类型 method=“通知类中方法名” pointcut=“切点表达式"></aop:通知类型>

  注意:通常情况下,环绕通知都是独立使用的

基于注解的AOP开发

通知类型

通知的配置语法:@通知注解(“切点表达式")

 当前四个通知组合在一起时,执行顺序如下:

@Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)

纯注解配置

@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy //替代 <aop:aspectj-autoproxy />
public class SpringConfig {
 
}

小结

* 使用@Aspect注解,标注切面类
* 使用@Before等注解,标注通知方法
* 使用@Pointcut注解,抽取切点表达式
* 配置aop自动代理 <aop:aspectj-autoproxy/> 或 @EnableAspectJAutoProxy

利用aop注解和环绕通知案例

public class TransactionManager {
  @Autowired
  ConnectionUtils connectionUtils;
  @Around("execution(* com.lagou.serivce..*.*(..))")
  public Object around(ProceedingJoinPoint pjp) {
    Object object = null;
    try {
      // 开启事务
      connectionUtils.getThreadConnection().setAutoCommit(false);
      // 业务逻辑
      pjp.proceed();
      // 提交事务
      connectionUtils.getThreadConnection().commit();
   } catch (Throwable throwable) {
      throwable.printStackTrace();
      // 回滚事务
      try {
        connectionUtils.getThreadConnection().rollback();
     } catch (SQLException e) {
        e.printStackTrace();
     }
   } finally {
      try {
        connectionUtils.getThreadConnection().setAutoCommit(true);
        connectionUtils.getThreadConnection().close();
        connectionUtils.removeThreadConnection();
     } catch (SQLException e) {
        e.printStackTrace();
     }
   }
    return object;
 }
}

Spring的JdbcTemplate

JdbcTemplate是什么?

JdbcTemplate是spring框架中提供的一个模板对象,是对原始繁琐的Jdbc API对象的简单封装。

Spring的事务

Spring中的事务控制方式

Spring的事务控制可以分为编程式事务控制和声明式事务控制。

编程式

开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。

声明式

开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的AOP思想。

编程式事务控制相关对象【了解】

PlatformTransactionManager

PlatformTransactionManager接口,是spring的事务管理器,里面提供了我们常用的操作事务的方法。

 注意:

* PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类。
* Dao层技术是jdbcTemplate或mybatis时:
  DataSourceTransactionManager
* Dao层技术是hibernate时:
  HibernateTransactionManager
* Dao层技术是JPA时:
  JpaTransactionManager

TransactionDefinition

TransactionDefinition接口提供事务的定义信息(事务隔离级别、事务传播行为等等)

1)事务隔离级别

设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读(幻读)。

* ISOLATION_DEFAULT 使用数据库默认级别
* ISOLATION_READ_UNCOMMITTED 读未提交
* ISOLATION_READ_COMMITTED 读已提交
* ISOLATION_REPEATABLE_READ 可重复读
* ISOLATION_SERIALIZABLE 串行化

2)事务传播行为

事务传播行为指的就是当一个业务方法【被】另一个业务方法调用时,应该如何进行事务控制。

 * read-only(是否只读):建议查询时设置为只读

* timeout(超时时间):默认值是-1,没有超时限制。如果有,以秒为单位进行设置

TransactionStatus

TransactionStatus 接口提供的是事务具体的运行状态。

 可以简单的理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态

Spring中的事务控制主要就是通过这三个API实现的

* PlatformTransactionManager 负责事务的管理,它是个接口,其子类负责具体工作
* TransactionDefinition 定义了事务的一些相关参数
* TransactionStatus 代表事务运行的一个实时状态

基于XML的声明式事务控制【重点】

在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。

声明式事务控制明确事项:

核心业务代码(目标对象) (切入点是谁?)
事务增强代码(Spring已提供事务管理器))(通知是谁?)
切面配置(切面如何配置?)

事务参数的配置详解

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED"
timeout="-1" read-only="false"/>
* name:切点方法名称
* isolation:事务的隔离级别
* propogation:事务的传播行为
* timeout:超时时间
* read-only:是否只读

CRUD常用配置

<tx:attributes>
  <tx:method name="save*" propagation="REQUIRED"/>
  <tx:method name="delete*" propagation="REQUIRED"/>
  <tx:method name="update*" propagation="REQUIRED"/>
  <tx:method name="find*" read-only="true"/>
  <tx:method name="*"/>
</tx:attributes>

基于注解的声明式事务控制【重点】

常用注解

1)修改service层,增加事务注解

@Service
public class AccountServiceImpl implements AccountService {
      @Autowired
      private AccountDao accountDao;
    @Transactional(propagation = Propagation.REQUIRED, isolation =
    Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)
      @Override
      public void transfer(String outUser, String inUser, Double money) {
        accountDao.out(outUser, money);
        int i = 1 / 0;
        accountDao.in(inUser, money);
     }
}

2)修改spring核心配置文件,开启事务注解支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w2.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:aop="http://www.springframework.org/schema/aop"
   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/context
http://www.springframework.org/schema/context/spring-context.xsd
   http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
   http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
  <!--省略之前datsSource、jdbcTemplate、组件扫描配置-->
 
  <!--事务管理器-->
  <bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
  </bean>
 
  <!--事务的注解支持-->
  <tx:annotation-driven/>
</beans>

纯注解

核心配置类

@Configuration  // 声明为spring配置类
@ComponentScan("com.lagou") // 扫描包
@Import(DataSourceConfig.class) // 导入其他配置类
@EnableTransactionManagement // 事务的注解驱动
public class SpringConfig {
  @Bean
  public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource) {
    return new JdbcTemplate(dataSource);
  }
  @Bean("transactionManager")
  public PlatformTransactionManager getPlatformTransactionManager(@Autowired
    DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }
}

数据源配置类

@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
  @Value("${jdbc.driver}")
  private String driver;
  @Value("${jdbc.url}")
  private String url;
  @Value("${jdbc.username}")
  private String username;
  @Value("${jdbc.password}")
  private String password;
  @Bean
  public DataSource getDataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    return dataSource;
 }
}

Spring集成web环境

ApplicationContext应用上下文获取方式

应用上下文对象是通过  new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从容器中获得Bean时都要编写  new ClasspathXmlApplicationContext(spring配置文件) ,这样的弊端是配置文件加载多次,应用上下文对象创建多次。

解决思路分析:

在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

Spring提供获取应用上下文的工具

上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封
装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供
了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。

所以我们需要做的只有两件事:

1. 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
2. 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

实现

1)导入Spring集成web的坐标

<dependency>
  <dependency>
   <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.5.RELEASE</version>
  </dependency>
   <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.1.5.RELEASE</version>
</dependency>

2)配置ContextLoaderListener监听器

<!--全局参数-->
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value></context-param>
<!--Spring的监听器-->
<listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

3)通过工具获得应用上下文对象

ApplicationContext applicationContext =  
  WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object obj = applicationContext.getBean("id");
posted @ 2021-02-22 20:58  曾鸿发  阅读(104)  评论(0编辑  收藏  举报