文章目录
- Spring注解方式管理bean(使用)
- 0x01_创建项目
- 0x02_xml注解方式创建对象
- 0x03_组件扫描配置注解识别
- 0x04_注解方式依赖注入DI
- 注解@Autowired和@Qualifier
- 总结@Autowired和@Qualifier
- 注解@Resource
- 用哪一个注解呢?
- @Autowired的源码
- @Resource的源码
- 用哪个?
- 注解@Value
- 注解@ComponentScan(了解)
- 注解@PropertySource
Spring注解方式管理bean(使用)
欢迎关注公众号“小东方不败”
0x01_创建项目
创建一个新的模块,类型选择maven
:
导入依赖:目前只需要spring-context
和junit
两个依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
准备实体类和applicationContext.xml
:
其中applicationContext.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>
里面有一些是之前学习时用到的约束说明,这里就不去掉了。如果想要创建“纯净”的spring的xml配置文件:
resources
目录—》new
—>XML Configuration File
—>Spring Config
0x02_xml注解方式创建对象
要用xml注解方式创建,必须满足:项目中要有aop
依赖,但是我们上面的项目只导入了Context
依赖,没关系嘛?
其实
aop
依赖已经导入了:
下面介绍重点:使用注解方式配置
@Component
放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中
该注解有三个子注解
-
@Controller
用于实例化controller层bean -
@Service
用于实例化service层bean -
@Repository
用于实例化持久层bean
当不确定是哪一层,就用@Component
这几个注解互相混用其实也可以,但是不推荐
package com.bones.bean;
import org.springframework.stereotype.Component;
@Component
//如果要指定id: @Component("user1")
//如果不指定id,那就是类名首字母小写
public class Person {
}
package com.bones.bean;
import org.springframework.stereotype.Component;
@Component
public class User {
}
在配置文件applicationContext.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- 如果有多个包需要扫描:方法1:指定扫描的包是上一层包com.bones
方法2:指定要扫描的包,用逗号隔开
比如:com.bones.bean,com.bones.service,com.bones.controller
-->
<context:component-scan base-package="com.bones">
</context:component-scan>
</beans>
测试方法:
package com.bones.test01;
import com.bones.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test1_Component {
@Test
public void TestComponent(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
}
}
成功获取到对象:
0x03_组件扫描配置注解识别
只扫描指定注解的包:
applicationContext.xml
<context:component-scan base-package="com.bones" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
注意:
use-default-filters
默认值是true
排除某一些注解扫描:
<context:component-scan base-package="com.bones" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
如果没法扫描,但是强制从容器中获取对象的话,会报下面的错:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'person' available
0x04_注解方式依赖注入DI
注解@Autowired和@Qualifier
准备代码:
UserDao
package com.bones.dao;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
int deleteUser();
}
UserDaoImplA
package com.bones.dao.impl;
import com.bones.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImplA implements UserDao {
@Override
public int deleteUser() {
System.out.println("UserDaoImplA");
return 0;
}
}
UserDaoImplB
package com.bones.dao.impl;
import com.bones.dao.UserDao;
import org.springframework.stereotype.Repository;
//@Repository
public class UserDaoImplB implements UserDao {
@Override
public int deleteUser() {
System.out.println("UserDaoImplB");
return 0;
}
}
UserService
package com.bones.service;
import org.springframework.stereotype.Service;
@Service
public interface UserService {
int deleteUser();
}
UserServiceImpl
package com.bones.service.impl;
import com.bones.dao.UserDao;
import com.bones.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public int deleteUser() {
userDao.deleteUser();
System.out.println("UserServiceImpl");
return 0;
}
}
applicationContext.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.bones" />
</beans>
测试方法:
package com.bones.test01;
import com.bones.bean.User;
import com.bones.service.UserService;
import com.bones.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2_Autowired {
@Test
public void TestComponent(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
userService.deleteUser();
}
}
但是注意:
如果UserDaoImplA
和UserDaoImplB
如果都加上@Repository
注解的话,就会报错:
No qualifying bean of type 'com.bones.dao.UserDao' available: expected single matching bean but found 2: userDaoImplA,userDaoImplB
这时候,需要指定到底UserServiceImpl
中的属性UserDao
到底是实例化哪一个实现类,需要加注解:
@Qualifier(value="userDaoImplA")
可以简写为:
@Qualifier("userDaoImplA")
总结@Autowired和@Qualifier
@Autowired
根据类型到容器中去寻找对应的对象,找到后给当前属性赋值
不需要依赖 set方法
属性类型可以是接口,会自动匹配对应的实现类对象
@Qualifier
根据属性名称注入依赖
注解@Resource
注意:这个注解是JDK的注解,属于
javax.annotation.Resource
包
在有冲突的时候,也需要加上具体实例化的类:
@Resource(name = "userDaoImplB")
不可以简写。
用哪一个注解呢?
前面体验了一下,实际强况下,很少会碰到这样的报错:
No qualifying bean of type 'com.bones.dao.UserDao' available: expected single matching bean but found 2: userDaoImplA,userDaoImplB
那么一般只需要写@Autowired
或者@Resource
,就可以了。那么到底写哪一个呢?
下面来详细说说:
@Autowired的源码
源码在org.springframework.beans.factory.annotation
包下
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
从源码中可以看出,是加在这些元素上:
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE
分别是:
构造方法
方法
方法的参数
成员变量
注解
总结:
1、@Autowired
是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor
类实现的依赖注入
2、@Autowired
可以作用在CONSTRUCTOR
、METHOD
、PARAMETER
、FIELD
、ANNOTATION_TYPE
3、@Autowired
默认是根据类型(byType
)进行自动装配的
4、如果有多个类型一样的Bean候选者,需要指定按照名称(byName
)进行装配,则需要配合@Qualifier
。
指定名称后,如果Spring IOC
容器中没有对应的组件bean抛出NoSuchBeanDefinitionException
。也可以将@Autowired
中required
配置为false
(其实一般配置为true也不影响运行,比如在Mybatis-plus
中),如果配置为false
之后,当没有找到相应bean的时候,系统不会抛异常
@Resource的源码
在javax.annotation
包下:
package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
String lookup() default "";
Class<?> type() default java.lang.Object.class;
enum AuthenticationType {
CONTAINER,
APPLICATION
}
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
boolean shareable() default true;
String mappedName() default "";
String description() default "";
}
源码中说明了使用的区域:
TYPE, FIELD, METHOD
分别是:
接口、类、枚举、注解
成员变量
方法
总结:
1、@Resource
是JSR250
规范的实现,在javax.annotation
包下
2、@Resource
可以作用TYPE
、FIELD
、METHOD
上
3、@Resource
是默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name
进行指定进行注入,name 的作用类似 @Qualifier
@Resource(name = "userDaoImplB")
用哪个?
1、@Autowired
是Spring自带的,@Resource
是JSR250规范实现的(JDK自带的)
2、@Autowired
如果需要按照名称匹配需要和@Qualifier
一起使用,@Resource
则通过name
进行指定
Spring官方建议用@Autowired
(啥原因都懂),其实这两个一般不会有很大区别,随便用吧,但是估计@Autowired
用的会多一点。
(更深入的以后再分析)
更深的可以参考链接:
注解@Value
@Value("爱因斯坦")
private String uname;
@Value("19")
private Integer uage;
如果想要从properties配置文件中读取,也可以:
@Value("${uname}")
private String uname;
@Value("${uage}")
private Integer uage;
其中配置文件内容:
uname=爱因斯坦
uage=18
同时在容器的配置文件中得读取properties配置文件:
<context:property-placeholder location="classpath:read.properties"/>
注解@ComponentScan(了解)
如果不希望有配置文件applicationContext.xml
(说实话,这么配置也怪麻烦的),可以创建一个config/SpringConfig
类,这个类专门用于扫描。
package com.bones.config;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = "com.bones")
public class SpringConfig {
}
测试:
package com.bones.test01;
import com.bones.config.SpringConfig;
import com.bones.service.UserService;
import com.bones.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2_Config_ComponentScan {
@Test
public void TestComponent(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
userService.deleteUser();
}
}
此时容器的创建,要调用的
AnnotationConfigApplicationContext
这个类,传入有扫描信息的类的字节码文件SpringConfig.class
。但是这种方式也有弊端,实际不怎么用,有一定局限性。
注解@PropertySource
想要在@ComponentScan
配置的基础上,读取properties文件的话,需要加注解:
@PropertySource("classpath:XXX.properties")