Spring学习记录----十二、Spring IoC注解式开发

我就是我 2024-03-17 22:51 127阅读 0赞

目录

十二、Spring IoC注解式开发

12.1 回顾注解

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

注解怎么使用?

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

代码

运行结果

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

代码

运行结果

12.2 声明Bean的注解

12.3 Spring注解的使用

第一步:加入aop的依赖

在pom.xml上加入spring context依赖:

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

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

即加入代码:

完整代码:

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

Bean类:User、Student、Order、Vip

编写测试程序:

执行结果:

上述程序的pom文件完整代码:

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

12.4 选择性实例化Bean

测试程序

执行结果:

12.5 负责注入的注解【非常重要!!!!】

12.5.1 @Value

代码

运行结果:

12.5.2 @Autowired与@Qualifier

看一下它的源码:

源码中有两处需要注意:

我们先在属性上使用@Autowired注解:

配置包扫描

测试程序

执行结果:

接下来,再来测试一下@Autowired注解出现在setter方法上:

执行结果:

我们再来看看能不能出现在构造方法上:

执行结果:

再来看看,这个注解能不能只标注在构造方法的形参上:

执行结果:

还有更劲爆的,当有参数的构造方法只有一个时,@Autowired注解可以省略。

执行结果:

当然,如果有多个构造方法,@Autowired肯定是不能省略的。

执行结果:

总结:

12.5.3 @Resource

@Resource注解的源码如下:

测试一下:

给这个UserDaoForOracle起名xyz

在UserService中使用Resource注解根据name注入

执行测试程序:

我们把UserDaoForOracle的名字xyz修改为userDao,让这个Bean的名字和UserService类中的UserDao属性名一致:

执行测试程序:

UserService的属性名修改为userDao2

执行结果:

UserService添加setter方法并使用注解标注

执行结果:

当然,也可以指定name:

执行结果:

一句话总结@Resource注解:

12.6 全注解式开发

配置类代替spring配置文件

编写测试程序:不再new ClassPathXmlApplicationContext()对象了。

执行结果


十二、Spring IoC注解式开发

12.1 回顾注解

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

来回顾一下:

  • 第一:注解怎么定义,注解中的属性怎么定义?
  • 第二:注解怎么使用?
  • 第三:通过反射机制怎么读取注解?

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

  1. package com.dong.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. }

以上是自定义了一个注解:Component

该注解上面修饰的注解包括:Target注解和Retention注解,这两个注解被称为元注解。

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

Retention注解用来设置Component注解的保持性策略,以上代表Component注解可以被反射机制读取。

String value(); 是Component注解中的一个属性。该属性类型String,属性名是value。

注解怎么使用?

  1. package com.dong.bean;
  2. import com.powernode.annotation.Component;
  3. @Component(value = "userBean")
  4. public class User {
  5. }

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

userBean为什么使用双引号括起来,因为value属性是String类型,字符串。

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

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

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

代码

  1. @Component 注解
  2. package com.dong.annotation;
  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. /**
  8. * 自定义注解
  9. */
  10. //@Target注解用来修饰 @Component可以出现的位置
  11. @Target(ElementType.TYPE)
  12. //@Retention也是一个元注解,用来标注 @Component 注解最终保留在class文件当中,并且可以呗反射机制读取
  13. @Retention(RetentionPolicy.RUNTIME)
  14. public @interface Component {
  15. //定义注解属性
  16. String value();
  17. }
  18. User.java
  19. package com.dong.bean;
  20. import com.dong.annotation.Component;
  21. @Component("userBean")
  22. public class User {
  23. /*private String name;*/
  24. }

测试类:ReflectAnnotationTest1.java

  1. package com.dong.client;
  2. import com.dong.annotation.Component;
  3. public class ReflectAnnotationTest1 {
  4. public static void main(String[] args) throws Exception {
  5. //通过反射机制读取注解
  6. //获取类
  7. Class<?> clazz = Class.forName("com.dong.bean.User");
  8. //先判断你这类上有没有这个 @Component注解
  9. if (clazz.isAnnotationPresent(Component.class)) {
  10. //获取类上的注解
  11. Component annotation = clazz.getAnnotation(Component.class);
  12. //访问注解属性
  13. System.out.println(annotation.value());
  14. }
  15. }
  16. }

运行结果

f640f5867b0c42e5a07e5c9d4f5646ff.png


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

接下来,我们来写一段程序,当Bean类上有Component注解时,则实例化Bean对象,如果没有,则不实例化对象。

我们准备3个Bean,2个上面有注解,1个上面没有注解。

代码

自定义@Component 注解:

  1. package com.dong.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. /**
  7. * 自定义注解
  8. */
  9. //@Target注解用来修饰 @Component可以出现的位置
  10. @Target(ElementType.TYPE)
  11. //@Retention也是一个元注解,用来标注 @Component 注解最终保留在class文件当中,并且可以呗反射机制读取
  12. @Retention(RetentionPolicy.RUNTIME)
  13. public @interface Component {
  14. //定义注解属性
  15. String value();
  16. }

1-

  1. package com.dong.bean;
  2. import com.dong.annotation.Component;
  3. @Component("userBean")
  4. public class User {
  5. /*private String name;*/
  6. }

2-

  1. package com.dong.bean;
  2. import com.dong.annotation.Component;
  3. @Component("orderBean")
  4. public class Order {
  5. }

3-【没有注解】

  1. package com.dong.bean;
  2. public class Vip {
  3. }
  4. 测试类ComponentScan.Java
  5. package com.dong.client;
  6. import com.dong.annotation.Component;
  7. import java.io.File;
  8. import java.net.URL;
  9. import java.util.Arrays;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. public class ComponentScan {
  13. public static void main(String[] args) {
  14. Map<String,Object>beanMap=new HashMap<>();
  15. //目前只知道一个包的名字,扫描这个包下的所有类,当这个类上有 @Component注解的时候,实例化该对象,然后放到Map集合中。
  16. String packageName = "com.dong.bean";
  17. //开始编写程序
  18. String packagePath = packageName.replaceAll("\\.", "/");
  19. // System.out.println(packagePath);
  20. URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
  21. //System.out.println(url);
  22. String path = null;
  23. if (url != null) {
  24. path = url.getPath();
  25. }
  26. //System.out.println(path);//得到绝对路径:/D:/CS/Code/IdeaCode/spring6-demo/review-annotation/target/classes/com/dong/bean
  27. File file = null;
  28. if (path != null) {
  29. file = new File(path);
  30. }
  31. File[] files = new File[0];//获取所有的子文件
  32. if (file != null) {
  33. files = file.listFiles();
  34. }
  35. if (files != null) {
  36. Arrays.stream(files).forEach(f -> {
  37. try{
  38. /* f:
  39. Order.class
  40. User.class
  41. Vip.class
  42. */
  43. //System.out.println(f.getName());
  44. // System.out.println(f.getName().split("\\.")[0]);
  45. /*
  46. Order 、User、Vip
  47. */
  48. //拼凑出全限定类名: 包名+“.”+类名
  49. String className=packageName+"."+f.getName().split("\\.")[0];
  50. //System.out.println(className);//得到类名:com.dong.bean.Order、com.dong.bean.User、com.dong.bean.Vip
  51. //通过反射机制解析注解
  52. Class<?> clazz = Class.forName(className);
  53. //先判断这些类上有没有 @Component注解
  54. if (clazz.isAnnotationPresent(Component.class)) {
  55. //有这个注解就创建对象
  56. //获取注解
  57. Component annotation = clazz.getAnnotation(Component.class);
  58. String id = annotation.value();
  59. //有这个注解的都要创建对象
  60. Object obj = clazz.newInstance();
  61. beanMap.put(id,obj);
  62. }
  63. }catch (Exception e){
  64. e.printStackTrace();
  65. }
  66. });
  67. }
  68. System.out.println(beanMap);
  69. }
  70. }

运行结果

创建了两个对象

b4e9a0a08fb3458aaa57fa74106ae20f.png


12.2 声明Bean的注解

负责声明Bean的注解,常见的包括四个:

  • @Component
  • @Controller
  • @Service
  • @Repository

源码如下:

@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. }

通过源码可以看到,@Controller、@Service、@Repository这三个注解都是@Component注解的别名。

也就是说:这四个注解的功能都一样。用哪个都可以。

只是为了增强程序的可读性,建议:

  • 控制器类上使用:Controller
  • service类上使用:Service
  • dao类上使用:Repository

他们都是只有一个value属性。value属性用来指定bean的id,也就是bean的名字。

b25b19ece4625ce28aecbaa6814fad96.png


12.3 Spring注解的使用

如何使用以上的注解呢?

  • 第一步:加入aop的依赖
  • 第二步:在配置文件中添加context命名空间
  • 第三步:在配置文件中指定扫描的包
  • 第四步:在Bean类上使用注解

第一步:加入aop的依赖

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

image.png

在pom.xml上加入spring context依赖:

  1. <!--spring context依赖-->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-context</artifactId>
  5. <version>6.0.11</version>
  6. </dependency>

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

spring.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. </beans>

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

即加入代码:

  1. <!--给spring框架指定要扫描哪些包中的类-->
  2. <context:component-scan base-package="com.dong.spring6.bean"/>

完整代码:

  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.dong.spring6.bean"/>
  9. </beans>

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

Bean类:User、Student、Order、Vip

  1. package com.dong.spring6.bean;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class User {
  5. }
  6. package com.dong.spring6.bean;
  7. import org.springframework.stereotype.Repository;
  8. @Repository(value = "studentBean")
  9. public class Student {
  10. }

如果注解的属性名是value,那么value是可以省略的。

如果把value属性彻底去掉,spring会被Bean自动取名吗?会的。并且默认名字的规律是:Bean类名首字母小写即可。

  1. package com.dong.spring6.bean;
  2. import org.springframework.stereotype.Controller;
  3. @Controller
  4. public class Vip {
  5. }
  6. package com.dong.spring6.bean;
  7. import org.springframework.stereotype.Service;
  8. @Service
  9. public class Order {
  10. }

编写测试程序:

  1. IOCAnnotationTest.java
  2. package com.dong.spring6.test;
  3. import com.dong.spring6.bean.Order;
  4. import com.dong.spring6.bean.Student;
  5. import com.dong.spring6.bean.User;
  6. import com.dong.spring6.bean.Vip;
  7. import org.junit.Test;
  8. import org.springframework.context.ApplicationContext;
  9. import org.springframework.context.support.ClassPathXmlApplicationContext;
  10. public class IOCAnnotationTest {
  11. @Test
  12. public static void main(String[] args) {
  13. ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
  14. User userBean = applicationContext.getBean("user", User.class);
  15. System.out.println(userBean);
  16. Vip vipBean = applicationContext.getBean("vip", Vip.class);
  17. System.out.println(vipBean);
  18. Object orderBean = applicationContext.getBean("order", Order.class);
  19. System.out.println(orderBean);
  20. Student studentBean = applicationContext.getBean("studentBean", Student.class);
  21. System.out.println(studentBean);
  22. }
  23. }
执行结果:

4个对象创建成功!

403ab9a844cb40d090107d8044bf575e.png

上述程序的pom文件完整代码:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.dong</groupId>
  7. <artifactId>spring6-008-ioc-annotation</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <!--依赖-->
  10. <dependencies>
  11. <!--spring context依赖-->
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-context</artifactId>
  15. <version>6.0.0</version>
  16. </dependency>
  17. <!--junit依赖-->
  18. <dependency>
  19. <groupId>junit</groupId>
  20. <artifactId>junit</artifactId>
  21. <version>4.12</version>
  22. <scope>test</scope>
  23. </dependency>
  24. </dependencies>
  25. <properties>
  26. <maven.compiler.source>19</maven.compiler.source>
  27. <maven.compiler.target>19</maven.compiler.target>
  28. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  29. </properties>
  30. </project>

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

  • 第一种:在配置文件中指定多个包,用逗号隔开。
  • 第二种:指定多个包的共同父包。

1、

  1. <context:component-scan base-package="com.dong.spring6.bean,com.dong.spring6.bean2"/>

2、

  1. <context:component-scan base-package="com.dong.spring6"/>

12.4 选择性实例化Bean

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

这里为了方便,将这几个类都定义到同一个java源文件中了

  1. package com.dong.spring6.bean3;
  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. }
  36. @Controller
  37. class F {
  38. public F() {
  39. System.out.println("F的无参数构造方法执行");
  40. }
  41. }

我只想实例化bean3包下的Controller。配置文件这样写:

spring-choose.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. <context:component-scan base-package="com.dong.spring6.bean3" use-default-filters="false">
  8. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  9. </context:component-scan>
  10. </beans>
  • use-default-filters=”true” 表示:使用spring默认的规则,只要有Component、Controller、Service、Repository中的任意一个注解标注,则进行实例化。
  • use-default-filters=”false” 表示:不再spring默认实例化规则,即使有Component、Controller、Service、Repository这些注解标注,也不再实例化。
  • 表示只有Controller进行实例化。

测试程序

  1. @Test
  2. public void testChoose(){
  3. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-choose.xml");
  4. }

执行结果:

image.png

也可以将use-default-filters设置为true(不写就是true),并且采用exclude-filter方式排出哪些注解标注的Bean不参与实例化:

spring-choose.xml

  1. <context:component-scan base-package="com.powernode.spring6.bean3">
  2. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
  3. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
  4. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  5. </context:component-scan>

12.5 负责注入的注解【非常重要!!!!】

@Component @Controller @Service @Repository 这四个注解是用来声明Bean的,声明后这些Bean将被实例化。接下来我们看一下,如何给Bean的属性赋值。给Bean属性赋值需要用到这些注解:

  • @Value
  • @Autowired
  • @Qualifier
  • @Resource

12.5.1 @Value

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

代码

  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.dong.spring6.bean3"/>
  8. </beans>
  9. package com.dong.spring6.bean3;
  10. import org.springframework.beans.factory.annotation.Value;
  11. import org.springframework.stereotype.Component;
  12. import javax.sql.DataSource;
  13. import java.io.PrintWriter;
  14. import java.sql.Connection;
  15. import java.sql.SQLException;
  16. import java.sql.SQLFeatureNotSupportedException;
  17. import java.util.logging.Logger;
  18. @Component
  19. public class MyDataSource implements DataSource {
  20. @Value(value = "com.mysql.cj.Driver")
  21. private String driver;
  22. @Value("jdbc:mysql://localhost:3306/spring6")
  23. private String url;
  24. @Value("root")
  25. private String username;
  26. @Value("123456")
  27. private String password;
  28. //使用@Value注解注入的话,可以用在属性上,并且可以不提供setter方法
  29. /* public void setDriver(String driver) {
  30. this.driver = driver;
  31. }
  32. public void setUrl(String url) {
  33. this.url = url;
  34. }
  35. public void setUsername(String username) {
  36. this.username = username;
  37. }
  38. public void setPassword(String password) {
  39. this.password = password;
  40. }*/
  41. @Override
  42. public String toString() {
  43. return "MyDataSource{" +
  44. "driver='" + driver + '\'' +
  45. ", url='" + url + '\'' +
  46. ", username='" + username + '\'' +
  47. ", password='" + password + '\'' +
  48. '}';
  49. }
  50. @Override
  51. public Connection getConnection() throws SQLException {
  52. return null;
  53. }
  54. @Override
  55. public Connection getConnection(String username, String password) throws SQLException {
  56. return null;
  57. }
  58. @Override
  59. public PrintWriter getLogWriter() throws SQLException {
  60. return null;
  61. }
  62. @Override
  63. public void setLogWriter(PrintWriter out) throws SQLException {
  64. }
  65. @Override
  66. public void setLoginTimeout(int seconds) throws SQLException {
  67. }
  68. @Override
  69. public int getLoginTimeout() throws SQLException {
  70. return 0;
  71. }
  72. @Override
  73. public Logger getParentLogger() throws SQLFeatureNotSupportedException {
  74. return null;
  75. }
  76. @Override
  77. public <T> T unwrap(Class<T> iface) throws SQLException {
  78. return null;
  79. }
  80. @Override
  81. public boolean isWrapperFor(Class<?> iface) throws SQLException {
  82. return false;
  83. }
  84. }
  85. package com.dong.spring6.test;
  86. import com.dong.spring6.bean.Order;
  87. import com.dong.spring6.bean.Student;
  88. import com.dong.spring6.bean.User;
  89. import com.dong.spring6.bean.Vip;
  90. import com.dong.spring6.bean3.MyDataSource;
  91. import org.junit.Test;
  92. import org.springframework.context.ApplicationContext;
  93. import org.springframework.context.support.ClassPathXmlApplicationContext;
  94. public class IOCAnnotationTest {
  95. @Test
  96. public void testDIByAnnotation(){
  97. ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-di-annotation.xml");
  98. MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);
  99. /*
  100. MyDataSource{driver='com.mysql.cj.Driver', url='jdbc:mysql://localhost:3306/spring6', username='root', password='123456'}
  101. */
  102. System.out.println(myDataSource);
  103. }
  104. }

运行结果:

f06675007d5d45ed918bf93be267a69e.png

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

12.5.2 @Autowired与@Qualifier

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

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

看一下它的源码:

  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. }
源码中有两处需要注意:
  • 第一处:该注解可以标注在哪里?

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

我们先在属性上使用@Autowired注解:

  1. package com.dong.spring6.dao;
  2. public interface UserDao {
  3. void insert();
  4. }
  5. package com.dong.spring6.dao;
  6. import org.springframework.stereotype.Repository;
  7. @Repository //纳入bean管理
  8. public class UserDaoForMySQL implements UserDao{
  9. @Override
  10. public void insert() {
  11. System.out.println("正在向mysql数据库插入User数据");
  12. }
  13. }
  14. package com.dong.spring6.service;
  15. import com.dong.spring6.dao.UserDao;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.stereotype.Service;
  18. @Service // 纳入bean管理
  19. public class UserService {
  20. @Autowired // 在属性上注入
  21. private UserDao userDao;
  22. // 没有提供构造方法和setter方法。
  23. public void save(){
  24. userDao.insert();
  25. }
  26. }

配置包扫描

  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.powernode.spring6.dao,com.powernode.spring6.service"/>
  8. </beans>

测试程序

  1. @Test
  2. public void testAutowired(){
  3. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-injection.xml");
  4. UserService userService = applicationContext.getBean("userService", UserService.class);
  5. userService.save();
  6. }

执行结果:

image.png

以上构造方法和setter方法都没有提供,经过测试,仍然可以注入成功。

接下来,再来测试一下@Autowired注解出现在setter方法上:

UserService

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. private UserDao userDao;
  8. @Autowired
  9. public void setUserDao(UserDao userDao) {
  10. this.userDao = userDao;
  11. }
  12. public void save(){
  13. userDao.insert();
  14. }
  15. }
执行结果:

image.png

我们再来看看能不能出现在构造方法上:

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. private UserDao userDao;
  8. @Autowired
  9. public UserService(UserDao userDao) {
  10. this.userDao = userDao;
  11. }
  12. public void save(){
  13. userDao.insert();
  14. }
  15. }
执行结果:

image.png

再来看看,这个注解能不能只标注在构造方法的形参上:

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. private UserDao userDao;
  8. public UserService(@Autowired UserDao userDao) {
  9. this.userDao = userDao;
  10. }
  11. public void save(){
  12. userDao.insert();
  13. }
  14. }
执行结果:

image.png

还有更劲爆的,当有参数的构造方法只有一个时,@Autowired注解可以省略。

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import org.springframework.stereotype.Service;
  4. @Service
  5. public class UserService {
  6. private UserDao userDao;
  7. public UserService(UserDao userDao) {
  8. this.userDao = userDao;
  9. }
  10. public void save(){
  11. userDao.insert();
  12. }
  13. }
执行结果:

image.png

当然,如果有多个构造方法,@Autowired肯定是不能省略的。

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import org.springframework.stereotype.Service;
  4. @Service
  5. public class UserService {
  6. private UserDao userDao;
  7. public UserService(UserDao userDao) {
  8. this.userDao = userDao;
  9. }
  10. public UserService(){
  11. }
  12. public void save(){
  13. userDao.insert();
  14. }
  15. }
执行结果:

0b341b9061105a1e8f5d925ca0ce3cd2.png

到此为止,我们已经清楚@Autowired注解可以出现在哪些位置了。

@Autowired注解默认是byType进行注入的,也就是说根据类型注入的,如果以上程序中,UserDao接口还有另外一个实现类,会出现问题吗?

错误信息中说:不能装配,UserDao这个Bean的数量大于1.

怎么解决这个问题呢?当然要byName,根据名称进行装配了。

@Autowired注解和@Qualifier注解联合起来才可以根据名称进行装配,在@Qualifier注解中指定Bean名称。

  1. package com.dong.spring6.dao;
  2. import org.springframework.stereotype.Repository;
  3. @Repository // 这里没有给bean起名,默认名字是:userDaoForOracle
  4. public class UserDaoForOracle implements UserDao{
  5. @Override
  6. public void insert() {
  7. System.out.println("正在向Oracle数据库插入User数据");
  8. }
  9. }
  10. package com.dong.spring6.service;
  11. import com.dong.spring6.dao.UserDao;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.beans.factory.annotation.Qualifier;
  14. import org.springframework.stereotype.Service;
  15. @Service
  16. public class UserService {
  17. private UserDao userDao;
  18. @Autowired
  19. @Qualifier("userDaoForOracle") // 这个是bean的名字。
  20. public void setUserDao(UserDao userDao) {
  21. this.userDao = userDao;
  22. }
  23. public void save(){
  24. userDao.insert();
  25. }
  26. }

执行结果:

878eeca6e6b77ee826877bf9af99a0ff.png

总结:

  • @Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。
  • 当带参数的构造方法只有一个,@Autowired注解可以省略。
  • @Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。

12.5.3 @Resource

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

  • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
  • @Autowired注解是Spring框架自己的。
  • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
  • @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
  • @Resource注解用在属性上、setter方法上。
  • @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

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

如果你是Spring6+版本请使用这个依赖

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

一定要注意:如果你用Spring6,要知道Spring6不再支持JavaEE,它支持的是JakartaEE9。(Oracle把JavaEE贡献给Apache了,Apache把JavaEE的名字改成JakartaEE了,大家之前所接触的所有的 javax.* 包名统一修改为 jakarta.*包名了。)

如果你是spring5-版本请使用这个依赖

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

@Resource注解的源码如下:

image.png


测试一下:

给这个UserDaoForOracle起名xyz
  1. package com.dong.spring6.dao;
  2. import org.springframework.stereotype.Repository;
  3. @Repository("xyz")
  4. public class UserDaoForOracle implements UserDao{
  5. @Override
  6. public void insert() {
  7. System.out.println("正在向Oracle数据库插入User数据");
  8. }
  9. }
在UserService中使用Resource注解根据name注入
  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. @Resource(name = "xyz")
  8. private UserDao userDao;
  9. public void save(){
  10. userDao.insert();
  11. }
  12. }
执行测试程序:

image.png

我们把UserDaoForOracle的名字xyz修改为userDao,让这个Bean的名字和UserService类中的UserDao属性名一致:

UserDaoForOracle

  1. package com.dong.spring6.dao;
  2. import org.springframework.stereotype.Repository;
  3. @Repository("userDao")
  4. public class UserDaoForOracle implements UserDao{
  5. @Override
  6. public void insert() {
  7. System.out.println("正在向Oracle数据库插入User数据");
  8. }
  9. }

UserService类中Resource注解并没有指定name

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. @Resource
  8. private UserDao userDao;
  9. public void save(){
  10. userDao.insert();
  11. }
  12. }
执行测试程序:

image.png

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

接下来把UserService类中的属性名修改一下:

UserService的属性名修改为userDao2

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. @Resource
  8. private UserDao userDao2;
  9. public void save(){
  10. userDao2.insert();
  11. }
  12. }
执行结果:

6876ff0bb751221e97f769ac988de4e1.png

根据异常信息得知:显然当通过name找不到的时候,自然会启动byType进行注入。以上的错误是因为UserDao接口下有两个实现类导致的。所以根据类型注入就会报错。

我们再来看@Resource注解使用在setter方法上可以吗?

UserService添加setter方法并使用注解标注

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. private UserDao userDao;
  8. @Resource
  9. public void setUserDao(UserDao userDao) {
  10. this.userDao = userDao;
  11. }
  12. public void save(){
  13. userDao.insert();
  14. }
  15. }

注意这个setter方法的方法名,setUserDao去掉set之后,将首字母变小写userDao,userDao就是name

执行结果:

image.png

当然,也可以指定name:

UserService

  1. package com.dong.spring6.service;
  2. import com.dong.spring6.dao.UserDao;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class UserService {
  7. private UserDao userDao;
  8. @Resource(name = "userDaoForMySQL")
  9. public void setUserDao(UserDao userDao) {
  10. this.userDao = userDao;
  11. }
  12. public void save(){
  13. userDao.insert();
  14. }
  15. }
执行结果:

fad0f8ae6b8a3607721dc4f59e950a85.png

一句话总结@Resource注解:

默认byName注入,没有指定name时把属性名当做name,根据name找不到时, 才会byType注入。byType注入时,某种类型的Bean只能有一个。

12.6 全注解式开发

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

配置类代替spring配置文件

  1. package com.dong.spring6.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.ComponentScans;
  4. import org.springframework.context.annotation.Configuration;
  5. @Configuration
  6. @ComponentScan({"com.dong.spring6.dao", "com.dong.spring6.service"})
  7. public class Spring6Configuration {
  8. }
编写测试程序:不再new ClassPathXmlApplicationContext()对象了。
  1. @Test
  2. public void testNoXml(){
  3. ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
  4. UserService userService = applicationContext.getBean("userService", UserService.class);
  5. userService.save();
  6. }
执行结果

image.png

发表评论

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

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

相关阅读