对于一些积累的问题总结
Bean是线程安全的吗不是,spring不保证bean线程安全,bean就是一个对象,关键是我们里面的代码写的是不是线程安全的
异步线程注解失效,和Spring事务注解失效他俩是一个原因,放在一块说,原因就是用的不是SpringAOP过的对象方法
要自己注入自己,然后用注入的对象的异步方法才对
我实习遇到过这个问题
解决方式有两种,就是自己注入自己然后然后用注入的对象的异步方法,或者写在别的类里面。我的代码它就写在这个类里合适,所以建议用第一种方法。
Bean的创建流程从容器启动开始:
1、 Spring启动首先扫描注解把扫描到的信息封装成BeanDefinition
2、 推断构造方法用反射创建单例Bean
3、 Bean的属性注入
5、 做接口的回调
4、 执行自定义的初始化方法给属性赋值,实现InitializingBean接口afterPropertiesSet方法或者用@PostConstruct注解标注在初始化方法上。
6、 最后一步做AOP,如果做AOP的话单例池放的就是代理对象,即包裹了一层。
补充细节:
扫描注解,注解会表示一些信息,比如单例模式原型懒加载等
在实例化对象的时候会选择一个合适的构造方法具体为:1、如果有有参的和无参的默认用无参的。2、当只有一个构造方法的时候就用这个。3、如果没有默认构造方法有很多非默认构造方法会报错,Spring不知道我们想用哪个,我们可以在想用的构造方法上加@Autowired注解。
@Component
public class MyClass{
Student student;
//会先ByType在ByName (ByName根据属性名字找,找不到就报错)
public UserService (Student student){
this.student = student;
}
}
Bean的属性注入是Bean也需要注入Bean,Bean之间有依赖关系的情况
接口回调用,会注入一些东西比如:
自定义初始化方法比如:我们其中一个属性需要查询数据库来得到,那就执行查询数据在赋值的逻辑。
补充:后置处理是Spring技术概念的统称。
Spring循环依赖在Spring里Bean有自己的创建过程,如果只有一级缓存,就找不到半成品的Bean。
在Java代码里(没用框架)如果如果a对象依赖b对象,b反过来又依赖a,或者又出现了c,都很好解决如下:
class A{
B b;
void setB(B b) {
this.b = b;
}
}
class B{
A a;
void setA(A a) {
this.a = a;
}
}
public static void main(String[] args) {
A a = new A();
B b = new B();
//直接就能set
a.setB(b);
b.setA(a);
}
Spring框架里这样就会产生循环依赖如下:
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
//这样A和B就循环依赖了
解释:
因为在上个问题Bean的创建流程中,如果BeanA的属性也要注入另一个BeanB但是BeanB不在容器里,就会出走创建BeanB的流程,创建BeanB的属性要注入BeanA,但是BeanA还没创建完成,容器里找不到BeanA,又来创建BeanA,循环往复。
解决:
Spring用了三级缓存来解决循环依赖。如果不用AOP的话,二级缓存就能解决,但是如果AOP的话要三级缓存解决循环依赖。
一级是HashMap放创建好的Bean。二级是HashMap放创建Bean的半成品。三级是ConcurrentHashMap,用Lambda表达式把结果直接放进二级缓存。
这时候创建Bean注入属性的时候不会先去一级缓存去找,而是去二级缓存找是否有半成品,这样才用提前暴露引用的方式,在上述BeanB注入BeanA得时候就能在二级缓存找到半成品引用啦。
但是单例Bean不能用多级缓存解决构造方法循环依赖,因为构造方法在创建对象Bean的推断构造方法里那部放,还没放进缓存中呢。
@Autowired和@Resource属性注入时候注意@Autowired:先ByType在ByName
1、先看容器里这Type个数一个就直接用,多个继续筛选。
2、再筛选掉Bean属性候选者=false的Bean。
3、再筛掉不符合@Qualifier的组,@Qualifier注解用在注册Bean的方法上又给Bean分了组名,@Autowired注入的地方结合@Qualifier找Bean的别名组。
4、再筛选根据@Primary找每个类型里的主Bean。
5、如果没写@Primary,再取优先级最高的Bean,@Priority(1)、@Priority(2)值小优先级高。
6、如果@Primary和@Priority()都没写,根据Name属性名字看是否存在,不存在就报错。
//注册Bean有个候选者属性默认是true
//方法的名字是Bean的名字
@Bean (autowireCandidate = false)
public Student student1(){
return new Student():
}
@Bean (autowireCandidate = true)
@Qualifie("别名组1")
public Student student2(){
return new Student():
}
//@Primary同一个类型只能标注一个主Bean
@Bean
@Qualifie("别名组1")
@Primary
public Student student3(){
return new Student():
}
//@Priority(1)值小优先级高
@Bean
@Priority(1)
public Student student4(){
return new Student():
}
@Bean
@Priority(2)
public Student student5(){
return new Student():
}
class MyClass{
@Autowired
@QualiQualifier("别名")
Student student123;
}
@Resource:先ByName在ByType
@Resource是Java提供的,Spring实现了它
1、@Resource有个name属性如果指定了,直接按name属性去找,找到就找到,找不到就报错。
2、如果没有指定name,那就根据属性的名字看容器里能否找到
3、找不到在看Type个数一个就找到,多个就报错
class MyClass{
@Resource(name = "student1")
Student student3;
@Resource
Student student123;
}
Spring的AOP实现
Spring的AOP是干嘛的:他是用来增强方法的方法前后插入我们的增强逻辑,
像是事务,日志,都是用AOP做的
代码怎么写AOP:
@Aspect //表示写的是一个切面类
@Component //当没有明确语义是用Component注册
public class MyAspect{
//定义一个切点//里面写表达式
@Pointcut("execution(com.service.Buy.play(..))")
public void AspectPoint() {}
//下面是5种通知
//before是前置 (里面写切点方法)
@Before ("AspectPoint()")
public void MyBefore() { System.out.println("这是前置逻辑");}
//后置
@After("切点方法")
//环绕就是前后
@Around("切点方法")
//在目标方法返回结果后调用
@AfterReturning("切点方法")
//在目标方法抛出异常后调用
@AfterThrowing("切点方法")
}
Spring两种AOP的实现
JDK动态代理(Spring默认):类要实现接口
CGLib:生成当前类的子类,所以类不能用final修饰
CGLib代理实现:有两种,一种是子类supper调用父类方法,还有一种就是把目标类当成属性,在方法前后增强之间调用目标类方法。
Spring一些常用的注解和作用@Lazy 使bean懒加载
@Transactional 声明式事务
@Value 自定义注入用于普通字符,配置文件等
@RestController 作用在类上前后端分离直接返回JSON不走视图解析器
@AutoWired、自动装配
@Qualifier、自动装配
@Resource自动装配
@RequestMapping、
@GetMapping、
@PostMapping
@Configuration、
@Bean
@Component
@Service、
@Controller、
@Repository
Spring配置多个数据源是可以的 Bean容器到底是啥Bean是啥


