Spring容器启动,启动完了会做一个简单的扫描,扫描完了以后把它变成BeanDefintion存到BeanDefintion Map当中,然后再去对BeanDefintion Map做一个遍历,遍历完成之后做一些验证,验证是否单例,是否原型,是否懒加载,是否有Depanson,是否抽象,是否factorybean,是否Bean的名称符合
验证完毕springbean容器会接着往下执行,获取实例化的类存不存在单列池中,有没有被提前暴露, 没有被提前暴露,那么spring bean就会开始来创建
第一步 通过推断构造方法,把当前spring bean所代表的类当中的构造方法得到一个最佳的构造方法。
第二步 通过反射去实例化一个java对象,之后在对这个bean做一些初始化的工作,比如是否要去做BeanDefinition合并,是否支持循环依赖,若支持循环依赖,会暴露当前bean(半成品)所对应的objectFactory.暴露就是存放一个二级缓存,一个map当中
第三步 做属性填充
第四步 然后aware方法的回调,比如一些applicationContextAware BeanNameAware ClassLoaderAware
第五步 接着生命周期初始化的回调
第六步 接着如果有aop会做一些代理
第七步 接着事件的发布
最后就是放在单例池当中,放入spring容器当中
在整个springframework的体系当中,我们的一个bean是由一个beandefintion来构建的,beandefinition可以理解为springbean的一个建模 如果要理解循环依赖的话,首先要理解springbean的生命周期。一共大体分为以下几个过程。。
Spring容器启动,启动完了会做一个简单的扫描,扫描完了以后把它变成BeanDefintion存到BeanDefintion Map当中,然后再去对BeanDefintion Map做一个遍历,遍历完成之后做一些验证,验证是否单例,是否原型,是否懒加载,是否有Depanson,是否抽象,是否factorybean,是否Bean的名称符合
验证完毕springbean容器会接着往下执行,获取实例化的类存不存在单列池中,有没有被提前暴露, 没有被提前暴露,那么spring bean就会开始来创建
第一步 通过推断构造方法,把当前spring bean所代表的类当中的构造方法得到一个最佳的构造方法。
第二步 通过反射去实例化一个java对象
当反射了一个x对象,会进行填充属性,发觉x依赖了y,就会有y的生命周期流程。与x一样,会判断y是否在单例池中,如果没有在单例池当中的话,再去判断y有没有被提前暴露,这个时候y是没有被提前暴露的,y接着往下执行,就会把y实例化好,然后对y做一些初始化的工作。比如说把y提前暴露 属性填充,当发现y要填充x,发现x并没有被完整的实例化好,所以不能填充,所以走一遍创建x的流程,发现x已经被提前暴露了一个objectfactory产生的一个对象,就完成了一个循环依赖
循环依赖是单例的,因为单例会在spring容器初始化的时候走我们的bean生命周期流程
Spring事务隔离级别比数据库事务隔离级别多一个default
1) DEFAULT (默认) 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
2) READ_UNCOMMITTED (读未提交) 这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3) READ_COMMITTED (读已提交) 保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
4) REPEATABLE_READ (可重复读) 这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
5) SERIALIZABLE(串行化) 这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。
propagation代表事物传播行为,默认值为Propagation.REQUIRE
- Propagation.required:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )
- Propagation.Supports**:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
- Propagation.Mandatory:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
- Propagation.requires_new**:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )
- Propagation.Not_supports**:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
- Propagation.Never**:以非事务的方式运行,如果当前存在事务,则抛出异常。
- Propagation.Nested:和 Propagation.REQUIRED 效果一样。
@Transactional注解相信大家都不陌生,平时在开发中spring框架中经常用到的事物注解。
他的作用就是控制程序执行的原子性,保证程序要提交就一起提交,如果出现错误,则一起进行回滚操作。
但是在使用@Transactional注解的时候有很多需要注意的地方,不然你就会发现这个事物注解会出现失效的情况。
@Transcational可以作用在接口、类、类方法。
- 作用类:当把
@Transcational注解放在类上时,表示所有该类的public方法都配置上相同的事物属性 - 作用方法:当配置了
@Transcational,方法也配置了@Transcational,那么方法上的事物属性会覆盖掉类上声明的事物属性。 - 作用接口:不推荐这种使用方式,因为一旦标注在
Interface上并且配置了Spring AOP使用CGLib动态代理,将会导致@Transcational失效
@Transactional @RestController @RequestMapping public class MybatisPlusController { @Autowired private CityInfoDictMapper cityInfoDictMapper;
@Transactional(rollbackFor = Exception.class)
@GetMapping("/test")
public String test() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setParentCityId(2);
cityInfoDict.setCityName("2");
cityInfoDict.setCityLevel("2");
cityInfoDict.setCityCode("2");
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert + "";
}
}
propagation代表事物传播行为,默认值为Propagation.REQUIRE
- Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )
- Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
- Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
- Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )
- Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
- Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
- Propagation.NESTED:和 Propagation.REQUIRED 效果一样。
1.如果@Transcational注解应用在非public修饰的方法上,Transcational将会失效。
之所以会失效是因为在Spring AOP代理时,如上图所示TranscationInterceptor(事物拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy的内部类)的intercept方法或JdkDynamicAopPorxy的invoke方法间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。
此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。 注意:protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。
2.@Transcational注解属性propagation设置错误
这种失效由于配置错误,若是错误的配置以下三种propagation,事物将不会发生回滚。 这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
-
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
-
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
3.@Transcational注解属性rollbackFor 设置错误
rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。
*// 希望自定义的异常可以进行回滚*
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
4.同一个类中方法调用,导致失效
开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。
那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
5.异常被你的catch吃了,导致@Transactional失效
这种情况是最常见的一种@Transactional注解失效场景
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/**
* A 插入字段为 2的数据
*/
insert = cityInfoDictMapper.insert(cityInfoDict);
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务还能正常回滚吗?
答案:不能!
会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
因为当ServiceB中抛出了一个异常以后,ServiceB标识当前事务需要rollback。但是ServiceA中由于你手动的捕获这个异常并进行处理,ServiceA认为当前事务应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException异常。
spring的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。
在业务方法中一般不需要catch异常,如果非要catch一定要抛出throw new RuntimeException(),或者注解中指定抛异常类型@Transactional(rollbackFor=Exception.class),否则会导致事务失效,数据commit造成数据不一致,所以有些时候try catch反倒会画蛇添足。
6.数据库引擎不支持
这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。
是的。
https://blog.csdn.net/weixin_44337261/article/details/91696633
**如果一个bean被声明为单例的时候,在处理多次请求的时候在Spring容器里只实例化出一个bean,后续的请求都公用这个对象,这个对象会保存在一个map里面。**当有请求来的时候会先从缓存(map)里查看有没有,有的话直接使用这个对象,没有的话才实例化一个新的对象,所以这是个单例的。但是对于原型(prototype)bean来说当每次请求来的时候直接实例化新的bean,没有缓存以及从缓存查的过程。
生成bean的时候先判断是单例还是原型
如果是单例的则先尝试从缓存里获取,没有在新创建
- 单例的bean只有第一次创建新的bean 后面都会复用该bean,所以不会频繁创建对象。
- 原型的bean每次都会新创建
3.1、单例bean的优势
由于不会每次都新创建新对象所以有一下几个性能上的优势:
(1)减少了新生成实例的消耗 新生成实例消耗包括两方面,首先,Spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法
(2)减少jvm垃圾回收 由于不会给每个请求都新生成bean实例,所以自然回收的对象少了
(3)可以快速获取到bean 因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快
3.2、单例bean的劣势
单例的bean一个很大的劣势就是他不能做到线程安全!!!,由于所有请求都共享一个bean实例,所以这个bean要是有状态的一个bean的话可能在并发场景下出现问题,而原型的bean则不会有这样问题(但也有例外,比如他被单例bean依赖),因为给每个请求都新创建实例。
Spring 为啥把bean默认设计成单例?
答案:为了提高性能!!!从几个方面:1.少创建实例2.垃圾回收3.缓存快速获取
有状态会话bean :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。简单来说,有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。
无状态会话bean :bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。简单来说,无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
package com.sw;
public class TestManagerImpl implements TestManager{
private User user; //有一个记录信息的实例
public void deleteUser(User e) throws Exception {
user = e ; //1
prepareData(e);
}
public void prepareData(User e) throws Exception {
user = getUserByID(e.getId()); //2
.....
//使用user.getId(); //3
.....
.....
}
}
一个有状态的bean
Spring对bean的配置中有四种配置方式,我们只说其中两种:singleton单例模式、prototype原型模式。
当然,scope的值不止这两种,还包括了request,session 等。但用的最多的还是singleton单态,prototype多态。Singleton、Prototype和有无状态的bean是两个概念。。
Singleton作用域表示该bean全局只有一个实例,Spring中bean的scope默认也是singleton,这种情况适用于无状态的Bean。
如Service层、Dao层用默认singleton就行,虽然Service类也有dao这样的属性,但dao这些类都是没有状态信息的,也就是相当于不变(immutable)类,所以不影响。Struts2中的Action因为会有User、BizEntity这样的实例对象,是有状态信息的,在多线程环境下是不安全的,所以Struts2默认的实现是Prototype模式。在Spring中,Struts2的Action中,scope要配成prototype作用域。
<bean id="testManager" class="com.sw.TestManagerImpl" scope="singleton" />
<bean id="testManager" class="com.sw.TestManagerImpl" scope="prototype" />
默认的配置是singleton。
singleton表示该bean全局只有一个实例。
prototype表示该bean在每次被注入的时候,都要重新创建一个实例,这种情况适用于有状态的Bean。
如果对有状态的bean使用了singleton的话会出现线程安全问题。
例如上面的例子
如果有两个用户同时访问
假定为user1,user2
当user1 调用到程序中的1步骤的时候,该Bean的私有变量user被付值为user1
当user1的程序走到2步骤的时候,该Bean的私有变量user被重新付值为user1_create
理想的状况,当user1走到3步骤的时候,私有变量user应该为user1_create;
但如果在user1调用到3步骤之前,user2开始运行到了1步骤了,由于单态的资源共享,则私有变量user被修改为user2
这种情况下,user1的步骤3用到的user.getId()实际用到是user2的对象。
1.将有状态的bean配置成prototype模式,让每一个线程都创建一个prototype实例。但是这样会产生很多的实例消耗较多的内存空间。
2.使用ThreadLocal变量,为每一条线程设置变量副本。
Spring使用ThreadLocal解决线程安全问题。我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
AOP,一般称为面向切面,作为面向对象的一种补充,
用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect)
减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
在面向对象编程中,关键的单元是对象,但AOP的关键单元是切面(关注点),像日志或者事务这些分散的切面(横切关注点——可以影响到整个应用的关注点)应该被尽量地集中到一个地方以方便管理(事务管理、权限、日志、安全)。而AOP就是作用于这些横切关注点,使得代码在当下和将来都变得容易维护。
关注点:我们的应用模块中实现以解决特定业务问题的方法。比如库存管理、航运管理、用户管理等。
横切关注点:贯穿整个应用程序的关注点。像事务管理、权限、日志、安全
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
(大白话)
作用非常大,在很多底层框架中都会用得到,比如struts,Spring等都用到了动态代理dao,
它的作用很简单,就是将你要使用的类,重新生成一个子类或本类,这样框架就可以利用这个新生成的类做一些事情,比如在该类的方法前后加一些代码
这样的话,你想像一下,你是不是不用修改任何已经编写好的代码,只要使用代理就可以灵活的加入任何东西,将来不喜欢了,不用也不会影响原来的代码
-
动态代理是一种设计模式,用于增强代理对象的功能的一种设计方式。更严格的来说代理包括静态代理和动态代理,只不过动态代理对于代码没有侵入性,技术实现难度大了些,所以大家谈的相对较多。
- AOP这是spring提出的用于增强spring bean中方法功能的设计思路和具体实现。AOP思路和动态代理很像,AOP的原则是一切皆可代理,口气比动态代理大多了。 AOP的具体实现比如cglib,javasist,cglib应该算是AOP实现的集大成者,这框架的能力强到爆炸,你可以随心所欲操作字节码。比如曾经使用spring AOP魔改jdk的代码。
至于作用就是这些,一般来说使用AOP可以解决我们日常工作中: 1. 拦截 2. 在不能动原有代码基础上动他的代码 3. 为了对上层服务透明,一般也都是AOP
最后举几个每天都在用的AOP例子:
1. 数据库事务操作
对于事务操作,只能是start transaction; ....; commit;/rollback;你以为加个注解或者加个配置就能完成那么复杂的事务,这都是AOP拦截对象,做了增强
2. 监控
SOA架构下监控是必不可少的,这种监控怎么搞。总不能每一个接口都加上一条监控?所以都是AOP拦截统计获得
3. spring有一个重试框架
这个框架允许你对一个方法配置重试次数,重试时间等等,这也都是AOP的作用
动态代理技术有:原生JDK,CGLIB,Javassist,ASM。其中,Spring常用JDK和CGLIB,
*动态代理实质,就是使用字节码技术,重新生成了一个新类,来达到增强的效果。*
区别:
*JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。*
*CGLib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类*
CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理
当我们在业务代码中使用多线程的时候,如果多线程中需要进行事务的管理,例如增加,修改,删除。需要保证每一个线程的事务要么一起提交,要么一起回滚操作。
因为线程不属于spring托管,线程不能默认使用spring的事务,也不能获取spring注入的bean在被spring声明式管理的方法内开启多线程,多线程内部的方法并不受事务控制。
即使在每一个多线程方法,放在其他的service里面,加上@Transactional,这样会导致,一个线程异常,异常线程执行事务回滚,其他的线程依然已经执行成功,不合符我们的要求。
1.事务补偿
2.将每一个线程事务都控制起来,最后来判断结果,如果有一个失败了,全部执行回滚操作,如果全部通过,那么全部执行通过
在类上加@Component或者其他注解时,该类会交给spring来管理,自动创建对象,但是如果是自己new出来的对象或者自己类没加注解,但是内部又需要注入其他对象以及需要注入配置文件中的属性时.
实例:
/**
* @ClassName ProcessDataContext
* @Description 获取流程清单工厂
* @Author HeX
* @Date 2020/9/23 16:46
* @Version 1.0
**/
@Component
public class ProcessServiceFactory {
private static final Map<String, ProcessDataStrategy> map = new HashMap<>();
static {
// 这里采用了new出UnapprovedData对象,所以,在UnapprovedData内部采用@Autowired会得不到对象
map.put(ProcessDataEnum.unapproved.getCode(), new UnapprovedData());
}
public static ProcessDataStrategy getProcessStrategy(String processType) {
return map.get(processType);
}
}解决办法:
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}
public static <T> T getBean(String beanName) {
assertApplicationContext();
return (T) applicationContext.getBean(beanName);
}
private static void assertApplicationContext() {
if (SpringContextHolder.applicationContext == null) {
throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了SpringContextHolder!");
}
}
}优化前无法注入:
@Autowired
ProcessMapper processMapper优化后可以注入:
/**
* 因从工厂new出的对象,所以这里采用手动注入
*/
@Autowired
ProcessMapper processMapper = SpringContextHolder.getBean("ProcessMapper");说明:
简单解释一下工具类为什么能够找到类,是因为在启动spring后,spring会将所有注解过的类加载容器内,并返回 给ApplicationContextAware,在其他注解后的类需要对象时,容器会自动注入,但是自己new的类缺不会被容器注入,此时,自己实现ApplicationContextAware,自己根据类的类型手动获得加载后的bean即可。
解决注入属性问题:
创建一个工具类:
@Component//注意此时要加注解
public class GetPropertiesUtil
{
private static String properties;
public static Stirng getProperties()
{
return properties;
}
@Value("${properties}")
public void setProperties(String properties)
{
this.properties = proterties;
}
}注意两点:1、该工具类要加上注解,否则会像第一种情况;
2、内部属性加@Value的地方是set方法上,而且set方法不是statis;
使用举例:
public MyClass
{
@Value("${properties}")
private String properties;
}上面类在new MyClass时,xxx属性注入会失败,此时改进一下情况即可:
public MyClass
{
private String properties = GetPropertiesUtil.getProperties();
}



