• SpringBean生命周期&扩展接口&简化配置


    目录

    1. 生命周期简图

    2. 扩展接口介绍

    BeanFactoryPostProcessor接口

    Aware接口

    BeanPostProcessor接口

    InitializingBean接口

    DisposableBean接口

    3. spring的简化配置

    1、项目搭建

    2、 Bean的配置和值注入

    3、AOP的示例


    1. 生命周期简图

    生命周期:容器启动--->Bean实例化--->检查Aware接口属性填充设置依赖--->BeanPostProcessor前置处理--->是否实现InitializingBean接口--->执行初始化接口的方法--->是否配置自定义init-method--->执行自定义初始化方法-->BeanPostProcessor后置方法--->注册销毁的接口--->是否实现DisposabeBean接口--->执行销毁接口的方法--->是否配置自定义destory-method-->执行自定义的销毁方法

     Spring帮助我们创建对象的具体步骤👇

    上面这张图要从下往上看,橙色的框表示在spring中使用xml配置或者注解式开发,整个图也可以说是new ClassPathXmlApplicationContext("spring*.xml");这串代码具体做的事情:使用单例模式,只生成一个实例👇

    • 当开发人员需要获取容器中的Bean时,就可以通过应用上下文API接口来获取(ApplicationContext)
    • 当容器启动,就会读取配置文件,将配置文件中的内容转化成BeanDefinition(Bean定义),即实例化,将其转变成一个对象便于在中内存操作(相当于ORM映射的一种)
    • BeanFactoryPostProcessor是一个扩展接口,检查是否有必须定义的Bean未定义、Bean中关键属性未设置,或者是否有重要的接口未实现等。中间的三条杠可以拓展的功能,实现了该接口的类,容器在会将你定义的规则一条条的检查判断是否通过(可以在此拓展,类似过滤器)。
    • BeanFactory相当于IOC的一个抽象工厂模式,通过权限命名实例化Bean,并通过Set方式注入属性,为了避免循环依赖问题,不使用构造函数的方式注入。
    • Aware结尾的接口是一个感知接口,根据接口的具体名称判断功能,比如ApplicationContext就是用来获取上下文的感知接口。感知接口中会将容器中的特定资源通过set方法给到特定的类,也可以在其中进行拓展。
    • 在初始化Bean时,会先检查这个Bean有没有实现InitializingBean这个接口或者配置文件中有没有配置init-method,如果有就调用初始化方法
    • (上图中黄色的横杠就相当于代理部分,AOP)BeanPostProcessor中有两个特定方法,会在Bean初始化前后进行拓展,详见下面接口的介绍👇

    2. 扩展接口介绍

    讲解上图中的接口的具体作用及其使用

    BeanFactoryPostProcessor接口

    该接口是在Bean实例化之前调用的(但BeanFactoryPostProcessor接口也是spring容器提供的扩展接口,所以在此处一同列出),如果有实现了BeanFactoryPostProcessor接口,则容器初始化后,并在Bean实例化之前Spring会回调该接口的postProcessorBeanFactory方法,可以在这个方法中获取Bean的定义信息,并执行一些自定义的操作,如属性检查等

    Aware接口

    在spring中Aware接口表示的是感知接口,表示spring框架在Bean实例化过程中以回调的方式将特定在资源注入到Bean中去(如:ApplicationContext, BeanName,BeanFactory等等)Aware接口本事没有声明任何方法,是一个标记接口,其下有多个子接口,如:BeanNameAware,ApplicationContextAware,BeanFactoryAware等。
    每个特定的子接口都会固定一个特定的方法,并注入特定的资源,如BeanFactoryAware接口,定义了setBeanFactory(BeanFactory beanFactory),在spring框架实例化Bean过程中,将回调该接口,并注入BeanFactory对象。再例如:ApplicationContextAware接口,定义了setApplicationContext(ApplicationContext applicationContext) 方法,在spring完成Bean实例化,将回调该接口,并注入ApplicationContext对象(该对象即spring的上下文)

    Aware接口示例(ApplicationContextAware 是 Aware 接口的子接口):

    1. public class ApplicationContextAwareTest implements ApplicationContextAware {
    2. private static ApplicationContext ctx;
    3. @Override
    4. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    5. ctx = applicationContext;
    6. System.out.println("---- ApplicationContextAware 示例 -----------");
    7. }
    8. public static T getBean(String beanName) {
    9. return (T) ctx.getBean(beanName);
    10. }
    11. }

    配置文件:

    1. <bean id="applicationContextAwareTest" class="org.lisen.springstudy.aware.ApplicationContextAwareTest">
    2. bean>

    BeanPostProcessor接口

    Bean在初始化之前会调用该接口的postProcessBeforeInitialization方法,在初始化完成之后会调用postProcessAfterInitialization方法

    除了我们自己定义的BeanPostProcessor实现外,spring容器也会自动加入几个,如ApplicationContextAwareProcessor、ApplicationListenerDetector,这些都是BeanPostProcessor的实现类。

    BeanPostProcessor接口的定义

    1. public interface BeanPostProcessor {
    2. Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    3. Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    4. }

    方法的第一个参数为bean实例,第二个参数为beanName,且返回值类型为Object,所以这给功能扩展留下了很大的空间,比如:我们可以返回bean实例的代理对象。

    开发示例:

    1. public class BeanPostProcessorTest implements BeanPostProcessor {
    2. @Override
    3. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    4. System.out.println(beanName + " postProcessBeforeInitialization");
    5. return bean;
    6. }
    7. @Override
    8. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    9. System.out.println(beanName + " postProcessAfterInitialization");
    10. return bean;
    11. }
    12. }

    配置文件:

    1. <bean id="beanPostProcessorTest"
    2. class="org.lisen.springstudy.beanpostprocessor.BeanPostProcessorTest">bean>

    InitializingBean接口

    该接口是Bean初始化过程中提供的扩展接口,接口中只定义了一个afterPropertiesSet方法。如果一个bean实现了InitializingBean接口,则当BeanFactory设置完成所有的Bean属性后,会回调afterPropertiesSet方法,可以在该接口中执行自定义的初始化,或者检查是否设置了所有强制属性等。

    也可以通过在配置init-method方法执行自定义的Bean初始化过程,init-method方法通常是无返回的

    示例:

    1. public class InitializingBeanTest implements InitializingBean {
    2. @Override
    3. public void afterPropertiesSet() throws Exception {
    4. System.out.println("InitializingBean.afterPropertiesSet() ......");
    5. }
    6. }

    配置文件:

    1. <bean id="initializingBeanTest" class="org.lisen.springstudy.initializingbean.InitializingBeanTest">
    2. bean>

    DisposableBean接口

    实现DisposableBean接口的Bean,在该Bean消亡时Spring会调用这个接口中定义的destroy方法

    1. public class TestService implements DisposableBean {
    2. public void hello() {
    3. System.out.println("hello work ... ");
    4. }
    5. @Override
    6. public void destroy() throws Exception {
    7. System.out.println("TestService destroy ..... ");
    8. }
    9. }

    在Spring的应用上下文关闭时,spring会回调destroy方法, 如果Bean需要自定义清理工作,则可以实现该接口。

    除了实现DisposableBean接口外,还可以配置destroy-method方法来实现自定义的清理工作

    3. spring的简化配置

    1、项目搭建

    启用注解,对spring的配置进行简化,这也是现在常用的方式

    1、创建一个maven web工程

    2、将web改为web3.1,见上期:Maven的下载与使用_小阿飞_的博客-CSDN博客

    3、修改pom.xml文件,引入必要的包

    1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    3. <modelVersion>4.0.0modelVersion>
    4. <groupId>com.zking.spdemogroupId>
    5. <artifactId>spdemoartifactId>
    6. <packaging>warpackaging>
    7. <version>0.0.1-SNAPSHOTversion>
    8. <name>spdemo Maven Webappname>
    9. <url>http://maven.apache.orgurl>
    10. <properties>
    11. <spring.version>5.3.18spring.version>
    12. <junit.version>4.12junit.version>
    13. properties>
    14. <dependencies>
    15. <dependency>
    16. <groupId>org.springframeworkgroupId>
    17. <artifactId>spring-contextartifactId>
    18. <version>${spring.version}version>
    19. dependency>
    20. <dependency>
    21. <groupId>org.springframeworkgroupId>
    22. <artifactId>spring-webartifactId>
    23. <version>${spring.version}version>
    24. dependency>
    25. <dependency>
    26. <groupId>org.springframeworkgroupId>
    27. <artifactId>spring-webmvcartifactId>
    28. <version>${spring.version}version>
    29. dependency>
    30. <dependency>
    31. <groupId>org.springframeworkgroupId>
    32. <artifactId>spring-ormartifactId>
    33. <version>${spring.version}version>
    34. dependency>
    35. <dependency>
    36. <groupId>org.springframeworkgroupId>
    37. <artifactId>spring-aspectsartifactId>
    38. <version>${spring.version}version>
    39. dependency>
    40. <dependency>
    41. <groupId>org.springframeworkgroupId>
    42. <artifactId>spring-testartifactId>
    43. <version>${spring.version}version>
    44. dependency>
    45. <dependency>
    46. <groupId>junitgroupId>
    47. <artifactId>junitartifactId>
    48. <version>${junit.version}version>
    49. <scope>testscope>
    50. dependency>
    51. dependencies>
    52. <build>
    53. <finalName>mavendemofinalName>
    54. <plugins>
    55. <plugin>
    56. <groupId>org.apache.maven.pluginsgroupId>
    57. <artifactId>maven-compiler-pluginartifactId>
    58. <version>3.7.0version>
    59. <configuration>
    60. <source>1.8source>
    61. <target>1.8target>
    62. <encoding>UTF-8encoding>
    63. configuration>
    64. plugin>
    65. plugins>
    66. build>
    67. project>

    4、在resources根目录下添加spring的配置文件 spring.xml

    即使用扫描器扫描指定包下的注解

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:context="http://www.springframework.org/schema/context"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context
    8. http://www.springframework.org/schema/context/spring-context.xsd">
    9. <context:component-scan base-package="com.zking"/>
    10. <bean id="propPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    11. <property name="locations">
    12. <list>
    13. <value>classpath:/project.propertiesvalue>
    14. list>
    15. property>
    16. bean>
    17. beans>

    程序方式注册如下(java配置类)

    配置类中的一个配置方法:相当于上面xml中配置id=propPlaceholder的bean,这两种配置方式作用相同,按照自己的习惯来选择

    @Bean:配置一个bean

    方法名PropertySourcesPlaceholderConfigurer 作为这个bean的id,并且在方法中进行初始化,和上面的配置文件中
                
                    classpath:/project.properties
                

            
    的作用相同

    1. @Configuration
    2. public class ConfigurationBean {
    3. @Bean
    4. public static PropertySourcesPlaceholderConfigurer setPropertiesFile() {
    5. PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer();
    6. ClassPathResource contextPath = new ClassPathResource("/project.properties");
    7. config.setLocation(contextPath);
    8. return config;
    9. }
    10. }

    5、在resources根目录下新建一个project.properties文件,和spring.xml的配置文件中的配置是相对应的👇

    2、 Bean的配置和值注入

    1、创建并注册一个Bean

    1. //相当于xml中配置bean,但是这个bean没有配置id,默认为student
    2. @Component
    3. public class Student {
    4. //将配置文件project.properties中的属性值注入
    5. @Value("${stu.name}")
    6. private String name;
    7. public String getName() {
    8. return name;
    9. }
    10. //但是在注入时使用的是注解,没有使用set注入
    11. /*
    12. * public void setName(String name) { this.name = name; }
    13. */
    14. @Override
    15. public String toString() {
    16. return "Student [name=" + name + "]";
    17. }
    18. }

    2、通过容器获取Bean

    1. ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
    2. Student stu = (Student)ctx.getBean("student");
    3. //stu.setName("zs");
    4. System.out.println(stu);

    3、AOP的示例

    1、创建一个切面,记录程序运行时间

    一个AOP拦截切面相当于AOP中的适配器(适配器=通知(Advice)+切入点(Pointcut))

    1. @Component //中配置bean
    2. @Aspect //一个切面
    3. @EnableAspectJAutoProxy //用来开发切面的功能的代理
    4. public class ProcessAop {
    5. //方法参数ProceedingJoinPoint不可变
    6. //环绕通知,拦截切面aop:
    7. /**
    8. * 1. 第一个* 对方法的返回值不作要求
    9. * 2. com.zking.mavendemo.service 包名
    10. * 3. .. 表示service包及其子包
    11. * 4. 第二*表示所有的类
    12. * 5. hello* 表示方法名以hello开头
    13. * 6. (..) 表示对方法的参数不做要求
    14. * @param joinPoint
    15. * @return
    16. * @throws Throwable
    17. */
    18. //execution执行对象,*拦截方法的返回值,拦截的方法对象com.zking.spdemo.service..*.hello*(..)
    19. @Around("execution(* com.zking.spdemo.service..*.hello*(..))")
    20. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    21. long t1=System.currentTimeMillis();
    22. //调用目标方法
    23. Object returnValue = joinPoint.proceed(joinPoint.getArgs());
    24. long t2=System.currentTimeMillis();
    25. System.out.println("所拦截的方法调用费时:"+(t2-t1));
    26. return returnValue;
    27. }
    28. }

    2、创建一个service接口和实现类演示AOP切面接口、实现类

    1. public interface IStudentService {
    2. void hello(String msg);
    3. }
    1. //@Component //spring托管的bean
    2. //Service层的组件
    3. @Service("studentService") //括号中可放id,也可不放
    4. public class StudentService implements IStudentService{
    5. @Override
    6. public void hello(String msg) {
    7. System.out.println(".....hello:"+msg);
    8. }
    9. }

    3、测试

    也可以使用注解进行测试👇

    1. @RunWith(SpringJUnit4ClassRunner.class) //spring-test
    2. //classpath:target下classpath下
    3. @ContextConfiguration(locations = "classpath:spring.xml") //上下文配置文件的地址,在测试运行前获取上下文
    4. public class BaseTest {
    5. }
    1. public class StudentTest extends BaseTest{
    2. @Autowired //从容器中直接获取bean放入变量保存,不用get,set
    3. private IStudentService studentService;
    4. @Test
    5. public void Test() {
    6. studentService.hello("李四");
    7. }
    8. }

    然后直接在类中右键选择jUnit Test进行测试即可

  • 相关阅读:
    Windows Server 2016使用MBR2GPT.EXE教程!
    Thanos Receiver
    实时流量监控
    LVGL源码分析(1):lv_ll链表的实现
    linux autogroup
    【蓝桥杯物联网赛项学习日志】Day2 中断矩阵按键
    微信小程序转为App并上架应用市场
    Linux Shell脚本编写指南
    语法分析出错,不是 GROUP BY 表达式
    18.2 使用NPCAP库抓取数据包
  • 原文地址:https://blog.csdn.net/yifei_345678/article/details/126443583