和XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测 到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作,本质上所有操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行
Spring中使用以下5个注解声明一个bean
@Controller、@Service、@Repository、@Component、@Configuration
查看@Controller、@Service、@Repository三个注解源码
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Component
- public @interface Service {
- String value() default "";
- }
可以看到只是在@Component注解的基础上起了三个新的名字,对于Spring使用IOC容器管理这些组件来说没有区别。Controller、Service、Repository三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
虽然本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
@Configuration
:如果一个Bean不在我们自己的package管理之内,例如ZoneId
,如何创建它?使用@Configuration注解定义一个类,并在
方法上标记一个@Bean
注解在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识便于在其他地方引用。
使用注解后每个组件仍然应该有一个唯一标识
默认情况类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。
可通过标识组件的注解的value属性设置自定义的bean的id @Service("userService")
第⼀步:加⼊aop的依赖
我们可以看到当加⼊spring-context依赖之后,会关联加⼊aop的依赖
第⼆步:在配置⽂件中添加context命名空间
- "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 http://www.springframework.org/schema/context/spring-context.xsd">
-
- beans>
第三步:在配置⽂件中指定要扫描的包
扫描指定包下所有被注解标注的组件
- "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 http://www.springframework.org/schema/context/spring-context.xsd">
-
- <context:component-scan base-package="com.springcode.example.controller">context:component-scan>
-
- <context:component-scan base-package="com.springcode.example.controller,com.springcode.example.entity">context:component-scan>
-
- <context:component-scan base-package="com.springcode.example">context:component-scan>
- beans>
排除指定组件
- <context:component-scan base-package="com.demo">
-
- <context:exclude-filter type="annotation"
- expression="org.springframework.stereotype.Controller"/>
-
- context:component-scan>
仅扫描指定组件
- <context:component-scan base-package="com.demo" use-default-filters="false">
- <context:include-filter type="annotation"
- expression="org.springframework.stereotype.Controller"/>
- context:component-scan>
第四步:在Bean类上使⽤注解
- @Controller
- public class UserController {
- }
编写测试程序
- public class SpringTest {
- @Test
- public void testFirst() {
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
- UserController user = applicationContext.getBean("userController", UserController.class);
- System.out.println(user);
- }
- }
可以通过上面注解声明一个类作为一个bean,也可以声明一个方法作为一个bean,如下
- @Configuration //也可以用@Component
- public class MyBean {
- //使用bean注解声明一个方法返回值作为一个bean,bean的名字是方法名
- @Bean
- public User userInfo(){
- return new User();
- }
- }
测试
- public class SpringTest {
- @Test
- public void testFirst() {
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
- User user = applicationContext.getBean("userInfo", User.class);
- System.out.println(user);
- }
- }
Configuration和
@Component区别如果一个Bean不在我们自己的package管理之内,例如ZoneId
,如何创建它?使用@Configuration注解定义一个类,并在
方法上标记一个@Bean
注解:
- @Configuration
- public class AppConfig {
- // 创建一个Bean:
- @Bean
- ZoneId createZoneId() {
- return ZoneId.of("Z");
- }
- }
查看@Configuration源码本质是使用@Component进行元注解,因此
或者 @ComponentScan
都能处理@Configuration
注解的类。
Spring @Configuration 和 @Component 区别,一句话概括就是 @Configuration
中所有带 @Bean
注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
如
- @Configuration
- public class MyBeanConfig {
-
- @Bean
- public Country country(){
- return new Country();
- }
-
- @Bean
- public UserInfo userInfo(){
- return new UserInfo(country());
- }
-
- }
相信大多数人第一次看到上面 userInfo() 中调用 country() 时,会认为这里的 Country 和上面 @Bean 方法返回的 Country 可能不是同一个对象,因此可能会通过下面的方式来替代这种方式:
@Autowired
private Country country;
实际上不需要这么做(后面会给出需要这样做的场景),直接调用 country() 方法返回的是同一个实例。
但是@Component
注解并没有通过 cglib 来代理@Bean
方法的调用,因此像下面这样配置时,就是两个不同的 country。
- @Component
- public class MyBeanConfig {
-
- @Bean
- public Country country(){
- return new Country();
- }
-
- @Bean
- public UserInfo userInfo(){
- return new UserInfo(country());
- }
-
- }
上面注解声明后,如何给Bean的属性赋值?给Bean属性赋值需要⽤到这些注解:
@Value、@Autowired、@Qualifier、@Resource
对于简单属性注入可以使用@value赋值
- @Component
- public class User {
- @Value(value = "tom")
- private String name;
- @Value("18")
- private int age;
-
- public User(){
- System.out.println("无参构造被调用");
- }
-
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
测试
- public class SpringTest {
- @Test
- public void testFirst() {
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
- User user = applicationContext.getBean("user", User.class);
- System.out.println(user);//User{name='tom', age=18}
- }
- }
通过以上代码可以发现,我们并没有给属性提供setter⽅法,但仍然可以完成属性赋值。 如果提供setter⽅法,并且在setter⽅法上添加@Value注解,可以完成注⼊吗?
- @Component
- public class User {
-
- private String name;
-
- private int age;
- @Value(value = "tom")
- public void setName(String name) {
- this.name = name;
- }
- @Value("18")
- public void setAge(int age) {
- this.age = age;
- }
-
- public User(){
- System.out.println("无参构造被调用");
- }
-
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
通过测试可以得知,@Value注解可以直接使⽤在属性上,也可以使⽤在setter⽅法上。都是可以的。都可以完成属性的赋值。
为了简化代码,以后我们⼀般不提供setter⽅法,直接在属性上使⽤@Value注解完成属性赋值。
是否能够通过构造⽅法完成注⼊
- @Component
- public class User {
- private String name;
- private int age;
-
- public User(@Value("tome") String name,@Value("18") int age){
- this.name = name;
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
通过测试得知:@Value注解可以出现在属性上、setter⽅法上、以及构造⽅法的形参上。可⻅Spring给我们提供了多样化的注⼊
使用@Autowired完成引用类型装配,单独使⽤@Autowired注解,默认根据类型装配。【默认是byType】
源码如下
- @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETE
- R, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface Autowired {
- boolean required() default true;
- }
源码理解1、该注解可以标注在哪⾥?
源码理解2、该注解有⼀个required属性,默认值是true,表示在注⼊的时候要求被注⼊的Bean必须是 存在的,如果不存在则报错。如果required属性设置为false,表示注⼊的Bean存在或者不存在都没 关系,存在的话就注⼊,不存在的话,也不报错
1、在属性上使⽤@Autowired注解
在成员变量上直接标记@Autowired注解即可完成自动装配,如在controller中装配service层bean
- @Controller
- public class UserController {
- @Autowired
- private UserService userService;
-
- public void saveUser(){
- userService.saveUser();
- }
- }
2、可以标记在set方法上
- @Service
- public class UserService {
- private UserDao userDao;
- @Autowired
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- public void save(){
- userDao.insert();
- }
- }
3、@Autowired注解还可以标记在构造器
- @Controller
- public class UserController {
- private UserService userService;
- @Autowired
- public UserController(UserService userService){
- this.userService = userService;
- }
- public void saveUser(){
- userService.saveUser();
- }
- }
4、标注在构造⽅法的形参上
- @Service
- public class UserService {
- private UserDao userDao;
- public UserService(@Autowired UserDao userDao) {
- this.userDao = userDao;
- }
- public void save(){
- userDao.insert();
- }
- }
并且当有参数的构造⽅法只有⼀个时,@Autowired注解可以省略
- @Service
- public class UserService {
- private UserDao userDao;
- public UserService(UserDao userDao) {
- this.userDao = userDao;
- }
- public void save(){
- userDao.insert();
- }
- }
如果有多个构造⽅法,@Autowired肯定是不能省略的。如下测试会报错
- @Service
- public class UserService {
- private UserDao userDao;
- public UserService(UserDao userDao) {
- this.userDao = userDao;
- }
-
- public UserService(){
-
- }
- public void save(){
- userDao.insert();
- }
- }
@Autowired注解默认是byType进⾏注⼊的,也就是说根据类型注⼊的,如果以上程序中,UserDao接⼝ 还有另外⼀个实现类,会出现问题吗?
可以测试是不能装配的,提示UserDao这个Bean的数量⼤于1. 怎么解决这个问题呢?当然要byName,根据名称进⾏装配了。 @Autowired注解和@Qualifier注解联合起来才可以根据名称进⾏装配,在@Qualifier注解中指定Bean名 称。
- @Service
- public class UserService {
- private UserDao userDao;
- @Autowired
- @Qualifier("userDaoTwo") // 这个是bean的名字。
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- public void save(){
- userDao.insert();
- }
- }
@Autowired工作流程
首先根据所需要的组件类型到IOC容器中查找,唯一直接返回,存在多个
没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行 匹配 能够找到:执行装配 找不到:装配失败
使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配 能够找到:执行装配 找不到:装配失败
@Autowired中有属性required,默认值为true,因此在自动装配无法找到相应的bean时,会装 配失败 可以将属性required的值设置为true,则表示能装就装,装不上就不装,此时自动装配的属性为 默认值
@Resource注解也可以完成⾮简单类型注⼊。那它和@Autowired注解有什么区别?
@Resource注解是JDK扩展包中的,也就是说属于JDK的⼀部分。所以该注解是标准注解,更加具 有通⽤性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
-
jakarta.annotation -
jakarta.annotation-api -
2.1.1
注意:如果你⽤Spring6,要知道Spring6不再⽀持JavaEE,它⽀持的是JakartaEE9。(Oracle 把JavaEE贡献给Apache了,Apache把JavaEE的名字改成JakartaEE了,⼤家之前所接触的所有的 javax.* 包名统⼀修改为 jakarta.*包名了。)
如果你是spring5-版本请使⽤这个依赖
-
javax.annotation -
javax.annotation-api -
1.3.2
@Resource注解的源码如下:
使用
- @Service
- public class UserService {
- @Resource
- private UserDao userDao;
- public void save(){
- userDao.insert();
- }
- }
@Resource注解也可以使⽤在setter⽅法上
- @Service
- public class UserService {
- private UserDao userDao;
- @Resource
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- public void save(){
- userDao.insert();
- }
- }
注意这个setter⽅法的⽅法名,setUserDao去掉set之后,将⾸字⺟变⼩写userDao,userDao就是name
当然,也可以指定name:
- @Service
- public class UserService {
- private UserDao userDao;
- @Resource(name = "userDaoForMySQL")
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- public void save(){
- userDao.insert();
- }
- }
⼀句话总结@Resource注解:默认byName注⼊,没有指定name时把属性名当做name,根据name找不 到时,才会byType注⼊。byType注⼊时,某种类型的Bean只能有⼀个。
所谓的全注解开发就是不再使⽤spring配置⽂件了。写⼀个配置类来代替配置⽂件。
- @Configuration
- @ComponentScan({"com.spring6demo.spring6.dao", "com.spring6demo.service"})
- public class Spring6Configuration {
- }
编写测试程序:不再new ClassPathXmlApplicationContext()对象了。
- @Test
- public void testNoXml(){
- ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
- UserService userService = applicationContext.getBean("userService", Use
- rService.class);
- userService.save();
- }
实际开发中以注解为主, xml为辅
IOC解决的是业务逻辑对象之间的耦合关系,也就是service和dao之家的解耦合
spring容器适合管理对象
不适合管理管理对象