Spring 注解

注解

0.注解定义

1.Annotation即注解(也被翻译为注释)

  • 所有的注解会自动继承java.lang.annotation.Annotation(这个接口是一个常规接口,并非一个注解接口),并不能继承别的类或是接口
  • 当一个注解的内部只有一个,且字段为value时,在使用的时候可以省略

2.自定义注解的使用:代码仓库见下

https://gitee.com/gqxm/gq-practice-glodon.git

  • 自定义注解的简单使用:主要对ElementType.TYPE,METHOD,FIELD,PARAMETER等进行了测试和使用
  • 自定义注解的简单使用:自定义NeedLogin注解,并且通过拦截器对其进行测试

1.jdk标准注解

1.元注解

JDK在java.lang.annotation包下提供了6个Meta注解(元注解),其中5个用于修饰其他的注解定义。

注解作用
@Target用于指定被修饰的注解能用于修饰哪些程序单元(ElementType)。一个没有被此注解限制的注解可以应用于任何项上。
@Retention用于指定被修饰的注解可以保留多长时间。当未使用该注解修饰时,默认值相当于是RetentionPolicy.CLASS
@Documented修饰注解类表明其将被javadoc工具提取成文档
@Inherited修饰作用于类的注解,指定被修饰的注解具有继承性:即该注解修饰的类的子类也将同样被该注解修饰
@Repeatable指明被修饰的这个注解可以在同一个元素上应用多次
@Native修饰成员变量,表示这个变量可以被本地代码引用,常常被代码生成工具使用(此注解不常用,了解即可)

将一个注解应用到它自身上是合法的。例如,@Documented注解被自身为注解为@Documented。因此,针对注解的Javadoc文件可以表明它们是否可被归档。

1.@Target

ElementType

关于程序单元的枚举类型ElementType

  • TYPE:类、接口、注解、枚举
  • FIELD:字段(含枚举值)
  • METHOD:方法
  • PARAMETER:形参(Formal parameter declaration)
  • CONSTRUCTOR:构造方法
  • LOCAL_VARIABLE:局部变量
  • ANNOTATION_TYPE:注解类型
  • PACKAGE:包
  • TYPE_PARAMETER:类型参数
  • TYPE_USE:类型用法
2.@Retention

RetentionPolicy

用于@Retention注解的保留策略:

保留规则描述
SOURCE不包括在类文件中的注解
CLASS包括在类文件中的注解,但是虚拟机不需要将它们载入
RUNTIME包括在类文件中的注解,并由虚拟机载入。通过反射API可获得它们
3.@Inerited 详解

如果一个类具有继承注解(即被@Inherited修饰的注解),那么它的所有子类都自动具有相同的注解。

  • 假设定义了一个继承注解@Persistent来指定一个类的对象可以存储到数据库中,那么该持久类的子类就会自动被注解为是持久性的。

    @Inherited
    @interface Persistent {
    }
    
    @Persistent
    class Employee {
    }
    
    class Manager extends Employee { // also @Persistent
    }
    
4.@Repeatable 详解

源码:

package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

对于Java SE 8来说,将同种类型的注解多次应用于某一项是合法的。为了向后兼容,可重复注解的实现者需要提供一个容器注解,它可以将这些重复注解存储到一个数组中

例子:

@Repeatable(TestRepeatables.class)
public @interface TestRepeatable {
    String content() default "请添加描述";
}

数组,用来存储

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestRepeatables {
    TestRepeatable[] value();
}
@TestRepeatable(content = "张三")
@TestRepeatable(content = "李四")
@TestRepeatable(content = "王五")
public class Student {
}
@Slf4j
public class StudentTest {
    @Test
    public void testRepeatable() {
        Annotation[] annotations = Student.class.getAnnotations();
        for (Annotation annotation : annotations) {
            // 输出注解的名称
            log.info(annotation.annotationType().getName());
            TestRepeatables testRepeatables = (TestRepeatables) annotation;
            // 输出注解的内容
            for (TestRepeatable a : testRepeatables.value()) {
                System.out.println(a.content());
            }
        }
    }

}

2.用于编译的注解

6个基本的注解如下,除了@Generated位于javax.annotation包,其它均定义在java.lang包下:

注解修饰的程序单元作用
@Deprecated全部表示某个程序元素(类、方法等)已过时,编译器会发出警告
@SuppressWarnings除了包和注解之外的所有情况取消显示指定的编译器警告
@SafeVarargs方法、构造器断言varargs参数可安全使用,抑制堆污染警告
@Override方法强制一个子类的方法必须是覆盖父类方法的方法
@FunctionalInterface接口指定某个接口必须是函数式接口
@Generated全部供代码生成工具来使用,任何生成的源代码都可以被注解,从而与程序员提供的代码区分开

3.用于管理资源的注解

注解修饰的程序单元作用
@PostConstruct @PreDestroy方法被标记的方法应该在构造之后/移除之前立即被调用
@Resource类、接口、方法、域在类/接口上:标记为在其他地方要用到的资源 在方法/域上:为“注入”而标记
@Resources类、接口一个资源数组

2.Spring常用注解

基本注解解释
@Component使用在类上,用于实例化Bean 代替xml的标签
@Controller@Component的衍生注解:使用在Web层上,用于实例化Bean
@Service@Component的衍生注解:使用在service层类上,用于实例化Bean
@Repository@Component的衍生注解:使用在dao层类上,用于实例化Bean
@Autowired通常使用在字段上,用于根据Bean的类型进行依赖注入
@Qualifier结合@Autowired一起使用用于同时根据类型和名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上,标注该方法是Bean的初始化方法
@PreDestroy使用在方法上,标注该方法是Bean的销毁方法
@Configuration用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定Spring在初始化容器时要扫描的包,作用和在Spring的xml配置文件中的<context:component-scan base-package="com.itheima"/>一样
@Bean使用在方法上,表示将该方法的返回值存储到Spring容器中,并可赋予指定名称
@PropertySource用于加载properties文件中的配置
@Import用于导入其他配置类
@AliasFor重新声明元注解中的属性

1.@Required

The @Required annotation and RequiredAnnotationBeanPostProcessor are formally deprecated as of Spring Framework 5.1.

/** @deprecated */
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Required {
}

The @Required annotation applies to bean property setter methods, as in the following example:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

This annotation indicates that the affected bean property must be populated at configuration time, through an explicit property value in a bean definition or through autowiring.

2.@Autowired

很多情况下,JSR 330提供的@Inject注解可以替换@Autowired的作用。

@Autowired根据类型注入Bean,对应类型的Bean需要是单例的。

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

注意,由@Autowired的定义可知,其作用范围为构造器、方法、形参、字段、注解,最常见的字段注入的方式只是其中之一而已。当然了,当用在方法上时,此注解肯定不可能用在静态方法上。

当在方法上使用@Autowired时,spring会在项目启动的过程中,自动调用一次加了@Autowired注解的方法,我们可以在该方法做一些初始化的工作

多个Bean类型相同

如果对应的类型有多个,@Autowired会失败,此时有三种解决方式

  • @Qualifier(合格者、修饰词)结合,在同类型的基础上通过名称再进行取舍;

  • 在被注入的Bean上加入优先级,如指定某一个Bean为@Primary

  • 另外,实际上可以将注入的字段改为集合类型:

    当集合为Map时,其key必须为String类型,含义为Bean的名字;其value为对应的Bean实例。

    @Service
    public class UserService {
    
        @Autowired
        private List<IUser> userList;
    
        @Autowired
        private Set<IUser> userSet;
    
        @Autowired
        private Map<String, IUser> userMap;
    
        public void test() {
            // userList:[User1@2513a118, User2@2bfb583b]
            System.out.println("userList:" + userList);
            // userSet:[User1@2513a118, User2@2bfb583b]
            System.out.println("userSet:" + userSet);
            // {user1=User1@2513a118, user2=User2@2bfb583b}
            System.out.println("userMap:" + userMap);
        }
    }
    
注入Bean集合

@Autowired注入的字段为集合类型时:

  • 集合不能为空,其中必须至少能注入一个Bean,否则Spring将抛异常。
  • 如果想控制集合中Bean出现的顺序,可以令相关的Bean实现Ordered接口,或通过@Order@Priority注解来定义相对的顺序。
  • 如果没有显式的顺序定义,其排列顺序将按照在IoC容器中注册的顺序。
关于required

对于普通方法:

  • 如果@Autowired修饰的是方法且@Autowired(required = false),当Spring发现存在某一个要注入的Bean不存在时,会导致该方法完全不会被执行。

不过,对于构造方法和工厂方法,规则略有不同,因为Spring的构造解析算法需要处理存在多个构造方法的场景:

  • 默认情况下,构造方法和工厂方法的参数都是必须存在的;
  • 但在只存在单个构造函数的场景中有一些特殊规则,例如,如果没有匹配的bean可用,则会将相应的“多元素注入点”(数组、集合、映射)解析为空对象。
public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}

As of Spring Framework 5.0, you can also use a @Nullable annotation:

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}
注入Spring框架相关Bean

You can also use @Autowired for interfaces that are well-known resolvable dependencies:

  • BeanFactory
  • ApplicationContext
  • Environment
  • ResourceLoader
  • ApplicationEventPublisher
  • MessageSource

These interfaces and their extended interfaces, such as ConfigurableApplicationContext or ResourcePatternResolver, are automatically resolved, with no special setup necessary.

self injection

Note that self injection is a fallback.

自Spring 4.3开始,@Autowired 也支持了“自注入(self injection)”,即注入自己。

Regular dependencies on other components always have precedence:

  • In that sense, self references do not participate in regular candidate selection and are therefore in particular never primary.
  • On the contrary, they always end up as lowest precedence.

实践中,应该将自注入作为一种最后的不得已的手段(for example, for calling other methods on the same instance through the bean’s transactional proxy),通常更建议将需要用到自注入的方法提取到另外一个委托Bean里。

Alternatively, you can use @Resource, which may obtain a proxy back to the current bean by its unique name.

通过@Bean方法来实现自注入也是一种有效的方法。但是,建议要么在实际需要的地方在方法签名中惰性地解析这些引用(与@Autowired正好相反),要么将受影响的@Bean方法声明为静态的,从而将它们与母体及其生命周期解耦。否则,此类Bean只会在fallback phase被考虑,而优先考虑其他的符合条件的Bean。

隐式的qualfier

除了直接使用 @Qualifier 注解来修饰Bean外,Spring也支持借助Java的泛型信息来作为一种隐式的修饰语,从而区别不同的Bean。

例如,假设有如下的配置类:

@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}

假定上述的Bean都实现了一个泛型接口,即Store<String>Store<Integer>,那么可以直接使用 @Autowire 来注入相应的泛型接口从而注入相应的Bean,即:

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore beancopyerrorcopiedcopyerrorcopied

该原则也适用于List, Map, array

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
失效原因排查

对象未被注入的可能原因:

  • 使用@Autowired的类不在IoC容器中(没有加@Componet, @Controller之类的注解)
  • 包未被spring扫描
  • @Autowiredrequired = false.
  • listenerfilter里面@Autowired某个bean:由于web应用启动的顺序是:listener -> filter -> servlet,而SpringMVC的启动是在DisptachServlet里面做的,执行在对应的bean还没有初始化,无法自动装配。此时可以通过其他途径来实现。
  • 循环依赖问题(在单例情况下多数没问题)

3.@Qualifier

package org.springframework.beans.factory.annotation;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
    String value() default "";
}

You can associate qualifier values with specific arguments, narrowing the set of type matches so that a specific bean is chosen for each argument.

In the simplest case, this can be a plain descriptive value, as shown in the following example:

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}copyerrorcopiedcopyerrorcopied

You can also specify the @Qualifier annotation on individual constructor arguments or method parameters:

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

注意,尽管Bean的id是唯一的,但@Qualfier也始终只是在@Autowired圈定的范围内进行挑选。

Good qualifier values are main or EMEA or persistent, expressing characteristics of a specific component that are independent from the bean id, which may be auto-generated in case of an anonymous bean definition.

@Qualfier的值不需要是唯一的,因此也可以据此来注入Bean集合。

当需要根据名称来注入Bean时,@Qualfier并不一定是必需的,因为默认情况下,当有多个候选项时(且没有其他限定条件,如优先级),Spring会根据字段或形参的名称来匹配同名的Bean。

另外,如果就是为了根据修饰性的名称来注入Bean,往往更推荐使用 JSR-250 提供的@Resource 注解,@Resource本意即是根据唯一的限定名称来注入Bean,不考虑Bean的类型。

4.@Resource

@Resource takes a name attribute. By default, Spring interprets that value as the bean name to be injected. In other words, it follows by-name semantics

@Autowired是Spring提供的注解,@Resource是Java(JSR-250)提供的注解。

  • @Autowired只包含一个参数:required,表示是否开启自动准入,默认是true;而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。
  • @Resource只能用在类、成员变量和方法上。
  • @Resource的搜索路径:
    • 未给定任何参数:首先根据名称匹配(即字段名或setter方法名),如果找不到再根据类型匹配
    • 仅指定了name:根据名称装配
    • 仅指定了type:根据类型装配
    • 同时指定了name和type:寻找名称和类型都匹配的Bean进行装配
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {

    String name() default "";

    String lookup() default "";

    Class<?> type() default java.lang.Object.class;

    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    boolean shareable() default true;

    String mappedName() default "";

    String description() default "";
    
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }
}

5.@Order, @Priority, @Primary

这三个注解总的来说都是用来做bean(注入时?)的排序。

  • @Order是Spring提供的注解,里面存储了一个代表顺序的值,默认为Integer.MAX_VALUE,值越小优先级越高。

    • @Order只控制Bean的注入顺序,不影响单例Bean的启动顺序,后者是由依赖关系和@DependsOn声明决定的。
    • @Order能控制 List 等有序集合里面存放的Bean的顺序,因为Spring的DefaultListableBeanFactory类会在注入时调用AnnotationAwareOrderComparator.sort(listA),该方法根据@Order或者Ordered接口返回的值排序。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    @Documented
    public @interface Order {
        int value() default 2147483647;
    }
    
  • @Priority是 JSR 250 标准,与@Order类似,同样接收一个表示顺序的值,值越小优先级越高。

    @Priority优先级比@Order更高,两者共存时优先加载@Priority

    @Target({TYPE,PARAMETER})
    @Retention(RUNTIME)
    @Documented
    public @interface Priority {
        /**
         * The priority value.
         */
        int value();
    }
    

    Note that the standard @Priority annotation is not available at the @Bean level, since it cannot be declared on methods. Its semantics can be modeled through @Order values in combination with @Primary on a single bean for each type.

  • @Primary同样是表达顺序的注解,它是Spring提供的注解,其表达的优先级最高的,如果同时有@Primary以及其他几个的话,@Primary注解的Bean会优先注入。

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Primary {
    }
    

6.@Value

定义
package org.springframework.beans.factory.annotation;
    
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
    String value();
}
释义

@Value is typically used to inject externalized properties:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

With the following configuration:

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

And the following application.properties file:

catalog.name=MovieCatalog

In that case, the catalog parameter and field will be equal to the MovieCatalog value.

Spring提供的内置转换器支持自动处理简单的类型转换(例如Integer或int),同时多个逗号分隔的值可以自动转换为String数组,而无需处理。

设置默认值

@Value也可以设置默认值,如下:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
        this.catalog = catalog;
    }
}
使用SpEL表达式

When @Value contains a SpEL expression the value will be dynamically computed at runtime as the following example shows:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
        this.catalog = catalog;
    }
}

7.@AliasFor

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AliasFor {
    @AliasFor("attribute")
    String value() default "";

    @AliasFor("value")
    String attribute() default "";

    Class<? extends Annotation> annotation() default Annotation.class;
}

@AliasFor用于声明注解属性的别名,有两种应用场景:

  • 同一个注解内的别名

    • @AliasFor注解的定义源码本身便是这样一个例子,其属性valueattribute互为别名
    • 也就是说,@AliasFor(value="xxx")@AliasFor(attribute=“xxx”)是等价的
    • 这种用法有几点注意事项:
      1. 别名属性必须声明相同的返回类型
      2. 别名属性必须声明默认值且默认值必须相同
  • “继承的”元注解中的属性的别名

    • 元注解

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.ANNOTATION_TYPE)
      public @interface BaseAnt {
          int value();
      
          String home() default "";
      }
      
    • 组合注解

      @BaseAnt(17)
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE)
      public @interface CrossAntAliasTester {
          @AliasFor(value = "home", annotation = BaseAnt.class) // 需声明对应的注解类型
          String address();
      }
      

@AliasFor注解在使用时,如果缺省annotation属性,表示其指向同一个注解中的属性;如果缺省value属性,则表示指向annotation字段关联的注解中的同名属性。

8.@Bean

释义

@Bean is a method-level annotation and a direct analog of the XML <bean/> element. You can use the @Bean annotation in a @Configuration-annotated or in a @Component-annotated class.

package org.springframework.context.annotation;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

如果没有通过@Bean的name或value属性来设置Bean的名称,默认情况下,@Bean方法定义的Bean的名称就是@Bean方法的名称。如下两者等价:

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}
<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

@Bean也可以修饰接口的默认方法,从而可以让多个配置类实现同一个接口使得代码得到复用:

public interface BaseConfig {

    @Bean
    default TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

@Configuration
public class AppConfig implements BaseConfig {

}

A @Bean-annotated method can have an arbitrary number of parameters that describe the dependencies required to build that bean.

@Bean定义Bean的配置信息

使用@Bean注解,在Spring的组件(Components)中也可以定义Bean的配置信息,就像在@Configureation配置类中所做的那样。例如:

@Component
public class FactoryMethodComponent {

    private static int i;
    
    public void doWork() {
        // Component method implementation omitted
    }

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }
}

@Bean标识了创建Bean的工厂方法及其他的Bean定义属性信息,例如@Qualifier, @Scope, @Lazy等。

InjectionPoint

自Spring 4.3以后,也可以在工厂方法中声明InjectionPoint类型的参数,从而可以获取该Bean在创建过程中的Inject Point.

  • Note that this applies only to the actual creation of bean instances, not to the injection of existing instances.

  • As a consequence, this feature makes most sense for beans of prototype scope.

    @Component
    public class FactoryMethodComponent {
    
        @Bean
        @Scope("prototype")
        public TestBean prototypeInstance(InjectionPoint injectionPoint) {
            return new TestBean("prototypeInstance for " + injectionPoint.getMember());
        }
    }
    
Lifecycle callback

@Bean方法定义的Bean支持正常的生命周期回调,这里不再过多赘述了。

示例:

public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

对于BeanOne,其等同于如下配置:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }

    // ...
}

值得一提的是,@Bean注解的destroyMethod属性提供了一个默认值"(inferred)",如果你不想在IoC容器shutdown的时候执行相应Bean的close或shutdown方法,需要设置destroyMethod=""

9.@DependsOn

此注解可以用来调整 Bean 的初始化顺序。比如如果一个BeanB依赖于BeanA的初始化,那么可以给BeanB加上注解@DependsOn("beanA"),让BeanA先于BeanB初始化。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {
    String[] value() default {};
}

10.@Conditional

@Conditional的作用是,在向spring容器中注册组件时,必须判断某个条件满足时才能注册。其定义为:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

如果一个@Configuration配置类被标识为@Conditional,意味着该配置类所关联的@Bean方法、@Import注解、@ComponentScan注解都会受限于该@Conditional的条件。

关于其中的Condition接口:

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotatedTypeMetadata;

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
  • Conditions are checked immediately before the bean-definition is due to be registered and are free to veto registration based on any criteria that can be determined at that point.
  • Conditions must follow the same restrictions as BeanFactoryPostProcessor and take care to never interact with bean instances. For more fine-grained control of conditions that interact with @Configuration beans consider implementing the ConfigurationCondition interface.

3.JSR-330 的注解

Starting with Spring 3.0, Spring offers support for JSR-330 standard annotations (Dependency Injection). To use them, you need to have the relevant jars in your classpath.

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>copyerrorcopiedcopyerrorcopied

When you work with standard annotations, you should know that some significant features are not available, as the following table shows:

Springjavax.inject.*javax.inject restrictions / comments
@Autowired@Inject@Inject has no ‘required’ attribute. Can be used with Java 8’s Optional instead.
@Component@Named / @ManagedBeanJSR-330 does not provide a composable model, only a way to identify named components.
@Scope(“singleton”)@SingletonThe JSR-330 default scope is like Spring’s prototype. However, in order to keep it consistent with Spring’s general defaults, a JSR-330 bean declared in the Spring container is a singleton by default. In order to use a scope other than singleton, you should use Spring’s @Scope annotation. javax.inject also provides a @Scope annotation. Nevertheless, this one is only intended to be used for creating your own annotations.
@Qualifier@Qualifier / @Namedjavax.inject.Qualifier is just a meta-annotation for building custom qualifiers. Concrete String qualifiers (like Spring’s @Qualifier with a value) can be associated through javax.inject.Named.
@Value-no equivalent
@Required-no equivalent
@Lazy-no equivalent
ObjectFactoryProviderjavax.inject.Provider is a direct alternative to Spring’s ObjectFactory, only with a shorter get() method name. It can also be used in combination with Spring’s @Autowired or with non-annotated constructors and setter methods.
2025-04-01 13:54:02.450 TRACE [org.springframework.web.method.HandlerMethod] - Arguments: [org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'deviceNo' not found. Available parameters are [startTime, device_no, endTime, param3, param1, param2]] org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'deviceNo' not found. Available parameters are [startTime, device_no, endTime, param3, param1, param2] at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:96) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441) at com.sun.proxy.$Proxy100.selectList(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224) at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForMany(MybatisMapperMethod.java:166) at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:77) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) at com.sun.proxy.$Proxy119.timePeriodTraffic(Unknown Source) at com.syzn.dv.biz.yunk.southbound.adapter.repo.impl.YunkStatisticDataRepoImpl.timePeriodTraffic(YunkStatisticDataRepoImpl.java:145) at com.syzn.dv.biz.yunk.manager.impl.YunkBigScreenManagerImpl.getTimePeriodTraffic(YunkBigScreenManagerImpl.java:120) at com.syzn.dv.biz.yunk.northbound.appservice.impl.YunkBigScreenAppServiceImpl.getTimePeriodTraffic(YunkBigScreenAppServiceImpl.java:59) at com.syzn.dv.biz.bigscreen.northbound.controller.BigScreenController.getTimePeriodTraffic(BigScreenController.java:51) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeM
最新发布
04-02
<think>好的,用户遇到了MyBatis的BindingException,提示找不到参数'deviceNo',而可用参数是startTime、device_no、endTime、param3等。我需要帮他分析原因并提供解决方案。 首先,根据引用中的例子,比如引用[2]和[3],这类错误通常是因为参数绑定问题。用户可能在Mapper接口的方法参数上没有正确使用@Param注解。例如,如果方法参数有多个,而没有用@Param指定名称,MyBatis可能会默认使用参数名或者arg0、arg1等,导致名称不匹配。 接下来,检查用户提供的错误信息。可用参数里有device_no,这可能是因为数据库列名是device_no,而用户代码中用了deviceNo作为参数名,导致不匹配。这时候需要确认是否在SQL语句中正确引用了参数名,比如#{deviceNo}是否应该改为#{device_no},或者在接口方法参数前添加@Param("device_no")。 另外,用户提到的可用参数中有startTime、endTime,这可能说明这两个参数正确使用了@Param注解,而deviceNo没有。因此,解决方案可能是在接口方法中为deviceNo参数添加@Param("deviceNo"),并确保SQL中的占位符名称一致。如果数据库列名是device_no,可能需要将占位符改为#{device_no},或者在参数注解中使用@Param("device_no")。 还要考虑MyBatis的版本问题,旧版本可能对参数名的处理不同,可能需要显式使用@Param。此外,参数名是否遵循驼峰命名和下划线的转换,检查是否有配置mapUnderscoreToCamelRadius,这可能会影响参数映射。 最后,根据引用[5],当参数是集合类型时也需要注解,但这里的情况可能不同。不过,确保所有参数都正确注解是解决此类问题的关键步骤。 总结可能的解决步骤:检查接口方法的@Param注解,确保SQL中的参数名与注解一致,检查数据库列名和参数名的匹配,考虑MyBatis配置的影响,并测试修改后的代码。</think>### MyBatis参数绑定异常解决方案 **问题分析** 出现`BindingException: Parameter 'deviceNo' not found`错误,表明MyBatis在接口方法与SQL语句之间无法正确映射参数名称。根据错误提示,可用参数为`[startTime, device_no, endTime, param3, param1, param2]`,而`deviceNo`未被识别,可能原因如下: 1. **缺少@Param注解** - 若Mapper接口方法**未显式标注`@Param("deviceNo")`**,MyBatis可能默认使用参数名(如`arg0`)或根据配置生成别名(如`param1`),导致名称不匹配[^2][^3]。 2. **参数名与占位符不匹配** - SQL语句中使用的占位符`#{deviceNo}`与Mapper方法参数名或注解名不一致,例如实际参数名是`device_no`(数据库字段命名风格),但占位符写成了`deviceNo`[^4]。 3. **MyBatis版本兼容性** - 旧版本MyBatis(如3.4.0之前)在未使用`@Param`注解时,无法直接通过参数名绑定,需依赖`arg0`或`param1`等默认别名[^5]。 --- **解决方案** **步骤1:检查Mapper接口方法** 确保方法参数**显式标注`@Param`注解**,且名称与SQL占位符完全一致: ```java // 错误示例:未标注@Param导致参数名丢失 List<Data> queryData(String deviceNo, Date startTime, Date endTime); // 正确示例:添加@Param注解 List<Data> queryData( @Param("deviceNo") String deviceNo, @Param("startTime") Date startTime, @Param("endTime") Date endTime ); ``` **步骤2:核对SQL语句占位符** 确认XML或注解中的SQL语句使用的参数名与`@Param`注解一致: ```sql -- 错误示例:占位符名称与@Param不匹配(如数据库字段为device_no) SELECT * FROM table WHERE device_no = #{deviceNo} -- 若数据库字段为device_no,需改为#{deviceNo}或调整@Param -- 正确示例:保持占位符与@Param名称一致 SELECT * FROM table WHERE device_no = #{deviceNo} AND time BETWEEN #{startTime} AND #{endTime} ``` **步骤3:检查MyBatis配置** 若项目启用了**驼峰命名转换**(`mapUnderscoreToCamelCase=true`),需确保参数名与数据库字段名对应: ```yaml # application.yml mybatis: configuration: map-underscore-to-camel-case: true # 开启后,device_no字段会自动映射到deviceNo参数 ``` **步骤4:更新MyBatis版本** 若使用旧版本MyBatis,建议升级到**3.4.0及以上**,该版本支持通过编译时参数名绑定(需添加`-parameters`编译选项)[^5]。 --- **验证与测试** 修改后执行以下检查: 1. 确认编译后的代码包含正确的参数名(可通过IDE反编译查看)。 2. 打印MyBatis最终执行的SQL语句,验证参数替换是否成功。 3. 单元测试覆盖多参数组合场景,确保无绑定异常。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值