SpringFramework部分学习总结

SpringFramework部分学习总结

Spring概述及Spring体系介绍

Spring概述

Spring是一个开源框架,它由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson)创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。

◆目的:解决企业应用开发的复杂性

◆功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

◆范围:任何Java应用

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

Spring体系

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式.
在这里插入图片描述
每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

关于Spring的安装配置

  1. 创建maven项目

  2. 添加maven依赖

    在自己创建的maven项目pom.xml文件中添加依赖:

    <dependencies>
        <!-- Spring IOC最小依赖是beans、context,我们引入context依赖,maven会自动将beans依赖一并引入 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
    </dependencies>
    

IOC(控制反转)

什么是IOC(控制反转)

**Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。**在Java开发中,**Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。**如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,请看下面分析:

●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(也包括文件等)。

●反转?,哪些方面反转了?:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

IOC 少不了IOC容器,也就是实例化抽象的地方。

IOC容器初始化

ApplicationContext实现类
  1. ClassPathXmlApplicationContext

    在class路径下加载 classpath:***.xml(类路径加载)

    是spring读取xml最常用的类。而我们一般操作的是她的接口ApplicationContext。

    使用方法:

    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student ss = ctx.getBean(Student.class);
    System.out.println(ss);
    
  2. AnnotationConfigApplicationContext

    用于基于注解的配置

  3. FileSystemXmlApplicationContext

    文件系统路径加载

  4. XmlWebApplicationContext

    专门为web应用准备的,从相对于Web根目录的路径中装载配置文件完成初始化

Application初始化路径

1.路径前缀

//1.前缀classpath:表示的是项目的classpath下相对路径 
ApplicationContext appCt = new ClassPathXmlApplicationContext("classpath:student.spring.xml"); 

//2.没有前缀:默认为项目的classpath下相对路径 
ApplicationContext appCt = new ClassPathXmlApplicationContext("student.spring.xml");

2.路径中的通配符


//使用通配符加载所有符合要求的文件 
  ApplicationContext appCt = new ClassPathXmlApplicationContext("*.spring.xml");
通过xml方式配置管理bean
1.优缺点

优点:xml配置方式对代码没有任何侵入性,更改配置无需重新编译

缺点:配置相对于注解多、工程量大

2.xml方式配置实例
<?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
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.lanou3g.spring.Student">   
        <!-- bean的管理和配置-->
    </bean>
</beans>

注:id属性代表该bean在Spring容器中的唯一标识

​ class属性指定该bean的类型,需要指定类的全名

3.导入其他配置文件
<beans>
    <!-- 被导入的文件必须包含完整的beans根节点 -->
    <import resource="student_beans.xml"/>
    <!-- 导入类路径下的resources目录下的student_beans.xml文件 -->
    <import resource="resources/auto_inject.xml"/>
    <!-- spring会忽略开头的/, 但是官方不建议路径以/开始,因为这里写的都是相对路径 -->
    <import resource="/resources/Source.xml"/>
    <bean id="stu1" class="com.lanou3g.spring.studentDaoImpl1"/>
    <bean id="stu2" class="com.lanou3g.spring.studentDaoImpl2"/>
</beans>
通过注解方式管理bean
1.优缺点:

优点:在Java类中已经包含很多上下文信息,所有在Java类上直接加注解可以省略很多属性,配置简单。

缺点:对代码有侵入性,如果改了是基于注解的配置信息,就需要重新编译、打包

2.两种方式开启注解

第一种:在xml配置文件中通过context命名空间中的annotation-config标签或component-scan标签开启注解配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解支持 -->
  	<context:annotation-config/>
  	<!-- 开启注解支持,同时指定扫描的包路径;若使用此方式无需annotation-config标签 -->
  	<context:component-scan base-package="com.lanou3g.spring" />
</beans>

第二种:通过注解的方式开启注解配置支持

@Configuration   //用`@Configuration`注解标注的类就相当于一个xml配置文件
@ComponentScan("com.lanou3g.spring")
public class App {
    
  	@Bean
    public StudentDao studentDao() {
       return new StudentDao();
    }
    public static void main(String[] args) {
      // 如果在非web工程中使用这种方式开启注解支持,需要使用下面的方式初始化ioc容器,否则@ComponentScan注解会被忽略 
      ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);
    }
}
3.常用注解
@Autowired

根据依赖的类型自动注入。该注解可以用于标注在 属性、setter方法、构造方法、或带参数的普通方法之上。Spring会将符合的参数自动注入到属性、或方法参数中。

注:如果注入的属性是一个单值类型,但Spring上下文中有多个匹配类型的候选Bean,那将会直接报错,因为Spring不知道该用哪个注入

@Resource

根据依赖bean的名称自动注入。除了可以通过Spring特有的@Autowired注解进行依赖注入外,Spring也支持原生JSR-250中的 @Resource注解。

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

@Resource注解的name属性不是必须的,如果不指定,那默认的名称将取决于变量名称、setter方法的名称(取决于注解在变量上还是setter方法上)

@Resource注解还有个好处,如果按名称无法找到一个匹配bean的时候,它会自动按照类型查找注入。

@Required

用于标注在setter方法之上,表示此属性必须要注入。如果容器初始化该bean时没有合适的值注入到该属性则直接抛出异常。

在spring5.1之后弃用,因为必须要注入的属性,我们可以通过构造参数来注入。

@Primary

当我们通过@Autowired注解来注入属性或者参数时,如果遇到上面说的单值属性有多个匹配类型候选bean,如果其中一个候选Bean上配置了@Primary注解或者在xml配置中设置了primary=“true”,那将不会报错,Spring会优先将带primary标记的候选bean注入(当然,如果有多个带primary标记的匹配类型还是会报错滴)。

@Qualifier

该注解可以让我们通过名称在多个候选bean中进一步限定。

public class Student {

    @Autowired
    @Qualifier("main")
    private People peo;
}

通过@Qualifier限定构造参数注入

public class Student {
    private People peo;
    private PeopleDao peoDao;
    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
        CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

给bean添加qualifier标识符

在xml中我们可以通过子标签来给一个bean添加标识符, 以供注入时@Qualifier使用。如果不显示指定标识符,spring会用id、name代替。

<bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 
</bean>

如果bean是通过注解的方式配置的,我们可以这样做

@Component
@Qualifier("Action")	// 给bean添加qualifier标识符
public class studentimpl implements Student {
}
@Component

@Component注解是一个通用注解,代表一个组件,可以用来标识所有希望让Spring管理的bean。

@Bean

@Bean注解允许我们通过注解的方式定义在Java代码中定义bean的配置元数据,相当于xml配置文件中的

@Configuration

@Configuration注解的类相当于xml配置文件中的

@Scope

@Scope注解可以限定通过注解配置的bean的作用域。适用于通过@Component(或其派生注解)、@Bean注解配置的bean。@Scope注解的默认值是singleton(单例)

@Configuration
public class MyConf{
    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
    }
}
@Qualifier

@Qualifier注解可以限定通过注解配置的bean的qualifier标识符。适用于通过@Component(或其派生注解)、@Bean注解配置的bean。 相当于xml配置的的子标签

@Import

通过注解的方式导入其他注解配置
示例:

@Import({MyConf.class})//导入MyConf类配置,MyConf类用@Configuration注解
@Configuration
public class Application {
    @Bean
    public StudentService studentService() {
      return new StudentServiceImpl();
    }
}


@Configuration
public class MyConf {
    @Bean
    public StudentDao studentDao() {
      return new StudentDaoImpl();
    }
}
@ImportResource

通过注解的方式引入xml配置

示例:

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

xml配置文件

<beans>
    <context:property-placeholder location="classpath:jdbc.properties"/>
</beans>

jdbc.properties文件

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=sa

main方法调用:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
}
@PropertySource

此注解用于加载properties文件到环境中,然后我们就可以通过env获取properties文件中定义的属性

使用示例:

@Configuration
@PropertySource("classpath:app.properties")
public class AppConfig {
    @Autowired
    Environment env;
    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}
@Inject

@Inject 注解作用和Spring的@Autowired注解效果一致

@Named

@Name注解有两个作用。 当用作方法的参数时和Spring的@Qualifier注解效果类似;而用作类上面的注解时作用和Spring的@Component注解类似

xml和注解方式混合使用

xml配置方式和注解方式各有优缺点,两者相互混合使用,更方便管理

Spring提供注解的方式虽说简单但并不是为了要完全取代xml配置方式 ,两者各有适用场景,比如数据源的配置, 就不适合通过注解来配置。否则数据源配置一发生变化就得改代码。

混合实例:

1.在xml中配置数据源

jdbc.properties

jdbc.url=jdbc:mysql://localhost:3306/test?charsetEncoding=utf8
jdbc.driver=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root
<beans>
    <!-- 开启注解配置支持 -->
    <!-- <context:annotation-config/> -->
	  <!-- 扫描注解的包路径,配置了这个后无需再配置上面那个 -->
  	<context:component-scan base-package="com.lanou3g.spring"/>
    <!-- 读取properties配置文件,用于替换spring配置中的${}占位符 -->
  	<context:property-placeholder location="classpath:jdbc.properties"/>
   <beanv id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="url" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

2.通过注解配置bean和依赖注入

@Configuration   //相当于用注解配置了一个xml配置文件(相当于xml中的<beans>)
public class App {
    @Autowired// 按照类型来自动注入属性
    private DataSource dataSource;
    @Bean
    public AccountRepository accountRepository() {
       return new JdbcAccountRepository(dataSource);
    }
    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }
    public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class); 
}
}
在一个配置中导入另一个配置

1.xml中导入其他xml配置

<beans>
    <!-- 被导入的文件必须包含完整的beans根节点 -->
    <import resource="student_beans.xml"/>
    <!-- 导入类路径下的resources目录下的student_beans.xml文件 -->
    <import resource="resources/auto_inject.xml"/>
    <!-- spring会忽略开头的/, 但是官方不建议路径以/开始,因为这里写的都是相对路径 -->
    <import resource="/resources/Source.xml"/>
    <bean id="stu1" class="com.lanou3g.spring.studentDaoImpl1"/>
    <bean id="stu2" class="com.lanou3g.spring.studentDaoImpl2"/>
</beans>

2.注解方式导入

在这可新建一个类用于演示,用@Configuration注解此类

@Configuration
public class MyConf {
    @Bean("sb")//相当于<bean id="sb">
    // @Bean//<bean id="samllPeople">
    public People smallPeople(){
        return new studentImpl2();
    }
}

@Configuration
@Import(MyConf.class)
@ImportResource("applicationContext.xml")//导入xml配置
/*@ComponentScan(basePackages = "com.lanou3g.spring")*/
/*@Component("app")*/
public class App 
{
     AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(App.class);
     ctx.registerShutdownHook();
     People people=ctx.getBean("sb",People.class);
     System.out.println(people);
}

注解导入xml配置

也就是用@Configuration注解的类导入xml配置

@Configuration
@ImportResource("classpath:/com/lanou3g.spring/properties-config.xml")//导入xml配置
public class AppConfig {
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}
管理bean的作用域
  • 我们可以通过scope指定bean的作用域
    singleton(默认): 单例
    prototype: 每调用一次getBean,都是一个新的对象

1.singleton

<bean id="messageDao" class="com.lanou3g.spring.dao.MessageDaoImpl2" scope="singleton" />

2.prototype

<bean id="messageDao" class="com.lanou3g.spring.dao.MessageDaoImpl2" scope="prototype" />
管理bean的生命周期

通过init-method、destroy-method指定bean的生命周期方法

init-method:bean的初始化,指定初始化回调方法,在实例化 bean 时,立即调用该方法

destroy-method:bean的销毁,销毁回调方法,只有从容器中移除 bean 之后,才能调用该方法。

我们希望在bean被初始化的时候,就初始化某些资源。为了达到这样的目的,我们可以给类添加初始化方法;又希望在bean被销毁的时候,就释放或关闭某些资源。为了达到这样的目的,我们可以给类添加指定的销毁方法。

public class MessageDaoImpl{
    public void myInit() {
        System.out.println("MessageDaoImpl init");
    }
    public void myDestroy() {
        System.out.println("MessageDaoImpl destroy");
    }
}

xml配置

<bean id="md" class="com.lanou3g.spring.dao.MessageDaoImpl"
    init-method="myInit" destroy-method="myDestroy"
/>

测试类调用

/**
 * 练习IOC容器中bean的生命周期
 */
public void testLifeCycle(ApplicationContext ctx) {
    MessageDaoImpl messageDao = ctx.getBean("md", MessageDaoImpl.class);
    System.out.println(messageDao);
}
实例化bean的方式
构造方法

这种实例化的方式可能在我们平时的开发中用到的是最多的,因为在xml文件中配置简单并且也不需要额外的工厂类来实现。

xml配置

 <!--applicationContext.xml配置:-->  
 <bean id="student" class="cn.mytest.service.impl.Student"></bean>  

id是对象的名称,class是要实例化的类,然后再通过正常的方式进调用实例化的类即可

Java代码

 public void App(){ 
         //加载spring配置文件  
      ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
       //调用getBean方法取得被实例化的对象。  
      Student lazyStudent = ctx.getBean(Student.class);
      System.out.println("名称:" + lazyStudent.getSname());
      System.out.println("爱称:" + lazyStudent.getNickName());
      lazyStudent.getFruit().eatFruit();
 }  

注:采用这种实例化方式要注意的是:要实例化的类中如果有构造器的话,一定要有一个无参的构造器。

静态工厂方法

想通过这种方式进行实例化就要具备两个条件:1、要有工厂类及其工厂方法;2、工厂方法是静态的。首先创建工程类及其静态方法:

package com.lanou3g.spring.bean;

import com.lanou3g.spring.simple.Apple;
import com.lanou3g.spring.simple.Banana;
import com.lanou3g.spring.simple.Fruit;

/**
 * 水果工厂
 * 练习静态工厂方法创建bean
 */
public class FruitFactory {

    /**
     * 静态工厂方法
     * @param name
     * @return
     */
    public static Fruit produceFruit(String name) {
        switch (name) {
            case "apple":
                return new Apple();
            case "banana":
                return new Banana();
            default:
                return null;
        }
    }
}

xml配置文件创建静态工厂bean

   <!-- 通过静态工厂创建bean -->
    <bean id="big_apple" class="com.lanou3g.spring.bean.FruitFactory"  factory-method="produceFruit">
        <constructor-arg name="name" value="banana" />
    </bean>

id是实例化的对象的名称,class是工厂类,也就实现实例化类的静态方法所属的类,factory-method是实现实例化类的静态方法。然后按照正常的调用方法去调用即可:

 public void App(){ 
         //加载spring配置文件  
      ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
       //调用getBean方法取得被实例化的对象。  
       //通过静态工厂方法来初始化bean
        Object obj = ctx.getBean("big_apple");
        System.out.println(obj);
 }  
非静态工厂方法

使用该实例化方式工厂方法不需要是静态的,但是在spring的配置文件中需要配置更多的内容,,首先创建工厂类及工厂方法:

package com.lanou3g.spring.bean;

import com.lanou3g.spring.simple.Apple;
import com.lanou3g.spring.simple.Banana;
import com.lanou3g.spring.simple.Fruit;

public class FruitFactory {
    /**
     *  (非静态)普通工厂方法
     * @param name
     * @return
     */
    public Fruit produceFruitByInstrance(String name) {
        switch (name) {
            case "apple":
                return new Apple();
            case "banana":
                return new Banana();
            default:
                return null;
        }
    }

}

然后再去配置spring配置文件

 <!-- 通过非静态工厂方法创建bean -->
    <bean id="fruitFactory" class="com.lanou3g.spring.bean.FruitFactory" />
    <bean id="big_banana" factory-bean="fruitFactory" factory-method="produceFruitByInstrance">
    <constructor-arg name="name" value="banana" />
    </bean>

这里需要配置两个bean,第一个bean使用的构造器方法实例化工厂类,第二个bean中的id是实例化对象的名称,factory-bean对应的被实例化的工厂类的对象名称,也就是第一个bean的id,factory-method是非静态工厂方法。

bean的name属性

对一个bean命名除了使用id属性命名外,还有name属性命名。

name属性和id属性的区别:

id用来标识bean,是唯一的,且只有一个,不能用特殊字符:×#@等 ,不能用数字开头;name可以给一个bean指定多个名称,并可能与其他的bean重名,name 可以用特殊字符,并且一个bean可以用多个名称:如name=“stuImpl_1,stuImpl_2,stuImpl_3”,用逗号隔开。通过id和name都可以取出该Bean.

实例:

在 applicationContext.xml中配置name属性

<bean id="people" name="stuImpl_1,stuImpl_2,stuImpl_3" class="com.lanou3g.spring.impl.studentImpl2"/>

在java类里面获取applicationContext.xml中配置的name属性stuImpl_1,stuImpl_2,stuImpl_3

 studentImpl2 stu= ctx.getBean("stuImpl_1",studentImpl2.class);
 System.out.println(stu.queryStudent());
优雅的关闭IOC容器——注册shutdown hook
 AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(App.class);
ctx.registerShutdownHook();
懒加载与非懒加载

什么是懒加载?

用lazy-init。告诉spring容器是否以懒加载的方式创造对象。用的时候才加载构造,不用的时候不加载;

取值:true(懒,真正调用到的时候再加载)、false(非懒,已启动spring容器就创建对象)、default(懒)

怎么用?

在这我先建一个测试类LazyLiuCheng

package com.lanou3g.spring.bean;
public class LazyLiuCheng {
    public  void init(){
        System.out.println("liucheng init");
    }
}

在xml配置文件中LazyLiuCheng的bean,b并设置lazy-init的值为true,即懒加载(调用时加载)

<bean id="lazyBean" class="com.lanou3g.spring.bean.LazyLiuCheng" init-method="init" lazy-init="true"/>

在java 类main方法中调用加载

LazyLiuCheng lazy=ctx.getBean( LazyLiuCheng.class);
System.out.println(lazy);
//运行结果:liucheng init
//        com.lanou3g.spring.bean.LazyLiuCheng@3571b748

若java类中未调用加载

不会显示上述运行结果

若xml配置文件中lazy-init未设置或为false,即非懒加载,不过是否调用加载,容器启动的时候立刻创建对象。

DI(依赖注入)

什么是DI(依赖注入)?

依赖注入(Dependency Injection)是Spring框架的核心之一。

所谓依赖注入指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。

常用依赖注入有以下几个方向:

构造方法注入、setter注入、自动装配、方法注入

构造方法注入

  • 有参注入

    实体类Student

    import lombok.Getter;
    import lombok.Setter;
    @Getter
    @Setter
    public class student {
        private String sname;
        private String gender;
        public student(String sname, String gender) {
            this.sname = sname;
            this.gender=gender;
        }
    

    xml配置

    
    <bean id="stu" class="com.lanou3g.spring.Student">       <constructor-arg name="sname" value="哈哈" />
          <constructor-arg name="age" value="22" />
    </bean>
    
  • 无参注入

<bean id="stu" class="com.lanou3g.spring.Student" />

setter注入

匿名内部注入

主要通过在xml配置文件中创建bean时加上标签,通过标签配置注入一个匿名内部bean。

具体实现如下:

  • 在xml配置文件中给Student注入一个匿名内部bean
    <bean id="stu1" class="com.lanou3g.spring.bean.Student">
        <property name="fruit">
            <bean class="com.lanou3g.spring.simple.Apple" />
        </property>
    </bean>
  • java文件中调用
 Student stu = ctx.getBean("stu1", Student.class);
 stu.getFruit().eatFruit();
//运行结果:eating Banana
集合类型属性注入

我们在java中常用的集合有List、Set、Map和Properties,

Spring为集合提供了对应的标签:

注入 list元素

注入 set元素

注入 map元素

注入 properties 元素

在xml配置文件中注入集合类型参数

 <!-- 注入集合、Map类型参数 -->
    <bean id="myCollection" class="com.lanou3g.spring.bean.InjectionSet" >
        <!-- 注入List集合 -->
        <property name="hobbies">
            <list>
                <value>玩游戏</value>
                <value>看书</value>
                <value>写代码</value>
            </list>
        </property>

        <!-- 注入Set集合 -->
        <property name="sex">
            <set>
                <value></value>
                <value></value>
                <value>不男不女</value>
            </set>
        </property>

        <!--注入map集合-->
        <property name="gameTitles">
            <map>
                <entry key="LOL" value="嘴强王者"></entry>
                <entry key="王者农药" value="甩锅大神"></entry>
                <entry key="和平精英">
                    <null />
                </entry>
            </map>
        </property>
        <!--注入properties-->
        <property name="nickName">
   <props>
       <prop key="张三">33</prop>
       <prop key="李四">44</prop>
   </props>
         </property>
    </bean>

在App.java类中测试

        InjectionSet set = ctx.getBean(InjectionSet.class);
        // 获取注入的list属性
        List<Object> hobbies = set.getHobbies();
        for(Object hobby : hobbies) {
            System.out.println("类型:" + hobby.getClass()+", 值:" + hobby);
        }
        // 获取注入的map属性
        System.out.println(set.getGameTitles());
        // 获取注入的set属性
        System.out.println(set.getSex());
        // 获取注入的Properties属性
        System.out.println(set.getNickName());
//运行结果
类型:class java.lang.String, 值:玩游戏
类型:class java.lang.String, 值:看书
类型:class java.lang.String, 值:写代码
{LOL=嘴强王者, 王者农药=甩锅大神, 和平精英=null}
[男, 女, 不男不女]
{张三=33, 李四=44}
注入NULL、空字符串
 <bean class="com.lanou3g.spring.bean.Null">
        <property name="name" value=""/>
    </bean>

    <bean class="com.lanou3g.spring.bean.Null">
        <property name="name">
            <null/>
        </property>
    </bean>

注入复合属性

通过下面的方式来配置

<bean id="stu" class="com.lanou3g.spring.bean.student">
        <!-- 注入复合属性 -->
        <property name="fred.bob.age" value="22"/>
 </bean>
注入外部属性properties文件属性值

注入外部properties配置文件,先要有一个properties配置文件

jdbc.url=jdbc:mysql://localhost:3306/lanou?charsetEncoding=utf8
jdbc.driver=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root

然后在xml配置文件中引入外部的properties文件,在此有两种方式

<!-- 引入外部的properties文件开始 -->
    <!-- 方式一 -->
    <<bean        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties" />
    </bean>

    <!-- 方式二 -->
    <!-- 引入外部的properties文件结束 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 将外部properties文件中的属性注入到bean中 -->
    <bean id="jdbcConf" class="com.lanou3g.spring.bean.JDBCConf">
        <property name="url" value="${jdbc.url}" />
        <property name="driver" value="${jdbc.driver}" />
        <property name="userName" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

新建一个JDBCConf类用于测试

package com.lanou3g.spring.bean;
import lombok.Getter;
import lombok.Setter;
/**
 * 各参数都从配置中注入
 */
@Setter
@Getter
public class JDBCConf {
    private String url;
    private String driver;
    private String userName;
    private String password;
}

最终main方法调用测试结果

 JDBCConf jdbcConf = ctx.getBean(JDBCConf.class);
        System.out.println(jdbcConf.getUrl());
        System.out.println(jdbcConf.getDriver());
        System.out.println(jdbcConf.getUserName());
        System.out.println(jdbcConf.getPassword());
//运行结果:url:jdbc:mysql://localhost:3306/test?charsetEncoding=utf8
//  driver:com.mysql.jdbc.Driver
//  username:root
//  password:root
p和c命名空间注入属性

通过p和c命名空间注入属性和构造参数

xml文件配置

//通过c命名空间来注入 通过set
   <bean id="liu" class="com.lanou3g.spring.bean.test" c:name="阿成" />
//通过p命名空间来注入 通过构造方法
   <bean id="liu1" class="com.lanou3g.spring.bean.test" p:name="阿成1" />

测试类运行代码

         test stu = ctx.getBean("liu", test.class);
        System.out.println("c注入,name: " + stu.getName());

        test stu1 = ctx.getBean("liu1", test.class);
        System.out.println("p注入, name: " +stu1.getName());
//运行结果c注入,name: 刘成
//p注入, name: 刘成1

还有不要忘了在xml文件中配置摘要,若不配置p和c命名空间注入不能用

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

自动装配

  • 属性装配(自动注入)

先创建一个类

package com.lanou3g.spring.bean;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AutoInjectByNameTeacher {
    private String tname;
    private student student;
}

再在spring配置xml文件中创建bean配置属性装配

 <bean id="teacherByName" class="com.lanou3g.spring.bean.AutoInjectByNameTeacher"
          autowire="byName">
        <property name="tname" value="John" />
    </bean>
  • 类型装配(自动注入)

创建一个AutoInjectByTypeTeacher类

package com.lanou3g.spring.bean;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class AutoInjectByTypeTeacher {
    private String tname;
    private LazyLiuCheng lazyLiuCheng;
}

在xml配置文件配置类型装配

 <bean id="teacherByType" class="com.lanou3g.spring.bean.AutoInjectByTypeTeacher"
          autowire="byType">
        <property name="tname" value="John" />
    </bean>
  • 构造器装配(自动注入)

创建一个AutoInjectByConstructorTeacher类

package com.lanou3g.spring.bean;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AutoInjectByConstructorTeacher {
    public AutoInjectByConstructorTeacher(){}
   /* public AutoInjectByConstructorTeacher(LazyLiuCheng lazyLiuCheng){
        System.out.println("构造参数传入:"+lazyLiuCheng);
    }*/
    private  String tname;
    private LazyLiuCheng lazyLiuCheng;
}

在xml配置文件配置类型装配

   <bean id="teacherByConstructor" class="com.lanou3g.spring.bean.AutoInjectByConstructorTeacher"
          autowire="constructor">
        <property name="tname" value="John" />
    </bean>
  • 测试调用(三种)
public void testAutoInject(ApplicationContext ctx) {
        // 按照名称自动注入属性
        AutoInjectByNameTeacher teacher = ctx.getBean("teacherByName" , AutoInjectByNameTeacher.class);
        System.out.println("教师名称: " + teacher.getTname());
        System.out.println("所教学生:" + teacher.getStudent().getSname());
        // 按照类型自动注入属性(容器中符合此类型的bean只能有一个,否则报错)
        AutoInjectByTypeTeacher teacherByType = ctx.getBean("teacherByType" , AutoInjectByTypeTeacher.class);
        System.out.println("教师名称(类型): " + teacherByType.getTname());
        teacherByType.getLazyStudent().destroy();
        // 下面这行示范了按照类型自动注入,但符合类型不唯一的情况(会直接报错)
        // 解决方案就是换成按照名称自动注入
        //System.out.println("学生姓名:" + teacherByType.getStudent().getSname());
        // 构造器参数自动注入(按照类型)(容器中符合此类型的bean只能有一个,否则报错)
        AutoInjectByConstructorTeacher teacherByConstructor = ctx.getBean("teacherByConstructor" , AutoInjectByConstructorTeacher.class);
        System.out.println("教师名称(构造参数): " +       teacherByConstructor.getTname());
        teacherByConstructor.getLazyStudent().destroy();

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lic_dream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值