04. Spring注解详解
Posted IT BOY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了04. Spring注解详解相关的知识,希望对你有一定的参考价值。
04 Spring注解详解
目录
BeanDefinitionRegistryPostProcessor
Pt1 发展历史
(1) Spring Framework 1.x 注解驱动启蒙时代
从Spring Framework 1.2.0版本开始,开始支持Annotation,虽然框架层面均已经支持@managedResource和@Transactional等注解,但主要还是以XML配置为准。
(2) Spring Framework 2.x 注解驱动过渡时期
Spring Framework 2.0在Anonotation支持方面添加了新的成员,@Required、数据相关的@Repository及AOP相关的@Aspect等,但同时提升了XML配置能力,即“可扩展的XML编写”。
(3) Spring Framework 2.5 新的骨架式Anonotation
@Service @Controller,@RequestMapping以及@ModelAttribut @Resource注入 @PostConstruct替代 @PreDestory替代
尽管此时提供了不少的注解,但编程的手段却不多,主要原因在于框架层面仍未“直接”提供驱动注解,仍需要XML驱动。
(4) Spring Framework 3.x 注解驱动黄金时代
Spring Framework 3.x是一个里程牌式的时代,在3.0时代中除了提升Spring模式注解“派生”的层次性,并替换XML配置方式,引入配置类注解@Configuration,该注解是内建的@Component的“派生”注解。同时使用@ImportResource和@Import。@ImportResource负责导入遗留的XML配置文件,@Import允许导入一个或多个类作为SpringBean
3.1引入注解@ComponentScan,替换XML的context:component-scan,距离全面注解驱动更近一步
(5) Spring Framework 4.x 注解驱动完善时代
3.1开始提供@Profil提供了配置化的条件组装,但这一方面仍为比较单一,4.0之后,引入条件注解@Conditional。通过自定义Condition实现配合,弥补了之前版本条件化装配的短板,4.0开始Profile反过来通过@Conditional实现
4.2开始新增事件监听器注解@EventListener,作为ApplicationListener接口编程的第二选择。@AliasFor解除注解派生的时候冲突限制。
在浏览器跨域资源访问方面,引入@CrossOrigin,作为CorsRegistry替换注解方案。
(6) Spring Framework 5.x 注解驱动成熟时代
SpringFramework5.0作为Spring Boot2.0的底层,注解驱动的性能提升方面不是很明显。在Spring Boot应用场景中,大量使用@ComponentScan扫描,导致Spring模式的注解解析时间耗时增大,因此,5.0时代引入@Indexed,为Spring模式注解添加索引。
Pt2 常用注解介绍
Pt2.1 Configure Components
配置类组件。
@Configuration
把一个类作为一个IoC容器,类中某个方法上如果注册了@Bean,就会将该方法返回值作为这个Spring容器中的Bean实例。
@Configuration是在Spring容器启动时实例化Bean对象,而不是在使用到时才进行实例化。
// 1. 定义Bean对象
@Data
public class User {
private String userId;
private String username;
private String password;
@Override
public String toString() {
return "User{" +
"userId='" + userId + '\\'' +
", username='" + username + '\\'' +
", password='" + password + '\\'' +
'}';
}
}
// 2. 定义IoC容器,管理Bean对象
@Configurable
public class MyConfig {
@Bean(value = "user")
public User user() {
User user = new User();
user.setUserId("14255");
user.setUsername("lucas");
user.setPassword("123456");
return user;
}
}
// 3. 测试
public class MyConfigTest {
@Test
public void ConfigTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("Start Config Test.");
// 获取所有User的实例化对象
String[] beanNames = context.getBeanNamesForType(User.class);
System.out.println(Arrays.toString(beanNames));
// 尝试获取Bean,在此之前我们没有自己New过User
Object obj = context.getBean("user");
System.out.println(obj.toString());
// 获取所有User的实例化对象
beanNames = context.getBeanNamesForType(User.class);
System.out.println(Arrays.toString(beanNames));
}
}
// 4. 输出结果
14:17:19.796 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4157f54e
14:17:19.825 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
14:17:19.979 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
14:17:19.985 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:17:19.989 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
14:17:19.996 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
14:17:20.026 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
14:17:20.045 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user'
Start Config Test.
[user]
User{userId='14255', username='lucas', password='123456'}
[user]
从输出日志中可以看出,Spring上下文环境在加载时,就会实例化User对象,类型是singleton(默认类型)。
@ComponentScan
在配置类上添加@ComponentScan注解。该注解默认会扫描该类所在的包下所有的配置类,相当于context:component-scan。
@SpringBootApplication
@ComponentScan(value = "com.learn.javaperformance.spring")
public class JavaPerformanceApplication {
...
}
@Scope
@Scope注解是springIoc容器中的一个作用域,在 Spring IoC 容器中具有以下几种作用域:singleton(默认)、prototype,Web 作用域(reqeust、session、globalsession),自定义作用域。
-
singleton单例模式 -- 全局有且仅有一个实例
-
prototype原型模式 -- 每次获取Bean的时候会有一个新的实例
-
request -- request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
-
session -- session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
-
globalsession -- global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义
// 1. 要实例化的Bean定义
@Data
public class User {
private String userId;
private String username;
private String password;
// 我们在Bean中加上了一个随机函数的属性,这样多个实例化对象就会有差异
private int age = (int) (Math.random() * 100);
@Override
public String toString() {
return "User{" +
"userId='" + userId + '\\'' +
", username='" + username + '\\'' +
", password='" + password + '\\'' +
", age=" + age +
'}';
}
}
// 2. 定义单例对象
@Configurable
public class MyConfig {
@Scope(value = "singleton")
@Bean(value = "user")
public User user() {
User user = new User();
user.setUserId("14255");
user.setUsername("lucas");
user.setPassword("123456");
return user;
}
}
// 3. 测试代码
@Test
public void ScopeTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("Start Scope Test.");
// 第一次尝试获取Bean
Object obj1 = context.getBean(User.class);
System.out.println(obj1.toString());
// 第二次尝试获取Bean
Object obj2 = context.getBean(User.class);
System.out.println(obj2.toString());
System.out.println("obj1 == obj2 -> " + (obj1 == obj2));
// 获取所有User的实例化对象
String[] beanNames = context.getBeanNamesForType(User.class);
System.out.println(Arrays.toString(beanNames));
}
// 4. 单例模式下的输出
Start Scope Test.
User{userId='14255', username='lucas', password='123456', age=29}
User{userId='14255', username='lucas', password='123456', age=29}
obj1 == obj2 -> true
[user]
// 5. 将singleton改为prototype模式
@Scope(value = "prototype")
// 6. 原型模式下的输出结果
Start Scope Test.
User{userId='14255', username='lucas', password='123456', age=54}
User{userId='14255', username='lucas', password='123456', age=83}
obj1 == obj2 -> false
[user]
@Lazy
表示延迟初始化Bean对象。
// 1. 非Lazy场景
@Configurable
public class MyConfig {
@Scope(value = "singleton")
@Bean(value = "user")
//@Lazy
public User user() {
User user = new User();
user.setUserId("14255");
user.setUsername("lucas");
user.setPassword("123456");
return user;
}
}
// 2. 测试代码
@Test
public void LazyTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("Start Lazy Test.");
// 获取所有User的实例化对象
boolean hasUser = ((AnnotationConfigApplicationContext) context).getBeanFactory().containsBean("user");
// 尝试获取Bean
Object obj1 = context.getBean(User.class);
System.out.println(obj1.toString());
}
// 3. 非Lazy输出结果:可以看到在测试代码执行前,user对象已经被创建
15:35:34.708 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4157f54e
15:35:34.730 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:35:34.802 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:35:34.805 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:35:34.806 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:35:34.808 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:35:34.818 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
15:35:34.827 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user'
Start Lazy Test.
User{userId='14255', username='lucas', password='123456', age=36}
// 4. 加上Lazy标识
@Configurable
public class MyConfig {
@Scope(value = "singleton")
@Bean(value = "user")
@Lazy
public User user() {
User user = new User();
user.setUserId("14255");
user.setUsername("lucas");
user.setPassword("123456");
return user;
}
}
// 5. Lazy下的输出结果:可以看到测试代码执行时,User还没有实例化,只有在第一次使用时,才打印了实例化日志
15:36:50.856 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4157f54e
15:36:50.884 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:36:51.098 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:36:51.102 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:36:51.104 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:36:51.111 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:36:51.125 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
Start Lazy Test.
15:36:51.201 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user'
User{userId='14255', username='lucas', password='123456', age=33}
@Conditional
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
// 1. 定义Bean对象
@Data
public class Order {
private String orderId;
private double amt;
private String buyer;
private String goods;
}
// 2. 定义Condition条件类:需要实现Condition接口
public class OrderCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
int cond = (int) (Math.random() * 100);
System.out.println(cond);
return cond >= 50;
}
}
// 3. 定义Bean加载条件
@Configurable
public class MyConfig {
@Bean
@Conditional(OrderCondition.class)
public Order order() {
Order order = new Order();
order.setOrderId("12345678");
order.setBuyer("lucas");
order.setAmt(100);
order.setGoods("饭卡充值");
return order;
}
}
// 4. 测试类
@Test
public void ConditionTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("Start Condition Test.");
Map<String, Order> beans = context.getBeansOfType(Order.class);
System.out.println(beans);
}
// 5. 测试结果1
16:02:45.045 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4157f54e
16:02:45.080 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
3
16:02:45.251 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
16:02:45.254 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:02:45.259 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
16:02:45.262 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
16:02:45.283 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
Start Condition Test.
{}
// 6. 测试结果2
16:04:53.053 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4157f54e
16:04:53.074 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
86
16:04:53.163 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
16:04:53.166 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:04:53.168 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
16:04:53.170 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
16:04:53.178 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
16:04:53.184 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'order'
Start Condition Test.
{order=Order(orderId=12345678, amt=100.0, buyer=lucas, goods=饭卡充值)}
@import
@Import注解是用来导入配置类或者一些需要前置加载的类。
// 1. 定义Bean对象
@Data
public class Goods {
private String goodsName;
private double goodsPrice;
}
// 2. 引入对象
@Import({Goods.class})
@Configurable
public class MyConfig {
...
// 3. 测试代码
@Test
public void ImportTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("Start Import Test.");
Map<String, Goods> beans = context.getBeansOfType(Goods.class);
System.out.println(beans);
}
// 4. 输出结果
16:13:42.596 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.learn.javaperformance.spring.bean.Goods'
16:13:42.596 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'order'
Start Import Test.
{com.learn.javaperformance.spring.bean.Goods=Goods(goodsName=null, goodsPrice=0.0)}
@PostConstruct
实现初始化和销毁bean之前进行的操作,只能有一个方法可以用此注释进行注释,方法不能有参数,返回值必需是void,方法需要是非静态的。
-
@PostConstruct:在构造方法和init方法(如果有的话)之间得到调用,且只会执行一次。
-
@PreDestory:注解的方法在destory()方法调用后得到执行。
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:
-
通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
-
通过 <bean> 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
-
在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用
但他们之前并不等价。即使3个方法都用上了,也有先后顺序: Constructor > @PostConstruct > InitializingBean > init-method
@PostConstruct和@PreDestroy的使用示例如下。
// 1. 定义Bean对象,同时定义Bean初始化过程步骤
@Data
public class PayRequest implements InitializingBean {
private String requestDate;
private String orderId;
private String payId;
public PayRequest() {
System.out.println("do construct");
}
@Override
public String toString() {
return "PayRequest{" +
"requestDate='" + requestDate + '\\'' +
", orderId='" + orderId + '\\'' +
", payId='" + payId + '\\'' +
'}';
}
public void init() {
System.out.println("");
}
@PostConstruct
public void post() {
System.out.println("do postConstruct");
}
@PreDestroy
public void destroy() {
System.out.println("do preDestroy");
}
// init-method
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("do init");
}
}
// 2. 定义Bean加载
@Bean(value = "payRequest")
@Primary
public PayRequest payRequest() {
PayRequest payRequest = new PayRequest();
payRequest.setOrderId("192345");
payRequest.setPayId("lucas");
payRequest.setRequestDate("20210306");
return payRequest;
}
// 3. 使用Bean对象
@Component
public class LifeComponent {
@Autowired
@Qualifier("payRequest")
private PayRequest payRequest;
public void getOrder() {
System.out.println(payRequest.toString());
}
}
// 4. 测试脚本
@Test
public void life() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
PayRequest bean = (PayRequest) context.getBean(PayRequest.class);
System.out.println(bean.toString());
}
// 5. 测试结果
23:42:21.283 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest'
do construct
do postConstruct
do init
PayRequest{requestDate='20210306', orderId='192345', payId='lucas'}
@PreDestroy
参照@PostConstruct部分。
@DependsOn
@DependsOn是用来表示一个bean A的实例化依赖另一个bean B的实例化, 但是A并不需要持有一个B的对象,如果需要的话就不用depends-on,直接用依赖注入就可以了或者ref标签。
@DependsOn用法
-
直接或者间接标注在带有@Component注解的类上面;
-
直接或者间接标注在带有@Bean 注解的方法上面;
// 1. 定义Bean对象
@Data
public class Response {
private String respCode;
private String respDesc;
@Override
public String toString() {
return "Response{" +
"respCode='" + respCode + '\\'' +
", respDesc='" + respDesc + '\\'' +
'}';
}
}
// 2. 定义Bean加载,同时定义加载顺序
@Configurable
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation")
public class MyConfig {
@Bean(value = "response")
@DependsOn("payRequest2")
public Response response() {
System.out.println("Response is initialize...");
Response response = new Response();
response.setRespCode("000");
response.setRespDesc("Success");
return response;
}
@Bean(value = "payRequest2")
@Lazy
public PayRequest payRequest2() {
System.out.println("payRequest2 is initialize...");
PayRequest payRequest2 = new PayRequest();
payRequest2.setOrderId("214567");
payRequest2.setPayId("张无忌");
payRequest2.setRequestDate("20000306");
return payRequest2;
}
}
// 3. 测试脚本
@Test
public void dependsOn() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Response bean = (Response) context.getBean(Response.class);
System.out.println(bean.toString());
}
// 4. 测试结果
07:50:21.123 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest2'
payRequest2 is initialize...
07:50:21.168 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'response'
Response is initialize...
Response{respCode='000', respDesc='Success'}
Pt2.2 Injection Components
@Component
泛指组件,当组件不好归类的时候,使用Component进行标注。太简单就不举例了。
@Service
用于标注业务层组件,太简单就不举例了。
@Controller
用于标注控制层组件,太简单就不举例了。
@Repository
用于标注数据访问组件,即DAO组件。
@Repository
public class MyDao {
private String flag = "1";
public void setFlag(String flag) {
this.flag = flag;
}
@Override
public String toString() {
return "MyDao{" +
"flag='" + flag + '\\'' +
'}';
}
}
@Value
此注解使用在字段、构造器参数和方法参数上。@Value可以指定属性取值的表达式,支持通过#{}使用SpringEL来取值,也支持使用${}来将属性来源中(Properties文件、本地环境变量、系统属性等)的值注入到bean的属性中。此注解的注入时发生在AutowiredAnnotationBeanPostProcessor中。
具体用法实例参照@PropertySource章节。
@Autowired
Autowired默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。可以手动指定按byName方式注入,使用@Qualifier。
// 1. 定义Bean对象
@Data
public class PayRequest {
private String requestDate;
private String orderId;
private String payId;
@Override
public String toString() {
return "PayRequest{" +
"requestDate='" + requestDate + '\\'' +
", orderId='" + orderId + '\\'' +
", payId='" + payId + '\\'' +
'}';
}
}
// 2. 加载Spring环境
// 定义了同一个Bean的两个实例化对象
@Bean(value = "payRequest")
public PayRequest payRequest() {
PayRequest payRequest = new PayRequest();
payRequest.setOrderId("192345");
payRequest.setPayId("lucas");
payRequest.setRequestDate("20210306");
return payRequest;
}
@Bean(value = "payRequest2")
public PayRequest payRequest2() {
PayRequest payRequest2 = new PayRequest();
payRequest2.setOrderId("214567");
payRequest2.setPayId("张无忌");
payRequest2.setRequestDate("20000306");
return payRequest2;
}
// 3. 定义引用组件
@Component
public class QualifierComponent {
@Autowired
@Qualifier("payRequest2")
private PayRequest payRequest;
public void getOrder() {
System.out.println(payRequest.toString());
}
}
// 4. 测试脚本
@Test
public void quelifier() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
QualifierComponent resource = (QualifierComponent) context.getBean(QualifierComponent.class);
resource.getOrder();
}
// 5. 输出结果
15:48:04.208 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
15:48:04.230 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest'
15:48:04.232 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'qualifierComponent'
15:48:04.248 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest2'
PayRequest{requestDate='20000306', orderId='214567', payId='张无忌'}
@PropertySource
@PropertySouce是spring3.1开始引入的基于java config的注解。
通过@PropertySource注解将properties配置文件中的值存储到Spring的 Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。
// 1. 定义Bean对象
@Data
public class Person {
private String address;
private String distance;
@Override
public String toString() {
return "Person{" +
"address='" + address + '\\'' +
", distance='" + distance + '\\'' +
'}';
}
}
// 2. 定义Property配置文件
config.properties
address=Shanghai, Fengxian Area, BaoLiXiangYu.
distance=31KM
// 3. 加载Spring和Property文件
@Configuration
@PropertySource(value = {"classpath:config.properties"})
@Data
public class PropertyConfig {
// 要想使用@Value 用${}占位符注入属性,这个bean是必须的,这个就是占位bean,另一种方式是不用value直接用Envirment变量直接getProperty('key')
@Autowired
private Environment env;
private String address;
private String distance;
@PostConstruct
public void config() {
System.out.println("initialize...");
this.address = env.getProperty("address");
this.distance = env.getProperty("distance");
}
@Bean(name = "person")
public Person person() {
Person person = new Person();
person.setAddress(this.address);
person.setDistance(this.distance);
return person;
}
}
@Configurable
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation")
public class MyConfig {
...
}
// 4. 测试代码
@Test
public void property() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, PropertyConfig.class);
Person bean = (Person) context.getBean("person");
System.out.println(bean.toString());;
}
// 5. 测试结果
23:04:11.123 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'person'
Person{address='Shanghai, Fengxian Area, BaoLiXiangYu.', distance='31KM'
// 6. 通过@Value方式加载Properties
// 将ENV的方式改为@Value来获取Properties中数据
@Configuration
@PropertySource(value = {"classpath:config.properties"})
@Data
public class PropertyConfig2 {
@Value("${address}")
private String address;
@Value("${distance}")
private String distance;
@Bean(name = "person")
public Person person() {
Person person = new Person();
person.setAddress(this.address);
person.setDistance(this.distance);
return person;
}
}
// 7. 相同的代码测试结果
23:10:55.292 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'person'
23:10:55.333 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'order'
Person{address='Shanghai, Fengxian Area, BaoLiXiangYu.', distance='31KM'}
@Qualifier
参照@Autowired部分。
@Primary
自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常。
// 1. 定义Bean对象
@Data
public class PayRequest {
private String requestDate;
private String orderId;
private String payId;
@Override
public String toString() {
return "PayRequest{" +
"requestDate='" + requestDate + '\\'' +
", orderId='" + orderId + '\\'' +
", payId='" + payId + '\\'' +
'}';
}
}
// 2. 定义两个相同类型不同名称的Bean实例对象,并使用@Primary标识优先级
@Configurable
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation")
public class MyConfig {
@Bean(value = "payRequest")
@Primary
public PayRequest payRequest() {
PayRequest payRequest = new PayRequest();
payRequest.setOrderId("192345");
payRequest.setPayId("lucas");
payRequest.setRequestDate("20210306");
return payRequest;
}
@Bean(value = "payRequest2")
public PayRequest payRequest2() {
PayRequest payRequest2 = new PayRequest();
payRequest2.setOrderId("214567");
payRequest2.setPayId("张无忌");
payRequest2.setRequestDate("20000306");
return payRequest2;
}
}
// 3. 引用多名称对象
@SuppressWarnings("ALL")
@Component
public class PrimaryComponent {
@Autowired
private PayRequest payRequest;
public void getOrder() {
System.out.println(payRequest.toString());
}
}
// 4. 测试脚本
@Test
public void primary() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
PayRequest bean = (PayRequest) context.getBean(PayRequest.class);
System.out.println(bean.toString());
}
// 5. 测试结果
23:26:44.405 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest'
23:26:44.407 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'primaryComponent'
23:26:44.417 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest2'
23:26:44.418 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'person'
PayRequest{requestDate='20210306', orderId='192345', payId='lucas'}
@Resource
默认按 byName自动注入,如果找不到再按byType找bean,如果还是找不到则抛异常,无论按byName还是byType如果找到多个,则抛异常。 可以手动指定bean,它有2个属性分别是name和type,使用name属性,则使用byName的自动注入,而使用type属性时则使用byType自动注入。
// 1. 定义Bean对象
@Data
public class PayRequest {
private String requestDate;
private String orderId;
private String payId;
@Override
public String toString() {
return "PayRequest{" +
"requestDate='" + requestDate + '\\'' +
", orderId='" + orderId + '\\'' +
", payId='" + payId + '\\'' +
'}';
}
}
// 加载Spring
@Configurable
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation")
public class MyConfig {
@Bean(value = "payRequest")
public PayRequest payRequest() {
PayRequest payRequest = new PayRequest();
payRequest.setOrderId("192345");
payRequest.setPayId("lucas");
payRequest.setRequestDate("20210306");
return payRequest;
}
}
// 3. 使用@Resource进行诸如
@Component
public class InjectionComponent {
@Resource(name = "payRequest")
private PayRequest payRequest;
public void getOrder() {
System.out.println(payRequest.toString());
}
}
// 4. 测试脚本
@Test
public void resource() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
InjectionComponent resource = (InjectionComponent) context.getBean(InjectionComponent.class);
resource.getOrder();
}
// 5. 输出结果
15:40:35.002 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'injectionComponent'
15:40:35.030 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest'
PayRequest{requestDate='20210306', orderId='192345', payId='lucas'}
Pt2.3 Weave Components
ApplicationContextAware
可以自定义类,实现ApplicationContextAware接口,重写方法SetApplicationContext,通过这个方法,可以获取到ApplicationContext上下对象,根据上下文对象可以轻松获取到容器中注册的bean。
// 1. 定义Bean对象@Data
public class PayRequest implements InitializingBean {
private String requestDate;
private String orderId;
private String payId;
@Override
public String toString() {
return "PayRequest{" +
"requestDate='" + requestDate + '\\'' +
", orderId='" + orderId + '\\'' +
", payId='" + payId + '\\'' +
'}';
}
}
// 2. 加载Spring Bean
@Configurable
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation")
public class MyConfig {
@Bean(value = "payRequest2")
@Lazy
public PayRequest payRequest2() {
System.out.println("payRequest2 is initialize...");
PayRequest payRequest2 = new PayRequest();
payRequest2.setOrderId("214567");
payRequest2.setPayId("张无忌");
payRequest2.setRequestDate("20000306");
return payRequest2;
}
}
// 3. 自定义Context
@Component
public class MyApplicationContext implements ApplicationContextAware {
private ApplicationContext context = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public ApplicationContext getContext() {
return this.context;
}
}
// 4. 测试脚本
public class WeaveTest {
@Test
public void context() {
// 启动Spring Context
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 获取自定义Context
MyApplicationContext myContext = context.getBean(MyApplicationContext.class);
// 获取Bean
Object bean = myContext.getContext().getBean("payRequest2");
System.out.println(bean.toString());
}
}
// 5. 测试结果
08:22:33.164 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest2'
08:22:33.165 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myApplicationContext'
PayRequest{requestDate='20000306', orderId='214567', payId='张无忌'}
BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor实现了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinitionRegistry的后处理器,用来注册额外的Bean。
// 1. 定义Bean对象
@Data
public class PayRequest implements InitializingBean {
private String requestDate;
private String orderId;
private String payId;
@Override
public String toString() {
return "PayRequest{" +
"requestDate='" + requestDate + '\\'' +
", orderId='" + orderId + '\\'' +
", payId='" + payId + '\\'' +
'}';
}
}
// 2. 自定义类加载逻辑
package com.learn.javaperformance.spring.annotation.config;
import com.learn.javaperformance.spring.annotation.OrderCondition;
import com.learn.javaperformance.spring.annotation.bean.;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.;
@Configurable
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation")
public class MyConfig {
@Bean
public BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {
BeanDefinitionRegistryPostProcessor processor = new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
BeanDefinition definition = configurableListableBeanFactory.getBeanDefinition("payRequest3");
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PayRequest.class);
builder.addPropertyValue("orderId", "111111");
builder.addPropertyValue("payId", "两会");
builder.addPropertyValue("requestDate", "20210307");
beanDefinitionRegistry.registerBeanDefinition("payRequest3", builder.getBeanDefinition());
}
};
return processor;
}
}
// 3. 测试脚本
@Test
public void definition() {
// 启动Spring Context
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 获取Bean
Object bean = context.getBean("payRequest3");
System.out.println(bean.toString());
}
// 4. 测试结果
09:06:45.521 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'payRequest3'
PayRequest{requestDate='20210307', orderId='111111', payId='两会'}
Pt2.4 Aspect Components
@Aspect
Spring AOP涉及几个关键注解:
-
@EnableAspectJAutoProxy:此配置用于开启基于注解的AOP模式;
-
@Aspect:定义切面类。开启AOP模式后,将会扫描切面切面类中定义的AOP逻辑进行处理;
-
@PointCut:公共切点表达式。定义了切面逻辑要应用到的类标识,指定切入点表达式,拦截那些方法,即为那些类生成代理对象;
其中,在切面中,提供了几个注解用于处理AOP的逻辑:
-
前置通知(@Before)
-
后置通知(@After)
-
返回通知 (@AfterReturning)
-
异常通知 (@AfterThrowing)
-
环绕通知 (@Around)
// 1. AOP依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1</version>
</dependency>
// 2. 定义Spring加载配置
@Configurable
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.learn.javaperformance.spring.annotation.aop")
public class AopConfig {
}
// 3. 定义Service操作类
@Service
public class AopService {
public void cook() {
System.out.println("我正在做饭.");
}
}
// 4. 定义切面Aspect
@Component
@Aspect
public class AopAspect {
@Pointcut("execution(* com.learn.javaperformance.spring.annotation.aop.AopService.cook())")
public void pointcut() {
}
@Before("pointcut()")
public void befort() {
System.out.println("我要想去菜场买菜。");
}
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("可以吃饭了。");
}
@After("pointcut()")
public void after() {
System.out.println("饭吃完开始洗碗了。");
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("突然停电了。");
}
//@Around("pointcut()")
public void around() {
System.out.println("我好忙啊");
}
}
// 5. 定义测试脚本
public class AopTest {
@Test
public void aop() {
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
AopService aop = context.getBean(AopService.class);
aop.cook();
}
}
// 6. 测试结果
14:23:57.385 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.learn.javaperformance.spring.annotation.aop.AopAspect.afterThrowing()
14:23:57.658 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'aopAspect'
14:23:57.658 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'aopService'
我要想去菜场买菜。
我正在做饭.
可以吃饭了。
饭吃完开始洗碗了。
@EnableTransactionManagement
SpringBoot使用事务非常简单,首先使用注解@EnableTransactionManagement开启事务支持后,然后在访问数据库的Service方法上添加注解@Transactional便可。
关于事务管理器,不管是JPA还是JDBC等都实现自接口PlatformTransactionManager如果你添加的是spring-boot-starter-jdbc依赖,框架会默认注入DataSourceTransactionManager实例。如果你添加的是spring-boot-starter-data-jpa依赖,框架会默认注入JpaTransactionManager实例。
你可以在启动类中添加如下方法,Debug测试,就能知道自动注入的是PlatformTransactionManager接口的哪个实现类。
@EnableTransactionManagement //启注解事务管理,等同于xml配置方式的<tx:annotation-driven/>
@SpringBootApplication
publicclassProfiledemoApplication{
@Bean
publicObjecttestBean(PlatformTransactionManagerplatformTransactionManager){
System.out.println(">>>>>>>>>>"+platformTransactionManager.getClass().getName());
returnnewObject();
}
publicstaticvoidmain(String[]args){
SpringApplication.run(ProfiledemoApplication.class,args);
}
}
@Transactional
@Transactional 是声明式事务管理编程中使用的注解。它的本质是使用了 JDBC 的事务来进行事务控制的,技术上依赖于 Spring AOP的动态代理的机制。
配置说明:
-
接口实现类或接口实现方法上,而不是接口类中。
-
访问权限:public 的方法才起作用。@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。 系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管理,由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能。
-
常见错误:
-
接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。
-
接口中异常(运行时异常)被捕获而没有被抛出。 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可通过@TransactionalrollbackFor进行配置。
-
多线程下事务管理因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean。在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。一个使用了@Transactional的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务。
-
@Transactional实现原理:
-
事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令。 [不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
-
事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象。
事务隔离级别:
-
@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,不可重复读)基本不使用;
-
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读);
-
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读);
-
@Transactional(isolation = Isolation.SERIALIZABLE):串行化;
参考资料
参考学习资料和相关文章列表,请参照如下链接:
https://blog.csdn.net/moonlight821/article/details/116463513
以上是关于04. Spring注解详解的主要内容,如果未能解决你的问题,请参考以下文章
spring注解注入:<context:component-scan>详解(转)
Java开发Spring之IOC详解第二篇(注解开发JdbcTemplatem模板Junit整合)