spring6-注解式开发

spring框架创建bean就是利用反射机制

反射机制的代码如下:

java 复制代码
public static void main(String[] args) throws Exception {
    System.out.println("Hello, World!");
    // 使用反射机制调用方法
    // 获取类
    Class<?> clazz = Class.forName("com.ali.bean2.SomeService");
    // 获取方法
    Method method = clazz.getMethod("doSomething", String.class, int.class);
    // 获取对象
    Object obj = clazz.newInstance();
    // 方法调用
    // obj: 哪个对象调用这个方法
    // "hello", 42: 方法参数
    // hello: 方法的返回值
    Object hello = method.invoke(obj, "hello", 42);
}

spring IoC注解式开发

注解主要是为了简化xml配置。

注解怎么定义

新建Java Class 时选择Annoation 类型的文件,这样就创建了一个新的注解

java 复制代码
// 自定义注解
// @Target 标注注解的注解,叫做元注解
// ElementType.TYPE: 表示可以标注在类上
// ElementType.FIELD: 表示可以标注在属性上
// 使用某个注解的时候,如果注解的属性名是value,可以省略属性名
// 使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个值,可以省略大括号
@Target(value = {ElementType.TYPE,ElementType.FIELD})
// @Retention 标注注解的生命周期,叫做元注解,表示最终保留在class文件中,并且可以被反射机制读取
// RetentionPolicy.SOURCE: 注解只在源码中存在,编译成字节码后就不存在了
// RetentionPolicy.CLASS: 注解在源码和字节码中都存在,运行时不存在(默认值),也就是不能被反射机制读取
// RetentionPolicy.RUNTIME: 注解在源码、字节码和运行时都存在
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

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

假设user类被注解@Component修饰

java 复制代码
@Component("userBean")
public class User {
}
java 复制代码
public static void main(String[] args) throws Exception {
    // 使用反射机制读取注解
    // 获取类
    Class<?> userClazz = Class.forName("com.ali.component.User");
    // 判断类上是否有某个注解
    boolean hasAnnotation = userClazz.isAnnotationPresent(Component.class);
    if (hasAnnotation) {
        // 获取类上的注解对象
        Component component = userClazz.getAnnotation(Component.class);
        // 访问注解的属性
        System.out.println("component value: " + component.value());
    }
}

组件扫描原理

主要是通过反射机制实例化注解标记的类的对象。

java 复制代码
public static void main(String[] args) {
    // 根据一个包名,扫描这个包下面的所有类,当这个类有@Component注解时,实例化这个类,key是@Component注解的value,value是实例化的对象
    String packageName = "com.ali.component";
    // 将包名中的"."替换成"/"
    String path = packageName.replaceAll("\\.", "/");
    // 包是在系统恨路径下的一个目录。获取它的绝对路径
    String absolutePath = ClassLoader.getSystemClassLoader().getResource(path).getPath();
    // 获取包下面的所有类文件
    File dir = new File(absolutePath);
    File[] files = dir.listFiles();
    Arrays.stream(files).forEach(file -> {
        // 获取类名
        String classname = packageName + "." + file.getName().replace(".class", "");
        // 通过反射机制解析注解
        try {
            Class<?> clazz = Class.forName(classname);
            // 判断类上是否有某个注解
            boolean hasAnnotation = clazz.isAnnotationPresent(Component.class);
            if (hasAnnotation) {
                // 获取类上的注解对象
                Component component = clazz.getAnnotation(Component.class);
                // 访问注解的属性
                System.out.println("component value: " + component.value());
                // 实例化这个类
                Object obj = clazz.getDeclaredConstructor().newInstance();
                System.out.println("实例化对象: " + obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

声明bean的注解

声明bean的注解有:@Component 、@Controller、 @Service 、@Repository。

实际上 @Controller @Service @Repository 这3个都是@Component 的别名,本质上就是一个注解。主要是为了增强代码的可读性。

当这4个注解标注在类上是,如果没有指定value值(也就是没有指定bean名称),那么bean名称默认成类名的首字母小写。

选择性实例化bean

假如有一个需求:只需要@Controller参与bean的管理。其他三个注解都不参与实例化,这种情况要怎么处理?

注意:别忘了添加配置文件的命名空间。

xml 复制代码
   <!-- 第一种方案:使用@ComponentScan自动扫描bean
    use-default-filters="false" 表示不使用默认的过滤器(即所有声明bean的注解全部失效)
    @Component 、@Controller、 @Service 、@Repository 全部失效-->
    <context:component-scan base-package="com.ali.bean2" use-default-filters="false">
        <!--    自定义过滤器,指定只扫描带有@Component注解的类
            只有@Component生效-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<!--            如果想让@Service生效,可以添加下面这一行-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>

<!--    第二种方案:use-default-filters="true" 或者不写该属性,表示 @Component 、@Controller、 @Service 、@Repository 全部生效-->
    <context:component-scan base-package="com.ali.bean2" >
        <!--   排除@Controller注解的类
            只有@Controller生效-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <!--            如果想让@Repository失效,可以添加下面这一行-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>

负责注入的注解

@value

@value:使用@value注入的话,属性可以不提供setter方法,负责注入简单类型

java 复制代码
public class User {

    @Value("Alice")
    private String name;
    @Value("22")
    private int age;
}

@value注解也可以加在setter方法上

java 复制代码
public class User {
    private String name;
    private int age;

    @Value("Alice")
    public void setName(String name) {
        this.name = name;
    }
    @Value("22")
    public void setAge(int age) {
        this.age = age;
    }
}

@value注解也可以加在构造方法上

java 复制代码
public class User {

    private String name;
    private int age;

    public User(@Value("Alice") String name,@Value("22") int age) {
        this.name = name;
        this.age = age;
    }
}

@Autowired

@Autowired可以用来注入非简单类型,翻译为:自动装配。

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

注意:假如一个接口被2个或以上的类实现,那么这个接口对象能被 @Autowired注入吗?这当然不行,因为 @Autowired是根据类型进行装配的。

那怎么解决这个问题呢?

可以将@Autowired和@Qualifier联合使用

java 复制代码
@Autowired
// 这里指定bean的名称,说明是根据名称进行自动装配的
@Qualifier("someServiceBeanForMysql") 
private SomeService someService;

@Autowired可以标注在属性上、setter方法上、构造方法的参数上。

当一个类中的构造方法只有一个,并且构造方法的参数和属性能对应上,@Autowired 可以省略 (不建议这样用)

@Resource

@Resource注解也能完成非简单类型的注入,那么他和 @Autowired有什么区别呢?

  • @Resource是jdk扩展包中的,属于jdk的一部分,所以该注解是标准注解,更加具有通用性。
  • @Autowired是spring框架自己的
  • @Resource默认根据名称自动装配,未指定name时,使用属性名作为name,如果通过name找不到的话。会启动通过类型自动装配。
  • @Autowired时根据类型自动装配。如果要根据名字装配,需要@Qualifier配合使用
  • @Resource用在属性上、setter方法上
  • @Autowired用在属性上、setter方法上、构造方法上、构造方法参数上。

使用@Resource时先加入相关依赖

Java 复制代码
<!--        @Resource注解的依赖-->
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
java 复制代码
// 进行属性注入
@Resource(name = "someServiceBean")
private SomeService someService;
相关推荐
云烟成雨TD20 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-20 小时前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫21 小时前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD21 小时前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly1 天前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh1 天前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode1 天前
Spring 依赖注入方式全景解析
java·后端·spring