Spring学习笔记8 Bean的循环依赖问题_biubiubiu0706的博客-CSDN博客
注解的存在主要是为了简化XML的配置.Spring6倡导全注解式开发
回顾下
注解怎么定义,注解中的属性怎么定义
注解怎么使用
通过反射机制怎么读取注解
注解的自定义
注解的使用
通过反射机制怎么读取注解
IOC之包扫描原理
需求:给定一个包名,扫描所有类,只要有@Component注解,就创建该类对象,然后放到Map集合中
Key为注解的value,Value为对象
bean包下新建
package com.example.client;
import com.example.annotation.Component;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 手写组件扫描
* @author hrui
* @date 2023/9/24 18:20
*/
public class ComponentScan {
public static void main(String[] args) throws Exception{
//创建存放注解值的key和对象的容器
Map<String,Object> beanMap=new HashMap<>();
//目前只知道包名,扫描包下所有类,当类上有@Component注解时候,实例化该对象,放到Map
String packageName="com.example.bean";
//开始扫描
//.这个正则表达式代表任意字符,这里的"."必须是一个普通的"."字符,不能是正则表达式中的"."
String packagePath = packageName.replaceAll("\\.", "/");
System.out.println(packagePath);
URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
String path = url.getPath();
System.out.println(path);
//获取绝对路径下所有文件
File files=new File(path);
File[] listFile = files.listFiles();
Arrays.stream(listFile).forEach(f->{
try {
System.out.println(f.getName());
System.out.println(f.getName().split("\\.")[0]);
//拼接包名 获得类名
String className=packageName+"."+f.getName().split("\\.")[0];
System.out.println(className);
//通过反射机制解析注解
Class<?> clazz = Class.forName(className);
//判断类上有没有@Component注解
if(clazz.isAnnotationPresent(Component.class)){
//获取注解
Component annotation = clazz.getAnnotation(Component.class);
String key=annotation.value();
//有该注解就创建对象
Object o = clazz.newInstance();
beanMap.put(key, o);
}
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println(beanMap);
}
}
上面就是包扫描的原理
声明Bean的注解,常见的包括4个
@Compoent 组件
@Controller 控制器
@Service 业务
@Repository DAO
新建模块spring-010
pom.xml
<dependencies> <!--Spring依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.10</version> </dependency> <!--junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--Log4j2依赖--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.19.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.19.0</version> </dependency> </dependencies>
引入日志文件,Spring默认用的log4j2 在resources目录下固定名称log4j2.xml日志文件即可
<?xml version="1.0" encoding="UTF-8"?> <configuration> <loggers> <!-- level指定⽇志级别,从低到⾼的优先级: ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF --> <root level="INFO"> <appender-ref ref="spring6log"/> </root> </loggers> <appenders> <!--输出⽇志信息到控制台--> <console name="spring6log" target="SYSTEM_OUT"> <!--控制⽇志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> </console> </appenders> </configuration>
上面4个注解的源码
@Component
看下@Controller @Service @Repository
说明@Component是老大 一样的效果
其实作用是一样的,只为增加程序的可读性
@Controller表示控制层 @Service表示业务层 @Repository表示持久层
那我非不想这么写,都用@Component行不行,答案是:可以.你喜欢,你随意
@Component @Controller @Service @Repository 这4个常用的Spring注解如何使用
1.引入spring-context依赖--->里面的spring--aop依赖 这个已经引入
2.在Spring配置文件中引入命名空间
3.配置包扫描
4.希望将对象交由Spring管理的类上加注解
测试
实际上,即使不取名,默认也有名,类名首字母小写
将4个bean的注解里的vlaue去掉
例如
SpringIOC解决多个包扫描问题
两种解决方案:
1.在配置文件中指定多个包,用逗号隔开
2.指定多个包的共同父包
测试
指定父包
SpringIOC注解式开发之选择性实例化Bean
例如某个包下有很多Bean,分别标注了@Component @Controller @Service @Repository
现在由于某种业务需要,允许@Controller注解的参与Bean管理,其他不实例化.
第一种解决方案
测试:只有A和C 创建对象了
第二种解决方案
负责注入的注解
@Component @Controller @Service @Repository 这四个注解是用来声明Bean的.
声明后这些Bean交由Spring管理.
如何给Bean的属性赋值,需要以下注解
@Value 用于注入简单类型 可以用在属性上 set方法上 构造方法的形参上
@Autowired 用于注入非简单类型 用于 构造方法上 方法上 形参上 属性上 注解上
@Qualifier
@Resource
@Value注解是专门用来注入简单类型的,专门用来代替
示例
现在不需要set方法
新建个配置文件
测试
再测一个
另外 @Value还可以用在set方法上
@Value还可以用在构造方法中
再测
注意:这样不行
SpringIOC注解之@Autowired和 @Qualifier
@Autowired注解可以用来注入非简单类型
单独使用@Autowried注解.默认根据类型匹配----->byType
如果要byName@Autowired和@Qualifier需要一起使用
注意:@Autowired单独使用 都是byType
需要byName要和@Qualifier一起使用
另外建个包com.example2
持久层接口
持久层实现类
业务层接口
业务层实现类
新建配置文件用以包扫描
测试
如果我接口下有多个实现类
就报错了 原因 它找到两个
需要@Autowired和@Qualifier联合使用
在Spring中,@Autowired
注解默认会使用"byType"方式进行自动装配,它会尝试按照被注入的类型(数据类型)去寻找匹配的依赖,然后将依赖注入到目标字段或方法参数中。如果存在多个匹配的依赖对象(同一类型的多个Bean),并且无法确定要注入哪一个时,它会引发一个异常。
如果 "byType" 自动装配失败,Spring 不会自动尝试 "byName" 自动装配。如果有多个相同类型的 Bean,但您希望显式选择其中一个进行注入,您可以使用 @Qualifier
注解与 @Autowired
结合使用,以指定要注入的 Bean 的名称
测试
下面演示@Autowired的可以自动装配的位置,@Autowired单独使用时,有两个同类型的会报错,
因此先把OrderDaoForMysql或者OrderDaoForOracle注释掉一个
下面演示@Autowired的可以自动装配的位置
@Autowired在构造方法上
测试
@Autowired在构造方法参数上
测试
有给属性赋值的构造方法,省略掉@Autowired行不行 (注意:这里要求构造方法必须只有一个,且该构造方法给属性赋值)
测试
@Autowired放在set方法上
关于@Resource注解
@Resource注解也可以完成非简单类型注入.它和@Autowired的区别是
1.@Resource是JDK扩展包中的注解,是javaEE的.更具通用性
2.@Autowired注解是Spring框架自己的
3.@Resource注解默认byName,为指定名字使用属性名为name.通过name找不到会自动启用byType装配
4.@Autowired默认byType,找不到的话不会自动用byName,需要通过@Qualifier来byName
5.@Resource注解用在属性上,set方法上
6.@Autowired注解用在属性上,set方法上,构造方法上,构造方法参数上
@Resource注解属于JDK扩展包(如果是JDK8不需要额外引入依赖,高于JDK11或低于JDK8需要额外引入依赖)
注意:Spring6开始不再支持JAVAEE,它支持JAKARTA9(需要引入依赖)
如果用的Spring6的引依赖
<dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency>
如果用的Spring5
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
这用的Spring6 引入依赖
新建个包
持久层接口
持久层实现类
业务层
新建配置文件包扫描
测试
测试放在set方法上
测试
@Resource不能放在构造方法上
@Resource(name="xxxxx")如果不指定名字 单写个@Resource会将属性名作为名字,如果找不到bean,会用byType
如果
SpringIOC全注解开发 将Spring配置文件干掉