跳转至

IoC 注解式开发

反射回顾

Class<?> clazz = Class.forName("Demo");
Method method = clazz.getDeclaredMethod("doSome", String.class, int.class);
Object obj = clazz.newInstance(); // 简化起见
String ret = (String) method.invoke(obj, "abc", 1);

在 xml 文件中配置好了类名和属性名,就可以:

Field field = clazz.getDeclaredField(property);
// 例如 set 方法就可以根据 ”set" 和 fieldName 拼出 methodName
Method setter = clazz.getDeclaredMathod(methodName, field.getType()); // 通过 getType 返回参数的 Class 对象

自定义注解回顾

  • @Target:自定义注解的注解,用于修饰自定义注解可以出现的位置
  • @Retention:用于标记自定义注解最终保留在哪里
    • Source 只保存在源文件中
    • Class 编译生成的 .class 文件中也有,但是不能被反射读取
    • RUNTIME 表示可以被反射读到
@Target(value = {ElementType.Type, ElementType.Field}) // 注解可以出现在类和属性上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

IoC 注解扫描

需求:给一个包,要求写一段程序扫描出这个包下的所有带有 MyAnnotation 注解的类,并实例化这些类放到 Map 集合中

public class AnnotationScan {
    public static void main(String[] args) {
        String packageName = "come.demo.folder";
        // 正则表达式替换,注意到 . 表示任意字符,此处的 . 必须用转义符表示普通的字符
        String packagePath = pakageName.replaceAll("\\.", "/");

        // 拿到绝对路径
        URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
        String path = url.getPath();

        // 获取绝对路径下的所有文件
        File file = new File(path);
        File[] files = file.listFiles();
        for (File file : files) {
            try {
                String className = packageName + "." + file.getName().split("\\.")[0];

                // 通过反射机制解析注解
                Class<?> aClass = Class.forName(className);

                // 判断类上是否有这个注解
                if (aClass.isAnnotationPresent(Component.class)) {
                    // 获取注解
                    Component annotation = aClass.getAnnotation(Component.class);
                    String id = annotation.value();
                    // 有这个注解的都要创建对象
                    Object obj = aClass.newInstance();
                    beanMap.put(id, obj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

注解分类与使用

控制反转:把创建对象的权力交给 Spring,把管理对象间的依赖关系的任务交给 Spring。通过依赖注入实现

声明 Bean 的注解

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

后三个都是 Component 的别名

@Bean

@Bean 通常和 @Configuration 一起使用,@Configuration 用于标识一个类是配置类。配置类的作用是定义和配置 Spring 容器中的 Bean。通过 @Configuration 注解,Spring 会将该类作为一个配置源,并处理其中定义的 Bean。

在一个方法前加上 @Bean 注解之后,Spring 会自动将方法的返回值交给 IoC 容器,使其成为 IoC 容器中的 bean 对象。

@Configuration
public class CommonConfig {
    @Bean
    public Resolver resolver() { // 对象名默认为方法名
       return new Resolver(); 
    }
}

在 SpringBoot 中,SpringBoot 的 spring-boot-autoconfigure模块中包含了 Redis 的自动配置类RedisAutoConfiguration,使得项目启动时 StringRedisTemplate 可以注入到项目中

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

负责注入的注解

  • @Value 初始化字段值(可以用在类的属性上,也可以用于方法上),用于注入简单类型
  • @Autowired 可以用于注入非简单类型,且单独使用时是根据类型装配。若有多个相同类型(或者接口下的多个实现类)的 Bean 会导致冲突

    例子可见 Bean - 依赖注入章节

  • @Qualifier 和 @Autowired 联合使用可以根据名称装配

    @Repository("orderDaoForMySQL")
    public class OrderDaoForMySQL implements OrderDao {
        @Override
        public void insert() {
    
        }
    }
    
    @Service
    public class OrderService {
        @Autowired
        @Qualifier("orderDaoForMySQL") // 规定类名
        private OrderDao orderDao;
    }
    
  • @Resource 属于 JDK 的一部分,属于 Java 规范的标准注解,默认根据名称装配,未指定名称时使用属性名作为 name,如果找不到 name 再根据类型进行装配

    不是 Spring 框架内置的,Spring6 及以后需要引 jakarta.annotation(Apache 软件基金会维护)

    @Resource(name = "orderDaoForMySQL")
    private OrderDao orderDao;
    

Spring 全注解开发

可以编写一个代替 Spring 框架的配置文件

@Configuration
@ComponentScan("需要扫描的包1", "需要扫描的包2")
public class SpringConfig {

}
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spring6Config.class);
    // 或者直接启动:new AnnotationConfigApplicationContext(Spring6Config.class);
}