【Spring6】| Spring IoC注解式开发

目录

一:Spring IoC注解式开发

1. 回顾注解

2. 声明Bean的四个注解

3. Spring注解的使用

4. 选择性实例化Bean

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

5.1 @Value

5.2 @Autowired与@Qualifier

5.3 @Resource

6. 全注解式开发


一:Spring IoC注解式开发

1. 回顾注解

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

我们来回顾一下:

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

②第二:注解怎么使用?

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

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

自定义注解:Component

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

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

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

package com.bjpowernode.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();
}

注解怎么使用?

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

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

package com.bjpowernode.annotation;

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

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

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

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

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

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

package com.bjpowernode.annotation;

public class Test {
    public static void main(String[] args) throws Exception{
        // 获取类
        Class<?> clazz = Class.forName("com.bjpowernode.annotation.User");
        // 看这个类上有没有Component注解
        if (clazz.isAnnotationPresent(Component.class)){
            // 有这个注解就获取注解的属性并就创建对象
            // 获取这个注解
            Component annotation = clazz.getDeclaredAnnotation(Component.class);
            // 获取这个注解的属性
            System.out.println(annotation.value());
            // 创建对象
            // User user = new User();
            User user = (User)clazz.newInstance()
            System.out.println(user);
        }
    }
}

执行结果:

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

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

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

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

User类:含有Component注解

package com.bjpowernode.annotation;

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

Vip类:没有Component注解

package com.bjpowernode.annotation;

public class Vip {
}

Student类:含有Component注解

package com.bjpowernode.annotation;

@Component("studentBean")
public class Student {
}

 ComponentScan类:编写测试

package com.bjpowernode.annotation;

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集合
        Map<String,Object> beanMap = new HashMap<>();

        // 目前只知道一个包名
        String packageName = "com.bjpowernode.annotation";
        // 把包名转换成路径的形式,使用正则表达式
        // 直接使用“.”,正则表达式中代表任意字符,会把所有的内容都替换成/
        // 正则表达式中“\.”代表“.” ,而java中双\\就代表\,有一个用来转义
        String packagePath = packageName.replaceAll("\\.", "/");
        // String packagePath = packageName.replace(".", "/"); 这样也可以
        // System.out.println(packagePath); // com/bjpowernode/annotation
        // com是类的根路径下的一个目录,获取系统类加载器,从根目录下进行扫描,返回一个URL
        URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
        // 获取绝对路径
        String path = url.getPath();
        // 开始扫描,获取一个绝对路径下的所有文件
        File file = new File(path);
        File[] files = file.listFiles();
        // 流式编程
        Arrays.stream(files).forEach(f -> { // f.getName得到的是对应的.class字节码文件
            // 拼串成全限定包名
            String className = packageName+"."+f.getName().split("\\.")[0];
            // System.out.println(className);
            try {
                // 通过反射机制创建对象
                // 获取类
                Class<?> clazz = Class.forName(className);
                // 检查类上有没有Component注解
                if (clazz.isAnnotationPresent(Component.class)) {
                    // 有的话获取注解
                    Component annotation = clazz.getDeclaredAnnotation(Component.class);
                    // 获取注解的属性值
                    String key = annotation.value();
                    // 创建对象
                    Object obj = clazz.newInstance();
                    // 把注解的属性值作为key,对象作为value放到Map集合当中
                    beanMap.put(key,obj);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        // 打印这个Map集合
        System.out.println(beanMap);


    }
}

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

2. 声明Bean的四个注解

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

①@Component注解:平常使用

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

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

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

源码如下:

@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 "";
}

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

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

①控制器类上使用:Controller

②service类上使用:Service

③dao类上使用:Repository

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

3. Spring注解的使用

如何使用以上的注解呢?

第一步:加入aop的依赖;

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

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

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

第一步:加入aop的依赖

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

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

<?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>

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

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

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

<?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.bjpowernode.spring.bean"/>
</beans>

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

User类:含有Component注解

package com.bjpowernode.spring.bean;

import org.springframework.stereotype.Component;

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

Vip类:含有Component注解

package com.bjpowernode.spring.bean;

import org.springframework.stereotype.Component;

@Component("vipBean")
public class Vip {

}

编写测试程序

package com.bjpowernode.spring.test;

import com.bjpowernode.spring.bean.User;
import com.bjpowernode.spring.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class springAnnotationTest {
    @Test
    public void testAnnotation(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User userBean = applicationContext.getBean("userBean", User.class);
        System.out.println(userBean);
        Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
        System.out.println(vipBean);

    }
}

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

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

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

package com.bjpowernode.spring.bean;

import org.springframework.stereotype.Component;

// 自动取名就是vip
@Component
public class Vip {

}

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

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

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

<?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.bean,com.powernode.spring6.dao"/>
    <!--第二种:也可以指定这多个包共同的父包,但是这肯定要牺牲一部分效率-->
    <context:component-scan base-package="com.powernode.spring6"/>
</beans>

4. 选择性实例化Bean

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

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

package com.bjpowernode.spring.bean;

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的无参数构造方法执行");
    }
}

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

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

<?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.bjpowernode.spring.bean" use-default-filters="false">
        <!--让@Controller注解生效-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
       <!--可配置多个
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
    </context:component-scan>
</beans>

测试程序

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

package com.bjpowernode.spring.test;

import com.bjpowernode.spring.bean.User;
import com.bjpowernode.spring.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

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

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

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

<?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.bjpowernode.spring.bean" use-default-filters="true">
        <!--让@Service、@Repository失效-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
</beans>

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

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

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

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

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

5.1 @Value

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

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

package com.bjpowernode.spring.bean;

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

@Component
public class Person {
    @Value("张三")
    private String name;
    @Value("18")
    private int age;
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

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

package com.bjpowernode.spring.bean;

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

@Component
public class Person {
    
    private String name;
    private int age;

    // 提供了set方法
    @Value("张三")
    public void setName(String name) {
        this.name = name;
    }
    @Value("18")
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

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

package com.bjpowernode.spring.bean;

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

@Component
public class Person {
 
    private String name;
    private int age;
    
    // 提供了构造方法的形参上
    public Person(@Value("张三")String name, @Value("18")int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

spring-annotation-di.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.bjpowernode.spring.bean"/>

</beans>

执行结果

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

5.2 @Autowired与@Qualifier

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

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

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

@Autowired源码

源码中有两处需要注意:

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

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

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;
}

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

UserDao接口

package com.bjpowernode.spring.dao;

public interface UserDao {
    void insert();
}

接口的实现类UserDaoImpl

package com.bjpowernode.spring.dao.impl;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository("userDaoImpl")
public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("正在插入信息");
    }
}

UserService类:在属性上直接注入

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    // 面向接口编程
    @Autowired // 不需要指定任何属性,直接使用即可
    private UserDao userDao;

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

spring-annotation-di.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.bjpowernode.spring"/>
</beans>

测试程序

package com.bjpowernode.spring.test;

import com.bjpowernode.spring.bean.Person;
import com.bjpowernode.spring.bean.User;
import com.bjpowernode.spring.bean.Vip;
import com.bjpowernode.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class springAnnotationTest {

    @Test
    public void testAnnotationDI(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-annotation-di.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.insertDate();
    }
}

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

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

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    // 不需要指定任何属性,直接使用即可
    @Autowired 
    // 要自动装配那个实现类,就把实现类的名字写上
    @Qualifier("userDaoImpl")
    private UserDao userDao;

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

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    
    private UserDao userDao;
    // 出现在set方法上
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    
    private UserDao userDao;

    // 出现在构造方法上
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

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

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    
    private UserDao userDao;

    // 出现在构造方法的属性上
    public UserService(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }

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

第五种情况:直接省略不写

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    
    private UserDao userDao;

    // 一个构造方法并且参数能和上面的属性对应上,直接可以省略不写
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

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

总结:

①@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+版本引入的依赖

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

spring5-版本引入的依赖

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

@Resource注解的源码如下

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

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {

    // 出现在属性上,必须加上name属性
    @Resource(name = "userDaoImpl")
    private UserDao userDao;
    
    public void insertDate() {
        userDao.insert();
    }
}

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
   
    private UserDao userDao;
    // 出现在set方法上,必须加上name属性
    @Resource(name = "userDaoImpl")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

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

UserDaoImpl类,名字修改为属性名

package com.bjpowernode.spring.dao.impl;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.stereotype.Repository;

// 这里也改成属性的名字
// @Repository("userDaoImpl")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("正在插入信息");
    }
}

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    // 这里未指定名字,会把属性名作为名字
    @Resource
    private UserDao userDao;

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

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

执行结果:

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

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

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

package com.bjpowernode.spring.dao.impl;

import com.bjpowernode.spring.dao.UserDao;
import org.springframework.stereotype.Repository;

// 名字不是属性名
@Repository("xxx")
public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("正在插入信息");
    }
}

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

package com.bjpowernode.spring.service;

import com.bjpowernode.spring.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {
    // 这里未指定名字,会把属性名作为名字
    @Resource
    private UserDao userDao;

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

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

重点:@Resource注解默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入;byType注入时,某种类型的Bean只能有一个(实现类有多个就不知道注入哪一个了)!

6. 全注解式开发

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

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

主要使用到两个注解:

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

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

package com.bjpowernode.spring.config;

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

@Configuration
@ComponentScan("com.bjpowernode.spring")
public class Spring6Configuration {
}

第二步:编写测试程序

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

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

package com.bjpowernode.spring.test;

import com.bjpowernode.spring.bean.Person;
import com.bjpowernode.spring.bean.User;
import com.bjpowernode.spring.bean.Vip;
import com.bjpowernode.spring.config.Spring6Configuration;
import com.bjpowernode.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class springAnnotationTest {

    @Test
    public void testNoXML(){
        // 创建AnnotationConfigApplicationContext对象
        ApplicationContext context = new AnnotationConfigApplicationContext(Spring6Configuration.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.insertDate();
    }
}

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

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

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

相关文章

Springboot+vue开发的图书借阅管理系统项目源码下载-P0029

前言图书借阅管理系统项目是基于SpringBootVue技术开发而来&#xff0c;功能相对比较简单&#xff0c;分为两个角色即管理员和学生用户&#xff0c;核心业务功能就是图书的发布、借阅与归还&#xff0c;相比于一些复杂的系统&#xff0c;该项目具备简单易入手&#xff0c;便于二…

基于深度学习的车型识别系统(Python+清新界面+数据集)

摘要&#xff1a;基于深度学习的车型识别系统用于识别不同类型的车辆&#xff0c;应用YOLO V5算法根据不同尺寸大小区分和检测车辆&#xff0c;并统计各类型数量以辅助智能交通管理。本文详细介绍车型识别系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Python的实现代码…

你掌握了吗?在PCB设计中,又快又准地放置元件

在印刷电路板设计中&#xff0c;设置电路板轮廓后&#xff0c;将零件(占地面积)调用到工作区。然后将零件重新放置到正确的位置&#xff0c;并在完成后进行接线。 组件放置是这项工作的第一步&#xff0c;对于之后的平滑布线工作是非常重要的工作。如果在接线工作期间模块不足…

MagicalCoder可视化开发平台:轻松搭建业务系统,为企业创造更多价值

让软件应用开发变得轻松起来&#xff0c;一起探索MagicalCoder可视化开发工具的魔力&#xff01;你是否为编程世界的各种挑战感到头痛&#xff1f;想要以更高效、简单的方式开发出专业级的项目&#xff1f;MagicalCoder低代码工具正是你苦心寻找的产品&#xff01;它是一款专为…

什么是Nginx

一.什么是nginxNginx (engine x) 是一个高性能的HTTP和反向代理web服务器&#xff0c;是一款由俄罗斯的程序设计师Igor Sysoev使用c语言开发的轻量级的Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&#xff0c;官方测试nginx能够支支撑5万…

蓝桥杯冲刺 - week1

文章目录&#x1f4ac;前言&#x1f332;day192. 递归实现指数型枚举843. n-皇后问题&#x1f332;day2日志统计1209. 带分数&#x1f332;day3844. 走迷宫1101. 献给阿尔吉侬的花束&#x1f332;day41113. 红与黑&#x1f332;day51236. 递增三元组&#x1f332;day63491. 完全…

Java四种内部类(看这一篇就够了)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

详细介绍less(css预处理语言)

详细介绍less&#xff08;css预处理语言&#xff09;什么是lessless解决什么问题less相比于css的优点如何使用less第一步&#xff1a;创建一个less文件第二步&#xff1a;引入less文件第三步&#xff0c;编写less文件&#xff08;和html一样的结构&#xff09;完整代码示例什么…

C 单链表及其相关算法 万字详解(通俗易懂)

目录 一、前言 : 二、线性结构 1.介绍 2.分类 3.数组和链表的区别 : 三、链表 [离散存储] 1.定义 2.相关概念 3.如何确定一个链表&#xff1f; 4.如何表示链表中的一个结点&#xff1f; 5.链表的分类 四、链表的相关算法 1.链表的创建和遍历 ①准备工作 : ②创建链表 :…

【Vue全家桶】带你全面了解通过Vue CLI初始化Vue项目

【Vue全家桶】带你全面了解通过Vue CLI初始化Vue项目 文章目录【Vue全家桶】带你全面了解通过Vue CLI初始化Vue项目写在前面一、Vue CLI脚手架1.1 认识Vue CLI1.2 Vue CLI 安装和使用二、Vue create 项目的过程2.1 创建项目2.2选择 Manually select features创建2.3 选择Vue的版…

基于Springboot+Vue2前后端分离框架的智慧校园系统源码,智慧学校源码+微信小程序+人脸电子班牌

▶ 智慧校园开发环境&#xff1a; 1、使用springboot框架Javavue2 2、数据库MySQL5.7 3、移动端小程序使用小程序原生语音开发 4、电子班牌固件安卓7.1&#xff1b;使用Java Android原生 5、elmentui &#xff0c;Quartz&#xff0c;jpa&#xff0c;jwt 智慧校园结构导图▶ 这…

后端接口返回近万条数据,前端渲染缓慢,content Download 时间长的优化方案

前言 性能优化&#xff0c;是前端绕过不去的一道门槛&#xff0c;甚是重要。最近一年&#xff0c;也很少有机会在项目中进行前端性能优化&#xff0c;一直在忙于业务开发。 最近终于是来了机会&#xff0c;遇到了这样的场景&#xff0c;心里也甚是激动&#xff0c;写个随笔记…

【K8S系列】深入解析Pod对象(二)

目录 序言 1.Volume 简单介绍 2 Projected Volume 介绍 2.1 Secret 2.1.1 yaml讲解 2.1.2 创建Pod 2.2 Downward API 2.2.1 yaml示例 2.2.2 Downward API 支持字段 3 投票 序言 任何一件事情&#xff0c;只要坚持六个月以上&#xff0c;你都可以看到质的飞跃。 在…

什么是语法糖?Java中有哪些语法糖?

本文从 Java 编译原理角度&#xff0c;深入字节码及 class 文件&#xff0c;抽丝剥茧&#xff0c;了解 Java 中的语法糖原理及用法&#xff0c;帮助大家在学会如何使用 Java 语法糖的同时&#xff0c;了解这些语法糖背后的原理1 语法糖语法糖&#xff08;Syntactic Sugar&#…

【0基础学爬虫】爬虫基础之网络请求库的使用

大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0基础学…

【Redis】高可用架构之哨兵模式 - Sentinel

Redis 高可用架构之哨兵模式 - Sentinel1. 前言2. Redis Sentinel 哨兵集群搭建2.1 一主两从2.2 三个哨兵3. Redis Sentinel 原理剖析3.1 什么哨兵模式3.2 哨兵机制的主要任务3.2.1 监控&#xff08;1&#xff09;每1s发送一次 PING 命令&#xff08;2&#xff09;PING 命令的回…

DevOps系列文章 - K8S构建Jenkins持续集成平台

k8s安装直接跳过&#xff0c;用Kubeadm安装也比较简单安装和配置 NFSNFS简介NFS&#xff08;Network File System&#xff09;&#xff0c;它最大的功能就是可以通过网络&#xff0c;让不同的机器、不同的操作系统可以共享彼此的文件。我们可以利用NFS共享Jenkins运行的配置文件…

C语言通讯录应用程序:从设计到实现

hello&#xff0c;这期给大家带来C语言实现静态通讯录,主要也是建立起创建大项目的思维&#xff0c;与往期这两篇博客有点类似 C语言实现三子棋 C语言实现扫雷 文章目录&#x1f913;通讯录介绍&#x1f636;‍&#x1f32b;️效果演示&#x1f920;主题框架头文件测试文件函数…

CSS 属性计算过程

CSS 属性计算过程 你是否了解 CSS 的属性计算过程呢&#xff1f; 有的同学可能会讲&#xff0c;CSS属性我倒是知道&#xff0c;例如&#xff1a; p{color : red; }上面的 CSS 代码中&#xff0c;p 是元素选择器&#xff0c;color 就是其中的一个 CSS 属性。 但是要说 CSS 属…

三十七、实战演练之接口自动化平台的文件上传

上传文件功能 上传文件功能主要针对需要测试上传文件的接口。原理是&#xff0c;把要测试上传的文件先上传到测试平台&#xff0c;然后把路径写入 用例中&#xff0c;后台真正测试时再将其进行上传。 一、上传文件模型 在testplans/models.py 模块中编写如下模型&#xff1a;…