目录
一: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是类的根路径下的一个目录,获取系统类加载器,从根目录下进行扫描,返回一个URLURL 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.xsdhttp://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.xsdhttp://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 {@Testpublic 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.xsdhttp://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.xsdhttp://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 {@Testpublic 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.xsdhttp://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;@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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.xsdhttp://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 {@Overridepublic 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.xsdhttp://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 {@Testpublic 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方法上@Autowiredpublic 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;// 出现在构造方法上@Autowiredpublic 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 {@Overridepublic 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 {// 这里未指定名字,会把属性名作为名字@Resourceprivate 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 {@Overridepublic 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 {// 这里未指定名字,会把属性名作为名字@Resourceprivate 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 {@Testpublic void testNoXML(){// 创建AnnotationConfigApplicationContext对象ApplicationContext context = new AnnotationConfigApplicationContext(Spring6Configuration.class);UserService userService = context.getBean("userService", UserService.class);userService.insertDate();}
}
执行结果:全注解式开发也没问题