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对象的方法

示例代码


 
     org.springframework
     spring-context
     5.2.14.RELEASE
 

 
     junit
     junit
     4.11
     test
 

<?xml version="1.0" encoding="UTF-8"?>


 

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();
     }
    }
    
    
    
    
  • 工厂实例方法:

    public class DynamicFactory {
     public DemoDao getDemoDao() {
         System.out.println("工厂实例方法执行……");
         return new DemoDaoImpl();
     }
    }
    
    
    
    

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();
     }
    }
    
    
    
     
     
    
    

    p名命空间方式:

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

    
        
    
    

    用p:的方式简写为:

    
    
  • 构造方法:

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

    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();
     }
    }
    
    
     
     
    
    

7、Spring加载配置文件

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

name=tom
age=18

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

abc.name=abc
abc.age=25

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





 
 


 
 

8、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中:



dataSourceConfig.xml中:









    
    
    
    

  • 使用配置类开发

    SpringConfiguration.java:

// 标识一个Spring核心配置类,相当于“applicationContext.xml”配置文件
@Configuration
// 开启组件扫描并指定基础包,相当于“”
@ComponentScan("org.silence")
// 用于加载配置文件,相当于“context:property-placeholder location="demo.properties,abc.properties"/>”
@PropertySource(value = {"classpath:abc.properties", "classpath:demo.properties"})
// 导入其他配置类,相当于“”
@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);
}

9、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配置文件的位置:



    applicationContextLocation
    applicationContext.xml

自定义的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容器。我们只需通过如下步骤即可使用:

添加依赖:


    org.springframework
    spring-web
    5.2.14.RELEASE

在web.xml中配置监听器:



    
    contextConfigLocation
    classpath:applicationContext.xml



    org.springframework.web.context.ContextLoaderListener

通过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 {

    }
}