11_Spring-IOC/DI

文章目录

  • Spring
    • IOC控制反转(存)
    • DI依赖注入(取)
    • Spring的优点
    • Spring的核心技术
    • 入门案例
      • 入门案例1
      • 入门案例2
    • 注解
      • 配置类
      • 组件注册功能(IOC)
        • 类直接注册
        • 配置类中注册(JavaConfig)
      • 组件注入功能(DI)
        • 配置类注解中的`@Bean`
        • 构造器注入
        • 方法注入
        • 成员变量注入
    • Spring单元测试
    • Bean
      • Bean的实例化
    • 作用域Scope
    • 生命周期
      • 初始化阶段的方法
      • 容器关闭阶段的方法

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的作用范围

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/520356.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Premiere Pro 2024:赋予创意翅膀,让你的视频飞翔 mac/win版

Premiere Pro 2024&#xff0c;作为Adobe旗下的旗舰视频编辑软件&#xff0c;自推出以来&#xff0c;一直在视频制作领域占据着重要的地位。随着技术的不断进步和创新&#xff0c;Premiere Pro 2024为用户带来了前所未有的编辑体验&#xff0c;重新定义了视频制作的标准。 Pre…

网络原理 - HTTP / HTTPS(5)——https协议

目录 一、HTTPS是什么 为什么要进行加密 二、“加密” 是什么 三、HTTPS的工作过程 &#xff08;1&#xff09;引入对称加密 对称密钥的特点&#xff1a; &#xff08;2&#xff09;引入非对称加密 非对称加密的特点&#xff1a; &#xff08;3&#xff09;中间人攻击…

【C++】STL--vector

目录 vector的使用 vector的定义 vector iterator的使用 vector空间增长问题 vector增删查改 vector深度剖析及模拟实现 vector核心接口模拟实现 使用memcpy拷贝问题 迭代器失效问题 vector的使用 vector的定义 C中&#xff0c;vector是一个模版&#xff0c;第一个参…

PHP数据类型

华子目录 数据类型PHP的八种数据类型基本数据类型&#xff0c;4类复合数据类型&#xff0c;2类特殊数据类型&#xff0c;2类 类型转换在PHP中有两种类型转换方式其他类型转bool类型其他类型转数值类型实例 类型判断获取和设定变量类型获取gettype(变量名)设置settype(变量名,类…

蓝桥杯单片机第十四届省赛模拟考试一

一、基本要求 使用大赛组委会提供的国信长天单片机竞赛实训平台&#xff0c;完成本试题的程序设计与调试。程序编写、调试完成后&#xff0c;选手需通过考试系统提交以准考证号命名的hex文件。不符合以上文件提交要求的作品将被评为零分或者被酌情扣分。 硬件设置&#xff1a; …

数组-二维数组

本笔记为47 数组-二维数组定义方式_哔哩哔哩_bilibili的学习笔记 二维数组 定义方式 注&#xff1a; 常用第二种方式定义&#xff0c;原因&#xff1a;第二种方式更清晰列数可以省行数不可省 &#xff0c;详见上述第四种定义方式 示例&#xff1a; 二维数组 数组名 作用&am…

CSS层叠样式表学习(引入方式)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS引入方式的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 五、CSS的三种样式表 5.1 CSS的三种样式表 按照CSS样式书写的位置(或者引入方式)&#xff0c;CSS样式表可…

笔记-Building Apps with the ABAP RESTful Application Programming Model-Week3

Week3 Unit 1: The Enhanced Business Scenario 本节介绍了将要练习的demo的业务场景,在前两周成果的基础上,也就是只读列表,也可以说是报表APP基础上启用了事务能力,也就是CURD以及自定义业务功能的能力,从创建基本的behavior definition,然后behavior definition proj…

基于GD32的简易数字示波器(2)- 原理图设计

这期记录的是项目实战&#xff0c;做一个简易的数字示波器。 教程来源于嘉立创&#xff0c;202&#xff1a;简易数字示波器项目文档 语雀 下图为示波器的指标 具有选择交流耦合还是直流耦合功能、输入信号不衰减或衰减50倍 输入频率理论最大800KHz输入幅值&#xff08;不衰…

2024HW-->Wireshark攻击流量分析

在HW中&#xff0c;最离不开的&#xff0c;肯定是看监控了&#xff0c;那么就要去了解一些wireshark的基础用法以及攻击的流量&#xff01;&#xff01;&#xff01;&#xff01; 1.Wireshark的基本用法 比如人家面试官给你一段流量包&#xff0c;你要会用 1.分组详情 对于我…

9_springboot_shiro_jwt_多端认证鉴权_整合jwt

1. Shiro框架回顾 到目前为之&#xff0c;Shiro框架本身的知识点已经介绍完了。web环境下&#xff0c;整个框架从使用的角度我们需要关注的几个点&#xff1a; 要使用Shiro框架&#xff0c;就要创建核心部件securityManager 对象。 SpringBoot项目中&#xff0c;引入shiro-spr…

蓝凌OA单点登录实现方案:以统一身份管理提升效率与安全新举措

蓝凌OA的优势与挑战 在数字化浪潮的推动下&#xff0c;企业对于高效、安全的身份管理需求愈发迫切。蓝凌OA系统&#xff0c;以其出色的流程管理和协同办公能力&#xff0c;已经成为众多企业实现数字化转型的重要工具。然而&#xff0c;随着企业信息化建设的不断深入&#xff0…

什么时候外部依赖接口慢拖死应用?

A应用调用B应用&#xff0c;当B应用的接口响应耗时平均都在3000ms的时&#xff0c;如果当前A调用B的请求数达300/s 那么在3s内A应用在途的请求数 300 * 3 900 &#xff0c;按照servlet原理一个http的请求需要一个线程提供服务&#xff0c;即需要900个线程提供服务&#xff0c…

CSS面试题常用知识总结day03

大家好我是没钱的君子下流坯&#xff0c;用自己的话解释自己的知识 前端行业下坡路&#xff0c;甚至可说前端已死&#xff0c;我还想在前段行业在干下去&#xff0c;所以从新开始储备自己的知识。 从CSS——>Javascript——>VUE2——>Vuex、VueRouter、webpack——>…

聚能共创下一代智能终端操作系统 软通动力荣膺“OpenHarmony优秀贡献单位”

近日&#xff0c;由开放原子开源基金会指导&#xff0c;以“开源共享未来”为主题的OpenHarmony社区年会在北京成功举办。本次活动汇集OpenHarmony项目群共建单位及生态伙伴等多方力量&#xff0c;旨在对2023年度OpenHarmony年度开源事业全面总结的同时&#xff0c;吸引更多伙伴…

【Java EE】SpringBoot的创建与简单使用

文章目录 &#x1f340;环境准备&#x1f333;Maven&#x1f332;SpringBoot是什么&#x1f384;Spring Boot 项目创建&#x1f338;使用Idea创建&#x1f338;创建SpringBoot项⽬&#x1f338;SpringBoot项目的运行 ⭕总结 &#x1f340;环境准备 如果你的IDEA是专业版&#…

STM32F103/F407/H743不同GPIO速度配置(HAL库)对应的最高速度

0 前言 如果我们只是使用MCU控制LED的亮灭&#xff0c;完全可以不关心GPIO的速度配置。但如果我们使用到了SPI、MII、RMII、FSMC等这些高速总线就必须要清楚每种GPIO速度配置对应的最高速度&#xff0c;避免因为GPIO速度配置过低导致总线速度提不上去。 既然如此&#xff0c;那…

Linux网络基础 (三) —— Socket

文章目录 Socket 编程基本概念Socket背景Socket 为了解决什么问题 socketsockaddr结构sockaddrsockaddr_insockaddr 和 sockaddr_in 的关系sockaddr_un 示例代码 &#x1f396; 博主的CSDN主页&#xff1a;Ryan.Alaskan Malamute &#x1f4dc; 博主的代码仓库主页 [ Gitee ]&…

OpenHarmony实战:Combo解决方案之ASR芯片移植案例

本方案基于 OpenHarmony LiteOS-M 内核&#xff0c;使用 ASR582X 芯片的 DEV.WIFI.A 开发板进行开发移植。作为典型的 IOT Combo&#xff08;Wi-FiBLE&#xff09;解决方案&#xff0c;本文章介绍 ASR582X 的适配过程。 编译移植 目录规划 本方案的目录结构使用 Board 和 So…

C和C++内存管理

目录&#xff1a; 一&#xff1a;C和C内存分布 二&#xff1a;C动态内存管理方式 三&#xff1a;C动态内存管理方式 四&#xff1a;operator new与operator delete函数 五&#xff1a;new和delete的实现原理 六&#xff1a;定位new表达式(placement-new) 七&#xff1…