11_Spring-IOC/DI

Spring

  • Spring Framework通常人们称之为Spring
  • 框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架
  • Spring是一个分层的Java SE/EE full-stack(一站式) 轻量级开源框架

在这里插入图片描述
eg:

在这里插入图片描述
上图中AdminServiceImpl类型的实例其实创建了多次,去建立引用关系多次,维护起来比较麻烦,也浪费了内存的空间

想要做的事情:在整个应用程序中使用同一个adminServiceImpl的实例

  • 将其放在ConcurrentHashMap中,然后后面每一次使用的时候通过同一个ConcurrentHashMap来取
  • 要保证使用的是同一个ConcurrentHashMap,使用ServletContext保存,并且做到了整个应用程序中的共享
@WebListener
public class InitializationListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
    
        Map<String, Object> data = new ConcurrentHashMap<>();
        data.put("adminService", new AdminServiceImpl());
        servletContext.setAttribute("applicationData", data);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

使用:
ServletContext servletContext = getServletContext();
Map map = (Map) servletContext.getAttribute("applicationData");
AdminService adminService = (AdminService) map.get("adminService");

在这里插入图片描述


IOC控制反转(存)

  • Inverse of Control
    • 控制:实例的生成权
      • 应用程序控制实例的生成
    • 反转:原来是应用程序控制实例的生成,现在把这个过程反转给Spring
      • Spring框架来控制实例的生成
    • 容器:Spring容器、IOC容器、容器
      • 管理的实例
    • 组件:Spring管理的实例对象可以称为组件
    • 注册:提供一些信息给Spring容器,管理这些信息对应的实例
      • 这些实例就成为了Spring容器的组件
  • 获取依赖对象被反转了,它是被动获取;
    • 由Spring去生成实例,并由Spring去管理实例
  • 正转就是自己去new一个对象,自己获取对象

DI依赖注入(取)

  • 经过了控制反转,Spring容器管理应用程序运行过程中的实例,从Spring容器中把这些实例取出来,就是依赖注入
  • 核心点:从容器中获得应用程序所需要的实例,并且给应用程序中的成员变量做赋值
  • DI这个概念是IOC的延续,DI是在IOC的基础上

Spring的优点

  • 方便解耦,简化开发(高内聚低耦合)
  • AOP编程的支持
  • 声明式 事务的支持
  • 方便程序的测试
  • 方便集成各种优秀框架
  • 降低JavaEE API的使用难度

Spring的核心技术

依赖注入、事件、资源、i18(nternationalizatio)n国际化、校验、数据绑定、类型转换、Spring Expression Language、AOP(面向切面编程)


入门案例

入门案例1

  1. 导入依赖
<dependencies>
    <!--引入依赖 beans、context、aop、expression、core、jcl 5+1-->
    <!--spring-context-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
  1. 提供接口和实现类
public interface UserService {
    public void sayHello(String name);
}
public class UserServiceImpl implements UserService{
    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }
}

  • 原先使用实例的时候,是通过构造方法生成的;后续要变更为来源Spring容器
  1. 在配置文件中注册组件
<!--spring配置文件的名称通常叫application(-xxx).xml-->
<!-- bean definitions here -->
<!--控制反转-->

<!--id属性 -- 组件在容器中的唯一标识-->
<!--name属性 -- 名称 -- 通常省略不写-->
<!--class 全类名 -- 实现类的全类名-->
<!--组件 注册 -- 将实例交给spring管理的过程我们称之为注册-->

<bean id="userService" class="com.coo1heisenberg.demo1.service.UserServiceImpl"/>
  1. 从容器中取出组件,执行方法
    • ApplicationContext底层封装了ConcurrentHashMap
      • ClassPathXmlApplicationContext加载的是.xml配置文件
      • AnnotationConfigApplicationContext加载的是配置类

在这里插入图片描述

  • 方法1:按照组件的id/name取出组件
// spring容器

// 1. 初始化容器,并注册组件
ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("application.xml");

// 2. 从容器中取出组件
UserService userService1 = (UserService) applicationContext.getBean("userService");
userService1.sayHello("spring");
// hello spring
  • 方法2:可以写接口的class,也可以写实现类的class
// 可以写接口的class,也可以写实现类的class
// 建议写接口
// 如果容器中某个类型的组件只有一个,可以按照类型取出

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

UserService userService2 = applicationContext.getBean(UserService.class);
  • 方法3:id + 类型
ApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("application.xml");

UserService userService3 = applicationContext.getBean("userService", UserService.class);

入门案例2

  • 维护组件之间的依赖关系,在容器中注册dao层和service层组件,并且service层的组件依赖于dao层组件
  • dao类
public interface UserDao {
}
public class UserDaoImpl implements UserDao{
}
  • service类
public interface UserService {
    public void sayHello(String name);
}
public class UserServiceImpl implements UserService{
    UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }
}

  • application.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">

    <bean id="userService" class="com.coo1heisenberg.demo1.service.UserServiceImpl">
        <!--注册userServiceImpl这个类型的组件的时候,实例化的过程会执行set方法(setUserDao)
            这个set方法的形参是UserDao类型的实例,通过id从容器中取出作为形参
            this.userDao = userDao; 给userServiceImpl这个组件的userDao成员变量进行赋值
            -> 依赖注入
        -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean id="userDao" class="com.coo1heisenberg.demo1.dao.UserDaoImpl"/>
</beans>
  • 单元测试类
@Test
public void test2() {
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("application.xml");
    UserDao userDao = applicationContext.getBean(UserDao.class);
    
    // 直接从容器中取出的userDao实例和直接从容器中取出的userService实例中的userDao成员变量是否是同一个
    UserService userService = applicationContext.getBean(UserService.class);
    // userDao的地址值与上面的同一个
}

注解

配置类

  • 配置类:承担做通用配置的功能,同时在配置类中可以组件注册
    • 我们在类定义上增加一些功能性的注解,增加一些通用性的配置
    • 我们在类中的方法里注册组件

eg:

/**
 * 配置类;替代配置文件
 *
 * 提供配置类,后面初始化容器的时候,加载的就是配置类(不再是配置文件)
 */

// 这里增加功能性的注解
@Configuration
public class ApplicationConfiguration {
	// 类中的方法做组件的注册
}

组件注册功能(IOC)

类直接注册
  • @Component组件
@Target(ElementType.TYPE)// 该注解写在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    */
   String value() default "";

}

过程:

  1. 组件注册功能首先要打开扫描开关
@Configuration
@ComponentScan("com.coo1heisenberg.demo2")
public class ApplicationConfiguration {
}
  1. 组件注册功能的注解@Component
@Component
public class AdminServiceImpl implements AdminService{
}

思考:为什么我们提供一个扫描包目录(@ComponentScan("包目录")),然后在包目录下的类中使用注解就可以注册组件?

  1. 提供包目录,能否获得这个包以及这个包的子包下的所有的类的全限定类名
  2. 通过全限定类名,通过反射的方式获得对应的class → Class.forName()
  3. List<Class> classList = 通过上面的过程获得
  4. 遍历获得其中的单个class呢
  5. class.isAnnotationPresent(注解的class) → 判断这个类上是否有注解

eg:

@SneakyThrows
@Test
public void testIsAnnotationPresent() {
    Map<String,Object> map = new ConcurrentHashMap<>();
    List<Class<?>> classList = Arrays.asList(UserServiceImpl.class, 
                                             GoodServiceImpl.class, 
                                             UserServiceImpl.class);
    for (Class<?> singleClass : classList) {
        if (singleClass.isAnnotationPresent(Component.class)) {
            Object instance = singleClass.newInstance();
            String key = singleClass.getName();
            map.put(key, instance);
        }
    }
    System.out.println(map);
}
  1. 组件id值的设定
/**
 * 增加其value属性,value属性值就是id
 * 如果没有增加value属性,id默认值就是类名的首字母小写
 */
@Component // 组件id的默认值是adminServiceImpl
@Component("adminService") // 使用value属性值指定了组件id为adminService
public class AdminServiceImpl implements AdminService{
}
  • 类似的注解:
    • @Service → 通常是Service层的组件使用的注解,Service层组件也能使用@Component
    • @Repository → 通常是Dao层的组件使用@Repository注解,dao层组件也能使用@Component
    • @Configuration配置类组件
    • @ControllerSpringMVC阶段
  • @Service@Repository@Controller@Configuration,这些注解的ElementType都是TYPE,也就是这些注解都是要写在类定义上

eg:

// @Component // 组件id的默认值是adminServiceImpl
// @Component("adminService") // 使用value属性值指定了组件id为adminService
@Service("adminService")
public class AdminServiceImpl implements AdminService{
}
  1. 使用
@Test
public void testFetchComponent() {
    // 1. 初始化容器
    ApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
            
    // 2. 取出组件
    AdminService adminService = applicationContext.getBean(AdminService.class);
    UserService userService = applicationContext.getBean(UserService.class);
    /**
     * NoSuchBeanDefinitionException:没加入到容器中
     */
}

配置类中注册(JavaConfig)
  • @Bean注解
    • 该注解增加在方法上,并且可以和其他注解共存
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {}

eg:

/**
 * 在配置类中注册AdminServiceImpl组件
 *
 * 在配置类中写的是方法 -> 提供一个返回值类型为AdminServiceImpl的实例
 * 应用程序启动的时候会加载配置类 -> 加载配置类 -> 执行配置类中的方法(记号:@Bean)
 * -> 方法的返回值注册为容器的组件
 */
 
@Bean
public AdminService adminService() {
    AdminServiceImpl adminService = new AdminServiceImpl();
    return adminService;
}
/**
 * 如果使用配置类中的组件,默认值为方法名
 * eg:Bean("userService")
 *
 * 可以使用Bean注解的value方法值指令
 * eg:Bean("userServiceImpl")
 */
@Bean
public UserService userService() {
    return new UserServiceImpl();
}


/**
 * NoUniqueBeanDefinitionException:没有唯一的Bean定义的异常
 */
/**
 * @Bean对应的方法形参,默认是按照类型从容器中取出组件
 * 
 * 如果这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
 */
 
@Bean
public UserService userService(@Qualifier("userDaoImpl") UserDao userDao) {
    UserServiceImpl userService = new UserServiceImpl();
    userService.setUserDao(userDao);
    return userService;
}

组件注入功能(DI)

注意:要求是容器中的组件,才能够使用注入功能的注解


配置类注解中的@Bean
  • 方法的形参,就是从容器中取出的组件

构造器注入
  • 如果仅仅执行了一个有参构造器,实例化的时候会执行有参构造方法
  • 如果有多个有参构造器,在要使用的构造器上面加上@Autowired

eg:

@Component
public class AdminServiceImpl implements AdminService {
    UserDao userDao;

    String username;

    /**
     * 如果存在多个构造器,可以使用指定的构造器
     * 使用注解 @Autowired 来指定构造器
     *
     */
    public AdminServiceImpl(UserDao userDao, String username) {
        this.userDao = userDao;
        this.username = username;
    }

    // 如果有无参构造方法,默认使用无参构造方法
    // 如果没有无参构造方法,默认实例化时会使用有参构造方法

    // 形参,默认是按照类型从容器中取出组件
    // 如果形参这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
    public AdminServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
}

/**
 * 因为String username在容器中不存在,所有给username赋一个初始值
 */
@Autowired
public AdminServiceImpl(UserDao userDao, @Value("zhangsan") String username) {
    this.userDao = userDao;
    this.username = username;
}

方法注入
  • 可以是组件中的任意方法,但是通常这样的方法我们用的是set方法在方法上增加@Autowired注解

eg:

/**
 * 方法注入
 */
@Component
public class GoodsServiceImpl implements GoodsService{
    UserDao userDao;

    // 形参,默认是按照类型从容器中取出组件
    // 如果形参这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

成员变量注入
  • 注入功能的注解使用这三组:
    1. @Autowired
    2. @Autowired + @Qualifier
    3. @Resource

eg:

@Component
public class UserServiceImpl implements UserService {
    // 形参,默认是按照类型从容器中取出组件
    // 如果形参这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
    @Autowired
    UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }
}

容器中注册了这些类型的组件,OrderDao类型的组件(orderDaoImpl)、UserDao类型的组件(userDaoImpl1userDaoImpl2

@Repository
public class OrderDaoImpl implements OrderDao{
}
@Repository
public class UserDaoImpl1 implements UserDao{
}
@Repository
public class UserDaoImpl2 implements UserDao{
}


从容器中取出的对应的组件,执行注入,要注意,要求是在容器中的组件里注入:

/**
 * 要使用注入功能注解,一定要保证当前类是容器中的组件
 */
@Service
public class UserServiceImpl implements UserService{
    @Autowired //容器中该类型的组件只有一个
    OrderDao orderDao;
    
    @Autowired //使用@Qualifier注解指定组件id
    @Qualifier("userDaoImpl1")
    UserDao userDao1;
    
    @Resource(name = "userDaoImpl2") //默认是按照组件的类型去注入,使用name属性指定组件id
    UserDao userDao2;
}

注意

  • 开发业务代码过程中,最常用的方式只使用一个@Autowired :绝大部分组件在容器中这个类型的组件只有一个
  • 要在容器中的组件中使用这些注解,使用注解的话,所处的类上要有组件注册功能的注解,且处于扫描包目录

Spring单元测试

  • 导入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.5.RELEASE</version>
    <scope>test</scope>
</dependency>
  • 使用@Runwith@ContextConfiguration注解,在单元测试类中可以使用注入功能的注解

eg:

@RunWith(SpringJUnit4ClassRunner.class) // 固定写法
@ContextConfiguration(classes = AppConfiguration.class) //配置类的属性
public class SpringTest {
    @Autowired
    CategoryService categoryService;
    
    @Autowired
    ApplicationContext applicationContext;
}

Bean

Bean的实例化

  • 无参构造方法默认方法

  • 有参构造方法

    • 如果你有无参构造方法默认使用无参构造方法
    • 如果你没有无参构造方法,实例化时会使用有参构造方法
    • 如果存在多个构造器,可以指定使用你标记构造器(@Autowired
  • 工厂

    • 工厂提供实例,而实例交给Spring容器来进行管理
    • 通过配置类中的方法,方法上增加@Bean注解,该方法的返回值注册为容器中的组件
    • 在什么情况下使用的工厂的方式:
      • 比如动态代理 ProxyFactoryBean
      • 使用构造器要提供大量的参数,可以将这些参数封装在工厂里
      • 对已有的框架或者代码进行封装
        • SqlSessionFactoryBean
    • 主要就是对已有的代码做些拓展
    @Configuration
    @ComponentScan("com.coo1heisenberg,demo1")
    public class AppConfiguration {
    
    	//这就是一种工厂
    	@Bean
    	public UserService serviceProxy(UserService userService) {
    		return ProxyUtil.getServiceProxy(userService);
    	}
    }
    
    • FactoryBean
      • Spring提供的接口
public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

	// 这个工厂生产的方法/返回实例的方法
    @Nullable
    T getObject() throws Exception;

	// 提供类型信息
    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

eg:

@Component
public class UserFactoryBean implements FactoryBean<User> {

    /**
     * 完成组件的实例化
     * @return 组件类型和返回值相关
     * @throws Exception
     */
    @Override
    public User getObject() throws Exception {
        User user = new User();
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

eg:

  • factory目录
@Component
public class UserServiceFactoryBean implements FactoryBean<UserService> {
    @Autowired
    UserService instance;

    //使用代理方式
    @Override
    public UserService getObject() throws Exception {
        UserService proxy = (UserService) Proxy.newProxyInstance(
                UserServiceFactoryBean.class.getClassLoader()
                , UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) 
                    throws Throwable {
                        System.out.println("start");
                        Object invoke = method.invoke(instance, args);
                        System.out.println("close");
                        return invoke;
                    }
                });
        return proxy;
    }

    @Override
    public Class<?> getObjectType() {
        return UserService.class;
    }
}

  • service目录
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void hello() {
        // System.out.println("start");
        System.out.println("hello world!");
        // System.out.println("close");
    }

    @Override
    public void goodBye() {
        // System.out.println("start");
        System.out.println("bye bye");
        // System.out.println("close");
    }
}

  • 测试目录
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfiguration.class)
public class MyTest {
    @Autowired
            @Qualifier("userServiceFactoryBean")
    UserService userService;

    @Test
    public void testFactoryBean() {
        userService.hello(); // InvocationHandler.invoke(userService, helloMethod, null)
    }
}

BeanFactory和FactoryBean之间的区别

  • BeanFactory是ApplicationContext的父接口,它就是容器注册并管理所有的组件去获得容器中的组件最终都是通过getBean方法获得
  • FactoryBean注册的是特定的组件getObject方法返回的是什么实例,注册的就是什么组件

作用域Scope

  • Singleton:单例,每一次取出组件都是同一个组件
  • Prototype:原型,每一次取出组件都是全新的组件
  • 默认值是singleton,我们通常省略不写
  • @Scope 使用value属性指定作用域

eg:

@Component
@Scope("singleton")
public class SingletonBean {
}

@Component
@Scope("prototype")
public class PrototypeBean {
}

@Component
public class DefaultBean {
}

注册3个组件,分别给到不同的scope,然后从容器中取出组件多次,查看是否是同一个组件查看内存地址

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfiguration.class)
public class ScopeTest {
    @Autowired
    SingletonBean singletonBean1; // singletonBean1:SingletonBean@2049
    @Autowired
    SingletonBean singletonBean2; // singletonBean2:SingletonBean@2049
    @Autowired
    SingletonBean singletonBean3; // singletonBean3:SingletonBean@2049

    @Autowired
    PrototypeBean prototypeBean1; // prototypeBean1:PrototypeBean@2050
    @Autowired
    PrototypeBean prototypeBean2; // prototypeBean2:PrototypeBean@2051
    @Autowired
    PrototypeBean prototypeBean3; // prototypeBean3:PrototypeBean@2052
    @Autowired
    DefaultBean defaultBean1; // defaultBean1:DefaultBean@2053
    @Autowired
    DefaultBean defaultBean2; // defaultBean2:DefaultBean@2053
    @Autowired
    DefaultBean defaultBean3; // defaultBean3:DefaultBean@2053
    
}

生命周期

  • 生命周期:组件在容器中要完成实例化,组件从实例化开始直至可用状态会执行到哪些过程
  • 组件开始执行生命周期的时间
    • 容器初始化的时候单例组件
  • prototype类型的组件开始执行生命周期的时间
    • 从容器中取出组件的时候开始执行生命周期,不取就不执行,取一次执行一次,取两次执行两次
  • 对于Bean(容器中的组件),在容器中也会经历这样的一些阶段:
    • 容器初始化的时候,组件做准备性工作 → 放入到容器中之前,会执行哪一些方法来准备实例
    • 组件可以从容器中取出,提供服务,比如从容器中取出userService实例,调用其sayHello方法
    • 容器关闭,组件做销毁工作

初始化阶段的方法

  1. Bean的实例化(有参构造方法、无参构造方法)
  2. 设置参数方法(方法注入、成员变量注入)
  3. BeanNameAwareBeanFactoryAwareApplicationContextAware
    • 如果从容器中取出一个UserService组件,这个组件是否知道它在容器中的组件名称,来自于哪里?
      • 容器知道,组件不知道
    • 这几个Aware就是为了让组件本身也能获取这些信息,是接口
  4. BeanPostProcessorBean的后处理器,这个后指的是实例化之后,其实还是在放入到容器中之前)的postProcessBeforeInitialization后面有初始化方法
  5. InitializingBeanafterPropertiesSet方法(通常是一些框架提供的类初始化的方式
  6. 自定义的init方法(我们自己做开发的时候通常使用的方式
  7. BeanPostProcessorpostProcessAfterInitialization方法
    • BeanPostProcessor和正在执行生命周期的组件并不是同一个,BeanPostProcessor是额外提供的,而额外提供的这个BeanPostProcessor组件。它的作用范围:除了BeanPostProcessor本身,其他的所有组件,具有通用性

在这里插入图片描述
eg:

@Service
public class UserServiceImpl implements UserService
        , BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
    public UserServiceImpl() {
        System.out.println("UserServiceImpl的实例化");
    }

    UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }


    String beanName;
    @Override
    public void setBeanName(String beanName) {
        System.out.println("beanName = " + beanName);
        this.beanName = beanName;
    }

    BeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("通常是一些框架提供的类,它的初始化方式");
    }

    @PostConstruct // 在实例化之后,自定义的初始化方法
    public void customInit() {
        System.out.println("通常是自定义的初始化方法");
    }
}
public class CommonBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行BeanPostProcessor的postProcessAfterInitialization");
        return bean;
    }
}

在这里插入图片描述


容器关闭阶段的方法

  • 单例的组件才会执行到对应的方法
  • DisposableBeandestroy方法(通常是框架提供的类采用这种方式
  • 自定义的destroy方法(通常是自定义的

eg:

 // 实现DisposableBean接口
 @Override
 public void destroy() throws Exception {
     System.out.println("实现DisposableBean接口的destroy");
 }
 
 // 实现自定义的Destroy
 @PreDestroy
 public void customDestroy() {
     System.out.println("自定义的destroy");
 }

注意事项

  • 生命周期的方法,不是都会执行到的,有些执行是需要条件的。另外要注意BeanPostProcessor的作用范围

相关推荐

  1. day10-16:Spring Security

    2024-04-13 03:42:02       8 阅读
  2. 11.spring boot 启动源码(一)

    2024-04-13 03:42:02       34 阅读
  3. 11Spring CLI中Action指南

    2024-04-13 03:42:02       21 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-13 03:42:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-13 03:42:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-13 03:42:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-13 03:42:02       20 阅读

热门阅读

  1. linux下的常用压缩格式及压缩命令

    2024-04-13 03:42:02       16 阅读
  2. C++项目实战与经验分享

    2024-04-13 03:42:02       14 阅读
  3. 什么是H5应用加固?

    2024-04-13 03:42:02       16 阅读
  4. 【2024】将二进制的node_exporter包制作成rpm包

    2024-04-13 03:42:02       17 阅读
  5. 学习笔记-微服务基础(黑马程序员)

    2024-04-13 03:42:02       16 阅读
  6. ORACAL执行计划

    2024-04-13 03:42:02       11 阅读
  7. 在vue3中使用pinia

    2024-04-13 03:42:02       13 阅读
  8. 正则表达式|*+?

    2024-04-13 03:42:02       33 阅读
  9. 深入理解 Golang 中 New() 和 make() 的区别

    2024-04-13 03:42:02       17 阅读
  10. 构建高效运维平台与数据中心可视化方案

    2024-04-13 03:42:02       15 阅读
  11. <网络安全>《72 微课堂<什么是靶场?>》

    2024-04-13 03:42:02       18 阅读