万字对深入Spring的底层核心原理解析

class userServiceProxy extends UserService(){  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }class userServiceProxy extends UserService(){  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }class userServiceProxy extends UserService(){  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }class userServiceProxy extends UserService(){  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }class userServiceProxy extends UserService(){  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }class userServiceProxy extends UserService(){  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }spring原理分析

主要了解spring全家桶最基本的原理、内容

Spring Framework

  • AOP

  • 依赖注入

springboot是基于Spring Framework

@Component
public class OrderService{
    
}
@Component
public class UserService{
    @Autowired
    private OrderService orderService;
    public void test(){
        System.out.println("test");
    }
}
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService)context.getBean("userService");
userService.test();
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService)context.getBean("userService");
userService.test();

以上两句都是学习spring时较为重要的两句话

第一句在springboot加载时被调用了 在spring3.0增加了该类 这个是以一个class的方式一个类的形式来充当一个配置文件 直接可以通过使用注解来定义扫描路径(同样也是创建一个spring容器)

第二句在使用springmvc时也调用了类似的PathXml了 作用是创建一个容器,传一个配置文件给spring容器(文件中可以定义一下扫描路径等操作) spring入门级时使用较多

然后可以通过上面的context来getBean获取bean对象,再执行方法

二者其实很相像,一个是以类class的形式,另一个是以xml配置的方式

spring中的bean与我们Java中普通对象有什么区别

这个我们可以测试

新增一个OrderService类这个类使用component注解标注为组件

并且在UserService中进行依赖注入(@Autowired)

这个时候我们观看语句一中context.getBean的userService中的orderService是否有值呢?

  • 肯定是有值的,因为OrderService是用@component注解标准,且在UserService中使用@Autowired依赖注入orderService

  • 此时我们再测试一下自己new UserService对象出来,看看其中OrderService是否有值---没有值

这就是spring中的bean与Java中普通对象的区别

一个小小的区别用来理解这个bean

现在开始关注spring是如何创建的bean(userService)呢

无参构造得到对象

UserService.class--->构造方法---》对象(得到一个普通对象)

  • 无论如何spring怎么操作,肯定是使用了UserService中的构造方法--》得到一个对象(可以发现UserService只有一个构造方法也就是无参构造方法---通过无参构造方法来获取对象),此时该对象中的orderService属性肯定是没有值的

依赖注入

这个时候就可以发现

UserService.class--->无参构造方法--->对象--->?--->bean

中间是什么操作使得对象中orderService有值(因为bean对象中的有值)

?==依赖注入(属性赋值)

spring会给加了@Autowired注解的属性赋值

依赖注入大概的工作原理(假设userService,其实任意对象均可以)

for(Field field:userService.getClass().getFields()){//获取userService对象中所有属性
    if(field.isAnnotationPresent(Autowired.class)){//判断属性上是否携带了@Autowired注解
        field.set(userService,????); //进行设置值
    }
}

这时候还有一个问题是 值 应该从哪里来

初始化前

又可以得出新的路线

UserService.class--->无参构造方法--->对象--->依赖注入(属性赋值)--->初始化前--->初始化--->初始化后--->bean

引入一个小需求
    定义了一个User对象(希望可以在mysql中保存数据-User对象-admin)
    再UserService中定义了这个user属性admin  
    如果我们在User对象上加@Component注解以及在UserService的User.admin加@Autowired注解
    那我们在userService对象中的admin是否有值,是否是我们需要的值呢
    有值,但是不满足我们的需求不是我们需要的值,它此时的值就相当于是一个new User对象
    因为我们没有做过任何对mysql进行操作的命令,spring根本不晓得到底连那个数据库等等,所以值是new User对象
    因此我们应该编写一个方法,该方法内应该定义一系列对数据库的操作返回什么结果,赋值给我们的admin(这一系列操作应该在我们的初始化前这里进行操作)
    UserService.class--->无参构造方法--->对象--->依赖注入(属性赋值)--->初始化前(方法调用)--->初始化--->初始化后--->bean
        在UserService中有多个方法,如何让spring去调用你指定得方法呢?需要告诉spring在创建这个bean的过程中间去调用这个方法(@PostConstruct)
        spring找的就是当前对象的哪些方法上面加了@PostConstruct注解
        spring内部初始化前调用方法大概实现
        for(Method method:userService.getClass().getMethods()){//获取到userService对象中的所有方法
            if(method.isAnnotationPresent(PostConstruct.class)){//看看那个方法上有@PostConstruct注解
                method.invoke(userService,参数);//执行那个方法并且传递什么参数
            }
        }

初始化

其实最开始spring只有初始化操作,没有初始化前与初始化后

以上初始化前的操作可以有另一种实现方式(不调用定义的方法不加@PostConstruct)

而是userService实现InitalizingBean并且实现其内部的afterPropertiesSet()方法

把以前定义的方法的内容全部放在这个afterPropertiesSet中

spring就会去调用这个afterPropertiesSet方法的内容

源码分析
    //初始化
    try{
        invokeInitMethods(beanName,wrappedBean,mbd);
    }catch(Throwable ex){
        throw new BeanCreationException(
        mbd!=null?mbd.getResourceDescription():null),
        beanName,"Invocation of init method failed",ex);
    }

进入invokeInitMethods方法查看会发现
    protected void invokeInitMethods(String beanName,Object bean,@Notable RootBeanDefinition mbd )
    这个Object bean其实就是我们spring根据某一个类创建的对象bean
    执行bean instanceof InitalizingBean得到一个结果boolean initalizingBean
    进行判断 这个bean是否为true实现了InitalizingBean接口么
    为true就会判断是否打开安全管理器   再执行 ((InitalizingBean)bean).afterPropertiesSet()方法
    为false就直接执行((InitalizingBean)bean).afterPropertiesSet()方法

初始化后

重点:实现了AOP

bean的创建生命周期

UserService.class--->无参构造方法--->对象(得到一个普通对象)--->依赖注入(属性赋值)--->初始化前(@PostConstruct)--->初始化(initializingBean)--->初始化后(AOP)--->代理对象--->bean

aop与spring事务联系紧密

定义了一个类

@Aspect
@Component
public class HHXYAspect{
    @Before("execution(public void xxx.xxx.UserService.test())") //切的UserService里的test方法  UserService有一个切面
    public void HHXYBefore(JoinPoint joinPoint){
        System.out.println("hhxybefore")
    }
}

开启AOP可以使用注解 @EnableAspectJAutoProxy

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService)context.getBean("userService");
userService.test();

在userService处打断点为什么userService中orderService没有值呢?为null

难道开启aop后依赖注入都不生效了么??? 并不是 其实我们如上面的步骤就可以发现了

UserService.class--->无参构造方法--->对象(得到一个普通对象)--->依赖注入(属性赋值)--->初始化前(@PostConstruct)--->初始化(initializingBean)--->初始化后(AOP)--->代理对象--->bean

spring进行初始化后(aop)--->得到代理对象--->bean 经过aop得到代理对象后续没有任何操作了直接变成bean了 设计时得到代理对象后续就没有依赖注入这种操作了

此时我们可以假设通过aop后得到代理对象中orderService属性就是空的

但是debug进入到UserService中的test方法就会发现orderService此时是有值的

这是为什么呢???

分析

UserService此时进行aop应该使用的是什么 cglib

cglib是通过什么进行aop也就是生成代理对象----》通过代理,父子量继承

//以上spring流程中代理对象其实也就是通过代理类生成的代理对象
//UserServiceProxy--->代理对象

//cglib底层大概原理
    class userServiceProxy extends UserService{  //生成代理类去继承UserService
        public void test(){ //就可以重写test方法
            
        }
    }

再看回过来看这个方法。其实也就是拿到代理对象,接着调用代理对象中的test方法

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = context.getBean("userService");
userService.test();

进入到代理类的test方法

//为什么生成代理类因为有一个切面HHXYAspect    
class userServiceProxy extends UserService{  //生成代理类去继承UserService
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            super.test();//子类的test方法可以调用父类的test方法  也就是调用被代理的那个test方法
        }
    }

但是我们回过头来仔细思考 它这里只发生了一下方法调用 (子类的test调用,父类的方法调用,中间的切面方法) 那么我的UserService里的OrderService为什么会有值呢

思路是正确的,但是具体是怎么做的呢???

//UserServiceProxy--->代理对象--->代理对象.target==普通对象    
//因为这个普通对象通过属性赋值(依赖注入),它的orderService是有值的
//其实这一步可以认为是生成代理对象的一步
class userServiceProxy extends UserService{  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }

为什么spring没有在代理对象那一步进行属性赋值呢----没太必要

通常切面都是用来切方法的 最终我会生成某一个代理对象

代理对象最重要的意义是啥 当这个代理对象去执行某个方法的时候 会来执行这些切面的逻辑

这时候在切面中为某一个属性进行赋值,用处不大

切面中最重要的还是那个方法 而且joinPoint.getTarget()一样可以获取到普通对象

为啥 代理对象.target==普通对象

创建bean的流程
查看方法doCreateBean

Object bean = instanceWrapper.getWrappedInstance() //通过构造方法得到一个对象

    Object exposedObject =bean;
//填充属性
    populateBean(beanName,mbd,instanceWrapper)
//初始化
    exposedObject = initializeBean(beanName,exposedObject,mbd)
        
        进入到initializeBean方法
        会将刚刚那个bean对象传递进来
        会将对象传到
        wrappedBean = applyBeanProcessorAfterInitialization(wrappedBean,beanName)  //这个方法就会进行aop了
        这个方法就可以看作生成一个代理类,代理类实例化成代理对象,再把这个wrappedBean赋值给这个代理对象就OK了
    

UserService.class--->无参构造方法--->对象(得到一个普通对象)--->依赖注入(属性赋值)--->初始化前(@PostConstruct)--->初始化(initializingBean)--->初始化后(AOP)--->代理对象--->bean

这个步骤中要如何判断UserService要aop呢 因为UserService中的一个方法test被切面切了所以要aop

那么我UserService从最开始bean流程走的时候怎么知道自己要不要进行aop,被切了呢?

切面也加了@Aspect**@Component**注解 也在spring容器中 切面bean 一个方法不一定是被一个切面bean来切,并不是只有一个切入点

  • 1,找到所有的切面Bean(找到@Aspect,@Component)

  • 2,遍历所有切面Bean

  • 3,遍历切面Bean中所有切面方法,找方法上是否标准了@Before、@After等注解的方法

  • 4,有类似这种注解的话,查看该注解中是否定义了的表达式

  • 5,拿着这些定义了的表达式去与当前spring正在创建的UserService去匹配

  • 6,当前所遍历的方法与UserService匹配,那么就说明我这个UserService是需要进行aop的

  • 7,就会将UserService匹配的方法 全部放到map里缓存起来

代理对象.test();

class userServiceProxy extends UserService{  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //先执行@Before       
            //再执行被代理的逻辑
            target.test();//所以代理类先调用子类test,接着调用切面逻辑,再而调用普通对象UserService的test方法(此时orderService是有值的)
        }
    }

方法存储起来的map,可以在@Before这里执行

可以知道此时这个代理对象就是UserService 代理对象.test()方法 首先执行切面的逻辑 也就是上面的@Before需要执行业务逻辑

执行切面逻辑可以直接通过UserService中map缓存里的方法全部都匹配出来

这样代理对象.test()可以执行的更快了

所以这个流程可以有两个方面好处:

1,一方面是看我这个UserService要不要进行aop 要进行aop就可以把这些方法都缓存起来

2,而当我真正要执行时(代理对象.test()时) ,就可以从缓存中把所有的UserService匹配的方法全部拿出来,执行切面的逻辑

接着了解下spring事务

加上注解@EnableTransactionManagement 开启事务管理

//AppConfig
//都是一些需要操作数据库使用的
@Component
@EnableTransactionManagement 
public class AppConfig{
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
    @Bean
    public PlatformTransactionManager transactionManager(){//配置事务管理器
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("xxx连接信息");
        dataSource.setUsername("用户名");
        dataSource.setPassword("密码");
        return dataSource;
    }
}

有一个表 giao 五个字段都是空的

在UserService中改造

//UserService.class
@Component
public class UserService{
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public void test(){
        jdbcTemplate.execute("insert giao values(1,1,1,1,1)");
        throw new NullPointerException();
    }
}

//Test.class
  public class Test {
      public static void main(String []args){
          AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService)context.getBean("userService");
userService.test();
      }
  }  

这个时候猜测数据库中是否插入数据 后台报NullPointerException

但是数据库中已经存在数据了

为什么会添加到数据库呢(没有在AppConfig类上添加@Configuration注解)

加了该注解后再执行一遍Test.class的mian方法

发现抛出NullPointerException异常,且数据库中没有数据

我们再userService.test()这里打断点 会发现这是一个代理对象 是由spring事务创建的一个代理对象那么看看spring事务的代理对象.test

class userServiceProxy extends UserService{  //生成代理类去继承UserService
    UserService target;
        public void test(){ //就可以重写test方法
            //代理逻辑是怎么样的呢
            //1,判断当前代理对象所执行方法上是否有@Transactional注解
            //2,创建一个数据库连接Conn(事务管理器dataSource)
            //3,conn.autocommit = true 默认是true  自动提交   spring将conn.autocommit = false 改成false
            可以执行方法完毕后,进行手动提交conn.commit();
            若是执行方法抛出异常,就可以调用conn.rollback();
            
            target.test();//这个时候就是调用普通方法的test(),也就是加了UserService中加了@Transactional注解的方法
            假设该方法中有几条需要执行的sql语句,有conn.autocommit = true 就会执行一条sql就会提交一次  那么此时我们的@Transactional注解还有什么意义呢   sql都提交了后面的sql执行在提交,后面异常也要抛出,那么这个回滚还回滚什么呢
        }
    }

事务失效的场景了解

//改造UserService.class
//UserService.class
@Component
public class UserService{
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public void test(){
        jdbcTemplate.execute("insert giao values(1,1,1,1,1)");
        a();
    }
    
    @Transactional(propagation=Propagation.NEVER)//当传播机制-NEVER  以非事务的方式执行,存在一个事务就会抛一个异常
    public void a(){
        
    }
}

按照理论是会抛出一个异常,因为已经test方法开启了一个事务 但是运行测试会发现并没有抛异常 为什么呢,我们查看a()是谁在调用 可以发现代理对象.test()不就是执行代理的test(),再执行普通对象UserService调用test() 在普通对象上加@Transactional是没有作用的

该如何解决这种事务失效的场景

要明确加了 @Transactional(propagation=Propagation.NEVER)注解该注解有没有用

是谁在调用 被调用的时候是不是代理对象在调用这个方法

只要是代理对象调用这个方法,那么上面写的这个注解它就有用

方案可以重新创建一个UserServiceBase类

就把a()方法写在里面

再从UserService中使用@Autowired注解依赖注入UserServiceBase()

再在test方法下把a()改成 userServiceBase.a();

运行测试可以发现确实抛出异常了 ----IllegalTransactionStateException

其实也可以不用拆分出UserServiceBase类

直接在UserService类中使用@Autowired注解自己依赖注入自己在UserService中

@Autowired
private UserService userService;
//spring会将UserService的代理对象赋值给userService这个属性 
//也就是userService.a()就是一个代理对象调用a()方法
//就会出现上述异常

为什么没加@Configuration注解事务失效,加了之后事务成功呢

没有加@Configuration可以发现代理类中的dataSource与代理对象.test()的dataSource不是同一个

没有加@Configuration时,AppConfig.class中JdbcTemplate与DataSourceManager持有的是两个dataSource,两个单独的dataSource,不是同一个

在代理对象.test()时

我是拿着事务管理器的dataSource去创建数据库连接,修改autocommit属性

而target.test()执行时,是由jdbcTemplate的dataSource去建立一个新的数据库连接 所以它执行的SQL就自动提交了

加了@Configuration注解时,可以确保jdbcTemplate与DataSourceManger的dataSource是同一个 这样事务才能生效

为什么这里添加了@Configuration注解就能确保dataSource保持一致呢

与spring中的代理模式有关

我们要使用jdbcTemplate与DataSourceManger都是要使用AppConfig这个类的

如果我们加上了@Configuration注解就可以使得jdbcTemplate与DataSourceManger使用的是AppConfig的代理对象

@Component
@EnableTransactionManagement 
@Configuration//保证datasource一致事务才会生效
public class AppConfig{//AppConfig代理对象
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());//此时dataSource()直接到spring容器中寻找,有的话就直接返回了
        //如果没有找到dataSource这个bean那就要真正执行下面的DataSource方法创建dataSource
    }
    @Bean
    public PlatformTransactionManager transactionManager(){//配置事务管理器
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());此时dataSource()直接到spring容器中寻找,有的话就直接返回了
        //如果没有找到dataSource这个bean那就要真正执行下面的DataSource方法创建dataSource
        return transactionManager;
    }
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("xxx连接信息");
        dataSource.setUsername("用户名");
        dataSource.setPassword("密码");
        return dataSource;
    }
}

你可能感兴趣的:(java,前端,数据库,spring,程序人生)