【Spring6】| Spring IoC注解式开发

缺乏、安全感 2024-03-29 15:57 139阅读 0赞

目录

一:Spring IoC注解式开发

  1. 回顾注解

  2. 声明Bean的四个注解

  3. Spring注解的使用

  4. 选择性实例化Bean

  5. 负责注入的注解(重点)

5.1 @Value

5.2 @Autowired与@Qualifier

5.3 @Resource

  1. 全注解式开发

一:Spring IoC注解式开发

1. 回顾注解

注解的存在主要是为了简化XML的配置,Spring6倡导全注解开发。

我们来回顾一下:

①第一:注解怎么定义,注解中的属性怎么定义?

②第二:注解怎么使用?

③第三:通过反射机制怎么读取注解?

注解怎么定义,注解中的属性怎么定义?

自定义注解:Component

(1)Target注解和Retention注解,这两个注解被称为元注解:

①@Target注解用来设置Component注解可以出现的位置,以下代表表示Component注解只能用在类和接口上。

②Retention注解用来设置Component注解的保持性策略,以下代表Component注解可以被反射机制读取。
注:使用某个注解的时候,如果注解的属性名是value的话,value可以省略。
注:使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,大括号可以省略。

  1. package com.bjpowernode.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(value = {ElementType.TYPE})
  7. @Retention(value = RetentionPolicy.RUNTIME)
  8. public @interface Component {
  9. // 属性
  10. String value();
  11. }

注解怎么使用?

(2)语法格式:@注解类型名(属性名=属性值, 属性名=属性值, 属性名=属性值……)

并且如果属性名是value,则在使用的时候可以省略属性名,例如:

  1. package com.bjpowernode.annotation;
  2. // @Component(value = "userBean")
  3. @Component("userBean")
  4. public class User {
  5. }

通过反射机制怎么读取注解?

(3)定义需求:当Bean类上有Component注解时,则实例化Bean对象,如果没有,则不实例化对象!

第一步:先使用Class.forName获取类。

第二步:调用isAnnotationPresent方法看这个类上是否有@Component注解,参数是Component.class,返回的是一个布尔类型。

第三步:如果存在,调用getDeclaredAnnotation方法获取这个注解,参数还是Component.class,然后就可以获取注解的属性值。

  1. package com.bjpowernode.annotation;
  2. public class Test {
  3. public static void main(String[] args) throws Exception{
  4. // 获取类
  5. Class<?> clazz = Class.forName("com.bjpowernode.annotation.User");
  6. // 看这个类上有没有Component注解
  7. if (clazz.isAnnotationPresent(Component.class)){
  8. // 有这个注解就获取注解的属性并就创建对象
  9. // 获取这个注解
  10. Component annotation = clazz.getDeclaredAnnotation(Component.class);
  11. // 获取这个注解的属性
  12. System.out.println(annotation.value());
  13. // 创建对象
  14. // User user = new User();
  15. User user = (User)clazz.newInstance()
  16. System.out.println(user);
  17. }
  18. }
  19. }

执行结果:

f4792de19bea423295f3ec97f9aeacec.png

(4)在(3)的基础上,提升一下难度;

假设我们现在只知道包名:com.bjpowernode.annotation;至于这个包下有多少个Bean我们不知道!哪些Bean上有注解,哪些Bean上没有注解,这些我们都不知道!如何通过程序全自动化扫描判断?

注:实际上@ComponentScan注解可以实现这个功能,包扫描,有Componenet注解的对象就会被创建出来,没有的则不会!

目前只知道一个包的名字,扫描这个包下所有的类,当这个类上有@Component注解的时候,实例化该对象,然后放到Map集合中!

User类:含有Component注解

  1. package com.bjpowernode.annotation;
  2. @Component("userBean")
  3. public class User {
  4. }

Vip类:没有Component注解

  1. package com.bjpowernode.annotation;
  2. public class Vip {
  3. }

Student类:含有Component注解

  1. package com.bjpowernode.annotation;
  2. @Component("studentBean")
  3. public class Student {
  4. }

ComponentScan类:编写测试

  1. package com.bjpowernode.annotation;
  2. import java.io.File;
  3. import java.net.URL;
  4. import java.util.Arrays;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. public class ComponentScan {
  8. public static void main(String[] args) {
  9. // 创建Map集合
  10. Map<String,Object> beanMap = new HashMap<>();
  11. // 目前只知道一个包名
  12. String packageName = "com.bjpowernode.annotation";
  13. // 把包名转换成路径的形式,使用正则表达式
  14. // 直接使用“.”,正则表达式中代表任意字符,会把所有的内容都替换成/
  15. // 正则表达式中“\.”代表“.” ,而java中双\\就代表\,有一个用来转义
  16. String packagePath = packageName.replaceAll("\\.", "/");
  17. // String packagePath = packageName.replace(".", "/"); 这样也可以
  18. // System.out.println(packagePath); // com/bjpowernode/annotation
  19. // com是类的根路径下的一个目录,获取系统类加载器,从根目录下进行扫描,返回一个URL
  20. URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
  21. // 获取绝对路径
  22. String path = url.getPath();
  23. // 开始扫描,获取一个绝对路径下的所有文件
  24. File file = new File(path);
  25. File[] files = file.listFiles();
  26. // 流式编程
  27. Arrays.stream(files).forEach(f -> { // f.getName得到的是对应的.class字节码文件
  28. // 拼串成全限定包名
  29. String className = packageName+"."+f.getName().split("\\.")[0];
  30. // System.out.println(className);
  31. try {
  32. // 通过反射机制创建对象
  33. // 获取类
  34. Class<?> clazz = Class.forName(className);
  35. // 检查类上有没有Component注解
  36. if (clazz.isAnnotationPresent(Component.class)) {
  37. // 有的话获取注解
  38. Component annotation = clazz.getDeclaredAnnotation(Component.class);
  39. // 获取注解的属性值
  40. String key = annotation.value();
  41. // 创建对象
  42. Object obj = clazz.newInstance();
  43. // 把注解的属性值作为key,对象作为value放到Map集合当中
  44. beanMap.put(key,obj);
  45. }
  46. } catch (Exception e) {
  47. e.printStackTrace();
  48. }
  49. });
  50. // 打印这个Map集合
  51. System.out.println(beanMap);
  52. }
  53. }

执行结果:应该创建User和Student对象

b649cb8f6aa2469a839f94e91ee3dfc5.png

2. 声明Bean的四个注解

负责声明Bean的注解(实例化),常见的包括四个:

①@Component注解:平常使用

②@Controller注解:一般控制器上使用

③@Service注解:一般service类上使用

④@Repositorys注解:一般dao类上使用

源码如下:

@Component注解

  1. package com.powernode.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(value = {ElementType.TYPE})
  7. @Retention(value = RetentionPolicy.RUNTIME)
  8. public @interface Component {
  9. String value();
  10. }

@Controller注解

  1. package org.springframework.stereotype;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import org.springframework.core.annotation.AliasFor;
  8. @Target({ElementType.TYPE})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Documented
  11. @Component
  12. public @interface Controller {
  13. @AliasFor(
  14. annotation = Component.class
  15. )
  16. String value() default "";
  17. }

@Service注解

  1. package org.springframework.stereotype;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import org.springframework.core.annotation.AliasFor;
  8. @Target({ElementType.TYPE})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Documented
  11. @Component
  12. public @interface Service {
  13. @AliasFor(
  14. annotation = Component.class
  15. )
  16. String value() default "";
  17. }

@Repository注解

  1. package org.springframework.stereotype;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import org.springframework.core.annotation.AliasFor;
  8. @Target({ElementType.TYPE})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Documented
  11. @Component
  12. public @interface Repository {
  13. @AliasFor(
  14. annotation = Component.class
  15. )
  16. String value() default "";
  17. }

(1)通过源码可以看到,@Controller@Service@Repository这三个注解都是@Component注解的别名。也就是说:这四个注解的功能都一样,用哪个都可以!

(2)那么为什么会提供那么多种方式呢?只是为了增强程序的可读性,建议:

①控制器类上使用:Controller注解

②service类上使用:Service注解

③dao类上使用:Repository注解

(3)都是只有一个value属性,value属性用来指定bean的id,也就是bean的名字!

7c4b13609b249c65a2806c99a4a39497.png

#

3. Spring注解的使用

如何使用以上的注解呢?

第一步:加入aop的依赖(不用管);

第二步:在配置文件中添加context命名空间;

第三步:在配置文件中指定扫描的包;

第四步:在Bean类上使用注解;

第一步:加入aop的依赖

我们可以看到当加入spring-context依赖之后,会关联加入aop的依赖,所以这一步不用做

ec7027beed7e9aa37328acf8c856631a.png

第二步:在配置文件中添加context命名空间

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. </beans>

第三步:在配置文件中指定要扫描的包

(1)就像我们前面回忆反射机制第四部分的代码一样,我们只需要知道包名,然后会扫描这个包下的所有类,通过反射机制,对含有注解的进行对象的创建!相当于我们只需要配置包名,就可以通过注解创建对象,不用一个个去进行配置了!

(2)注:就是使用context命名空间的component-scan进行组件扫描,使用属性base-package执行要扫描的包!

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--给Spring框架指定要扫描哪些包中的类-->
  8. <context:component-scan base-package="com.bjpowernode.spring.bean"/>
  9. </beans>

第四步:在Bean类上使用注解

User类:含有Component注解

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.stereotype.Component;
  3. @Component("userBean")
  4. public class User {
  5. }

Vip类:含有Component注解

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.stereotype.Component;
  3. @Component("vipBean")
  4. public class Vip {
  5. }

编写测试程序

  1. package com.bjpowernode.spring.test;
  2. import com.bjpowernode.spring.bean.User;
  3. import com.bjpowernode.spring.bean.Vip;
  4. import org.junit.Test;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;
  7. public class springAnnotationTest {
  8. @Test
  9. public void testAnnotation(){
  10. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
  11. User userBean = applicationContext.getBean("userBean", User.class);
  12. System.out.println(userBean);
  13. Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
  14. System.out.println(vipBean);
  15. }
  16. }

执行结果:成功创建两个对象

f970530e0fad4e6fa7d6703bc4c2d41b.png

问题1:如果把value属性彻底去掉,spring会被Bean自动取名吗?

答:会的,并且默认名字是:Bean类名首字母小写;例如:Vip类对应的名字就是vip

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.stereotype.Component;
  3. // 自动取名就是vip
  4. @Component
  5. public class Vip {
  6. }

问题2:如果是多个包怎么办?有两种解决方案:

①第一种:在配置文件中指定多个包,用 “逗号,” 隔开。

②第二种:指定多个包的共同父包,牺牲一部分效率。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--多个包的解决方案-->
  8. <!--第一种:使用逗号隔开。-->
  9. <context:component-scan base-package="com.powernode.spring6.bean,com.powernode.spring6.dao"/>
  10. <!--第二种:也可以指定这多个包共同的父包,但是这肯定要牺牲一部分效率-->
  11. <context:component-scan base-package="com.powernode.spring6"/>
  12. </beans>

4. 选择性实例化Bean

需求:假设在某个包下有很多Bean,有的Bean上标注了@Component,有的标注了@Controller,有的标注了@Service,有的标注了@Repository,现在由于某种特殊业务的需要,只允许其中所有的Controller注解的参与Bean管理,其他的都不实例化!这应该怎么办呢?

为了便于理解,我们将这几个类都定义到同一个java源文件中,并且采用不同的注解方式!

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.stereotype.Component;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.stereotype.Repository;
  5. import org.springframework.stereotype.Service;
  6. @Component
  7. public class A {
  8. public A() {
  9. System.out.println("A的无参数构造方法执行");
  10. }
  11. }
  12. @Controller
  13. class B {
  14. public B() {
  15. System.out.println("B的无参数构造方法执行");
  16. }
  17. }
  18. @Service
  19. class C {
  20. public C() {
  21. System.out.println("C的无参数构造方法执行");
  22. }
  23. }
  24. @Repository
  25. class D {
  26. public D() {
  27. System.out.println("D的无参数构造方法执行");
  28. }
  29. }
  30. @Controller
  31. class E {
  32. public E() {
  33. System.out.println("E的无参数构造方法执行");
  34. }
  35. }

第一种解决方案:use-default-filters=”false”

如果这个属性是false,表com.bjpowernode.spring.bean包下所有的带有声明Bean的注解全部失效;然后在使用include-filter标签包含让那个注解生效(使用全限定类名)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--让所有注解都失效-->
  8. <context:component-scan base-package="com.bjpowernode.spring.bean" use-default-filters="false">
  9. <!--让@Controller注解生效-->
  10. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  11. <!--可配置多个
  12. <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
  13. </context:component-scan>
  14. </beans>

测试程序

当ClassPathXmlApplicationContext方法创建出来,类中对应的构造方法就会执行!

  1. package com.bjpowernode.spring.test;
  2. import com.bjpowernode.spring.bean.User;
  3. import com.bjpowernode.spring.bean.Vip;
  4. import org.junit.Test;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;
  7. public class springAnnotationTest {
  8. @Test
  9. public void testChoose(){
  10. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-choose.xml");
  11. }
  12. }

执行结果:只有Controller注解生效,对应的应该是B和E

3fc4468d70324ec1b42d238ff59b4eda.png

第二种解决方案:use-default-filters=”true”

如果这个属性的值是true,表示com.bjpowernode.spring.bean下的所有的带有声明Bean的注解全部生效;use-default-filters=”true”默认值就是true,不用写也行。然后在使用exclude-filter标签去除让那个注解失效(使用全限定类名)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <context:component-scan base-package="com.bjpowernode.spring.bean" use-default-filters="true">
  8. <!--让@Service、@Repository失效-->
  9. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
  10. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
  11. </context:component-scan>
  12. </beans>

执行结果:只有Service和Repository注解失效,对应的应该是A、B、E

8d909df0c81a4db4a689dcd7538fc94e.png

5. 负责注入的注解(重点)

(1)@Component @Controller @Service @Repository这四个注解是用来声明Bean的,声明后这些Bean将被实例化。

(2)接下来我们看一下,如何给Bean的属性赋值,给Bean属性赋值需要用到这些注解:

@Value、@Autowired、@Qualifier、@Resource。

5.1 @Value

当属性的类型是简单类型时,可以使用@Value注解进行注入。

第一种情况:什么都不提供,直接在属性上使用

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Person {
  6. @Value("张三")
  7. private String name;
  8. @Value("18")
  9. private int age;
  10. @Override
  11. public String toString() {
  12. return "Person{" +
  13. "name='" + name + '\'' +
  14. ", age=" + age +
  15. '}';
  16. }
  17. }

第二种情况:提供set方法,在set方法上使用

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Person {
  6. private String name;
  7. private int age;
  8. // 提供了set方法
  9. @Value("张三")
  10. public void setName(String name) {
  11. this.name = name;
  12. }
  13. @Value("18")
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. @Override
  18. public String toString() {
  19. return "Person{" +
  20. "name='" + name + '\'' +
  21. ", age=" + age +
  22. '}';
  23. }
  24. }

第三种情况:提供构造方法,在构造方法的形参上使用

注:直接声明构造方法上不可以,一定是声明在在构造方法的参数上才可以!

  1. package com.bjpowernode.spring.bean;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Person {
  6. private String name;
  7. private int age;
  8. // 提供了构造方法的形参上
  9. public Person(@Value("张三")String name, @Value("18")int age) {
  10. this.name = name;
  11. this.age = age;
  12. }
  13. @Override
  14. public String toString() {
  15. return "Person{" +
  16. "name='" + name + '\'' +
  17. ", age=" + age +
  18. '}';
  19. }
  20. }

spring-annotation-di.xml配置,在配置中指定包扫描即可

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--指定要扫描的包-->
  8. <context:component-scan base-package="com.bjpowernode.spring.bean"/>
  9. </beans>

执行结果

通过测试得知:@Value注解:可以出现在属性上、setter方法上、以及构造方法的形参上,可见Spring给我们提供了多样化的注入;很灵活了!

841839e3fa354ad0a521d612d6ea1430.png

5.2 @Autowired与@Qualifier

@Autowired注解可以用来注入非简单类型。被翻译为:自动连线的,或者自动装配

单独使用@Autowired注解,默认根据类型装配。【默认是根据byType

@Autowired注解和@Qualifier注解联合使用**才能根据名字装配根据byName**】

@Autowired源码

源码中有两处需要注意:

①第一处:该注解可以标注在哪里?构造方法上、方法上、形参上、属性上、注解上

②第二处:该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在,如果不存在则报错。如果required属性值设置为false,表示被注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错!

  1. package org.springframework.beans.factory.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface Autowired {
  11. boolean required() default true;
  12. }

第一种情况:什么都不提供,直接在属性上注入

UserDao接口

  1. package com.bjpowernode.spring.dao;
  2. public interface UserDao {
  3. void insert();
  4. }

接口的实现类UserDaoImpl

  1. package com.bjpowernode.spring.dao.impl;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.stereotype.Repository;
  4. @Repository("userDaoImpl")
  5. public class UserDaoImpl implements UserDao {
  6. @Override
  7. public void insert() {
  8. System.out.println("正在插入信息");
  9. }
  10. }

UserService类:在属性上直接注入

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. @Service("userService")
  6. public class UserService {
  7. // 面向接口编程
  8. @Autowired // 不需要指定任何属性,直接使用即可
  9. private UserDao userDao;
  10. public void insertDate(){
  11. userDao.insert();
  12. }
  13. }

spring-annotation-di.xml配置包扫描

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  7. <!--配置两个包下的公共子包-->
  8. <context:component-scan base-package="com.bjpowernode.spring"/>
  9. </beans>

测试程序

  1. package com.bjpowernode.spring.test;
  2. import com.bjpowernode.spring.bean.Person;
  3. import com.bjpowernode.spring.bean.User;
  4. import com.bjpowernode.spring.bean.Vip;
  5. import com.bjpowernode.spring.service.UserService;
  6. import org.junit.Test;
  7. import org.springframework.context.ApplicationContext;
  8. import org.springframework.context.support.ClassPathXmlApplicationContext;
  9. public class springAnnotationTest {
  10. @Test
  11. public void testAnnotationDI(){
  12. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-annotation-di.xml");
  13. UserService userService = applicationContext.getBean("userService", UserService.class);
  14. userService.insertDate();
  15. }
  16. }

执行结果:构造方法和setter方法都没有提供,经过测试,仍然可以注入成功

c401c2bf791e4f30a162a05b9d4e1d50.png

问题:目前UserDao下只有一个实现类,可根据类型完成自动装配;那如果在有多个实现类,那么在private UserDao userDao面向接口编程,Spring就不知道装配哪一个实现类?

答:此时就要使用@Autowired注解和@Qualifier注解联合使用,根据名字进行转配!

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.stereotype.Service;
  6. @Service("userService")
  7. public class UserService {
  8. // 不需要指定任何属性,直接使用即可
  9. @Autowired
  10. // 要自动装配那个实现类,就把实现类的名字写上
  11. @Qualifier("userDaoImpl")
  12. private UserDao userDao;
  13. public void insertDate(){
  14. userDao.insert();
  15. }
  16. }

第二种情况:出现在setter方法上

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.stereotype.Service;
  6. @Service("userService")
  7. public class UserService {
  8. private UserDao userDao;
  9. // 出现在set方法上
  10. @Autowired
  11. public void setUserDao(UserDao userDao) {
  12. this.userDao = userDao;
  13. }
  14. public void insertDate() {
  15. userDao.insert();
  16. }
  17. }

第三种情况:出现在构造方法上

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.stereotype.Service;
  6. @Service("userService")
  7. public class UserService {
  8. private UserDao userDao;
  9. // 出现在构造方法上
  10. @Autowired
  11. public UserService(UserDao userDao) {
  12. this.userDao = userDao;
  13. }
  14. public void insertDate() {
  15. userDao.insert();
  16. }
  17. }

第四种情况:出现在构造方法的属性上

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.stereotype.Service;
  6. @Service("userService")
  7. public class UserService {
  8. private UserDao userDao;
  9. // 出现在构造方法的属性上
  10. public UserService(@Autowired UserDao userDao) {
  11. this.userDao = userDao;
  12. }
  13. public void insertDate() {
  14. userDao.insert();
  15. }
  16. }

第五种情况:直接省略不写(前提要有构造方法)

注:如果一个类中构造方法只有一个,并且构造方法上的参数和属性能够对应上;此时@Autowired注解能够省略!

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.stereotype.Service;
  6. @Service("userService")
  7. public class UserService {
  8. private UserDao userDao;
  9. // 一个构造方法并且参数能和上面的属性对应上,直接可以省略不写
  10. public UserService(UserDao userDao) {
  11. this.userDao = userDao;
  12. }
  13. public void insertDate() {
  14. userDao.insert();
  15. }
  16. }

总结:

①@Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。

②当带参数的构造方法只有一个,@Autowired注解可以省略。

③@Autowired注解默认根据类型注入;如果要根据名称注入的话,需要配合@Qualifier注解一起使用。

5.3 @Resource

(1)**@Resource注解也可以完成非简单类型注入,那它和@Autowired注解有什么区别?**

@Resource注解是JDK扩展包中的,也就是说属于JDK的一部分;所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型,JSR是Java规范提案)

②@Autowired注解是Spring框架自己的。

@Resource注解默认根据名称装配byName;未指定name时,使用属性名作为name;

通过name找不到的话会自动启动通过类型byType装配。

④@Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。

@Resource注解主要用在属性上、setter方法上。

@Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上

(2)@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖,高于JDK11或低于JDK8需要引入以下依赖】

注意:如果用Spring6,要知道Spring6不再支持JavaEE,它支持的是JakartaEE9!

Spring6+版本引入的依赖

  1. <dependency>
  2. <groupId>jakarta.annotation</groupId>
  3. <artifactId>jakarta.annotation-api</artifactId>
  4. <version>2.1.1</version>
  5. </dependency>

spring5-版本引入的依赖

  1. <dependency>
  2. <groupId>javax.annotation</groupId>
  3. <artifactId>javax.annotation-api</artifactId>
  4. <version>1.3.2</version>
  5. </dependency>

@Resource注解的源码如下

虽然可以用在类上,但是主要是用在属性上和方法上!

7743234e922453d00baadca17c136f5e.png

第一种情况:出现在属性上,使用时必须加上name属性(根据名字装配)

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.stereotype.Service;
  7. @Service("userService")
  8. public class UserService {
  9. // 出现在属性上,必须加上name属性
  10. @Resource(name = "userDaoImpl")
  11. private UserDao userDao;
  12. public void insertDate() {
  13. userDao.insert();
  14. }
  15. }

第二种情况:出现在set方法上,使用时必须加上name属性(根据名字装配);不能出现在构造方法上

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.stereotype.Service;
  7. @Service("userService")
  8. public class UserService {
  9. private UserDao userDao;
  10. // 出现在set方法上,必须加上name属性
  11. @Resource(name = "userDaoImpl")
  12. public void setUserDao(UserDao userDao) {
  13. this.userDao = userDao;
  14. }
  15. public void insertDate() {
  16. userDao.insert();
  17. }
  18. }

第三种情况:不指定名字,还是根据name自动装配,把属性作为name(根据名字装配)

UserDaoImpl类,名字修改为属性名

  1. package com.bjpowernode.spring.dao.impl;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.stereotype.Repository;
  4. // 这里也改成属性的名字
  5. // @Repository("userDaoImpl")
  6. @Repository("userDao")
  7. public class UserDaoImpl implements UserDao {
  8. @Override
  9. public void insert() {
  10. System.out.println("正在插入信息");
  11. }
  12. }

UserService类:使用Resource注解,不指定名字,默认把属性名作为名字

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.stereotype.Service;
  7. @Service("userService")
  8. public class UserService {
  9. // 这里未指定名字,会把属性名作为名字
  10. @Resource
  11. private UserDao userDao;
  12. public void setUserDao(UserDao userDao) {
  13. this.userDao = userDao;
  14. }
  15. public void insertDate() {
  16. userDao.insert();
  17. }
  18. }

执行结果:

通过测试得知,当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名。

da30870d52214e47b90e626dd8236795.png

第四种情况:当使用属性名字装配时,再找不到;才会按照类型进行装配(根据类型装配)

UserDaoImpl类,名字修改为不是属性名

  1. package com.bjpowernode.spring.dao.impl;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import org.springframework.stereotype.Repository;
  4. // 名字不是属性名
  5. @Repository("xxx")
  6. public class UserDaoImpl implements UserDao {
  7. @Override
  8. public void insert() {
  9. System.out.println("正在插入信息");
  10. }
  11. }

UserService类:使用Resource注解,不指定名字,默认把属性名作为名字,但是与上面名字不匹配,此时也能执行成功;此时不是根据名字进行装配,而是根据类型进行装配了!

  1. package com.bjpowernode.spring.service;
  2. import com.bjpowernode.spring.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.annotation.Qualifier;
  6. import org.springframework.stereotype.Service;
  7. @Service("userService")
  8. public class UserService {
  9. // 这里未指定名字,会把属性名作为名字
  10. @Resource
  11. private UserDao userDao;
  12. public void setUserDao(UserDao userDao) {
  13. this.userDao = userDao;
  14. }
  15. public void insertDate() {
  16. userDao.insert();
  17. }
  18. }

总结:

①自己指定名字,@Resource注解默认根据名称【byName】注入,只要UserDaoImpl类的@Repository(“userDaoImpl”)注解的名称与UserService类里面的userDao属性上面的@Resource(“userDaoImpl”)注解名称保持一致即可!【byName】

②当不指定名字时,直接使用@Resource注解,此时是根据属性userDao的名字进行注入;需要保证UserDaoImpl类的@Repository(“userDao”)注解的名称就是userDao属性的名字!【byName】

③如果②中UserDaoImpl类的@Repository(“userXxx”)注解的名称与属性userDao的名字没有保持一致,**根据属性名找不到时,才会根据类型进行注入!【byType】**

④byType注入时,某种类型的Bean只能有一个(实现类有多个就不知道注入哪一个了),此时@Resource注解也可以省略不写!

6. 全注解式开发

所谓的全注解开发就是不再使用spring配置文件了;写一个配置类来代替配置文件!

第一步:配置类代替spring配置文件

主要使用到两个注解:

①@Configuration:就相当于xml文件中的基础配置;

②@ComponentScan:包扫描,用来指定包名;

  1. package com.bjpowernode.spring.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @ComponentScan("com.bjpowernode.spring")
  6. public class Spring6Configuration {
  7. // 类中什么都不用写
  8. }

第二步:编写测试程序

①不需要在编写Spring.xml配置文件了,直接在测试代码中引用上面的类即可!

②不再new ClassPathXmlApplicationContext()对象了,而是创建AnnotationConfigApplicationContext对象,参数就是上面我们的配置类Spring6Configuration对应的.class!

  1. package com.bjpowernode.spring.test;
  2. import com.bjpowernode.spring.bean.Person;
  3. import com.bjpowernode.spring.bean.User;
  4. import com.bjpowernode.spring.bean.Vip;
  5. import com.bjpowernode.spring.config.Spring6Configuration;
  6. import com.bjpowernode.spring.service.UserService;
  7. import org.junit.Test;
  8. import org.springframework.context.ApplicationContext;
  9. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  10. import org.springframework.context.support.ClassPathXmlApplicationContext;
  11. public class springAnnotationTest {
  12. @Test
  13. public void testNoXML(){
  14. // 创建AnnotationConfigApplicationContext对象
  15. ApplicationContext context = new AnnotationConfigApplicationContext(Spring6Configuration.class);
  16. UserService userService = context.getBean("userService", UserService.class);
  17. userService.insertDate();
  18. }
  19. }

执行结果:全注解式开发也没问题

7d12513cae594dc49f125ddc79de38fc.png

发表评论

表情:
评论列表 (有 0 条评论,139人围观)

还没有评论,来说两句吧...

相关阅读