Spring源码分析

Spring的理解

spring指的时spring framework , 他有两大核心

  1. IOC和DI  spring的核心是一个打的工厂容器, 用于维护和管理对象创建和依赖的关系
  2. AOP spring提供了面向切面编程, 允许我们将日志和业务的代码分离开进行管理使用

bean创建过程

spring创建一个bean对象首先需要获取一个实例对象, 这种对象一般使用无参构造的方法创建实例对象,这种对象并不是我们想象中的bean对象, 普通对象和bean对象之间还差一个依赖注入的过程

//普通对象
Student stu = new Student(); 

//bean对象
ApplicationContext ac = new ClassPathXmlApplicationContext("xx.xml");
Student stu = ac.getBean("stu");

通过无参构造获取一个实例对象, 再根据@Autowired的注解来判断属性注入

for(Field field: stu.getClass().getDeclaredFields()){
    if(field.isAnnotationPresent(Autowired.class)){
        field.set(stu, ??);   //对stu这个对象的field字段赋值
    }
}

spring创建的对象并不是简单的利用反射创建一个对象, 创建的对象如果有依赖于其他对象, 通过依赖注入之后的普通对象就成为了bean对象

Student ==> 无参构造 ==> 对象 ==> 依赖注入 ==> bean对象

单例bean

spring创建的容器默认使用创建的bean是单例bean ,也就是说使用同一个名字,在ioc容器中获得的都是同一个对象,这些单例bean通常都放在单例池中(使用map实现), 需要使用的时候再从单例池中获取. 

初始化

spring为了可扩展性强, 在bean完成创建之前, 引入了初始化前、初始化和初始化后这样的方法

Student ==> 无参构造 ==> 对象 ==> 依赖注入 ==> 初始化前 ==> 初始化 ==> 初始化后 ==> 存入单例池 ==> bean对象

@Componet
public class UserService{
    @Autowired
    private OrderService orderService;
    
    //@Autowired  admin在这边不能使用autowired注解自动注入
    private User admin;
    

    public void a(){
        //mysql ==> user对象 ==>this.admin
    
    }
    
}

在这段代码中, admin这个对象需要查询数据库,对admin属性赋值,这些数据库为admin赋值spring无法完成, 需要手动写出一些代码逻辑对admin赋值

初始化前

通过对a方法的调用, 可以完成对admin的赋值,spring需要自动调用这个方法, 在方法上面加上@PostConstruct让spring自动调用

@PostConstruct
public void a(){
   //mysql ==> user对象 ==>this.admin
    
}

初始化 

spring也提供了一个接口, 用于初始化这个阶段, InitializingBean,我们可以通过实现这个接口, 进而完成对admin的赋值

@Componet
public class UserService implements InitializingBean{
    @Autowired
    private OrderService orderService;
    
    //@Autowired  admin在这边不能使用autowired注解自动注入
    private User admin;
    
    @Override
    public void afterPropertiesSet() throws Exception{
        //mysql ==> user对象 ==>this.admin
        
    }
    
}

在spring源码中, 在doCreatBean这个方法中有个invokeInitMethods这个初始化方法, 在这个方法中,会对bean做  bean instanceof InitializingBean这个判断

初始化后

初始化后是在属性赋值完成之后, spring会做一些aop操作, 经过aop代理后, 最后是将代理对象放入单例池中, 通过名字取出来的就是代理对象,如果不需要代理, 那么放入单例池的就是普通对象

流程总结

Student ==> 无参构造 ==> 对象 ==> 依赖注入 ==> 初始化前(PostConstruct) ==> 初始化(afterPropertiesSet) ==> 初始化后(aop) ==> 代理对象 ==> 存入单例池 ==> bean对象

后置处理器 

这些初始化前中后操作和依赖注入的底层都是通过spring的后置处理器来实现的

有参构造

如果spring的类中, 有两个构造方法, 那么spring应该用哪个构造方法呢 ?

@Component
public class UserService{
    private OrderService orderService;
    public UserService(){
        System.out.println(0);
    }
    public UserService(OrderService orderService){
        this.orderService = orderService;
        System.out.println(1);
    }
}

默认情况下, 如果是多个构造 ,而且有无参构造, 使用的是无参构造 

如果类中只有一个有参构造, spring直接使用这个构造进行处理

@Component
public class UserService{
    private OrderService orderService;
    public UserService(OrderService orderService){
        this.orderService = orderService;
        System.out.println(1);
    }
}

 如果类中有两个构造, 而且没有无参, 这种情况spring无法决定使用什么构造, 通过异常抛出处理

@Component
public class UserService{
    private OrderService orderService;
    public UserService(OrderService orderService){
        this.orderService = orderService;
        System.out.println(1);
    }
    public UserService(OrderService orderService,OrderService orderService1){
        this.orderService = orderService;
        System.out.println(2);
    }
}

 如果确实存在多个构造, 也没有无参构造, 通常使用@Autowired注解为spring做出标记, 使用当前被autowired标记的构造方法

@Component
public class UserService{
    private OrderService orderService;
    public UserService(OrderService orderService){
        this.orderService = orderService;
        System.out.println(1);
    }
    @Autowired
    public UserService(OrderService orderService,OrderService orderService1){
        this.orderService = orderService;
        System.out.println(2);
    }
}

有参构造的参数细节

对于有参构造来说, 如果构造函数中的参数在spring容器中没有相应的bean, 就会无法创建该对象, 会抛出异常,而不是传入null

@Component
public class UserService{
    private OrderService orderService;
    public UserService(OrderService orderService){
        this.orderService = orderService;
        System.out.println(1);
    }
}
//没有加@Component
public class OrderService {
}

有参匹配规制

spring的有参构造究竟是以类型匹配还是按照名字匹配 

@Component
public class UserService{
    private OrderService orderService;
    public UserService(OrderService memberService){ //Map<'orderService', OrderService;'memeberService', MemberServie>
        this.orderService = orderService;
        System.out.println(1);
    }
}

在探讨匹配问题前, 这边描述一个细节, 单例bean不是指的是该类只有一个实例对象,而是对应名字只能有一个对象,他们是三个单独的对象

@Component
public class OrderService{
}
@Configuration
public class Config{
    @Bean
    public OrderService orderservice1(){
        return new OrderService();
    }
    @Bean
    public OrderService orderservice2(){
        return new OrderService();
    }
}

所以构造函数匹配首先是按照类型匹配, 如果有多个, 再按照名字选出唯一的一个,先byType 再byName,像这种需要进一步推断才能确定属性值的叫做推断构造方法

参数匹配规制

参数匹配规制和构造函数匹配规制相同, 都是通过先byType再byName

AOP

cglib代理

这里使用target.test()而不是supper.test(); 如果使用的是supper, 这里执行的只是父类的代码, 而且父类属性是对象属性,不是类属性,属性上值为null, 使用创建出来的普通对象target的属性中有值

class UserServiceProxy extends UserService{
    UserServie target;
    @Override
    public void test(){
        //@Before 切面逻辑
        //userService的test方法 这里不是使用super.test();
        target.test();
    }
}

通过代理的普通对象并不存在于单例池中, 而是在jvm中,代理对象在单例池中 

@Aspect
@Component
public class MAspect{
    @Before("execution(public void com.service.UserService.test()))
    public void before(JoinPoint joinPoint){//joinPoint.getTarget() == 需要代理的普通对象
       System.out.println("before");
    }
}

spring事务

事务的配置

@ComponentScan("com")
@EnableTransactionManagement
public class AppConfig{
    @Bean
    public JDBCTemplate jdbcTemplate(){
        return new JDBCTemplate(datasource());
    }
    @Bean
    public PlatformTransactionManager transactionManager(){
        DatasourceTransactionManager transactionManager = new DatasourceTransactionManager();
        transactionManager.setDataSource(datasource());
    }
    @Bean
    public DataSource datasource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/db");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

这里的 sql语句会成功, 因为上面的bean配置都没有成功, AppConfig没有配置@Configuration注解

@Component
public class UserService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void test()
        jdbcTemplate.execute("insert into t value(1,1,1)");
        throw new NullPointException();
    }
}

如果加上@Configuration, 事务会回滚

spring事务的代理对象

class UserServiceProxy extends UserService{
    UserService target;
    public void test{
        //判断有无@Transal注解
        // 利用事务管理器建立数据库连接conn,修改conn.autocommit = false;
        target.test();//sql执行
        //conn.rollback, conn.commit();
    }
}

@Configuration代理对象

JdbcTemplate需要一个datasource , transactionManager需要一个datasource, 在没有加入configuration注解之前, 在java层面,他就是两次方法调用,  那么两次都会创建一个datasource对象,  也就是说jdbcTemplate和transactionManager中的连接是两个不同的连接,如果是两个不同的连接, jdbc执行完成之后(jdbc已经提交了), 再抛出异常也没有什么用处.

public void test(){
    //@Transactional
    //事务管理器创建连接conn , ThreadLocal<Map<DataSource,conn>> 一个数据源对应一个连接, 支持多数据源
    //conn.autocommit = false;
    //jdbcTemplate获取连接 conn 执行sql, 这个conn是事务管理器创建的连接,
    //jdbc用自己的datasource去从ThreadLocal获取对应datasource的conn, 如果有,直接使用,没有再创建一个
}

使用configuration不仅可以实例化对象, 而且被@configuration标记的类是个代理类,通过方法调用也能获取到一样的datasource,而不是全新的datasource.

事务传播行为

如果是直接在方法中调用另一个方法, 这是普通对象调用普通对象的方法, 事务不会发生传播行为

@Component
public class UserService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void test()
        jdbcTemplate.execute("insert into t value(1,1,1)");
        a();
    }
    @Transactional(propagation = Propagation.NEVER)
    public void a(){
        jdbcTemplate.execute("insert into t value(1,1,1)");
    }
}

那如何解决事务无法传播呢? 因为事务是靠aop代理得来的, 调用方法的对象中一定有个已经接受事务代理的对象,如果执行成功, 那么就会抛出异常

@Component
public class UserService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private UserServiceBase userServiceBase;
    @Transactional
    public void test()
        jdbcTemplate.execute("insert into t value(1,1,1)");
        userServiceBase.a();
    }
}
@Component
public class UserServiceBase {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional(propagation = Propagation.NEVER)
    public void a(){
        jdbcTemplate.exexute("insert into t values(1,1,1)");
    }
}

但是这种方式还需要再写一个类, 比较麻烦, 可以自己注入自己

@Component
public class UserService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private UserService userService;
    @Transactional
    public void test()
        jdbcTemplate.execute("insert into t value(1,1,1)");
        userService.a();
    }
    @Transactional(propagation = Propagation.NEVER)
    public void a(){
        jdbcTemplate.exexute("insert into t values(1,1,1)");
    }
}

spring三级缓存

像这种情况属性之间互相依赖, 如果不做特殊处理, 就会出现一种死循环的情况

public static void main(String[] args){
    AnnotationConfigApplicationContext application = new AnnotationConfigApplication(AppConfig.class);
    AService aService = (AService)application.getBean("AService");// 这里的A是大写的, spring默认情况下,如果前两个字符都是大写的,那么名字第一个字母不会小写
    aService.test();
}
@Component
public class BService{ 
    @Autowired
    private AService aService;
    public void test(){
        System.out.println(aService);
    }
}
@Component
public class AService{ 
    @Autowired
    private BService bService;
    public void test(){
        System.out.println(bService);
    }
}

AService创建的生命周期

  1. 创建一个AService普通对象 ==> map 将普通对象和名字放入map中 map.put(AService,对象);
  2. 填充bService属性 ==> 去单例池中找BService对象 ==> map ==> 创建BService
  3. 其他属性
  4. 其他操作
  5. 初始化后
  6. 放入单例池

BService创建的生命周期

  1. 创建一个BService普通对象
  2. 填充aService属性 ==> 去单例池中找AService对象 ==> map(获取AService普通对象)
  3. 其他属性
  4. 其他操作
  5. 初始化后
  6. 放入单例池

 像这样我们可以通过使用单例池和额外的一个map来解决循环依赖问题, 而且如果在没有aop的情况下, 似乎可以完美解决循环依赖问题

A a = new A();
a.b = ?

B b = new B();
b.a = a; //这里的b获取的A是普通对象

a.b = b; //通过a的引用将b赋值

那为什么需要使用三级缓存呢?  在上面我也提醒了, 如果是没有aop的情况下, 因为aop之后, 存入单例池的对象不在是普通对象, 而是代理对象, 也就是说, autowired进入的值必须是代理对象,像这种情况使用二级缓存是无法完美解决问题的.

提前aop

如果我们可以提前将aop的对象放入map中, BService的aService属性获取的就是AService的代理对象, 就此思路我们可以提前对AService进行aop处理, 以下是对aop提前创建的抽象流程

AService 

  1. creatingSet("AService") 表示当前AService正在创建过程
  2. 创建AService普通对象
  3. 填充bService属性 ==>去单例池中找BService ==>从createSet()找有无BService正在创建(无) ==> map ==> 创建BService

 BService

  1. creatingSet("BService")
  2. 创建一个BService对象
  3. 填充aService属性 ==> 去单例池中找AService ==> creatingSet存在AService..表明出现了循环依赖问题 ==> 提前aop ==> AService代理对象

这样看起来是不是已经解决了循环依赖而且aop问题也得到了解决.  但这只是我们讨论两个类, 那如果是三个类呢.

@Component
public class AService{ 
    @Autowired
    private BService bService;
    public void test(){
        System.out.println(bService);
    }
}
@Component
public class BService{ 
    @Autowired
    private AService aService;
}
@Component
public class CService{ 
    @Autowired
    private AService aService;
}

BService

  1. creatSet("BService")
  2. 创建BService 普通对象
  3. 填充aService属性 ==> 单例池 ==>creatingSet判断 ==> 出现循环依赖 ==> 二级缓存中查找AService==>AService提前Aop ==> 放入earlySingletonObjects<"AService",AService代理对象>
  4. 后面的逻辑

CService

  1. creatSet("CService")
  2. 创建CService 普通对象
  3. 填充aService属性 ==> 单例池 ==>creatingSet判断 ==> 出现循环依赖 ==> AService提前Aop ==> 放入earlySingletonObjects<"AService",AService代理对象>
  4. 后面的逻辑

三个map

  1. 单例池 singletonObjects bean(完成各种属性赋值操作,经过完整的生命周期,可以为外界所用)
  2. 二级缓存 earlySingletonObjects (没有经过完整生命周期的单例bean ,但是这些bean的地址将来一定是对应属性的地址值)
  3. 三级缓存 singletonFactories (存放lambda表达式(普通对象,beanDefinition, 名字) ) 如果要进行aop , 给二级返回代理对象, 如果不需要, 返回普通对象, 不执行lambda表达式

能够打破循环的只是三级缓存,  单例池和二级缓存是保证不重复创建对象

总结

spring拦截器和过滤器的区别

在正式讨论拦截器和过滤器之前, 我想声明一点, filter只作用在请求到达controller之前, 而拦击器后前置处理和后置处理,分别在controller之前和之后.

file和interceptor都是aop思想的一种体现, 用来解决莫一类问题的两种接口, 可以对代码做一些增强. 那他们的区别是什么呢 ? 

过滤器拦截器
出身不同来自servlet来自spring
使用范围不同过滤器实现了javax.servlet.Filter接口,依赖于tomcat, 只能用在web程序中org.spirngframework.web.servlet,由spirng管理,不依赖于tomcat
实现原理不同基于过滤链ApplicationFilterChain实现基于java反射
使用场景不同字符编码设置,响应数据压缩(偏向通用功能)登录判断, 权限判断,日志等(偏向业务)

 

filter和interceptor都可以对request和response做一些增强操作 

 

filter

首先我想明确的一点是, filter只能对web的request和response做处理 ,也就是说你可以对请求响应做一些参数更改处理

@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //2.判断是否是登录
        if(request.getURI().getPath().contains("/login")){
            //放行
            return chain.filter(exchange);
        }

       

        //6.放行
        return chain.filter(exchange);
    }
}

interceptor

拦截器, 从方法的参数来看, 除了response和request, 它还可以对handler做处理

其次, 我在这个例子中加入了ThreadLocal, 对当前线程添加了一个变量, 线程结束后为了防止内存泄漏, 将变量删除, filter就无法做到这一点

public class WmTokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String userId = request.getHeader("userId");
        if (userId != null) {
            WmUser wmUser = new WmUser();
            wmUser.setId(Integer.valueOf(userId));
            WmThreadLocalUtil.setUser(wmUser);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        WmThreadLocalUtil.clear();
    }
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WmTokenInterceptor())
        .addPathPatterns("/**");
    }
}

Spring容器启动流程是怎么样的

  1. 首先进行扫描, 得到所有的BeanDefinition对象, 并存在一个map中, 这些BeanDefination存储着bean的类描述信息, bean是单例的还是多列的, 懒加载还是非懒加载,一个BeanDefination对应着一个Bean对象
  2. 然后筛选出非懒加载的单例BeanDefination进行创建Bean,对于多例bean并不需要在启动过程中去创建, 对于多例bean会在每次获取bean时,用BeanDefination去创建
  3. 在创建过程中间就包括bean的生命周期
  4. 当bean创建完成之后, spring发布容器启动事件
  5. spring启动结束

Spring中的单例bean是线程安全的吗

先说结论, bean不具备线程安全的特性. Spring的bean在上下文中只有一个, 在多线程情况下, 单例bean的状态是可被修改的, 可能存在线程安全问题.

那么为了保证bean线程的安全性, 这里有三种方法

  • 使用无状态的bean, bean中不含任何实例变量, 只包含方法和局部变量, 因此就不会有线程安全问题, 那什么是无状态bean, 在多线程中, 只会对bean的成员变量进行查询操作而不会修改成员变量值的bean(比如: 常量), 就是无状态bean(并不是没有成员变量)
@Component
public class A{
    public void print(String s){
        System.out.println(s);
    }
}
  •  多例bean(prototype), 在每次使用bean的时候, 都会创建一个bean, 这样就可以保证, 线程的安全问题,线程间不存在bean共享的问题
  • 在类中定义ThreadLocal, 将可变的成员变量保存在ThreadLocal中,因为ThreadLocal本身具备线程隔离性, 因此可以使用ThreadLocal来存放可变的成员变量;
public class A{
    ThreadLocal<Object> threadLocal = new ThreadLocal<>();
}

  • 41
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值