一、IOC 和 DI 的区别
1. 概念区分
概念 | 全称 | 定义 | 关系 |
---|---|---|---|
IOC | Inversion of Control (控制反转) | 将对象创建和依赖管理的控制权从程序转移到容器 | 是设计思想 |
DI | Dependency Injection (依赖注入) | 容器通过构造函数、setter或接口等方式注入依赖对象 | 是IOC的实现方式 |
ioc的好处
1:使用者不用关心引用bean的实现细节,譬如对于A a = new A(c,d,e,f);来说,如果要使用A,那还要把c,d,e,f多个类全都感知一遍,这显然是非常麻烦且不合理的
2:不用创建多个相同的bean导致浪费。
3:Bean的修改使用方无需感知。同样是上面的例子,假如说BeanA需要修改,如果没有IOC的话,所有引用到A的其他bean都需要感知这个逻辑,并且做对应的修改。但是如果使用了IOC,其他bean就完全不用感知到
对于Spring的IOC来说,它是IOC思想的一种实现方式。在容器启动的时候,它会根据每个bean的要求,将bean注入到SpringContainer中。如果有其他bean需要使用,就直接从容器中获取即可,
2. 代码示例对比
// 传统方式(控制权在类内部) class UserService { private UserRepository repo = new UserRepositoryImpl(); // 主动创建依赖 } // IOC+DI方式(控制权在容器) class UserService { private UserRepository repo; // 构造器注入(DI的实现) public UserService(UserRepository repo) { this.repo = repo; // 由容器注入 } }
二、IOC 实现原理
1:从配置元数据中获取要DI的业务POJO(这里的配置元数据包括xml,注解,configuration类等)
2:将业务POJO形成BeanDefinition注入到Spring Container中
3:使用方通过ApplicationContext从Spring Container直接获取即可。如下图所示:
1. 核心流程
2. 简易版的ioc实现方式
// 简化的IOC容器实现 public class SimpleContainer { private Map<String, Object> beans = new HashMap<>(); public void register(String name, Object bean) { beans.put(name, bean); } public Object getBean(String name) { return beans.get(name); } // 依赖注入示例 public void injectDependencies() { for (Object bean : beans.values()) { for (Field field : bean.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { Object dependency = beans.get(field.getType().getName()); field.setAccessible(true); field.set(bean, dependency); } } } } }
3:DI注入(spring支持字段、setter,构造器注入)
Autowired:Autowired在获取bean的时候,先是byType的方式,再是byName的方式。意思就是先在Spring容器中找以Bean为类型的Bean实例,如果找不到或者找到多个bean,则会通过fieldName来找。缺点:单一职责问题,可能产生NPE,隐藏依赖,不利于测试
Resource:Resource在获取bean的时候,和Autowired恰好相反,先是byName方式,然后再是byType方式。当然,我们也可以通过注解中的参数显示指定通过哪种方式。
作用域不同
Autowired可以作用在构造器,字段,setter方法上
Resource 只可以使用在field,setter方法上
支持方不同
Autowired是Spring提供的自动注入注解,只有Spring容器会支持,如果做容器迁移,是需要修改代码的
Resource是JDK官方提供的自动注入注解(JSR-250)。它等于说是一个标准或者约定,所有的IOC容器都会支持这个注解。假如系统容器从Spring迁移到其他IOC容器中,是不需要修改代码的。
默认要求不同
@Autowired注解默认要求要注入的Bean必须存在,如果找不到匹配的Bean会抛出异常。
@Resource注解默认允许注入的Bean可以缺失,如果找不到匹配的Bean会使用默认值null。
4:beanfactory和factorbean的区别:
这两个东西都是接口(interface),然后都是在org.springframework.beans.factory包下面的。
BeanFactory比较常用,名字也比较容易理解,就是Bean工厂,他是整个Spring IoC容器的一部分,负责管理Bean的创建和生命周期。是Spring IoC容器的一个接口,用来获取Bean以及管理Bean的依赖注入和生命周期。
FactoryBean是一个接口,用于定义一个工厂Bean,它可以产生某种类型的对象。通常用于创建很复杂的对象,比如需要通过某种特定的创建过程才能得到的对象。例如,创建与JNDI资源的连接或与代理对象的创建。就如我们的Dubbo中的ReferenceBean。
三、AOP 实现原理
和IOC一样,AOP也指的是一种思想。AOP思想是OOP(Object-Oriented Programming)的补充。OOP是面向类和对象的,但是AOP则是面向不同切面的。一个切面可以横跨多个类和对象去操作,极大的丰富了开发者的使用方式,提高了开发效率。
譬如,一个订单的创建,可能需要以下步骤:
1权限校验
2事务管理
3创建订单
4日志打印
如果使用AOP思想,我们就可以把这四步当成四个“切面”,让业务人员专注开发第三个切面,其他三个切面则是基础的通用逻辑,统一交给AOP封装和管理。
1. AOP核心概念
术语 | 说明 | 示例 |
---|---|---|
Aspect | 切面(横切关注点的模块化) | 日志切面 |
Advice | 切面中的具体行为 | @Before, @After |
Pointcut | 定义何处切入 | @Pointcut("execution(* com.service..(..))") |
Join Point | 程序执行点(如方法调用) | UserService.save() |
2. 两种动态代理实现
从Bean的初始化流程中来讲,Spring的AOP会在bean实例的实例化已完成,进行初始化后置处理时创建代理对象,
Spring AOP默认使用标准的JDK动态代理进行AOP代理。这使得任何接口可以被代理。但是JDK动态代理有一个缺点,就是它不能代理没有接口的类。
所以Spring AOP就使用CGLIB代理没有接口的类。
(1) JDK动态代理
特点:
-
基于接口
-
通过
Proxy
和InvocationHandler
实现 -
性能较好
代码demo:
public class JdkProxyDemo { interface Service { void serve(); } static class RealService implements Service { public void serve() { System.out.println("Real service"); } } public static void main(String[] args) { Service proxy = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, (proxy1, method, args1) -> { System.out.println("Before call"); Object result = method.invoke(new RealService(), args1); System.out.println("After call"); return result; }); proxy.serve(); } }
(2) CGLIB动态代理
特点:
-
基于类继承
-
通过
MethodInterceptor
实现 -
无需接口
-
生成子类覆盖方法
示例代码:
public class CglibProxyDemo { static class RealService { public void serve() { System.out.println("Real service"); } } public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealService.class); enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> { System.out.println("Before call"); Object result = proxy.invokeSuper(obj, args1); System.out.println("After call"); return result; }); RealService proxy = (RealService) enhancer.create(); proxy.serve(); } }
3. Spring AOP实现选择
// 在ProxyCreatorSupport类中 protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { // 如果有接口使用JDK代理,否则使用CGLIB if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return createJdkDynamicAopProxy(); } return createCglibAopProxy(); }
四、Bean的作用域
1. 标准作用域
作用域 | 说明 | 配置方式 | 适用场景 |
---|---|---|---|
singleton | 默认,单例 | @Scope("singleton") | 无状态服务 |
prototype | 每次获取新实例 | @Scope("prototype") | 有状态对象 |
request | 每个HTTP请求一个实例 | @Scope("request") | Web请求相关 |
session | 每个会话一个实例 | @Scope("session") | 用户会话数据 |
application | ServletContext生命周期 | @Scope("application") | 全局共享 |
2. 作用域配置示例
// Java配置方式 @Configuration public class AppConfig { @Bean @Scope("prototype") public Service prototypeService() { return new ServiceImpl(); } } // XML配置方式 <bean id="userService" class="com.example.UserService" scope="singleton"/>
3. 作用域实现原理
// 在AbstractBeanFactory中 public Object getBean(String name) throws BeansException { // 根据scope决定创建新实例还是返回缓存 if (isSingleton(name)) { return getSingletonInstance(name); } else if (isPrototype(name)) { return createNewInstance(name); } // 其他作用域处理... }
4:Bean的生命周期是什么样的?
整个生命周期可以大致分为3个大的阶段,分别是:创建、使用、销毁。还可以进一步分为5个小的阶段:实例化、初始化、注册Destruction回调、Bean的正常使用以及Bean的销毁。
更细一点就是:
1实例化Bean:
○Spring容器首先创建Bean实例。
○在AbstractAutowireCapableBeanFactory类中的createBeanInstance方法中实现
2设置属性值:
○Spring容器注入必要的属性到Bean中。
○在AbstractAutowireCapableBeanFactory的populateBean方法中处理
3检查Aware:
○如果Bean实现了BeanNameAware、BeanClassLoaderAware等这些Aware接口,Spring容器会调用它们。
○在AbstractAutowireCapableBeanFactory的initializeBean方法中调用
4调用BeanPostProcessor的前置处理方法:
○在Bean初始化之前,允许自定义的BeanPostProcessor对Bean实例进行处理,如修改Bean的状态。BeanPostProcessor的postProcessBeforeInitialization方法会在此时被调用。
○由AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInitialization方法执行。
5调用InitializingBean的afterPropertiesSet方法:
○提供一个机会,在所有Bean属性设置完成后进行初始化操作。如果Bean实现了InitializingBean接口,afterPropertiesSet方法会被调用。
○在AbstractAutowireCapableBeanFactory的invokeInitMethods方法中调用。
6调用自定义init-method方法:
○提供一种配置方式,在XML配置中指定Bean的初始化方法。如果Bean在配置文件中定义了初始化方法,那么该方法会被调用。
○在AbstractAutowireCapableBeanFactory的invokeInitMethods方法中调用。
7调用BeanPostProcessor的后置处理方法:
○在Bean初始化之后,再次允许BeanPostProcessor对Bean进行处理。BeanPostProcessor的postProcessAfterInitialization方法会在此时被调用。
○由AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization方法执行
8注册Destruction回调:
○如果Bean实现了DisposableBean接口或在Bean定义中指定了自定义的销毁方法,Spring容器会为这些Bean注册一个销毁回调,确保在容器关闭时能够正确地清理资源。
○在AbstractAutowireCapableBeanFactory类中的registerDisposableBeanIfNecessary方法中实现
9Bean准备就绪:
○此时,Bean已完全初始化,可以开始处理应用程序的请求了。
10调用DisposableBean的destroy方法:
○当容器关闭时,如果Bean实现了DisposableBean接口,destroy方法会被调用。
○在DisposableBeanAdapter的destroy方法中实现
11调用自定义的destory-method
○如果Bean在配置文件中定义了销毁方法,那么该方法会被调用。
○在DisposableBeanAdapter的destroy方法中实现
关于常用函数的顺序
构造函数>@PostConstruct > afterPropertiesSet > init-method
整个Bean的创建的过程都依赖于AbstractAutowireCapableBeanFactory这个类,而销毁主要依赖DisposableBeanAdapter这个类
五、完整AOP实战示例
1. 定义切面
@Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.service.*.*(..))") private void serviceLayer() {} @Before("serviceLayer()") public void logBefore(JoinPoint jp) { System.out.println("Entering: " + jp.getSignature()); } @Around("serviceLayer()") public Object measureTime(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); System.out.println("Execution time: " + (System.currentTimeMillis() - start)); return result; } }
2. 启用AOP
@Configuration @EnableAspectJAutoProxy // 启用AOP public class AppConfig { // Bean定义... }
六、Spring容器核心架构
七:Spring 事务(声明式事务和编程式事务)
声明式事务管理方法允许开发者配置的帮助下来管理事务,而不需要依赖底层API进行硬编码。开发者可以只使用注解或基于配置的 XML 来管理事务。@Transactional即可给test方法增加事务控制。使用了 AOP 实现的,本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)包含三个参数:
propagation
Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。
Spring的事务规定了7种事务的传播级别,默认的传播机制是REQUIRED
●REQUIRED,如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行
●REQUIRES_NEW,每次执行新开一个事务,如果当前存在事务,则把当前事务挂起
●SUPPORTS,有事务则加入事务,没有事务则普通执行
●NOT_SUPPORTED,有事务则暂停该事务,没有则普通执行
●MANDATORY,强制有事务,没有事务则报异常
●NEVER,有事务则报异常
●NESTED,如果之前有事务,则创建嵌套事务,嵌套事务回滚不影响父事务,反之父事务影响嵌套事务
rollback
rollbackFor是Spring事务中的一个属性,用于指定哪些异常会触发事务回滚。
在一个事务方法中,如果发生了rollbackFor属性指定的异常或其子类异常,则事务会回滚。如果不指定rollbackFor,则默认情况下只有RuntimeException和Error会触发事务回滚。
异常类.class
1、私有方法调用
2、静态方法调用
3、final方法调用
4、类内部自调用
5、内部类方法调用
声明式事务失效的场景:
1、@Transactional应用在非 public 修饰的方法上,
2、@Transactional注解属性 propagation 设置错误
3、@Transactional注解属性 rollbackFor 设置错误
4、同一个类中方法调用,导致@Transactional失效
5、异常被catch捕获导致@Transactional失效
6、数据库引擎不支持事务
7、Bean没有被Spring管理,而是自己new了一个Bean
8、final、static方法
9、如果在事务中,开启了新的线程,那么就会导致多个线程之间不在同一个事务中,会导致事务隔离。但是,如果在一个新的线程开启后,又开始了一个事务,那么在这个线程执行过程中,当前线程的所有操作都是在同一个事务中的。
10、多线程情况下。使用的是 ThreadLocal 机制来存储事务上下文,而 ThreadLocal 变量是线程隔离的,即每个线程都有自己的事务上下文副本。因此,在多线程环境下,Spring 的声明式事务会“失效”,即新线程中的操作不会被包含在原有的事务中。
八、最佳实践建议
-
IOC使用:
-
优先使用构造器注入(强制依赖)
-
可选依赖使用setter注入
-
避免字段注入(不利于测试)
-
-
AOP使用:
-
切面粒度要适中(通常按功能模块划分)
-
避免在切面中处理过多业务逻辑
-
注意代理失效问题(同类调用不会触发AOP)
-
-
作用域选择:
-
默认使用singleton(性能最佳)
-
有状态bean使用prototype
-
Web相关作用域要确保线程安全
-