五,Spring Bean生命周期

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

1 Spring Bean的生命周期概念重点

对象的生命周期对象从生到死的过程。

Spring工厂中Bean的生命周期并不像想象的那么简单Spring对工厂中的Bean的生命周期进行了细致的划分并允许开发者通过编码或配置的方式定制Bean在生命周期的各个阶段的操作。

  1. 初始化阶段在对象创建后进行一些初始化操作比如对属性值的检查
  2. 销毁阶段在回收对象之前执行一些销毁操作比如资源的释放

1.1 初始化阶段

在Spring工厂创建对象并为属性赋值后就进入到了Bean的初始化阶段Spring会自动调用Bean的初始化方法。在初始化方法中可以做一些初始化操作比如对刚创建的对象进行检查操作。而初始化方法可以由开发者实现接口或者配置的方式定义。

实现InitializingBean接口

public class XxxServiceImpl implements XxxService, InitializingBean {
    private XxxDao xxxDao;

    public XxxServiceImpl() {
        System.out.println("XxxServiceImpl()");
    }

    public XxxDao getXxxDao() {
        return xxxDao;
    }

    public void setXxxDao(XxxDao xxxDao) {
        System.out.println("XxxServiceImpl.setXxxDao");
        this.xxxDao = xxxDao;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("XxxServiceImpl.afterPropertiesSet");
        if(xxxDao == null){
            throw new RuntimeException("xxxDao must not be null");
        }
    }
    ...
}

通过Bean标签的init-method属性

public class XxxServiceImpl implements XxxService, InitializingBean {
    private XxxDao xxxDao;

    public XxxServiceImpl() {
        System.out.println("XxxServiceImpl()");
    }

    public XxxDao getXxxDao() {
        return xxxDao;
    }

    public void setXxxDao(XxxDao xxxDao) {
        System.out.println("XxxServiceImpl.setXxxDao");
        this.xxxDao = xxxDao;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("XxxServiceImpl.afterPropertiesSet");
        if(xxxDao == null){
            throw new RuntimeException("xxxDao must not be null");
        }
    }

    private void initMethod(){
        System.out.println("XxxServiceImpl.init");
    }
    ...
}
<bean id="xxxDao" class="com.bz.dao.impl.XxxDaoImpl"/>
<bean id="xxxService" class="cbzl" init-method="initMethod">
    <property name="xxxDao" ref="xxxDao"/>
</bean>

InitializingBean接口和init-method属性的应用场景的区别

接口方式开发简单一次实现该类型的所有Bean配置都自动生效。

init-method属性对于旧代码而言无需修改即可完成对旧代码的配置。

实战中二者一般不会同时出现如果同时出现执行顺序先接口中的方法后init-method属性配置的方法。

1.2 销毁阶段

在关闭Spring工厂时Spring会销毁其创建的对象此时就进入到了Bean的销毁阶段。在此阶段Spring会自动调用Bean的销毁方法在对象销毁前执行一些操作比如释放资源。

实现DisposableBean接口

public class XxxServiceImpl implements XxxService, InitializingBean, DisposableBean{
    ...
     @Override
    public void destroy() throws Exception {
        System.out.println("XxxServiceImpl.destroy");
    }
}

@Test
public void testInitializingBean(){
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    ctx.close();
}

通过Bean标签的destroy-method属性

public class XxxServiceImpl implements XxxService, InitializingBean, DisposableBean{
    ...
     @Override
    public void destroy() throws Exception {
        System.out.println("XxxServiceImpl.destroy");
    }
    
    public void destroyMethod(){
        System.out.println("XxxServiceImpl.destroyMethod");
    }
}
<bean id="xxxService" class="cbzl" init-method="initMethod" destroy-method="destroyMethod">
    <property name="xxxDao" ref="xxxDao"/>
</bean>

实战中二者一般不会同时出现如果同时出现执行顺序先接口中的方法后destroy-method属性配置的方法。

2 BeanPostProcessor后置Bean处理

BeanPostProcessor可以对Spring工厂中所有的Bean对象实例化后对Bean对象再次加工处理。

编码

User.java

public class User implements Serializable, InitializingBean {
    private Integer userId;
    private String username;
    private String password;

    public User() {
    }

    public User(Integer userId, String username, String password) {
        this.userId = userId;
        this.username = username;
        this.password = password;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("User.afterPropertiesSet");
    }

    public void initMethod(){
        System.out.println("User.initMethod");
    }
    
    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

MyBeanPostProcessor.java

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    //在初始化阶段前调用
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization");
        if (bean.getClass() == User.class) {
            User user = (User)bean;
            user.setPassword("654321");
        }
        return bean;
    }

    @Override
    //在初始化阶段后调用
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor.postProcessAfterInitialization");
        return bean;
    }
}

配置

<bean id="u" class="com.bz.entity.User" init-method="initMethod">
    <property name="userId" value="1"/>
    <property name="username" value="xiaohei"/>
    <property name="password" value="123456"/>
</bean>

<bean class="com.bz.factory.MyBeanPostProcessor"/>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1NwDyJUg-1673778159260)(Spring%20day05.assets/image-20200824224909657.png)]

BeanPostProcessor可以对Spring中配置的所有Bean进行统一处理并且Spring中可以配置多个BeanPostProcessor。

3 Spring AOP的实现原理

AOP面向切面编程灵活的动态的将额外功能添加到现有的原始对象上。

3.1 Spring AOP的实现方式

Spring AOP采用动态代理将额外功能添加到原始对象中。

在这里插入图片描述

Spring的动态代理底层JDK动态代理默认+CGLib动态代理。

3.2 JDK动态代理

JDK动态代理面向接口编程

在这里插入图片描述

JDK动态代理要求原始类型必须实现的有接口。

@Test
public void testJDKProxy(){
    // 创建原始对象
    StudentService studentService = new StudentServiceImpl();
    // 定义额外功能
    java.lang.reflect.InvocationHandler handler = new java.lang.reflect.InvocationHandler(){
        /*
                proxy生成的代理对象
                method: 原始方法
                args: 调用原始方法的参数
             */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //定义额外功能
            System.out.println("前置通知");
            Object result = null;
            try {
                //调用原始对象的方法
                result = method.invoke(studentService, args);
                System.out.println("后置通知");
            }catch(Exception e){
                System.out.println("异常通知");
                throw  e;
            }finally {
                System.out.println("最终通知");
            }
            return result;
        }
    };
    // 创建代理对象
    /*
            classLoader:类加载器
            interfaces: 原始类型实现的接口
            handler: 额外功能
         */
    StudentService proxy = (StudentService)Proxy.newProxyInstance(StudentService.class.getClassLoader(), studentService.getClass().getInterfaces(), handler);
    proxy.showStudents();
    System.out.println(proxy.getClass());
    System.out.println(proxy.getClass().getSuperclass());

}

类加载器的解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-URCUe9l9-1673778159262)(Spring day05.assets/image-20200407155251367.png)]

JDK生成的动态代理类型实现了原始类型的接口但同时继承了Proxy父类只支持为面向接口编程的原始类型生成代理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVz79S9y-1673778159263)(Spring day05.assets/image-20200407155409363.png)]

3.3 CGLib动态代理

CGLib支持面向接口和面向继承的代理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3fWi6we7-1673778159263)(Spring day05.assets/image-20200407161214142.png)]

@Test
public void testCGLib(){
    // 创建原始对象
    BookServiceImpl bookService = new BookServiceImpl();
    // 定义额外功能
    org.springframework.cglib.proxy.InvocationHandler handler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //定义额外功能
            System.out.println("前置通知");
            Object result = null;
            try {
                //调用原始对象的方法
                result = method.invoke(bookService, args);
                System.out.println("后置通知");
            }catch(Exception e){
                System.out.println("异常通知");
                throw  e;
            }finally {
                System.out.println("最终通知");
            }
            return result;
        }
    };
    // 生成代理对象
    Enhancer enhancer = new Enhancer();
    //设置代理类的夫类型
    enhancer.setSuperclass(BookServiceImpl.class);
    //设置额外功能
    enhancer.setCallback(handler);

    BookServiceImpl proxy = (BookServiceImpl)enhancer.create();
    proxy.showBooks();

    System.out.println(proxy.getClass());
    System.out.println(proxy.getClass().getSuperclass());

}

Spring默认使用JDK动态代理如何设置Spring使用CGLib

<aop:config proxy-target-class="true">
        <aop:pointcut id="servicePointcut" expression="execution(* com.bz.service.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
    </aop:config>

3.4 BeanPostProcessor技术

Spring何时使用动态代理生成代理对象

​ Spring使用BeanPorstProcessor后置处理Bean具体讲就是在Spring工厂创建对象之后对这个对象再次加工处理的过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8vuAKglc-1673778159264)(Spring day05.assets/image-20200407164533851.png)]

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before");
        System.out.println("bean = [" + bean + "], beanName = [" + beanName + "]");

        if (bean.getClass() == User.class) {
            User user = (User)bean;
            user.setPassword("654321");
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after");
        System.out.println("bean = [" + bean + "], beanName = [" + beanName + "]");
        return bean;
    }
}

Spring中可以配置多个BeanPostProcessor.在内置BeanPostProcessor中根据切点和切面为特定的原始对象生成代理对象。

4 Spring事务控制的实现原理

4.1 JDBC中事务控制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0W3c5bdn-1673778159265)(Spring day05.assets/image-20200407141236505.png)]

第1次获取连接后将连接保存到线程对象中接下来的多次获取直接从线程对象中获取同1个连接。保证service和dao使用同1连接从而保证事务控制。

4.2 Spring事务控制的原理

Spring保证控制事务的连接和mybatis中执行sql的连接是同1个。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EbriWG02-1673778159266)(Spring day05.assets/image-20200407144923391.png)]

TransactionManager: 获取连接把连接保存到线程中。

MyBaits集成Spring后sqlSession内部获取连接优先从线程中获取。

保证了控制事务Spring的连接和执行SQLMyBaits的连接是同1个。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring