Spring入门和Bean的基础配置

 1.Spring核心思想

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

  • IOC(Inversion of Control)控制反转

在使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。Spring技术对IOC思想进行了实现,提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"。

  • Bean

IOC容器中所存放的一个个对象就叫Bean或Bean对象

  • DI(Dependency Injection)依赖注入

在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入

在Spring中,使用IOC容器管理bean(IOC),在IOC容器内将有依赖关系的bean进行关系绑定(DI)

最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系,达到充分解耦的目的

 2.入门案例

1.创建所需类

创建BookDao,BookDaoImpl,BookService和BookServiceImpl四个类

BookDao

public interface BookDao {
    public void save();
}

BookDaoImpl

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save...");
    }
}

BookService

public interface BookService {
    public void save();
}

BookServiceImpl

public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }

    //提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

 2.在pom.xml添加对应依赖

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

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.在resources下添加spring配置文件applicationContext.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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        id:bean的id
        class:bean的类型,即配置bean的全路径名
    -->
    <bean id="bookDao" class="com.Dao.impl.BookDaoImpl"/>
    <!--
        DI:
        name:表示配置哪一个具体的属性,
             是对应BookServiceImpl里面的那个私有bookDao,利用set方法进行对象注入
        ref: 表示参照哪一个bean,是上面一个Bean对象的id
    -->
    <bean id="bookService" class="com.Service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

</beans>

 4.编写测试类

在com下创建一个Test类

public class Test {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //获取容器中的Bean对象
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

控制台输出

3.bean的基础配置和实例化方法

3.1bean的基础配置

3.1.1bean的id和class属性

    <!--
        id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一
        class:bean的类型,即配置bean的全路径名
    -->
    <bean id="" class=""/>

3.1.2bean的name属性

bean的name属性可以定义别名,别名可以有多个,使用逗号,分号,空格进行分隔

    <!--bookDao2是bookDao的别名-->
    <bean id="bookDao" name="bookDao2" class="com.Dao.impl.BookDaoImpl"/>
    
    <!--bookService2和bookService3都是bookService的别名-->
    <bean id="bookService" name="bookService2 bookService3" class="com.Service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao2"/>
    </bean>

在测试类中测试

public class Test {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //获取容器中的Bean对象
        BookService bookService = (BookService) ctx.getBean("bookService3");
        bookService.save();
    }
}

控制台输出

3.1.3bean的scope属性

bean的作用范围是由scope属性来配置的,在不配置scope属性的情况下,Spring创建的对象默认都是单例的

在com包下创建一个TestScope类

public class TestScope {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
        BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao1);
        System.out.println(bookDao2);
    }
}

控制台输出

scope属性有singleton和prototype两个值

当设置scope="singleton"时

<bean id="bookDao" name="bookDao2" scope="singleton" class="com.Dao.impl.BookDaoImpl"/>

 控制台输出

当设置scope="prototype"时

<bean id="bookDao" name="bookDao2" scope="prototype" class="com.Dao.impl.BookDaoImpl"/>

控制台输出

当scope="singleton"(默认)时,所创建的bean对象都是单例的,

当scope="prototype"时,所创建的bean对象为非单例的

3.2bean的三种实例化方法

3.2.1构造方法

BookDaoImpl中写一个无参构造方法

    public BookDaoImpl() {
        System.out.println("book dao constructor is running ....");
    }

运行Test类

在利用构造方法进行实例化时,无论是public还是private,因为Spring底层用的是反射,所以都能成功创建函数

但不能使用含有参数的构造方法,因为Spring底层调用的类是无参构造方法,当我们创建了一个含参的构造方法时,系统便不在给我们默认创建一个无参构造方法,会导致程序报错

3.2.2静态工厂 

创建OrderDaoOrderDaoImpl

OrderDao

public interface OrderDao {
    public void save();
}

OrderDaoImpl

public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order dao save ...");
    }
}

创建一个com.factory.OrderDaoFactory类

public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        return new OrderDaoImpl();
    }
}

在applicationContext.xml中写入对应的bean

    <!--factory-method:工厂内对应的方法名-->
    <bean id="orderDao" class="com.factory.OrderDaoFactory" factory-method="getOrderDao"/>

创建一个TestInstanceOrder测试类

public class TestInstanceOrder {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");

        orderDao.save();
    }
}

控制台输出

之所以会输出之前创建的那个无参构造方法,是因为Spring 在启动时会把 applicationContext.xml 配置文件中所有的 bean 都实例化并装配好,即使它们没有被调用,也会创建对应的 bean 实例。这是因为 Spring 使用了依赖注入实现 bean 之间的解耦,同时也避免了循环依赖等问题的发生

如果希望避免无用的 bean 实例化,可以考虑使用懒加载(lazy loading)的方式,在需要调用某个 bean 时再进行实例化,可以通过将 bean 的 lazy-init 属性设置为 true 来开启懒加载模式

    <bean id="bookDao" name="bookDao2" scope="prototype" lazy-init="true" class="com.Dao.impl.BookDaoImpl"/>

    <bean id="bookService" name="bookService2 bookService3" lazy-init="true" class="com.Service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao2"/>
    </bean>

    <bean id="orderDao" class="com.factory.OrderDaoFactory" lazy-init="true" factory-method="getOrderDao"/>

 如果只设置了bookDao,在spring创建bookService的时候也会创建其所依赖的bookDao对象,所以都启用懒加载模式

控制台输出

3.2.3动态工厂(FactoryBean

  •  普通方法

创建UserDao和UserDaoImpl类

UserDao 

public interface UserDao {
    public void save();
}

 UserDaoImpl

public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order dao save ...");
    }
}

创建一个com.factory.UserDaoFactory类

public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

在applicationContext.xml中写入对应的bean

    <!--
        第一个userFactory从创建对应的工厂实例化对象
        第二个userDao依赖第一个userFactory对象,调用对应方法
    -->
    <bean id="userFactory" class="com.factory.UserDaoFactory" lazy-init="true"/>
    <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory" lazy-init="true"/>

创建一个TestInstanceUser测试类 

public class TestInstanceUser {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserDao userDao = (UserDao) ctx.getBean("userDao");
        
        userDao.save();
    }
}

控制台输出

由于上述过程太过繁琐,Spring为了简化这种配置方式就提供了一种叫FactoryBean的方式来简化开发。

  • FactoryBean

创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法

public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    //返回所创建类的Class对象
    public Class<?> getObjectType() {
        return UserDao.class;
    }

//    //如果需要创建的对象是非单例则重写isSingleton()方法
//    public boolean isSingleton() {
//        return false;
//    }

}

 在applicationContext.xml进行配置

    <bean id="userDaoFactoryBean"  class="com.factory.UserDaoFactoryBean"/>

创建一个TestFactorBean测试类

public class TestFactorBean {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserDao userDaoFactoryBean = (UserDao) ctx.getBean("userDaoFactoryBean");

        userDaoFactoryBean.save();
    }
}

控制台输出

在具体实践中,构造方法和FactoryBean是使用最多的两种方法,如果需要含参的构造方法,则也需重写无参的构造方法,保证Spring能调用对应的无参构造方法

3.3bean的三种获取方式

 获取bean有如下三种获取方式

1.按名称获取,但需进行类型强转

BookDao bookDao = (BookDao) ctx.getBean("bookDao");

2.按名称获取,解决了类型强转的问题 

 BookDao bookDao = ctx.getBean("bookDao",BookDao.class);

 3.按类型获取,但必须确保IoC容器中该类型对应的bean只有一个

BookDao bookDao = ctx.getBean(BookDao.class);

3.4bean的生命周期 

bean的生命周期是bean对象从创建到销毁的整体过程,可以控制bean创建后到销毁前做的一些事情。

对于bean的整个生命周期

初始化容器
  1. 创建对象(内存分配)
  2. 执行构造方法
  3. 执行属性注入(set操作)
  4. 执行bean初始化方法
使用 bean
  1. 执行业务操作
关闭 / 销毁容器
  1. 执行bean销毁方法

以下具体实现在bean创建之后和bean销毁之前的两个阶段

  • bean创建之后,想要添加内容,比如用来初始化需要用到资源
  • bean销毁之前,想要添加内容,比如用来释放用到的资源
在BookServiceImpl 类中添加两个接口 InitializingBean DisposableBean 并实现接口中的两个方法afterPropertiesSet destroy 
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }

    //提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        //查看是先进行setter注入还是初始化
        System.out.println("set ...");
        this.bookDao = bookDao;
    }

    @Override
    //bean销毁前对应的操作
    public void destroy() throws Exception {
        System.out.println("service destory");
    }

    @Override
    //bean初始化对应的操作
    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
}

运行Test测试类

会发现没有销毁前对应的操作,这是因为Spring的IOC容器是运行在JVM中运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行 main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了,所以没有调用对应的destroy方法

 要解决该问题有如下两种解决办法

  • close关闭容器
ApplicationContext中没有 close 方法,需要将ApplicationContext 更换成ClassPathXmlApplicationContext
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

执行的业务操作结束后调用close方法

ctx.close();

Test类做如下更改

public class Test {
    public static void main(String[] args) {
        //获取IOC容器
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        //获取容器中的Bean对象
        BookService bookService = (BookService) ctx.getBean("bookService3");
        bookService.save();
        ctx.close();
    }
}

控制台输出

  • 注册钩子关闭容器  
在容器未关闭之前,提前设置好回调函数,让 JVM 在退出之前回调此函数来关闭容器

调用ctxregisterShutdownHook()方法

ctx.registerShutdownHook();

Test类做如下更改

public class Test {
    public static void main(String[] args) {
        //获取IOC容器
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //registerShutdownHook()不用放在业务结束后,JVM退出之前会回调此函数来关闭容器
        ctx.registerShutdownHook();

        //获取容器中的Bean对象
        BookService bookService = (BookService) ctx.getBean("bookService3");
        bookService.save();

//        ctx.close();
    }
}

控制台输出

 注:ConfigurableApplicationContextApplicationContext的子类

close和registerShutdownHook都可以用来关闭容器,不同的是close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值