1 Bean 注解方式的扫描
1.1 .1注解理解
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
1.1.2 扫描理解
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。
使用注解方式:
(1):我们需要在ioc容器里添加的组件上添加上注解,该注解并不能执行,只是后来为了方便让ioc容器知道需要在ioc容器中添加上什么类。
(2):编写配置扫描信息,ioc容器并不是挨着扫描所有包的所有类,来看需要将哪个类添加在容器中,这样效率很慢,我们需要指定哪些包有需要添加的组件。
(3):创建容器....获取bean对象.
1.1.3 应用:
a.准备项目pom文件。
<dependencies>
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.6</version>
</dependency><!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
b.准备组件类
普通组件
package com.cky; import org.springframework.stereotype.Component; @Component public class Common { }
Controller组件
package com.cky; import org.springframework.stereotype.Controller; @Controller public class UserCon { }
Service组件
package com.cky; import org.springframework.stereotype.Service; @Service public class UserSerive { }
Dao组件
package com.cky; import org.springframework.stereotype.Repository; @Repository public class UserDao { }
c.编写配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--方法1:直接导入具体的包 1.包要精准,提高性能! 2.会扫描指定的包和子包内容 3.多个包可以使用 用逗号分割--> <!-- <context:component-scan base-package="com.cky"></context:component-scan>--> <!--导入固定的注解,但是要注意,我们要设置use-default-filters为false,即让基础包下的不生效,接着在追加规则--> <context:component-scan base-package="com.cky" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> <!--排除不扫描的组件 context:exclude-filter标签:指定排除规则 --> <!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 --> <!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 --> <!-- <context:component-scan base-package="com.cky">--> <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>--> <!-- </context:component-scan>--> </beans>
d.测试
public class testdemo { @Test public void test_01(){ //实例化容器 ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("demo01.xml"); //得到Bean对象 UserDao userDao=context.getBean("userDao",UserDao.class); System.out.println(userDao); UserSerive userSerive=context.getBean(UserSerive.class); System.out.println(userSerive); } }
1.1.4 组件注解区别:
Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。
通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
对于Spring使用IOC容器管理这些组件来说没有区别,也就是语法层面没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性、程序结构严谨!我们肯定不能随便胡乱标记。
1.1.5 组件beanname 问题
在我们使用 XML 方式管理 bean 的时候,每个 bean 都有一个唯一标识——id 属性的值,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。
默认情况:
类名首字母小写就是 bean 的 id。例如:SoldierController 类对应的 bean 的 id 就是 soldierController。
但是,我们也可以使用value进行配置id
@Repository(value = "userdao") public class UserDao { }
1.1.6 总结:
1. 注解方式IoC只是标记哪些类要被Spring管理
2. 最终,我们还需要XML方式或者后面讲解Java配置类方式指定注解生效的包
3. **现阶段配置方式为 注解 (标记)+ XML(扫描)**
2、扩展周期方法和作用域
至于周期方法和作用域具体是什么,可见文章1,在这里不重复介绍。
使用注解的方式来编写周期方法:
A.周期方法声明
public class BeanOne {
//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
@PostConstruct //注解制指定初始化方法
public void init() {
// 初始化逻辑
}
}public class BeanTwo {
@PreDestroy //注解指定销毁方法
public void cleanup() {
// 释放资源逻辑
}
}
B.组件作用域配置
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) //单例,默认值
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多例 二选一
public class BeanOne {//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
@PostConstruct //注解制指定初始化方法
public void init() {
// 初始化逻辑
}
}
注意:单例的话会在容器关闭的时候进行销毁工作,但是多例不会。
3、Bean属性赋值:引用类型自动配置(DI)
3.1 场景及实例:
UserController 需要 UserService
UserService需要UserDao
a. 准备组件 并在组件中配置 IOC 和DI
package demo03; import org.springframework.stereotype.Repository; @Repository public class UserDao { public void show2(){ System.out.println("Userdao"); } }package demo03; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service public class UserService { // @Autowired // @Qualifier(value ="userdao") @Resource(name = "userdao") private UserDao userDao; public void show1(){ userDao.show2(); System.out.println("UserService"); } }package demo03; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { @Autowired private UserService userService; public void show(){ userService.show1(); System.out.println("usercontroller"); } }
b.编写配置文件 确定扫描哪写包
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="demo03"></context:component-scan> </beans>
c.编写测试代码:
@Test public void test_03(){ //实例化容器 ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("demo03.xml"); //得到Bean对象 UserController userController=context.getBean(UserController.class); userController.show(); }
d.结果:
3.2 对实例讲解
3.2.1 @Autowired 注解
在使用注解方式时,我们没必要像 XML方式那样,在组件中写上setter,使用<bean>之后使用配置的方式进行DI配置。只需要在成员变量上加上@Autowired注解即可,不需要提供setXxx()方法。以后我们在项目中的正式用法就是这样。
A.该注解可添加在成员变量上(最常用)
@Service("smallDog")
public class SoldierService {
@Autowired
private SoldierDao soldierDao;
public void getMessage() {
soldierDao.getMessage();
}
}
B. 构造方法上
@Controller(value = "tianDog")
public class SoldierController {
private SoldierService soldierService;
@Autowired
public SoldierController(SoldierService soldierService) {
this.soldierService = soldierService;
}
……
C. Set方法上
@Controller(value = "tianDog")
public class SoldierController {private SoldierService soldierService;
@Autowired
public void setSoldierService(SoldierService soldierService) {
this.soldierService = soldierService;
}
……
3.2.2 工作流程
- 首先根据所需要的组件类型到 IOC 容器中查找
- 能够找到唯一的 bean:直接执行装配
- 如果完全找不到匹配这个类型的 bean:装配失败
- 和所需类型匹配的 bean 不止一个
- 没有 @Qualifier 注解:根据 @Autowired 标记位置成员变量的变量名作为 bean 的 id 进行匹配
- 能够找到:执行装配
- 找不到:装配失败
- 使用 @Qualifier 注解:根据 @Qualifier 注解中指定的名称作为 bean 的id进行匹配
- 能够找到:执行装配
- 找不到:装配失败
只有一个bean的我们不讨论,这里仅需要根据类型需查找ioc中是否有该组件类型即可。
下边讨论一下不止一个bean。
比如一个接口,我们的两个类都实现了该接口。那么这两个类与该接口 的instanceof 都为true。
在我们在其它组件中注入该接口时,ioc容器会发现有两个类 都是该接口的实例化组件,此时就会报错,解决方法①:我们可以使用bean id的方式来找到其中一个实例化组件,就是在编写该接口变量时,接口变量的名字写成其中一个组件的id,那么就会根据该id确定其中的一个实例化组件。
方法②:我们可以 在该接口变量的上方,通过@Autowried 和@Qualifier注解,在后者里边添加上value属性,即我们要选用实例化组件的id。
方法③:我们可以使用@Resource 注解,不过该注解是java的一种规范化技术,
我们在使用该注解时,需要导入依赖
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
3.2.3 @Resource与@Autowried 区别
@Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?
- @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
- @Autowired注解是Spring框架自己的。
- **@Resource注解默认根据Bean名称装配,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型装配。**
- @Autowired注解默认根据类型装配,如果想根据名称装配,需要配合@Qualifier注解一起用。
- @Resource注解用在属性上、setter方法上。
- @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
总结:
(1)@Autowired注解默认根据类型装配,如果类型没有,则报错。如果想根据名称装配,需要配合@Qualifier注解一起用。一旦使用名称来装配,即使类型有,但是名称不匹配也会报错。
(2)@Resource注解默认根据Bean名称装配,如果Bean名称找不到,则会启动根据类型来装配。如果指定了名称,类型有但是名称不匹配就会报错。如果没有启动名称匹配,则后续会在进行类型匹配。
4、使用@Value注解
对于基本数据类型的DI
对于基本数据类型
我们可以直接赋值的方式
比如
private int age=8;
同时我们也可以使用@Value注解 这个注解主要是为了配置外部属性
a.配置组件
package demo04; @Service public class JavaBean { private int age=8; @Value("${url}") private String url; @Value("${a:cui}") 冒号后边是默认值,即配置中如果没有a属性的话,就自动赋值为冒号后边的值。 private String name; @Override public String toString() { return "JavaBean{" + "age=" + age + ", url='" + url + '\'' + ", name='" + name + '\'' + '}'; } }
b. 配置信息 demo04.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> // 扫描的包 <context:component-scan base-package="demo04"> </context:component-scan> //把配置的包导入进来,之后在该容器中的组件都可以通过{$xxx}来访问xxx属性名的值 <context:property-placeholder location="aoo.properties"></context:property-placeholder> </beans>
c.一些外部配置文件 aoo,properties
5、总结:
基于注解的方式,我们还是需要xml配置文件
xml配置:①扫描包的配置 ②外部配置包的部署 ③第三方组件 如jdbctemplate等
之后我们将学习配置类的方式,使用上配置类我们就可以不用在使用xml的方式来完成上边三种配置了。