专业的编程技术博客社区

网站首页 > 博客文章 正文

Spring源码阅读(一)(spring源码从哪儿看起)

baijin 2024-08-15 16:54:39 博客文章 7 ℃ 0 评论

Spring启动大致流程

@ComponentScan("com.mark")
public class AppConfig {
    public static void main(String[] args) {}

    @Bean
    public User user1() {
        return new User();
    }

    @Bean
    public User user2() {
        return new User();
    }

}

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean("UserService");
userService.test();
  • 先去解析AppConfig.class获取到扫描的路径
  • 扫描遍历路径下类上存在@Component、@Service等注解.
  • 将扫描到的注解存放到一个Map<beanName,Class>中
  • 当调用getBean时候会从这个map中拿去

Bean的生命周期,创建过程

  1. 实例化:将扫描到类,调用构造方法进行实例化.
  2. 依赖注入:扫描实例化里边的属性如果被@Autowired修饰,进行依赖注入
  3. 初始化前,判断是否有方法被@PostConstruct修饰,并调用
  4. 初始中,断是否实现了InitializingBean接口.调用afertPropertisSet()
  5. 初始化后.判断是否需要进行AOP,进行代理生成,替换原来的bean

实例化

找构造方法

  • 当存在无参构造的时候,调用无参构造
  • 当没有无参构造的时候
  • 只有一个有参 构造,调用有参构造
  • 当有多个有参构造的时候,判断是否有@Autowired,有这个注解,调用这个构造,没有注解的话报错
  • 当有无参和有参构造,并且有参构造上包含@Autowired时候,调用被注解修饰 的构造方法
@Component
public class CustomerService {

    @Autowired
    public User user1;
    @Autowired
    public User user2;

    
    public CustomerService(User user1, User user2) {}
    public CustomerService(User user1) {}
    
}
//会报找不到默认构造方法
==No default constructor found; nested exception is java.lang.NoSuchMethodException
@Component
public class CustomerService {

    @Autowired
    public User user1;
    @Autowired
    public User user2;

    @Autowired
    public CustomerService(User user1, User user2) {}
    public CustomerService(User user1) {}
}

当调用构造方法的时候,会根据类型和名字去springMap中查找,并传参.如果没有找到bean会报错

No qualifying bean of type 'com.mark.UserService'

先通过类型去找,如果找到多个bean再通过名字去找.怕万一直接通过名字找,找到的类型不匹配.

依赖注入

遍历类里边的变量,寻找到所有@Autowired

for (Field field : CustomerService.class.getDeclaredFields()) {
   if (field.isAnnotationPresent(Autowired.class)) {
      System.out.println(field.getName());
      field.set(target,map.get(field.getName()));
   }
}

我们在AppConfig.class中定义了两个User Bean(@Bean),名字分别user1和user2.在依赖注入的时候也是先通过类型找,如果找到多个再用名字去找.当我们把CustomerService中user1名字改了.

    @Autowired
    public User user3;
#spring会报错 Unsatisfied dependency expressed through field 'user3'

当我们把AppConfig.class中的一个user bean(@Bean)删除掉,就不会报错了.原因就是因为spring通过类型找到了多个bean,然后才会通过名字去判断对应的bean.如果找到一个就直接返回.

初始化前

在方法中添加@PostConstruce注解,依赖注入之后会被调用.

@Component
public class CustomerService {
    @Autowired
    public User user3;
    @Autowired
    public User user2;
    public CustomerService(){
        System.out.println(111);
    }
    @PostConstruct
    public void test(){
        System.out.println(user2);
        System.out.println(222);
    }
    @PostConstruct
    public void test1(){
        System.out.println(333);
    }
}
//结果,user2 有值,说明是再依赖注入之后
111
com.mark.User@311d617d
222
333

初始化

实现 InitializingBean 接口重写 afterPropertiesSet

@Component
public class CustomerService implements InitializingBean {
    @Autowired
    public User user3;
    @Autowired
    public User user2;
    public CustomerService(){
        System.out.println(111);
    }
    @PostConstruct
    public void test(){
        System.out.println(user2);
        System.out.println(222);
    }
    @PostConstruct
    public void test1(){
        System.out.println(333);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(444);
    }
}
//结果
111
com.mark.User@ed17bee
222
333
444

判断是否实现了 InitializingBean

CustomerService customerService = new CustomerService();
        if(customerService instanceof InitializingBean){
            (InitializingBean)customerService.afterPropertiesSet();
    }   

初始化后(AOP)

在AppConfig上添加注解@EnableAspectJAutoProxy

添加切面

@Component
@Aspect
public class CustomerPointcut {
    /**
     *  定义切点
     */
    @Pointcut("execution(public void com.mark.CustomerService.test())")
    public void pointCut(){}

    /**
     *  织入
     * @param joinPoint
     */
    @Before("pointCut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("代理类执行前");
    }
}
CustomerService customerService = context.getBean(CustomerService.class);
System.out.println(customerService.getClass());
customerService.test();

打印出来的是CustomerService$EnhancerBySpringCGLIB$c557704a代理对象.

大致执行流程是

proxy.test() ->intercept() ->执行前->target.test() ->执行后

代理类中user变量是没有值的.在target中才会有值.

==

修改切点

    /**
     *  定义切点
     */
    @Pointcut("execution(public void com.mark.CustomerService.test*())")
    public void pointCut(){}

在test方法中调用test1()

@Component
public class CustomerService {

    @Autowired
    private User user;

    public void test(){
        System.out.println("这是test");
        test1();
    }

    public void test1(){
        System.out.println("这是test1");
    }
}
#执行结果===
#代理类执行前
#这是test
#这是test1

这时候test1()并没有走代理逻辑.只有test()方法走了.

修改方法,自己注入自己.然后再调用,就可以了

@Component
public class CustomerService {

    @Autowired
    private User user;
    @Autowired
    private CustomerService customerService;

    public void test(){
        System.out.println("这是test");
        customerService.test1();
    }

    public void test1(){
        System.out.println("这是test1");
    }
}
#结果=====
#代理类执行前
#这是test
#代理类执行前
#这是test1

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表