Spring 注解

16 篇文章 0 订阅

在官方文档注解相关的文档开头,有一段

Are annotations better than XML for configuring Spring?

注解是否比配置spring的XML更好?

基本观点就是 注解 和 xml 各有优缺点,没有什么谁一定比谁好。对于开发者,根据具体的情况,个人爱好,选择合适的配置方式,spring框架都对他们有充分的支持,而且他们两个还可以混合起来用

注意:通过注解的方式注入,执行的比XML注入要早,所以如果两者都配置了,XML配置会覆盖注解的配置

注解的方式在spring中默认不打开,所以我们需要配置文件中打开

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
	
    <!-- 打开注解 -->
    <context:annotation-config/>
</beans>
序号注解 & 描述
1@Required 注解应用于 bean 属性的 setter 方法。
2@Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。
3@Qualifier通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。
4JSR Annotations Spring 支持 JSR 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。

@Required

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

@Required 注解 应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。

意思就是说,movieFinder 必须被初始化(这个方法必须被使用,无论是通过显示的配置还是自动装配的方式)

这个注解在 Framework 5.1开始正式弃用,支持使用构造函数注入所需的设置(或者自定义实现InitializingBean.afterPropertiesSet()以及bean属性setter方法)

@Autowired

这个注解极为强大,可以用于:构造,基本类型属性,引用类型属性,setter方法

  • 注释在构造方法上,指定spring容器创建对象时使用这个构造方法。

    注意,如果构造方法有多个,那么必须在其中的 一个上 注释 @Autowired ,告诉容器要创建哪一个对象,如果没有声明要使用哪个,那么spring会默认使用类中的第一个构造方法,从4.3开始如果构造方法只有一个,那么非必须注解@Autowired,它可以自己识别

  • 如果是在 基本属性类型上注解@Autowired,那么效果类似@Required,要求创建必须被beans.xml配置給值,否则会抛出异常。

    还可以单独设置属性,如果注解@Autowired(required=false),这代表这个属性是非必须的,在创建时会尝试给这个属性初始化,如果没有可用的函数,会忽略这个属性。

    默认的情况是@Autowired(required=true),要求必须初始化成功,否则抛出异常

  • 对应引用类型的属性,如果其注解了@Autowired,那么spring会自动的通过byType的方式来初始化该属性

    同样,对于@Autowired(required=false)的写法,表示这个属性非必须的

    由于注解了@Autowired的属性,使用率byType的自动装配方式,所以setXxx方法不需要写

  • @Autowired如果注解在setter方法上,spring它会在方法中试图执行 byType 自动连接。

    setter 方法可以由多个参数,他们通过类型来区分,如果有两个参数的类型一致,那么得到的对象都是同一个

    支持集合类型

接下来是一些示例代码

示例代码一

首先是一些固定不变的代码

在Beans.xml中配置bean,没有显式配置注入

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 打开注解 -->
	<context:annotation-config/>
    
	<bean id="book" class="com.bean.Book"></bean>
    
    <bean id="teacher" class="com.bean.Teacher"></bean>
    
</beans>

测试类

package com.tutorialspoint;

import com.bean.Book;

public class MainApp {
	public static void main(String[] arg) {
		
		ApplicationContext  context = new FileSystemXmlApplicationContext("src/Beans.xml");
		
		Book obj1 = (Book)context.getBean("book");
		
		System.out.println(obj1.toString());
		
	}
}

用到的Teacher类,每当创建一个 Teacher对象,就会输出一句话

package com.bean;
public class Teacher {
	private String name;
	public Teacher() {
		System.out.println("new Teacher Object");
	}
	public void setName(String name) {
		this.name = name;
	}
}

下面是要改变的代码部分,通过改变他们来 认识 到@Autowired 注解的作用

对于构造函数,下面的类中有两个,通过参数的类型来区别,里面什么注解都没有

  package com.bean;
  
  public class Book {
  
  	Teacher a;
  	
  	
  	public Book() {
  		System.out.println("new Book Object");
  	}
  	
  	
  	public Book(Teacher a) {
  		this.a = a;
  		System.out.println("new Book Object by 2");
  	}
  	
  	@Override
  	public String toString() {
  		return "Book [a=" + a + "]";
  	}
  	
  }
  
//运行测试类,输出结果如下
/*
new Book Object
new Teacher Object
Book [a=null]
*/

可以看到,容器创建了 book,teacher对象。

我们并没有 显式的配置将teacher对象注入到Book中,并且也没有注解,所以Book中的teacher属性引用为null

在这里,我们注意到,有两个Book的构造函数,spring使用了第一个构造方法

接下来,我们对代码进行修改

package com.bean;

import org.springframework.beans.factory.annotation.Autowired;

public class Book {
	
	Teacher a;
	
	
	public Book() {
		System.out.println("new Book Object");
	}
	
	@Autowired
	public Book(Teacher a) {
		this.a = a;
		System.out.println("new Book Object by 2");
	}
	
	@Override
	public String toString() {
		return "Book [a=" + a + "]";
	}
	
}
//运行测试类,输出结果如下
/*
new Teacher Object
new Book Object by 2
Book [a=com.bean.Teacher@28ac3dc3]
*/

将@Autowired加到 第二个构造上方,在这种情况下,我们告诉spring指定 了要使用这个构造方法来创建对象,调用 了 有参的构造给 Teacher对象赋值

那么,如果把@Autowired 加到第一个无参的构造方法上呢? 其结果 就是调用第一个 构造方法,和上边什么注解也没加的 输出一致

new Book Object
new Teacher Object
Book [a=null]```

原因在于:如果有多个构造函数,spring会使用第一个出现的构造

那么,两个构造都加@Autowired呢?

Error creating bean with name 'book': Invalid autowire-marked constructor: public com.bean.Book(com.bean.Teacher). Found constructor with 'required' Autowired annotation already: public com.bean.Book()

会出现错误已经指定了Book(),你不能再指定其他的了

对于构造函数的优先级,如果都没有注解@Autowired,那么使用第一个出现的,如果有注解,那么使用注解指定的构造方法,如果出现多个注解指定的,那么抛出异常,无法创建对象

示例代码二

配置bean

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 打开注解 -->
	<context:annotation-config/>
    
    <bean id="student" class="com.bean.Student">
    	<property name="id" value="001"></property>
    	<property name="name" value="张三"></property>
    </bean>
    
</beans>

测试类

package com.tutorialspoint;

import com.bean.Student;

public class MainApp {
	public static void main(String[] arg) {

		ApplicationContext context = new FileSystemXmlApplicationContext("src/Beans.xml");

		Student obj1 = (Student) context.getBean("student");

		System.out.println(obj1.toString());

	}
}

bean

package com.bean;

public class Student {
	
	private String id;
	
	private String name;

	
	public String toString() {
		return "Student [id=" + id + ", name=" + name + "]";
	}

	public void setId(String id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
//运行测试类,输出结果如下
/*
Student [id=001, name=张三]
*/

上面是一个一切完好的 基于xml 配置的 bean,可以看到,一切正常,得到了一个很好的bean对象student

接下来,我们进行改造

如果我们将Student里面的 setId()方法去掉会怎么样?运行一下试试?

Invalid property 'id' of bean class [com.bean.Student]: Bean property 'id' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
bean类[com.bean.Student]的属性'id'无效:Bean属性'id'不可写或具有无效的setter方法。 setter的参数类型是否与getter的返回类型匹配?

我们在bean中配置了属性,id 的值为 001,但是我却没有给它提供set方法,无法写入这个值,自然要有错误了

如果 我们改变一下setId() -> setId2()这样呢?

Invalid property 'id' of bean class [com.bean.Student]: Bean property 'id' is not writable or has an invalid setter method. Did you mean 'id2'?
bean类[com.bean.Student]的属性'id'无效:Bean属性'id'不可写或具有无效的setter方法。你是说'id2'吗?

报错信息与上边差不多,就是最后一句厉害了“Did you mean id2”,错误信息提示的非常明显

我们按照提示,把bean里的id 改为 id2 ,就可以正常运行了,或者把setId2改回去。

恢复代码原样,我们继续往下看

那,如果我们去掉Beans.xml中的<property name="id" value="001"></property>呢?

Student [id=null, name=张三]

运行正常,没有错误,结果 id的值为空(这是当然啦,我们那就没有设置嘛)

保持Beans.xml里的代码不变

去Student类里,给 private String id;上方加上注解 @Autowired

会发生什么呢?

No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
没有'java.lang.String'类型的限定bean可用:至少需要有1个bean可以作为autowire候选者。依赖注释:{@ org.springframework.beans.factory.annotation.Autowired(required = true)}

看,没有String类可用,String是java里的字符串类型,spring并不会给我们自动注入

如果我们手动加上这一行,在Beans.xml中

<bean id="string1" class="java.lang.String"></bean>

把String类加上去,我们运行,就会得到结果:Student [id=, name=张三],spring使用了无参的构造给了我们一个String对象。

如果id的类型是 int,double,这些基本类型呢?

无论你怎么做,它都不可以运行通过,你必须显式的给他们一个值

Integer、Double等等这些基本类型无法被初始
比如:你配置了 <bean id="int_1" class="java.lang.Integer"></bean>
会有如下错误
 Failed to instantiate [java.lang.Integer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: java.lang.Integer.<init>()

对于基本数据类型@Autowired,的作用就是,让他们在创建时必须被初始化(经由setter方法或者是构造方法)

当然我们也可以通过改变@Autowired默认值,使其变为:@Autowired(required=false)来解除这一限制

@Autowired(required=false)
private String id;
/*运行正常
Student [id=null, name=张三]
*/
示例代码三

这是一段运行正常的,基于xml配置的代码

bean

package com.bean;

import org.springframework.beans.factory.annotation.Autowired;

public class Student {
	Book book;
	
	Teacher teacher;

	public void setBook(Book book) {
		this.book = book;
	}

	public void setTeacher(Teacher teacher) {
		this.teacher = teacher;
	}

	@Override
	public String toString() {
		return "Student [book=" + book + ", teacher=" + teacher + "]";
	}
	
}

package com.bean;

public class Book {
	
	
	public Book() {
		System.out.println("new Book Object");
	}
	
}

package com.bean;

public class Teacher {
	
	public Teacher() {
		System.out.println("new Teacher Object");
	}
	
}

beans.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<bean id="student" class="com.bean.Student">

		<property name="teacher" ref="teacher"></property>

		<property name="book" ref="book"></property>

	</bean>

	<bean id="teacher" class="com.bean.Teacher"></bean>
	<bean id="book" class="com.bean.Book"></bean>

</beans>

测试类

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.bean.Student;

public class MainApp {
	public static void main(String[] arg) {

		ApplicationContext context = new FileSystemXmlApplicationContext("src/Beans.xml");

		Student obj1 = (Student) context.getBean("student");

		System.out.println(obj1.toString());

	}
}
/*运行结果
new Teacher Object
new Book Object
Student [book=com.bean.Book@757942a1, teacher=com.bean.Teacher@4a87761d]
*/

我们按照xml显式配置,正确的注入了Teacher、Book到Student中了

接下来,我们换种写法,使用基于注解的配置方式

首先是对Beans.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 打开注解 -->
	<context:annotation-config />

	<bean id="student" class="com.bean.Student"></bean>

	<bean id="teacher" class="com.bean.Teacher"></bean>
	<bean id="book" class="com.bean.Book"></bean>

</beans>

删除了student当中显示的配置,打开了注解的方式

其次是Student类

package com.bean;

import org.springframework.beans.factory.annotation.Autowired;

public class Student {
	
	@Autowired
	Book book;
	
	@Autowired
	Teacher teacher;

	@Override
	public String toString() {
		return "Student [book=" + book + ", teacher=" + teacher + "]";
	}
	
}

移除了setter方法,在book和teacher方添加了@Autowire的注解

然后我们运行,看

new Book Object
new Teacher Object
Student [book=com.bean.Book@4a87761d, teacher=com.bean.Teacher@66d1af89]

结果一模一样,@Autowired注解的作用就是这个

它通过byType的自动注入方式来执行代码,有了它你将不必写setter方法,和在bean里显示配置

注意要和在基本类型上方的情形做区分

示例代码四

对于示例三的代码,你在非xml显式配置的情况下,还可以这么写。

package com.bean;

import org.springframework.beans.factory.annotation.Autowired;

public class Student {
	
	Book book;
	
	Teacher teacher;
	
	@Autowired
	public void setAll(Book book,Teacher teacher,int a) {
		this.book = book;
		this.teacher = teacher;
		this.a = a;
	}

	@Override
	public String toString() {
		return "Student [book=" + book + ", teacher=" + teacher + "]" ;
	}
	
}

把注解放在setter方法上面,spring会尝试通过byType的方式来自动注入bean 的

集合也可以支持

下面看一个示例代码

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 打开注解 -->
	<context:annotation-config />

	<bean id="student" class="com.bean.Student">
	</bean>

	
	<bean id="book" class="com.bean.Book"></bean>
	<bean id="book2" class="com.bean.Book"></bean>
	<bean id="book3" class="com.bean.Book"></bean>

</beans>

bean

package com.bean;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

public class Student {
	
	List<Book> bookList;
	
	Set<Book> bookSet;
	
	Map<String,Book> bookMap;
	
	@Autowired
	public void setAll(List<Book> bookList,Set<Book> bookSet,Map<String,Book> bookMap) {
		this.bookList = bookList;
		this.bookSet = bookSet;
		this.bookMap = bookMap;
	}

	@Override
	public String toString() {
		return "Student [\nbookList=" + bookList + "\nbookSet=" + bookSet + "\nbookMap=" + bookMap + "]";
	}

	public Map<String, Book> getBookMap() {
		return bookMap;
	}

}
/*输出
new Book Object
new Book Object
new Book Object
Student [
bookList=[com.bean.Book@18bf3d14, com.bean.Book@4fb64261, com.bean.Book@42607a4f]
bookSet=[com.bean.Book@18bf3d14, com.bean.Book@4fb64261, com.bean.Book@42607a4f]
bookMap={book=com.bean.Book@18bf3d14, book2=com.bean.Book@4fb64261, book3=com.bean.Book@42607a4f}]
*/

对于map,会将 <bean id="book1" class="....."></bean>中的book1作为key,book对象作为值,来存入map中

@Qualifier

有没有想过,有好几个相同类型的bean,里面的内容不同,我们只需要其中一个合适的对象来注入(还懒,想通过自动注入完成),这该怎么办啊?

这个时候就可以用 @qualifier来完成哦!

像这样

package com.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Student {
	
	@Autowired
	@Qualifier("teacher1")
	Teacher teacher;

	@Override
	public String toString() {
		return "Student [teacher=" + teacher + "]";
	}
}

手动指定 ,我要 teacher1

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 打开注解 -->
	<context:annotation-config />

	<bean id="student" class="com.bean.Student">
	</bean>
	
	<bean id="teacher1" class="com.bean.Teacher"></bean>
	<bean id="teacher2" class="com.bean.Teacher"></bean>
	<bean id="teacher3" class="com.bean.Teacher"></bean>

</beans>

测试

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.bean.Student;

public class MainApp {
	public static void main(String[] arg) {

		ApplicationContext context = new FileSystemXmlApplicationContext("src/Beans.xml");

		Student obj1 = (Student) context.getBean("student");

		System.out.println(obj1.toString());


	}
}
/*
new Teacher Object
com.bean.Teacher@3e57cd70
new Teacher Object
com.bean.Teacher@5bb21b69
new Teacher Object
com.bean.Teacher@6b9651f3
Student [teacher=com.bean.Teacher@3e57cd70]
*/

可以看到第一个被创建出来的Teacher对象地址 是“@3e57cd70”,而注入到Student里的是“@3e57cd70”,是一样的哦。

这样就拿到了我们想要的了

JSR注解

我们简单的介绍一下

  • @PostConstruct 创建行
@PostConstruct
public void init(){
    System.out.println("Bean is going through init.");
}
  • @PreDestroy 销毁执行
@PreDestroy
public void destroy(){
   System.out.println("Bean will destroy now.");
}
  • @Resource 使用一个 ‘name’ 属性,该属性以一个 bean 名称的形式被注入。它遵循 by-name 自动连接语义
@Resource(name= "spellChecker")
public void setSpellChecker( SpellChecker spellChecker ){
   this.spellChecker = spellChecker;
}

如果没有明确地指定一个 ‘name’,默认名称源于字段名或者 setter 方法。在字段的情况下,它使用的是字段名;在一个 setter 方法情况下,它使用的是 bean 属性名称。

java注解

  • @Configuration 和 @Bean 注解
    带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。
@Configuration
public class HelloWorldConfig {
   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

等同于

<beans>
   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>

还可以指定生命周期回调方法

@Configuration
public class AppConfig {
   @Bean(initMethod = "init", destroyMethod = "cleanup" )
   public Foo foo() {
      return new Foo();
   }
}

指定范围

@Configuration
public class AppConfig {
   @Bean
   @Scope("prototype")
   public Foo foo() {
      return new Foo();
   }
}
  • @import 注解允许从另一个配置类中加载 @Bean 定义。
@Configuration
@Import(ConfigA.class)
public class ConfigB {
   @Bean
   public B a() {
      return new A(); 
   }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值