画册做的比较好的网站,网站备案在哪里查询,北京百度推广代理公司,wordpress qq登陆评论文章目录 1、单例Bean不是线程安全的2、AOP3、Spring中事务的实现4、Spring事务失效的场景4.1 情况一#xff1a;异常被捕获4.2 情况二#xff1a;抛出检查异常4.3 注解加在非public方法上 5、Bean的生命周期6、Bean的循环引用7、Bean循环引用的解决#xff1a;Spring三级缓… 文章目录 1、单例Bean不是线程安全的2、AOP3、Spring中事务的实现4、Spring事务失效的场景4.1 情况一异常被捕获4.2 情况二抛出检查异常4.3 注解加在非public方法上 5、Bean的生命周期6、Bean的循环引用7、Bean循环引用的解决Spring三级缓存8、构造方法出现循环依赖9、面试 1、单例Bean不是线程安全的
Scope注解下singleton表示Bean在IOC容器中只有一个实例prototype则表示一个Bean可以有多个实例 但单例Bean不是线程安全的Demo代码如下线程1进来count被改成1线程2进来count被改成10线程交替执行就乱套了单例Bean UserController就不线程安全 多个用户同时请求一个微服务每个请求对应一个线程如果并发的这段代码中对单例的成员属性修改了则存在线程安全问题。上面例子中UserController这个Bean的成员属性count在被并发修改存在可修改的成员变量此时单例的Bean不是线程安全的。相反UserService的这个Bean是不可变状态不涉及其成员属性的修改因此UserService的这个单例Bean是线程安全的。
最后补充一句/getById/{id}id属于局部变量更无线程安全问题了。
2、AOP
项目相关做链路追踪埋点采集所有接口的数据库层、Redis执行时间等信息采用了AOP实现。
AOP面向切面编程将那些公共的逻辑代码抽取封装匹配要切入的点实现统一加功能的操作。减少了系统的重复代码提高了可维护性。其使用场景如自己做公共日志采集上报、Spring用来实现事务 3、Spring中事务的实现
Spring中事务的实现可以编程式也可以声明式。前者使用TransactionTemplate实现对业务代码有侵入性。后者使用Transactional注解。加注解实现事务底层用的是AOP 实现如上切点表达式匹配Transactional注解使用环绕通知执行前加开启事务的代码执行后加提交事务的代码出现异常回滚事务。
4、Spring事务失效的场景
4.1 情况一异常被捕获
如下不加try-catch发生异常后事务回滚。加了try-catch发生异常后被catch捕获处理了。Transactional对应的AOP没有收到异常就会去提交事务。导致了异常后面的业务代码没执行就会出现转账账户扣钱了但被转账的用户余额不变的情况。 4.2 情况二抛出检查异常
以下读取的文件不存在抛出了FileNotFound异常但事务并不会回滚 因为Spring默认只会回滚非检查异常RunTimeException)解决办法是添加属性指明事务回滚的异常种类以下写法即只要发生异常就回滚
Transactional(rollbackForException.class)4.3 注解加在非public方法上 Spring为方法创建代理AOP添加事务通知的前提是方法是public的此时需要改为public方法
5、Bean的生命周期
首先Spring容器将xml中的bean信息封装成一个个BeanDefinition对象该接口的相关方法如下可获取全类名、初始化方法、作用域其中后面步骤的初始化也是从这儿BeanDefition拿的方法名 接下来⇒
STEP1实例化
由BeanDefinition拿到构造方法通过反射去拿到构造函数来new Instance实例化拿到空对象即纯净态的Bean当然实例化也可能是实例工厂、静态工厂等
STEP2属性赋值
解析自动装配DI的体现可byName、byType、constractor这里当然还有循环依赖的情况
STEP3初始化
调用那些XXXAware的回调方法调用初始化生命周期的回调方法init-method如果Bean涉及了AOP还要为这个Bean创建动态代理
STEP4销毁
在Spring容器关闭的时候调用调用销毁生命周期的回调方法destroy-method 注意Bean前置处理器和Bean后置处理器的执行时机是在Bean的init方法执行前后。一般来说框架中Bean创建动态代理常用后置处理器即实现BeanPostProcessor。代码验证下
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;Component
public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {public User() {System.out.println(User的构造方法执行了.........);}private String name ;Value(张三) //依赖注入public void setName(String name) {System.out.println(setName方法执行了.........);}Overridepublic void setBeanName(String name) {System.out.println(setBeanName方法执行了.........);}Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println(setBeanFactory方法执行了.........);}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println(setApplicationContext方法执行了........);}PostConstructpublic void init() {System.out.println(init方法执行了.................);}Overridepublic void afterPropertiesSet() throws Exception {System.out.println(afterPropertiesSet方法执行了........);}PreDestroypublic void destory() {System.out.println(destory方法执行了...............);}}关于前置处理器和后置处理器接口的实现指定如果是user的Bean就打印一句话。前面提到框架中Bean创建动态代理常用后置处理器。下面就体现了模拟框架动态代理给user这个Bean做cglib动态代理的效果
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;Component
public class MyBeanPostProcessor implements BeanPostProcessor {Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals(user)) {System.out.println(postProcessBeforeInitialization方法执行了-user对象初始化方法前开始增强....);}return bean;}Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals(user)) {System.out.println(postProcessAfterInitialization-user对象初始化方法后开始增强....);//cglib代理对象Enhancer enhancer new Enhancer();//设置需要增强的类enhancer.setSuperclass(bean.getClass());//执行回调方法增强方法enhancer.setCallback(new InvocationHandler() {Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {//执行目标方法return method.invoke(method,objects);}});//创建代理对象return enhancer.create();}return bean;}}效果 6、Bean的循环引用
如下A这个Bean创建时依赖B这个Bean。而B这个Bean的创建又依赖A这个Bean即循环引用。有点死锁的感觉。 由Bean的生命周期可知刚开始从Beanfinition获取构造方法反射拿到半成品的A这个Bean即纯净态的Bean此时没有依赖注入即b属性为null。
接下来对A这个Bean进行初始化需要依赖注入设置b的属性此时就需要从IoC容器中拿到B这个Bean。很明显容器中没有因此去创建B这个Bean。
同A纯净态的B在实例化时依赖注入设置a的属性发现需要A这个Bean。于是又走到了实例化A这条线路。到此闭环了走不出来了。 7、Bean循环引用的解决Spring三级缓存
Spring框架已经帮开发者解决了大部分的循环引用 ⇒ 三级缓存三个Map ) 三个Map中存的分别是单例已经走完全部流程的成熟态Bean、存半成品的Bean、对象工厂 一级缓存作用是限制 bean 在 beanFactory 中只存一份即实现 singleton scope。 仅靠它解决不了循环引用还是会从1-6步一直循环 引入二级缓存后将原始对象A放入二级缓存再去找B这个Bean。同理B的原始对象也会先放入二级缓存。接下来B需要依赖注入A这个Bean时会去从二级缓存中获取然后B这个Bean创建成功存储到一级缓存的单例池中同时将B在二级缓存中的半成品删掉。再回到造A的路上将B从容器中注入到AA也创建成功将A在二级缓存中的半成品也删掉。 到此一般对象之间的循环引用到二级缓存就解决了。但还有个场景如果主要注入的A对象是个代理对象则需要三级缓存 8、构造方法出现循环依赖
Spring三级缓存解决了大部分的循环依赖set注入时的循环依赖但如果是构造方法的循环依赖则需自己处理。如下写法报错Is there an unresolvable circular reference? Bean的生命周期第一步是通过BeanDefinition获取方法名反射去拿到构造函数来new Instance实例化一个纯净态的Bean还没到set依赖注入这一步依赖注入的方式不是set方法而是构造函数就发现需要一个B这个Bean同理B一开始执行构造方法就需要A这个Bean。此时Spring框架的三级缓存不能解决。需要加 Lazy 9、面试