什么是基于注解的方式管理Bean
在 Spring 框架中,基于注解的方式管理 Bean 是一种非常流行且现代的方法。它允许你通过在类、方法或字段上添加特定的注解来声明 Bean 的创建和依赖注入,从而避免了在 XML 配置文件中定义 Bean 的繁琐工作。
注解和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。
用通俗的话来说就是,不管是注解还是XML实际上就相当于,我们在现实生活中,假设你手上有一群人,然后你需要这群人去做三个任务,对于你标记为红色区域的,要放置红色的花朵,你标记为黄色的区域就放置黄色话多,标记为绿色的地方,就放置绿色草块,你标记完毕之后,剩下的放置花朵和草块的地方就交给你手上的那群人去完成就行。
扫描
上面我举了一个例子,用来标记不同颜色的地方,然后分别对不同颜色的地方做不同的事情,那么Spring是如何知道程序员在哪些地方标记了哪些注解呢?
spring是通过扫描的方式来进行检测,在检测成功之后,根据我们配置的注解来进行后续操作。
<?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这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
案例
目录结构
创建组件
创建控制层
@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接口
public interface UserService {void add();void saveUser();
}
创建Service接口实现层
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic void add() {System.out.println("添加成功");}@Overridepublic void saveUser() {userDao.saveUser();}
}
创建Dao层接口
public interface UserDao {/*** 保存用户*/int saveUser();
}
创建Dao层接口实现层
@Repository
public class UserDaoImpl implements UserDao {@Overridepublic int saveUser() {System.out.println("保存成功");return 1;}
}
创建一个User类
//其实没啥用
public class User {public void sayHello(){System.out.println("hello");}
}
创建一个spring-annotation.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>
创建测试类
@Autowiredprivate UserService userService;@Testpublic 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容器中*/@Testpublic 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
一起使用。
@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类似,也是用于依赖注入,但它可以根据名称进行注入,如果没有指定名称,则默认按照类型匹配。
@Resource
private SomeService someService;
@Configuration 和 @Bean
@Configuration类允许你通过Java类的方式提供Spring容器的配置,而@Bean注解告诉Spring这是一个Bean的定义,用来创建Bean实例。
@Configuration
public class AppConfig {@Beanpublic MyBean myBean() {return new MyBean();}
}
@Value
@Value注解用于注入属性值,可以直接注入硬编码的值,也可以注入外部配置文件中的值。
@Value("${property.name}")
private String propertyName;
@Scope
@Scope注解用于定义Bean的作用域,默认是singleton(单例),也可以设置为prototype(原型)、request、session等。
@Scope("prototype")
@Service
public class MyPrototypeService {}
启用组件扫描
为了使Spring能够自动发现这些带有注解的类,需要在配置类或XML配置文件中启用组件扫描。在Java配置中,可以使用@ComponentScan注解来实现:
@Configuration
@ComponentScan(basePackages = {"com.example.myapp"})
public class AppConfig {}
Sping和SpringBoot中的一些注解介绍
/*** @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 添加定时任务注解*/