【Spring6】| Spring IoC注解式开发

news2024/10/6 16:29:28

目录

一: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();
    }
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开放式耳机的颠覆之作!南卡OE Pro新皇降临!佩戴和音质双重突破

千呼万唤的南卡OE Pro终于要在最近正式官宣上线&#xff0c;此消息一经放出&#xff0c;蓝牙耳机市场就已经沸腾。NANK南卡品牌作为国内的音频大牌&#xff0c;发展和潜力一直备受业内关注&#xff0c;这次要上线的南卡OE Pro更是南卡十余年来积累的声学技术结晶之一。 据透露…

ChatGPT惨遭围剿?多国封杀、近万人联名抵制……

最近&#xff0c;全世界燃起一股围剿ChatGPT的势头。由马斯克、图灵奖得主Bengio等千人联名的“暂停高级AI研发”的公开信&#xff0c;目前签名数量已上升至9000多人。除了业内大佬&#xff0c;欧盟各国和白宫也纷纷出手。 最早“动手”的是意大利&#xff0c;直接在全国上下封…

【Redis】Redis 是如何保证高可用的?(背诵版)

Redis 是如何保证高可用的&#xff1f;1. 说一下 Redis 是如何保证高可用的&#xff1f;2. 了解过主从复制么&#xff1f;2.1 Redis 主从复制主要的作用是什么?2.2 Redis 主从模式的拓扑结构&#xff1f;&#xff08;1&#xff09;一主一从结构&#xff08;2&#xff09;一主多…

客快物流大数据项目(一百一十一):Spring Boot项目部署

文章目录 Spring Boot项目部署 一、​​​​​​​​​​​​​​项目打包 二、运行 Spring Boot项目部署

Hadoop之Mapreduce序列化

目录 什么是序列化&#xff1a; 什么是反序列化&#xff1a; 为什么要序列化&#xff1a; Java的序列化&#xff1a; Hadoop序列化: 自定义序列化接口&#xff1a; 实现序列化的步骤&#xff1a; 先看源码进行简单分析&#xff1a; 序列化案例实操&#xff1a; 案例需…

【JavaEE】常见的锁策略都有哪些?

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶在Java多线程中&#xff0c;常见的锁策略都有哪些&#xff1f;这些锁策略应该怎么理解&#xff1f; &#xff08;乐观锁vs悲观锁&#xff0c;轻量级锁vs重量级锁&#xff0c;自旋锁vs挂起等待…

Day919.生产就绪 -SpringBoot与K8s云原生微服务实践

生产就绪 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于生产就绪的内容。 互联网软件交互阶段 如上&#xff0c;3个阶段中的“”生产就绪”是什么&#xff1f; 什么是生产就绪 生产就绪是指在生产过程中所需的一切准备工作已经完成&#xff0c;可以开始生产的状…

MySQL视图 视图的作用、视图常用语法

视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&#xff0c;不保存查询结果。 常…

Qt音视频开发27-ffmpeg视频旋转显示

一、前言 用手机或者平板拍摄的视频文件,很可能是旋转的,比如分辨率是1280x720,确是垂直的,相当于分辨率变成了720x1280,如果不做旋转处理的话,那脑袋必须歪着看才行,这样看起来太难受,所以一定要想办法解析到视频的旋转角度,然后根据这个角度重新绘制。在窗体那边也…

SpringBoot使用Spring Validation案例

简单使用 Java API规范(JSR303)定义了Bean校验的标准validation-api&#xff0c;但没有提供实现。hibernate validation是对这个规范的实现&#xff0c;并增加了校验注解如Email、Length等。 Spring Validation是对hibernate validation的二次封装&#xff0c;用于支持spring m…

Machine Learning-Ex4(吴恩达课后习题)Neural Networks Learning

目录 1. Neural Networks 1.1 Visualizing the data 1.2 Model representation 1.3 Feedforward and cost function 1.4 Regularized cost function 2. Backpropagation 2.1 Sigmoid gradient 2.2 Random initialization 2.3 Backpropagation 2.4 Gradient Checking…

工厂模式白话 - 3种都有哦

前言 工厂模式&#xff08;Factory Pattern&#xff09;里所谓的“工厂”和现实生活中的工厂一样 主要作用都是生产产品 像食品厂、服装厂、汽车厂生产吃的、穿的、开的 设计模式里的工厂则是生产对象 划分 工厂模式可分为简单工厂、工厂方法、抽象工厂3种 有啥不同呢&a…

RecvByteBufAllocator内存分配计算

虽然了解了整个内存池管理的细节&#xff0c;包括它的内存分配的具体逻辑&#xff0c;但是每次从NioSocketChannel中读取数据时&#xff0c;应该分配多少内存去读呢&#xff1f; 例如&#xff0c;客户端发送的数据为1KB , 应该分配多少内存去读呢&#xff1f; 例如&#xff1a;…

梳理ERP与CRM、MRP、PLM、APS、MES、WMS、SRM的关系

数字化转型中少不了ERP系统的存在&#xff0c;CRM、MRP、PLM、APS、MES、WMS、SRM这些系统都需要一起上吗&#xff1f; 如下图所示&#xff0c;是某企业IT系统集成架构流图。 先了解一下ERP是做什么的&#xff0c;ERP就是企业资源管理系统&#xff0c;从企业的价值链分析&…

在CSDN创作了6个月,我收获了什么?文末送书~

作者主页&#xff1a;阿玥的小东东主页&#xff01; 正在学习&#xff1a;python和C/C 期待大家的关注哦 目录 一次很好的机会&#xff0c;让我开始了CSDN之旅 首先来看看我的几位领路人 创作动力 1W粉丝 在CSDN我收获了什么&#xff1f; 很高的展现量 认证创作者身份 社…

构建自动过程:FinalBuilder 8.0 Crack

使用 FinalBuilder 自动化您的构建过程很简单。使用 FinalBuilder&#xff0c;您无需编辑 xml 或编写脚本。可视化定义和调试您的构建脚本&#xff0c;然后使用 Windows 调度程序安排它们&#xff0c;或将它们与 Continua CI、Jenkins 或任何其他 CI 服务器集成。 成千上万的软…

手把手调参 YOLOv8 模型之 训练|验证|推理配置-详解

YOLO系列模型在目标检测领域有着十分重要的地位&#xff0c;随着版本不停的迭代&#xff0c;模型的性能在不断地提升&#xff0c;源码提供的功能也越来越多&#xff0c;那么如何使用源码就显得十分的重要&#xff0c;接下来通过文章带大家手把手去了解Yolov8&#xff08;最新版…

Android开发—Jetpack四件套

2017年&#xff0c;Google发布了Android Architecture Components&#xff0c;包括Room、LiveData、ViewModel和Paging等组件&#xff0c;旨在帮助开发者更轻松地实现MVVM架构。 2018年&#xff0c;Google在I/O大会上推出的一套Android开发组件库&#xff0c;旨在帮助开发者更…

Python 小型项目大全 56~60

五十六、质数 原文&#xff1a;http://inventwithpython.com/bigbookpython/project56.html 质数是只能被 1 和它自己整除的数。质数有各种各样的实际应用&#xff0c;但是没有算法可以预测它们&#xff1b;我们必须一次计算一个。然而&#xff0c;我们知道有无限多的质数有待发…

技术招聘漫谈 | Java工程师招聘难?你可能需要这份独家指南

两周前&#xff0c;我们发布了一篇关于怎样招聘前端工程师的文章&#xff08;点击此处顾&#xff09;。在文章中&#xff0c;我们分析了前端岗位有哪些必不可少的考察要点&#xff0c;以及如何在面试中考核对方是否能写出高质量的代码&#xff0c;这篇文章得到了大量技术面试官…