概述
一、组件注册
1、@Configuration搭配 @Bean 注解
将xml配置文件变成配置类,将bean标签变成 @Bean注解,容器对象从ClassPathXmlApplicationContext变成AnnotationConfigApplicationContext
那么现在使用注解驱动开发,就不需要xml配置了
对象的注册:
@Configuration //等价于 applicationContext.xml配置文件
public class MainConfig {
@Bean //等价于 <bean> 标签
public Student student(){
return new Student();
}
}
对象的创建:
public class Main {
public static void main(String[] args) {
//注解版的IOC容器是 AnnotationConfigApplicationContext,构造器中传入的是配置类
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
Student bean = ctx.getBean(Student.class);
System.out.println("bean = " + bean);
}
}
1、@Bean注解注册的bean默认的bean name是方法名,可以通过 @Bean注解的name属性给bean赋予新的name
2、@Bean 注解的 initMethod 属性可以实现对象的初始化操作
2、@ComponetScan 搭配 @Component 注解
在注解开发中,将使用 @ComponentScan 注解代替< context:component-scan> 标签
@ComponentScan(basePackages = "com.qiuguan.spring") //代替包扫描标签
@Configuration //等价于 applicationContext.xml配置文件
public class MainConfig {
@Bean //等价于 <bean> 标签
public Student student(){
return new Student();
}
}
那么在com.qiuguan.spring 包下的标注了@Component注解的Person类将会纳入到Spring的IOC容器中。
@CompoentScan 注解会扫描指定包路径下的 @Component, @Controller, @Service, @Repository 注解。
3、@ComponentScan 指定过滤规则
@ComponentScan(basePackages = "com.qiuguan.spring", //指定包扫描路径
//指定排除规则,不用去扫描
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = HelloService.class)
//如果想要"只"包含 includeFilters 的规则,需要 useDefaultFilters = false,禁用掉默认的规则
}, includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Person.class)
}, useDefaultFilters = false
)
@Configuration //等价于 applicationContext.xml配置文件
public class MainConfig {
//它不受@ComponentScan管理
@Bean //等价于 <bean> 标签
public Student student() {
return new Student();
}
}
过滤规则说明:
- FilterType#ANNOTATION: 按照注解过滤
- FilterType#ASSIGNABLE_TYPE: 按照指令的类型过滤
- FilterType#ASPECTJ: 按照 AspectJ 规则过滤
- FilterType#REGEX: 按照正则过滤
- FilterType#CUSTOM: 按照自定义规则过滤
上面的栗子中,先通过 excludeFilters 属性指定在扫描的时候按照什么规则排除那些组件,其中包括了要排除 @Controller注解,以及我自定义的接口HelloService(它的所有实现类都将会排除);
同时还指定了 includeFilters 属性,用来指定扫描的时候需要包含哪些组件,意思是扫描包路径下的 Person类,注意:如果想要"只扫描" Person类,那么需要将 useDefaultFilters = false,不然默认是true, 将使用默认的过滤规则,除了Person类以外,其他符合的(比如@Component注解标注了)也将放入容器中。
还可以自定义过滤规则,自定义过滤规则需要实现 TypeFilter接口
4、@Import 注解导入Bean
使用 @Import 注解可以给容器导入bean, bean name 默认是全类名。
@Import({ Water.class, Sun.class})
@Configuration
public class MainConfig2 {
}
5、@Conditional 完成按条件注册
6、FactoryBean 注册组件
二、生命周期
1、@Bean 注解指定初始化和销毁方法
public class Bird {
public Bird(){
System.out.println("周命周期: Bird 构造器");
}
public void init(){
System.out.println("生命周期:Bird init");
}
public void destroy(){
System.out.println("生命周期:Bird destroy");
}
}
//@ImportResource("classpath:applicationContext.xml")
@Configuration
public class MainConfig4 {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Bird bird(){
return new Bird();
}
}
public class Main4 {
public static void main(String[] args) {
//启动时会创建单实例bean, 进而会调用初始化方法
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig4.class);
//销毁时调用 destroy 方法
ctx.close();
}
}
输出结果:
周命周期: Bird 构造器
生命周期:Bird init
生命周期:Bird destroy
如果 Bird的作用域是@Scope(“prototype”), 那么容器启动时不会创建对象,只有当获取的时候才会创建对象并调用初始化方法,而且Spring不会管理@Scope(“prototype”)的bean, 所以容器关闭时,不会执行 destroy 方法。
2、使用 @PostConstruct 和 @PreDestroy注解完成初始化和销毁
public class Bird {
public Bird(){
System.out.println("周命周期: Bird 构造器");
}
/**
* 销毁注解
*/
@PreDestroy
public void destroy(){
System.out.println("生命周期:Bird @PreDestroy destroy");
}
/**
* 初始化注解
*/
@PostConstruct
public void init() {
System.out.println("生命周期:Bird @PostConstruct init");
}
}
//@ImportResource("classpath:applicationContext.xml")
@Configuration
public class MainConfig4 {
@Bean
public Bird bird(){
return new Bird();
}
}
public class Main4 {
public static void main(String[] args) {
//启动时会创建单实例bean, 进而会调用初始化方法
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig4.class);
//销毁时调用 destroy 方法
ctx.close();
}
}
输出结果:
周命周期: Bird 构造器
生命周期:Bird @PostConstruct init
生命周期:Bird @PreDestroy destroy
三、属性赋值
在注解驱动开发中,可以使用 @Value 注解完成属性的赋值。
teacher.workDate=2022-08-16
teacher.whatTeach=physical
public class Teacher {
//TODO:直接赋值,"false", "34"
@Value("张三")
private String name;
//TODO:可以写SPEL表达式
@Value("#{30 - 5}")
private int age;
@Value("#{T(Math).random()}")
private double salary;
//TODO: $ 表示从环境变量中取值
@Value("${teacher.workDate}")
private String workDate;
//TODO:如果环境变量中没有该配置,则给一个默认值
@Value("${teacher.whatTeach:english}")
private String whatTeach;
//TODO: toString()方法略,因为@Value是通过反射进行赋值的,客户忽略set方法
}
//TODO:将配置文件导入到环境变量中
@PropertySource("classpath:teacher.properties")
@Configuration
public class MainConfig5 {
@Bean
public Teacher teacher(){
return new Teacher();
}
}
四、依赖注入
1、@Autowired 按类型注入
@Autowired 默认是按照类型进行装配,如果类型有多个,则按照名字就行装配。完成一个bean 注入到另一个bean中
//定义一个接口
public interface OrderService {
}
//实现类1
@Service
public class OrderServiceImpl implements OrderService {
@Override
public String toString() {
String number = "1号";
return "OrderServiceImpl{" +
"number='" + number + ''' +
'}';
}
}
//实现类2
@Service
public class OrderServiceImpl2 implements OrderService {
@Override
public String toString() {
String number = "2号";
return "OrderServiceImpl2{" +
"number='" + number + ''' +
'}';
}
}
@Controller
public class HelloController {
@Autowired
private OrderService orderService;
public void test(){
System.out.println("orderService = " + orderService);
}
}
会发现报错:需要注入唯一的一个bean,但是却发现了2个匹配的
我们知道 @Autowired 注解默认是按照类型进行装配的,如果发现有多个,它可以继续按照名字去匹配,找到了就注入进来。
所以 第一种解决办法:就是修改属性名,使其与bean Nmae 匹配。
@Controller
public class HelloController {
/**
* 将 属性名orderService 改成 beanName, 也就是 orderServiceImpl 或者 orderServiceImpl2
*/
@Autowired
private OrderService orderServiceImpl;
public void test(){
System.out.println("orderService = " + orderServiceImpl);
}
}
第二种解决办法:搭配 @Qualifier注解
@Controller
public class HelloController {
//TODO:指定某个bean的名字
@Qualifier("orderServiceImpl2")
@Autowired
private OrderService orderService;
public void test(){
System.out.println("orderService = " + orderService);
}
}
第三种解决办法:搭配 @Primary 注解
在某个类上标注 @Primary注解,那么在有多个的时候,会优先注入标注了该注解的bean
//TODO:标注优先级高的注解
@Primary
@Service
public class OrderServiceImpl2 implements OrderService {
@Override
public String toString() {
String number = "2号";
return "OrderServiceImpl2{" +
"number='" + number + ''' +
'}';
}
}
注意:@Autowired 默认是必须找到某个bean进而完成注入,如果找不到,则将抛出异常,如果在找不到的情况下不抛出异常,只需要将 @Autowired 注解的 required=false 即可。
如何一次性将多个实现类全部注入进来?比如上面的 OrderService 有两个实现类,如何将这两个实现类同时注入进来?只需要用 Map, Collection, 数组 去接收就可以了。
演示:
@Controller
public class HelloController {
@Qualifier("orderServiceImpl2")
@Autowired
private OrderService orderService;
@Autowired
private Map<String, OrderService> orderServiceMap;
/**
* Collection的实现类也可以,比如 List, Set
*/
@Autowired
private Collection<OrderService> orderServiceCollection;
@Autowired
private OrderService[] orderServices;
public void test(){
System.out.println("orderService = " + orderService);
System.out.println("orderServiceMap = " + orderServiceMap);
System.out.println("orderServiceCollection = " + orderServiceCollection);
System.out.println("orderServices = " + Arrays.asList(orderServices));
}
}
第四种解决办法: 搭配 @Priority注解
2、@Resource 按名称注入
1、如果指定了name,type,则从Spring容器中找一个名称和类型相当应的一个bean,找不到则报错。
2、如果只指定了name,则从Spring容器中找一个名称和name一样的bean,找不到则报错。
3、如果只指定了type,则从Spring容器中找一个类型和type一样的bean,找不到或者找到多个则报错。
4、如果没有指定参数,则默认找字段名称装配,找不到则按类型装配,找不到则报错。
@Controller
public class HelloController {
@Resource
private OrderService orderService;
public void test() {
System.out.println("orderService = " + orderService);
}
}
我们知道,OrderService有两个实现类,beanName分别是 orderServiceImpl 和 orderServiceImpl2, 在上面的代码中,Spring 将按照类型找到2个实现类,但是由于没有与之匹配的beanName,所以无法完成匹配,进而抛出错误。
所以,上面代码中,改成这样就可以完成注入:
@Controller
public class HelloController {
//TODO:指定特定的beanName完成注入,或者直接需改属性名为orderServiceImpl2
@Resource(name = "orderServiceImpl2")
private OrderService orderService;
public void test() {
System.out.println("orderService = " + orderService);
}
}
同样的,@Resource 注解也可以完成多个bean的一次性注入:
@Controller
public class HelloController {
@Resource(name = "orderServiceImpl2")
private OrderService orderService;
@Resource
private Map<String, OrderService> orderServiceMap;
/**
* Collection的实现类也可以,比如 List, Set
*/
@Resource
private Collection<OrderService> orderServiceCollection;
@Resource
private OrderService[] orderServices;
public void test(){
System.out.println("orderService = " + orderService);
System.out.println("orderServiceMap = " + orderServiceMap);
System.out.println("orderServiceCollection = " + orderServiceCollection);
System.out.println("orderServices = " + Arrays.asList(orderServices));
}
}
3、@Inject 按照类型注入
它完全就可以当做 @Autowired 注解来使用。
使用时要导入依赖:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
说明:@Autowired 是Spring原生提供的注解,@Resource 和 @Inject 是Java规范提供的。
4、@Profile 按环境注册
@Profile 指定组件在哪个环境下才会被注册到容器中,如果不指定则所有环境都可以注册。
五、单例/多例模式问题
Spring的bean默认注入是单例的,它在Spring容器初始化的时候创建对象
多例模式在进行注入时,不能使用 @Autowired,否则注入的还是单例模式,实现多例模式需要使用工厂模式;
此外,@Autowired+@Qualifier(“student”) 与@Resource作用一致,当获取对象时,并不能稳定获 取到多例,在使用时需要注意。
最后
以上就是健忘煎蛋为你收集整理的Spring注解驱动开发一、组件注册二、生命周期三、属性赋值四、依赖注入五、单例/多例模式问题的全部内容,希望文章能够帮你解决Spring注解驱动开发一、组件注册二、生命周期三、属性赋值四、依赖注入五、单例/多例模式问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复