什么是基于注解的方式管理Bean
在 Spring 框架中,基于注解的方式管理 Bean 是一种非常流行且现代的方法。它允许你通过在类、方法或字段上添加特定的注解来声明 Bean 的创建和依赖注入,从而避免了在 XML 配置文件中定义 Bean 的繁琐工作。
注解和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。
用通俗的话来说就是,不管是注解还是XML实际上就相当于,我们在现实生活中,假设你手上有一群人,然后你需要这群人去做三个任务,对于你标记为红色区域的,要放置红色的花朵,你标记为黄色的区域就放置黄色话多,标记为绿色的地方,就放置绿色草块,你标记完毕之后,剩下的放置花朵和草块的地方就交给你手上的那群人去完成就行。
扫描
上面我举了一个例子,用来标记不同颜色的地方,然后分别对不同颜色的地方做不同的事情,那么Spring是如何知道程序员在哪些地方标记了哪些注解呢?
spring是通过扫描的方式来进行检测,在检测成功之后,根据我们配置的注解来进行后续操作。
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SSM</artifactId>
<groupId>com.miaow</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-annotation</artifactId>
<packaging>jar</packaging>
<name>spring-annotation</name>
<description>Spring基于注解管理Bean</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@Component, @Service, @Repository, @Controller
这些是组件扫描时使用的注解,它们都是@Component的特殊化,用于标记特定类型的类。Spring会自动发现并注册这些带有注解的类为Bean。
- @Component:通用注解,可以用于任何层次的类。
- @Service:通常用于标注业务层服务类。
- @Repository:通常用于标注数据访问层(如DAO类)。
- @Controller:用于标注控制器层,主要用于处理HTTP请求。
我们通过看了@Controller
注解为例子,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
案例
目录结构
创建组件
创建控制层
java
@Controller("controller") //自定义bean的id
//@Controller
public class UserController {
/**
* 能够找到唯一的bean:直接执行装配
* 如果完全找不到匹配这个类型的bean:装配失败
* 如果找到多个匹配这个类型的bean:
* 1.如果这个类型的bean只有一个,那么直接装配
* 2.如果这个类型的bean有多个,那么就需要通过@Qualifier注解来指定装配哪个bean
*
* 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配
*
*/
@Autowired
@Qualifier("userServiceImpl") //byName 根据@Qualifier注解中指定的名称作为bean的id进行匹配
private UserService userService;
public String getUser(){
return "user";
}
public UserController(UserService userService) {
this.userService = userService;
}
public void savaUser(){
userService.saveUser();
}
}
创建Service接口
java
public interface UserService {
void add();
void saveUser();
}
创建Service接口实现层
java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void add() {
System.out.println("添加成功");
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
创建Dao层接口
java
public interface UserDao {
/**
* 保存用户
*/
int saveUser();
}
创建Dao层接口实现层
java
@Repository
public class UserDaoImpl implements UserDao {
@Override
public int saveUser() {
System.out.println("保存成功");
return 1;
}
}
创建一个User类
java
//其实没啥用
public class User {
public void sayHello(){
System.out.println("hello");
}
}
创建一个spring-annotation.xml配置文件
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">
<!-- 扫码组件的几种方式 -->
<!-- 1 基于包内的最基本的扫描方式 -->
<context:component-scan base-package="com.miaow.spring">
<!-- 2:指定要排除的组件 -->
<!-- context:exclude-filter标签:指定排除规则 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<!-- 排查扫描控制层 建议配置方式-->
<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>-->
<!-- 单个指定太麻烦了,不推荐方式 -->
<!-- <context:exclude-filter type="assignable" expression="com.miaow.spring.controller.UserController"/>-->
<!-- 3 仅仅扫描指定的组件 use-default-filter="false" -->
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>-->
<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"></context:include-filter>-->
<!-- <context:include-filter type="assignable" expression="com.miaow.spring.controller.UserController"/>-->
</context:component-scan>
</beans>
创建测试类
java
@Autowired
private UserService userService;
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
// UserController controller = (UserController) context.getBean("userController");//这样发现我们获取不到
UserController controller = (UserController) context.getBean("controller");//这样我们发现可以获到相关值
System.out.println(controller);
}
//测试@AutrWired注解
/**
* Autowired工作流程
* 1. 通过反射获取类中的属性
* 2. 通过反射获取属性上的注解
* 3. 通过注解获取属性的名称
* 4. 通过名称获取bean
* 5. 将bean设置到属性上
* 6. 将bean设置到ioc容器中
*/
@Test
public void testAutowired(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
// UserController controller = (UserController) context.getBean("userController");//这样发现我们获取不到
UserController controller = (UserController) context.getBean("controller",UserController.class);//这样我们发现可以获到相关值
System.out.println(controller);
controller.savaUser();
}
其他注解(拓展)
@Autowired
@Autowired
注解用于自动装配Bean,Spring会自动将匹配的Bean注入到标记了该注解的字段或方法上。默认按类型匹配,如果需要按名称匹配 ,可以与@Qualifier
一起使用。
java
@Autowired
private SomeService someService;
首先根据所需要的组件类型到IOC容器中查找
- 能够找到唯一的bean:直接执行装配
- 如果完全找不到匹配这个类型的bean:装配失败
- 和所需类型匹配的bean不止一个
-
没有
@Qualifier
注解 :根据@Autowired
标记位置成员变量的变量名作为bean的id进行匹配 -
能够找到:执行装配
-
找不到:装配失败
-
使用
@Qualifier
注解 :根据@Qualifier
注解中指定的名称作为bean的id进行匹配 -
能够找到:执行装配
-
找不到:装配失败
-
@Autowired
中有属性required
,默认值为true
,因此在自动装配无法找到相应的bean时,会装配失败,可以将属性required的值设置为true,则表示能装就装,装不上就不装,此时自动装配的属性为默认值,但是实际开发时,基本上所有需要装配组件的地方都是必须装配的,用不上这个属性。
@Resource
@Resource注解来源于JDK,与@Autowired类似,也是用于依赖注入,但它可以根据名称进行注入,如果没有指定名称,则默认按照类型匹配。
java
@Resource
private SomeService someService;
@Configuration 和 @Bean
@Configuration类允许你通过Java类的方式提供Spring容器的配置,而@Bean注解告诉Spring这是一个Bean的定义,用来创建Bean实例。
java
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
@Value
@Value注解用于注入属性值,可以直接注入硬编码的值,也可以注入外部配置文件中的值。
java
@Value("${property.name}")
private String propertyName;
@Scope
@Scope注解用于定义Bean的作用域,默认是singleton(单例),也可以设置为prototype(原型)、request、session等。
java
@Scope("prototype")
@Service
public class MyPrototypeService {}
启用组件扫描
为了使Spring能够自动发现这些带有注解的类,需要在配置类或XML配置文件中启用组件扫描。在Java配置中,可以使用@ComponentScan注解来实现:
java
@Configuration
@ComponentScan(basePackages = {"com.example.myapp"})
public class AppConfig {}
Sping和SpringBoot中的一些注解介绍
java
/**
* @Contoller 添加控制层注解
* @Service 添加service层注解
* @Repository 添加dao层注解
* @Component 添加普通bean注解
* @Autowired 添加自动注入注解
* @Qualifier 添加bean的名称注解
* @Resource 添加自动注入注解
* @Value 添加属性注入注解
* @Primary 添加主要bean注解
* @Lazy 添加延迟加载注解
* @Scope 添加作用域注解
* @Configuration 添加配置类注解
* @Bean 添加bean注解
* @Import 添加导入注解
* @ImportResource 添加导入资源注解
* @PropertySource 添加属性源注解
* @ComponentScan 添加组件扫描注解
* @Conditional 添加条件注解
* @Profile 添加配置文件注解
* @ConfigurationProperties 添加配置属性注解
* @EnableAspectJAutoProxy 添加切面注解
* @Aspect 添加切面注解
* @Pointcut 添加切点注解
* @Before 添加前置通知注解
* @After 添加后置通知注解
* @AfterReturning 添加返回通知注解
* @AfterThrowing 添加异常通知注解
* @Around 添加环绕通知注解
* @Order 添加排序注解
* @Transactional 添加事务注解
* @EnableTransactionManagement 添加事务管理注解
* @EnableAspectJAutoProxy 添加切面注解
* @EnableCaching 添加缓存注解
* @Cacheable 添加缓存注解
* @CacheEvict 添加缓存注解
* @CachePut 添加缓存注解
* @Caching 添加缓存注解
* @EnableCaching 添加缓存注解
* @EnableScheduling 添加定时任务注解
* @Scheduled 添加定时任务注解
*/