敖胤

绳锯木断,水滴石穿;聚沙成塔,集腋成裘。

导航

Spring回顾总结

1、Spring开发流程

  • 引入Spring的jar包
  • 编写Spring配置文件
  • 配置Bean
  • 获取Spring容器实例
  • 通过Spring API获取对象

2、Bean的作用域

Spring的Bean一共有五种作用域:

  • singleton:单例模式,默认。在整个Spring容器中,只存在一个Bean实例。

  • prototype:原型模式。每次获取的都是一个新的实例


  • request:每次Http请求都产生一个新的实例。只有在WEB项目中有效。

  • session:每个Http session生成一个新的实例。只有在WEB项目中有效。

  • globalsession:每个全局的HTTP session生成一个新的实例。只有在WEB项目中且使用Portlet context时有效。

3、Bean的生命周期

对于singleton类型的Bean,默认情况下,在Spring容器创建时初始化实例化一次,并且该实例交由Spring容器管理,随着Spring容器的关闭而销毁。

对于prototype类型的Bean,在Spring容器创建时初始化,在每次获取时实例化一个对象,实例化后的对象不由Spring容器管理,而是交由JVM管理,由JVM的垃圾回收器负责销毁。

4、Bean标签属性详解

属性 作用 取值 必须
id Bean实例在Spring容器中的唯一标识 符合Java名命规范的字符串
class 指定Bean的类名 Bean的Class的全限定名
scope 指定Bean的作用域 枚举:singleton(默认)、prototype、
request、session、globalsession
init-method 指定Bean实例化时执行的方法 自定义方法的方法名
destroy-method 指定Bean对象销毁时执行的方法 自定义方法的方法名
lazy-init 是否启用懒加载模式 枚举:default(默认false)、false、true
factory-bean 当采用工厂模式实例化Bean对象时,
指定实例化此Bean对象的工厂Bean
配置的工厂Bean的id值
factory-method 当采用工厂模式实例化Bean对象时,
指定实例化此Bean对象的工厂方法
工厂中用于获取此Bean对象的方法

示例代码

<dependencies>
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>5.2.14.RELEASE</version>
 </dependency>

 <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.11</version>
     <scope>test</scope>
 </dependency>
</dependencies>
<?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="demoDao" class="org.silence.dao.impl.DemoDaoImpl" scope="singleton" init-method="init"
       destroy-method="destroy" lazy-init="false"></bean>
</beans>
public interface DemoDao {
 void save();
}

public class DemoDaoImpl implements DemoDao {
 public void init() {
     System.out.println("DemoDao实例化……");
 }

 public void destroy() {
     System.out.println("DemoDao被销毁……");
 }

 public void save() {
     System.out.println("save方法执行……");
 }
}
// 测试类
public class SpringTest {

 @Test
 // 基本使用测试
 public void test() {
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     DemoDao demoDao = (DemoDao) context.getBean("demoDao");
     demoDao.save();
 }

 @Test
 // 作用域测试,验证singleton与prototype下实例的个数
 public void testScope() {
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     DemoDao demoDao1 = (DemoDao) context.getBean("demoDao");
     DemoDao demoDao2 = (DemoDao) context.getBean("demoDao");
     System.out.println(demoDao1==demoDao2);
 }

 @Test
 // 生命周期测试,测试singleton与prototype下Bean的实例化时机,以及配置了lazy-init="true"时,singleton下Bean的实例化时机
 public void testLife() {
     // 此处设置断点,用debug模式验证
     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     DemoDao demoDao1 = (DemoDao) context.getBean("demoDao");
     demoDao1.save();
     context.close();
 }
}

5、Bean实例化的三种方式

  • 无参构造:默认,当类中没有无参构造时,将无法创建实例。

  • 工厂静态方法:

    // 定义工厂类,内部有一个静态方法来获取对象
    public class StaticFactory {
     public static DemoDao getDemoDao() {
         return new DemoDaoImpl();
     }
    }
    
    <!--  配置Bean,class指向工厂类,factory-method指向工厂的静态方法  -->
    <bean id="demoDaoByStaticFactory" class="org.silence.factory.StaticFactory" factory-method="getDemoDao"></bean>
    
  • 工厂实例方法:

    public class DynamicFactory {
     public DemoDao getDemoDao() {
         System.out.println("工厂实例方法执行……");
         return new DemoDaoImpl();
     }
    }
    
    <bean id="dynamicFactory" class="org.silence.factory.DynamicFactory"/>
    <bean id="demoDaoByDynamicFactory" factory-bean="dynamicFactory" factory-method="getDemoDao"/>
    

6、依赖注入(DI)

依赖注入是Spring控制反转(IOC)的具体实现。通过IOC,只是将对象的创建交给了Spring容器。而对象之间的相互依赖关系则是通过DI来解决。

依赖注入主要有两种方式:构造方法和属性注入

  • 属性注入:

    通过属性方式,使用set方法来注入依赖

    public class DemoServiceImpl implements DemoService {
    
     // 将所依赖的对象以属性的方式声明
     private DemoDao demoDao;
    	// set方法
     public void setDemoDao(DemoDao demoDao) {
         this.demoDao = demoDao;
     }
    
     @Override
     public void save() {
         System.out.println("DemoService.save()执行……");
         demoDao.save();
     }
    }
    
    <bean id="demoDao" class="org.silence.dao.impl.DemoDaoImpl" />
    <bean id="demoService" class="org.silence.service.impl.DemoServiceImpl">
     <!-- 注意:此处name的值并不是service中定义的属性名,而是set方法名“setXXX”去掉“set”之后并将首字母小写后的值;ref表示容器中Bean的id -->
     <property name="demoDao" ref="demoDao"/>
    </bean>
    

    p名命空间方式:

    使用setter注入时,可以将<property>标签省略

    <bean id="demoService" class="org.silence.service.impl.DemoServiceImpl">
        <property name="demoDao" ref="demoDao"/>
    </bean>
    

    用p:的方式简写为:

    <bean id="demoService" class="org.silence.service.impl.DemoServiceImpl" p:demoDao-ref="demoDao"/>
    
  • 构造方法:

    通过有参构造的方式将依赖注入。

    public class DemoServiceImpl implements DemoService {
    
     private DemoDao demoDao;
    
     public DemoServiceImpl() {
     }
    
    
     // 定义有参构造,将依赖注入
     public DemoServiceImpl(DemoDao demoDao) {
         this.demoDao = demoDao;
     }
    
     @Override
     public void save() {
         System.out.println("DemoService.save()执行……");
         demoDao.save();
     }
    }
    
    <bean id="demoService2" class="org.silence.service.impl.DemoServiceImpl">
     <!-- name值表示构造方法中的形参名,ref表示容器中的Bean的id -->
     <constructor-arg name="demoDao" ref="demoDao"/>
    </bean>
    

7、面向切面AOP

Aspect oriented programming,面向切面编程,是通过预编译和运行期动态代理实现统一维护程序功能的一种技术。

通过AOP,可以在程序运行期间,不修改源码的情况下对方法进行功能增强。从而减少代码重复,降低耦合,提高开发效率,便于后期维护。

动态代理方式:

  • JDK代理:基于接口的动态代理(目标类必须实现某个接口),代理对象通过实现目标接口对目标对象进行功能增强。

    底层实现方式

    // 目标接口
    public interface Target {
     void demo();
    }
    // 目标对象类
    public class TargetImpl implements Target {
     public void demo() {
         System.out.println("执行业务逻辑……");
     }
    }
    // 消息通知类(增强功能)
    public class Advice {
     public void before() {
         System.out.println("前置通知……");
     }
    
     public void after() {
         System.out.println("后置通知……");
     }
    }
    // 底层实现测试
    public class TestJdkProxy {
     public static void main(String[] args) {
         // 目标对象
         final TargetImpl target = new TargetImpl();
         // 消息通知对象
         Advice advice = new Advice();
         // 获取动态代理对象
         Target proxy = (Target) Proxy.newProxyInstance(
             // 目标对象类加载器
             target.getClass().getClassLoader(),
             // 目标对象的接口字节码数组
             target.getClass().getInterfaces(),
             // 代理处理器
             new InvocationHandler() {
                 // 调用代理对象的任何方法,实质上都是执行invoke方法
                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                     advice.before();// 执行前置通知
                     method.invoke(target, args);// 执行目标方法
                     advice.after();// 执行后置通知
                     return null;
                 }
             }
         );
         // 调用代理对象的方法
         proxy.demo();
     }
    }
    
  • cglib代理:基于父类的动态代理,代理对象通过为目标对象动态生成一个子对象从而对目标对象进行功能增强。

    底层实现方式

    需要导入cglib的依赖包(spring-core包已经集成了)

    // 目标对象类
    public class TargetImpl implements Target {
     public void demo() {
         System.out.println("执行业务逻辑……");
     }
    }
    // 消息通知类(增强功能)
    public class Advice {
     public void before() {
         System.out.println("前置通知……");
     }
    
     public void after() {
         System.out.println("后置通知……");
     }
    }
    // 底层实现测试
    public class TestCglibProxy {
     public static void main(String[] args) {
         // 目标对象
         final TargetImpl target = new TargetImpl();
         // 消息通知对象
         Advice advice = new Advice();
         /*动态代理步骤*/
         // 1、创建增强器
         Enhancer enhancer = new Enhancer();
         // 2、设置父类
         enhancer.setSuperclass(TargetImpl.class);
         // 3、设置回调
         enhancer.setCallback(new MethodInterceptor() {
             @Override
             public Object intercept(Object proxy, Method method, Object[] rags, MethodProxy methodProxy) throws Throwable {
                 advice.before();// 执行前置通知
                 System.out.println("cglib的动态代理");
                 Object invoke = method.invoke(target, args);// 执行目标方法
                 advice.after();// 执行后置通知
                 return invoke;
             }
         });
         // 4、创建代理类
         TargetImpl  proxy = (TargetImpl) enhancer.create();
    
         // 调用代理对象的方法
         proxy.demo();
     }
    }
    

Spring会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

示例代码

<!-- 添加依赖 -->
<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.6</version>
</dependency>

基于xml文件的配置

<!--目标对象-->
<bean id="target" class="org.silence.aop.TargetImpl"/>

<!--切面对象-->
<bean id="aspect" class="org.silence.aop.Aspect"/>

<!--配置织入:告诉Spring框架哪些切点需要哪些增强-->
<aop:config>
 <!--指定切面-->
 <aop:aspect ref="aspect">
     <aop:before method="before" pointcut="execution(public void org.silence.aop.TargetImpl.demo())"/>
     <aop:after method="after" pointcut="execution(public void org.silence.aop.TargetImpl.demo())"/>
 </aop:aspect>
</aop:config>

切点表达式

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

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

通知类型

通知类型 标签 说明
前置通知 <aop:before> 在切入点方法执行之前执行通知方法
后置通知 <aop:after-returning> 在切入点方法执行之后执行通知方法
环绕通知 <aop:around> 在切入点方法执行前后执行通知方法
异常通知 <aop:after-throwing> 在切入点方法执行异常时执行通知方法
最终通知 <aop:after> 在切入点方法执行完成,无论是否异常,执行通知方法

示例代码:

基于配置文件

// 消息通知类(增强功能)
public class DemoAspect {
 public void before() {
     System.out.println("前置通知……");
 }

 public void afterReturn() {
     System.out.println("后置通知……");
 }

 public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
     System.out.println("环绕前通知……");
     Object obj = joinPoint.proceed();// 执行目标方法
     System.out.println("环绕后通知……");
     return obj;
 }

 public void afterThrowing() {
     System.out.println("异常通知……");
 }

 public void after() {
     System.out.println("最终通知……");
 }
}
<!--目标对象-->
<bean id="target" class="org.silence.aop.TargetImpl"/>

<!--切面对象-->
<bean id="aspect" class="org.silence.aop.DemoAspect"/>

<!--配置织入:告诉Spring框架哪些切点需要哪些增强-->
<aop:config>
 <!--指定切面-->
 <aop:aspect ref="aspect">
     <!--配置前置通知-->
     <aop:before method="before" pointcut="execution(public void org.silence.aop.TargetImpl.demo())"/>
     <!--配置后置通知 表示org.silence.aop包下任意类的任意方法 -->
     <aop:after-returning method="afterReturn" pointcut="execution(* org.silence.aop.*.*(..))"/>
     <!--配置环绕通知-->
     <aop:around method="around" pointcut="execution(* org.silence.aop.*.*(..))"/>
     <!--配置异常通知-->
     <aop:after-throwing method="afterThrowing" pointcut="execution(* org.silence.aop.*.*(..))"/>
     <!--配置最终通知-->
     <aop:after method="after" pointcut="execution(* org.silence.aop.*.*(..))"/>
 </aop:aspect>
</aop:config>

基于注解:

<!--开启扫描-->
<context:component-scan base-package="org.silence"/>

<!--开启AOP自动代理-->
<aop:aspectj-autoproxy/>
// 目标对象类
@Component
public class TargetImpl implements Target {
 public void demo() {
     System.out.println("执行业务逻辑……");
     int i = 1/0;
 }
}
// 消息通知类(增强功能)
@Component
@Aspect
public class DemoAspect {

 @Before("execution(* org.silence.aop.*.*(..))")
 public void before() {
     System.out.println("前置通知……");
 }

 @AfterReturning("execution(* org.silence.aop.*.*(..))")
 public void afterReturn() {
     System.out.println("后置通知……");
 }

 @Around("execution(* org.silence.aop.*.*(..))")
 public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
     System.out.println("环绕前通知……");
     Object obj = joinPoint.proceed();// 执行目标方法
     System.out.println("环绕后通知……");
     return obj;
 }

 @AfterThrowing("pointcut1()")
 public void afterThrowing() {
     System.out.println("异常通知……");
 }

 @After("DemoAspect.pointcut2()")
 public void after() {
     System.out.println("最终通知……");
 }

 // 可以单独抽取切入点
 @Pointcut("execution(* org.silence.aop.*.*(..))")
 public void pointcut1() {
     
 }

 @Pointcut("execution(* org.silence.proxy.*.*(..))")
 public void pointcut2() {

 }
}

8、Spring加载配置文件

假设在resources下有demo.properties配置文件,内容如下:

name=tom
age=18

有abc.properties配置文件,内容如下:

abc.name=abc
abc.age=25

要将配置文件加载到Spring容器中,非常简单,只需要在Spring的配置文件中添加如下内容

<context:property-placeholder location="demo.properties,abc.properties"/>

<!-- 此时,使用SpEL就可以读取到demo.properties文件中的内容 -->
<bean id="tom" class="org.silence.entity.User">
 <property name="name" value="${name}"/>
 <property name="age" value="${age}"/>
</bean>
<bean id="abc" class="org.silence.entity.User">
 <property name="name" value="${abc.name}"/>
 <property name="age" value="${abc.age}"/>
</bean>

9、Spring事务管理

  • 基于xml配置文件的声明式事务管理
<!--配置数据源-->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${dataSource.driverClassName}"/>
    <property name="url" value="${dataSource.url}"/>
    <property name="username" value="${dataSource.username}"/>
    <property name="password" value="${dataSource.password}"/>
</bean>

<!--配置平台事务管理器:DataSourceTransactionManager只适用于原生JDBC、JDBCTemplate、Mybatis-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!--配置切点方法的事务属性 name:方法名称;isolation:隔离级别;propagation:传播行为;timeout:超时时间;read-only:是否只读-->
        <!--配置方法名为save的方法的事务属性-->
        <tx:method name="save" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/>
        <!--配置以update开头的方法的事务属性-->
        <tx:method name="update*" isolation="READ_COMMITTED" propagation="REQUIRES_NEW"/>
        <!--配置通用的事务属性-->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<!--配置织入-->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* org.silence.service.*.*(..))"/>
</aop:config>
  • 基于注解的声明式事务
<!--配置数据源-->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${dataSource.driverClassName}"/>
    <property name="url" value="${dataSource.url}"/>
    <property name="username" value="${dataSource.username}"/>
    <property name="password" value="${dataSource.password}"/>
</bean>

<!--配置平台事务管理器:DataSourceTransactionManager只适用于原生JDBC、JDBCTemplate、Mybatis-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--开启组件扫描-->
<context:component-scan base-package="org.silence"/>

<!--开启事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Service("demoService")
// 事务注解,可以配置在类和方法上,同时配置时,方法上配置的优先(就近原则)
@Transactional
public class DemoService {

    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRES_NEW,timeout = 30)
    public void demo() {

    }
}

10、Spring常见注解

注解 用途
@Component 标识Bean,通用
@Controller 标识Bean,用在web层的类上
@Service 标识Bean,用在业务层的类上
@Repository 标识Bean,用在持久层的类上
@Scope 标识Bean的作用域
@Autowired 标识根据类型依赖注入,用在字段上
@Qualifier 必须结合Autowired一起使用,表示根据Bean的id进行注入
@Resource 相当于@Autowired + @Qualifier
@Value 用于普通类型的属性注入
@PostConstruct 用在方法上,标识Bean的初始化方法
@PreDestroy 用在方法上,标识Bean的销毁方法

注解 用途
@Configuration 用在类上,表示当前类是一个Spring的核心配置类,相当于该类替代了一个xml配置文件
@Bean 用在方法上,表示将该方法的返回值作为一个Bean实例存放到Spring容器中
@ComponentScan 用在配置类上,表示组件扫描的包,默认扫描当前类所在包及其子包
@PropertySource 用在配置类上,用于加载.properties文件
@Import 用在配置类上,用于导入其他配置类

示例代码

  • 使用配置文件配置druid数据库连接池:

    applicationContext.xml中:

<!-- 导入其他的配置文件 -->
<import resource="dataSourceConfig.xml"/>

dataSourceConfig.xml中:

<!-- 加载properties文件 -->
<context:property-placeholder location="jdbc.properties"/>

<!-- 使用注解开发时,用于开启组件扫描并指定需要扫描的基础包 -->
<context:component-scan base-package="org.silence"/>

<!-- 配置druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${dataSource.driverClassName}"/>
    <property name="url" value="${dataSource.url}"/>
    <property name="username" value="${dataSource.username}"/>
    <property name="password" value="${dataSource.password}"/>
</bean>
  • 使用配置类开发

    SpringConfiguration.java:

// 标识一个Spring核心配置类,相当于“applicationContext.xml”配置文件
@Configuration
// 开启组件扫描并指定基础包,相当于“<context:component-scan base-package="org.silence"/>”
@ComponentScan("org.silence")
// 用于加载配置文件,相当于“context:property-placeholder location="demo.properties,abc.properties"/>”
@PropertySource(value = {"classpath:abc.properties", "classpath:demo.properties"})
// 导入其他配置类,相当于“<import resource="dataSourceConfig.xml"/>”
@Import(DataSourceConfiguration.class)
public class SpringConfiguration {

}

DataSourceConfiguration.java:

@Configuration
@ComponentScan("org.silence")
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
    @Value("${dataSource.driverClassName}")
    private String driverClassName;

    @Value("${dataSource.url}")
    private String url;

    @Value("${dataSource.username}")
    private String username;

    @Value("${dataSource.password}")
    private String password;

    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

测试:

@Test
public void testConfigClass() {
    // 使用注解配置实现的应用上下文实现
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    DruidDataSource dataSource = (DruidDataSource) context.getBean("druidDataSource");
    System.out.println(dataSource);
}

11、Spring集成WEB环境

Spring集成WEB环境,也就是解决在WEB项目中如何获取Spring容器ApplicationContext的问题。

显然,WEB项目中不再适应通过new ClassPathXmlApplicationContext("applicationContext.xml");的方式来获取Spring容器,因为这样不但编写代码时显得繁琐冗余,更会造成极大的资源开销,降低效率。

解决思路:在WEB应用启动时,就加载一次Spring的配置文件,创建好应用上下文对象ApplicationContext,并将其存放在ServletContext(Application)域中,整个应用共享唯一的一个ApplicationContext对象,任何位置都能获取到此对象。

实现方式:方式有两种,其一是使用Listener实现,通过实现ServletContextListener来监听应用的启动;其二是使用Servlet来实现,通过创建一个在应用启动时实例化的Servlet。这两种方式理论上都能实现在应用启动时创建Spring容器。而Spring官方采用的是Listener的方式。

模拟实现:

在web.xml中配置一个初始化参数,指定Spring配置文件的位置:

<!-- 全局初始化参数,指定Spring配置文件位置 -->
<context-param>
 <param-name>applicationContextLocation</param-name>
 <param-value>applicationContext.xml</param-value>
</context-param>

自定义的ServletContextListener:

@WebListener()
public class ContextLoaderListener implements ServletContextListener{
 public void contextInitialized(ServletContextEvent sce) {
     // 获取servletContext
     ServletContext servletContext = sce.getServletContext();
     // 读取web.xml中的初始化参数,获取Spring配置文件的位置
     String contextLocation = servletContext.getInitParameter("applicationContextLocation");
     // 创建ApplicationContext对象
     ApplicationContext context = new ClassPathXmlApplicationContext(contextLocation);
     // 将ApplicationContext对象保存到ServletContext域中
     servletContext.setAttribute("app",context);
 }

 public void contextDestroyed(ServletContextEvent sce) {

 }
}

获取Spring容器的工具类:

public class ApplicationContextUtil {
 // 通过ServletContext域对象获取Spring容器的工具方法
 public static ApplicationContext getApplicationContext(ServletContext servletContext) {
     return (ApplicationContext) servletContext.getAttribute("app");
 }
}

使用场景:

@WebServlet(name = "DemoServlet")
public class DemoServlet extends HttpServlet {
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     // 通过request对象或者this获取ServletContext域对象
     //        ServletContext context = request.getServletContext();
     ServletContext context = this.getServletContext();
//        ApplicationContext app = (ApplicationContext) context.getAttribute("app");
     // 通过自定义工具类获取存储在ServletContext域中的ApplicationContext对象
     ApplicationContext app =ApplicationContextUtil.getApplicationContext(context);
     // 通过Spring容器获取Bean
     DemoDao demoDao = (DemoDao) app.getBean("demoDao");
     demoDao.save();
 }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 }
}

Spring框架提供的获取IOC容器的工具

Spring框架提供了一个监听器ContextLoaderListener,已经实现创建ApplicationContext对象并保存到ServletContext域中的功能。而且还提供了一个工具类WebApplicationContextUtils,用于从ServletContext域中获取IOC容器。我们只需通过如下步骤即可使用:

添加依赖:

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

在web.xml中配置监听器:

<!-- 全局初始化参数,指定Spring配置文件位置 -->
<context-param>
 <!-- 注意此参数名不可更改-->
 <param-name>contextConfigLocation</param-name>
 <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

通过WebApplicationContextUtils获取IOC容器

@WebServlet(name = "DemoServlet")
public class DemoServlet extends HttpServlet {
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     // 通过request对象或者this获取ServletContext域对象
     //        ServletContext context = request.getServletContext();
     ServletContext context = this.getServletContext();
//        ApplicationContext app = (ApplicationContext) context.getAttribute("app");
     // 通过自定义工具类获取存储在ServletContext域中的ApplicationContext对象
//        ApplicationContext app =ApplicationContextUtil.getApplicationContext(context);
     // 通过Spring提供的工具类获取存储在ServletContext域中的ApplicationContext对象
     ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(context);
     // 通过Spring容器获取Bean
     DemoDao demoDao = (DemoDao) app.getBean("demoDao");
     demoDao.save();
 }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

 }
}

posted on 2022-03-24 17:35  敖胤  阅读(34)  评论(0编辑  收藏  举报