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

目录

十二、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倡导全注解开发

来回顾一下:

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

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

package com.dong.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

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

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

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

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

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

注解怎么使用?

package com.dong.bean;

import com.powernode.annotation.Component;

@Component(value = "userBean")
public class User {
}

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

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

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

package com.dong.bean;

import com.powernode.annotation.Component;

//@Component(value = "userBean")
@Component("userBean")
public class User {
}

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

代码

@Component 注解
package com.dong.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解
 */
//@Target注解用来修饰 @Component可以出现的位置
@Target(ElementType.TYPE)
//@Retention也是一个元注解,用来标注 @Component 注解最终保留在class文件当中,并且可以呗反射机制读取
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //定义注解属性
    String value();
}
User.java
package com.dong.bean;

import com.dong.annotation.Component;

@Component("userBean")
public class User {
    /*private String name;*/
}

测试类:ReflectAnnotationTest1.java

package com.dong.client;


import com.dong.annotation.Component;


public class ReflectAnnotationTest1 {

    public static void main(String[] args) throws Exception {
        //通过反射机制读取注解
        //获取类
        Class<?> clazz = Class.forName("com.dong.bean.User");
        //先判断你这类上有没有这个 @Component注解
        if (clazz.isAnnotationPresent(Component.class)) {
            //获取类上的注解
            Component annotation = clazz.getAnnotation(Component.class);
            //访问注解属性
            System.out.println(annotation.value());
        }
    }
}

运行结果


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

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

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

代码

自定义@Component 注解:

package com.dong.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解
 */
//@Target注解用来修饰 @Component可以出现的位置
@Target(ElementType.TYPE)
//@Retention也是一个元注解,用来标注 @Component 注解最终保留在class文件当中,并且可以呗反射机制读取
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //定义注解属性
    String value();
}

1-

package com.dong.bean;

import com.dong.annotation.Component;

@Component("userBean")
public class User {
    /*private String name;*/
}

2-

package com.dong.bean;

import com.dong.annotation.Component;

@Component("orderBean")
public class Order {
}

3-【没有注解】

package com.dong.bean;

public class Vip {
}
测试类ComponentScan.Java
package com.dong.client;

import com.dong.annotation.Component;

import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ComponentScan {
    public static void main(String[] args) {
        Map<String,Object>beanMap=new HashMap<>();

        //目前只知道一个包的名字,扫描这个包下的所有类,当这个类上有 @Component注解的时候,实例化该对象,然后放到Map集合中。
        String packageName = "com.dong.bean";
        //开始编写程序
        String packagePath = packageName.replaceAll("\\.", "/");
        // System.out.println(packagePath);
        URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
        //System.out.println(url);
        String path = null;
        if (url != null) {
            path = url.getPath();
        }
        //System.out.println(path);//得到绝对路径:/D:/CS/Code/IdeaCode/spring6-demo/review-annotation/target/classes/com/dong/bean
        File file = null;
        if (path != null) {
            file = new File(path);
        }
        File[] files = new File[0];//获取所有的子文件
        if (file != null) {
            files = file.listFiles();
        }
        if (files != null) {
            Arrays.stream(files).forEach(f -> {
                try{
                     /* f:
                      Order.class
                      User.class
                      Vip.class
                 */
                    //System.out.println(f.getName());
                    // System.out.println(f.getName().split("\\.")[0]);
                /*
                    Order 、User、Vip
                 */
                    //拼凑出全限定类名:  包名+“.”+类名
                    String className=packageName+"."+f.getName().split("\\.")[0];
                    //System.out.println(className);//得到类名:com.dong.bean.Order、com.dong.bean.User、com.dong.bean.Vip

                    //通过反射机制解析注解
                    Class<?> clazz = Class.forName(className);
                    //先判断这些类上有没有 @Component注解
                    if (clazz.isAnnotationPresent(Component.class)) {
                        //有这个注解就创建对象
                        //获取注解
                        Component annotation = clazz.getAnnotation(Component.class);
                        String id = annotation.value();
                        //有这个注解的都要创建对象
                        Object obj = clazz.newInstance();
                        beanMap.put(id,obj);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
        }
        System.out.println(beanMap);
    }
}

运行结果

创建了两个对象 


12.2 声明Bean的注解

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

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

源码如下:

@Component注解

package com.powernode.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

@Controller注解

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Service注解

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Repository注解

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

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

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

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

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

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


12.3 Spring注解的使用

如何使用以上的注解呢?

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

第一步:加入aop的依赖

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

image.png

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

        <!--spring context依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.11</version>
        </dependency>

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

spring.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 http://www.springframework.org/schema/context/spring-context.xsd">

</beans>


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

即加入代码:

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

完整代码:

<?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 http://www.springframework.org/schema/context/spring-context.xsd">

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


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

Bean类:User、Student、Order、Vip

package com.dong.spring6.bean;

import org.springframework.stereotype.Component;

@Component
public class User {
}
package com.dong.spring6.bean;

import org.springframework.stereotype.Repository;

@Repository(value = "studentBean")
public class Student {
}

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

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

package com.dong.spring6.bean;

import org.springframework.stereotype.Controller;

@Controller
public class Vip {
}
package com.dong.spring6.bean;

import org.springframework.stereotype.Service;

@Service
public class Order {
}

编写测试程序:

IOCAnnotationTest.java
package com.dong.spring6.test;

import com.dong.spring6.bean.Order;
import com.dong.spring6.bean.Student;
import com.dong.spring6.bean.User;
import com.dong.spring6.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IOCAnnotationTest {
    @Test
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        User userBean = applicationContext.getBean("user", User.class);
        System.out.println(userBean);

        Vip vipBean = applicationContext.getBean("vip", Vip.class);
        System.out.println(vipBean);

        Object orderBean = applicationContext.getBean("order", Order.class);
        System.out.println(orderBean);

        Student studentBean = applicationContext.getBean("studentBean", Student.class);
        System.out.println(studentBean);
    }
}

执行结果:

4个对象创建成功!

 上述程序的pom文件完整代码:
<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dong</groupId>
    <artifactId>spring6-008-ioc-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--依赖-->
    <dependencies>
        <!--spring context依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.0</version>
        </dependency>

        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

    </dependencies>
    
    <properties>
        <maven.compiler.source>19</maven.compiler.source>
        <maven.compiler.target>19</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

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

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

1、

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

 2、

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

12.4 选择性实例化Bean

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

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

package com.dong.spring6.bean3;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

@Component
public class A {
    public A() {
        System.out.println("A的无参数构造方法执行");
    }
}

@Controller
class B {
    public B() {
        System.out.println("B的无参数构造方法执行");
    }
}

@Service
class C {
    public C() {
        System.out.println("C的无参数构造方法执行");
    }
}

@Repository
class D {
    public D() {
        System.out.println("D的无参数构造方法执行");
    }
}

@Controller
class E {
    public E() {
        System.out.println("E的无参数构造方法执行");
    }
}

@Controller
class F {
    public F() {
        System.out.println("F的无参数构造方法执行");
    }
}

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

spring-choose.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 http://www.springframework.org/schema/context/spring-context.xsd">

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

测试程序

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

执行结果:

image.png

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

spring-choose.xml

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

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

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

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

12.5.1 @Value

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

代码

<?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 http://www.springframework.org/schema/context/spring-context.xsd">

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

</beans>
package com.dong.spring6.bean3;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

@Component
public class MyDataSource implements DataSource {
    @Value(value = "com.mysql.cj.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring6")
    private String url;
    @Value("root")
    private String username;
    @Value("123456")
    private String password;

    //使用@Value注解注入的话,可以用在属性上,并且可以不提供setter方法
/*    public void setDriver(String driver) {
        this.driver = driver;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }*/    

    @Override
    public String toString() {
        return "MyDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}
package com.dong.spring6.test;

import com.dong.spring6.bean.Order;
import com.dong.spring6.bean.Student;
import com.dong.spring6.bean.User;
import com.dong.spring6.bean.Vip;
import com.dong.spring6.bean3.MyDataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IOCAnnotationTest {
    @Test
    public void testDIByAnnotation(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-di-annotation.xml");
        MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);
        /*
          MyDataSource{driver='com.mysql.cj.Driver', url='jdbc:mysql://localhost:3306/spring6', username='root', password='123456'}
         */
        System.out.println(myDataSource);

    }
}

 运行结果:

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

12.5.2 @Autowired与@Qualifier

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

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

看一下它的源码:

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
	boolean required() default true;
}

源码中有两处需要注意:

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

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

package com.dong.spring6.dao;

public interface UserDao {
    void insert();
}
package com.dong.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository //纳入bean管理
public class UserDaoForMySQL implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向mysql数据库插入User数据");
    }
}
package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service // 纳入bean管理
public class UserService {

    @Autowired // 在属性上注入
    private UserDao userDao;
    
    // 没有提供构造方法和setter方法。

    public void save(){
        userDao.insert();
    }
}

配置包扫描

<?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 http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.powernode.spring6.dao,com.powernode.spring6.service"/>
</beans>

测试程序

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

执行结果:

image.png

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

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

UserService

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

执行结果:

image.png

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

执行结果:

image.png

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    public UserService(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

执行结果:

image.png

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

执行结果:

image.png

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public UserService(){
        
    }

    public void save(){
        userDao.insert();
    }
}

执行结果:

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

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

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

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

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

package com.dong.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository // 这里没有给bean起名,默认名字是:userDaoForOracle
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}
package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    @Autowired
    @Qualifier("userDaoForOracle") // 这个是bean的名字。
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

执行结果:

总结:

  • @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+版本请使用这个依赖

<dependency>
  <groupId>jakarta.annotation</groupId>
  <artifactId>jakarta.annotation-api</artifactId>
  <version>2.1.1</version>
</dependency>

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

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

<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>javax.annotation-api</artifactId>
  <version>1.3.2</version>
</dependency>

@Resource注解的源码如下:

image.png


测试一下:

给这个UserDaoForOracle起名xyz

package com.dong.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository("xyz")
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Resource(name = "xyz")
    private UserDao userDao;

    public void save(){
        userDao.insert();
    }
}

执行测试程序:

image.png

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

UserDaoForOracle

package com.dong.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDaoForOracle implements UserDao{
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Resource
    private UserDao userDao;

    public void save(){
        userDao.insert();
    }
}

执行测试程序:

image.png

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

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

 UserService的属性名修改为userDao2

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Resource
    private UserDao userDao2;

    public void save(){
        userDao2.insert();
    }
}

执行结果:

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

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

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

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

@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

执行结果:

image.png

当然,也可以指定name:

UserService

package com.dong.spring6.service;

import com.dong.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

@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只能有一个。

12.6 全注解式开发

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

配置类代替spring配置文件

package com.dong.spring6.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"com.dong.spring6.dao", "com.dong.spring6.service"})
public class Spring6Configuration {
}

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

@Test
public void testNoXml(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.save();
}

执行结果

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/42986.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

时序数据库 TDengine 与金山云两大产品完成兼容互认证

万物互联时代&#xff0c;企业数字化转型和政企上云如火如荼。在云计算迎来重大发展机遇的同时&#xff0c;数据库在企业数字化转型中也扮演着重要的角色——随着业务量的激增&#xff0c;数据库的弹性扩容、容灾备份等需求逐渐显现&#xff0c;在此挑战下&#xff0c;时序数据…

哈希:探索快速的数据存储和搜索方法

哈希&#xff1a;探索快速的数据存储和搜索方法 哈希表作为一种高效的数据存储结构&#xff0c;可以使数据的存储位置与关键码之间建立一一映射的关系&#xff0c;从而加快元素的搜索速度。然而&#xff0c;哈希方法也面临着哈希冲突的问题&#xff0c;即不同的关键字通过相同…

MyBatis操作数据库

1.MyBatis是什么&#xff1f; MyBatis 是⼀款优秀的持久层框架&#xff0c;它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接⼝和 Java POJO&#xf…

力扣 -- 918. 环形子数组的最大和

一、题目&#xff1a; 题目链接&#xff1a;918. 环形子数组的最大和 - 力扣&#xff08;LeetCode&#xff09; 二、解题步骤&#xff1a; 下面是用动态规划的思想解决这道题的过程&#xff0c;相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码&#xff1…

Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析

1. HTTP协议解读 什么是协议&#xff1f; 协议实际上是某些人&#xff0c;或者某些组织提前制定好的一套规范&#xff0c;大家都按照这个规范来&#xff0c;这样可以做到沟通无障碍。协议就是一套规范&#xff0c;就是一套标准。由其他人或其他组织来负责制定的。我说的话你能…

辅助驾驶功能开发-功能规范篇(23)-2-Mobileye NOP功能规范

5.2 状态机要求 5.2.1 NOP/HWP 状态机 NOP/HWP状态机如下所示&#xff1a; 下表总结了这些状态&#xff1a; 状态描述Passive不满足功能条件&#xff0c;功能无法控制车辆执行器。Standby满足功能条件。该功能不是由驾驶员激活的。功能不控制车辆执行器。Active - Main功能由…

Mac 四大常用清理软件推荐,软件特色下载教程横向评测

Mac 一般来说基本是不会中毒的&#xff0c;而且像 现在的 windows 也是很少中毒&#xff0c;但我们可能还是需要一款杀毒清理软件&#xff0c;主要是为了清理垃圾&#xff0c;统一查看并管理软件开机自启、权限信息等&#xff0c;统一卸载清理等功能&#xff0c;另外我们可能还…

商城-学习整理-基础-分布式组件(三)

目录 一、前言二、Spring Cloud&Spring Cloud Alibaba1、Spring Cloud 与Spring Cloud Alibaba简介2、为什么使用Spring Cloud Alibaba3、版本选择4、项目中的依赖 三、Spring Cloud Alibaba-Nacos作为注册中心1、Nacos1&#xff09;、下载 nacos-server2&#xff09;、启动…

数据库运维——备份恢复

数据库备份&#xff0c;数据库为school&#xff0c;素材如下 1.创建student和score表 CREATE TABLE student ( id INT(10) NOT NULL UNIQUE PRIMARY KEY , name VARCHAR(20) NOT NULL , sex VARCHAR(4) , birth YEAR, department VARCHAR(20) , address VARCHAR(…

k8s部署高可用-redis

一、项目地址 ​GitHub - spotahome/redis-operator: Redis Operator creates/configures/manages high availability redis with sentinel automatic failover atop Kubernetes.​ 二、部署过程 一、部署operator控制器 1.把项目clone下来直接部署方便。 到这个目录下的这个…

华硕ROG幻14 2023 GA402X原装Windows11预装系统 工厂模式恢复安装带ASUSRecevory一键还原

华硕ROG幻14 2023 GA402X原装Windows11预装系统 工厂模式恢复安装带ASUSRecevory一键还原 安装还原方法 第一步&#xff1a;需要拥有文件格式为6个底包的文件第二步&#xff1a;创建系统u盘引导分区和存储 第三步&#xff1a;复制文件到u盘之后&#xff0c;启动华硕工厂模式…

Spring Security从入门到精通

Spring Security从入门到精通&#xff08;学习三更老师的视频&#xff09; 视频地址&#xff1a;我觉得讲的不赖。三更老师的Spring Security视频 课程介绍 0. 简介 ​ Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供…

数学建模的赛题类型

一、预测类 指通过分析已有的数据或者现象&#xff0c;找出其内在发展规律&#xff0c;然后对未来情形做出预测的过程。 根据已知条件和求解目的&#xff0c;往往将预测类问题分为&#xff1a;小样本内部预测&#xff0c;大样本内部预测。 解决预测类赛题的一般步骤&#xff…

文心千帆大模型平台,一站式企业级大模型平台

文心千帆大模型平台&#xff0c;一站式企业级大模型平台 0. 前言1. 人工智能发展历程1.1 传统机器学习1.2 深度学习1.3 大模型时代 2. 文心千帆2.1 文心千帆介绍2.2 文心千帆应用场景2.3 文心千帆平台优势 3. 文心千帆初体验3.1 注册流程3.2 创建应用3.3 在线测试3.4 数据服务3…

AC自动机(java)

AC自动机 AC自动机介绍代码演示 indexTree AC自动机介绍 AC自动机算法是一种基于Trie树和有限状态机的字符串匹配算法。它在查找字符串时&#xff0c;利用额外的失配指针进行回退&#xff0c;转向其他分支&#xff0c;避免重复匹配前缀&#xff0c;从而提高算法效率。当一个字典…

Flink-端到端精确一次(End-To-End Exactly-Once)

1.总结 目的&#xff1a;想要在故障恢复后不丢数据 输入端 保证可以重复发送数据如果是kafka&#xff0c;Flink负责维护offset&#xff0c;不用kafka维护设置kafka的隔离级别为&#xff1a;读已提交flink 开启检查点采用对齐或者不对齐的精确一次输出端 kafka 幂等事务两阶段…

【微信小程序】使用iView组件库的ActionSheet组件实现底部选择功能

效果1 效果2 要在微信小程序中使用iView组件库的ActionSheet组件&#xff0c;可以按照以下步骤进行&#xff1a; 首先&#xff0c;确保已经引入了iView组件库的样式和脚本文件。可以在app.wxss中引入iView的样式文件&#xff1a; import "/path/to/iview/weapp/dist/sty…

AutoSAR系列讲解(实践篇)7.4-实验:配置SWCRTE

注意: 实验篇是重点,有条件的同学最好跟着做一遍,然后回头对照着7.1-7.3理解其配置的目的和意义。实验下篇将在7.7节中继续做 一、实验概览 1、实验目的 通过本次实验,主要是让大家对Dev的配置有一个全流程的学习。这里会用到前两节的内容,将其串联起来,让大家能完整的…

Spring MVC异常处理【单个控制异常处理器、全局异常处理器、自定义异常处理器】

目录 一、单个控制器异常处理 1.1 控制器方法 1.2 编写出错页面 1.3 测试结果 二、全局异常处理 2.1 一个有异常的控制器类 2.2 全局异常处理器类 2.3 测试结果 三、自定义异常处理器 3.1 自定义异常处理器 3.2 测试结果 往期专栏&文章相关导读 1. Maven系列…

STM32入门之创建工程模板

1.STM32固件库的结构图如下。从图中可以看出&#xff0c;我们在配置STM32的固件库时需要配置用户层、CMSIS层的文件。配置库文件即正确的配置这些函数的文件。CMSIS(Cortex Microcontroller Software Interface Standard)是ARM公司提供的微控制器软件接口标准&#xff0c;所有使…