04. Spring注解详解

 

04 Spring注解详解


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 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 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 销毁前执行特定的操作,常用的设定方式有以下三种:

  1. 通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;

  2. 通过 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;

  3. 在指定方法上加上@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用法

  1. 直接或者间接标注在带有@Component注解的类上面;

  2. 直接或者间接标注在带有@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依赖

	org.springframework
	spring-aop
	5.2.9.RELEASE


	org.aspectj
	aspectjrt
	1.9.6


	org.aspectj
	aspectjweaver
	1.9.6


	cglib
	cglib
	2.1


// 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配置方式的
@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拦截及事务的处理,可能影响系统性能。

  • 常见错误:

    1. 接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。

    2. 接口中异常(运行时异常)被捕获而没有被抛出。 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可通过@TransactionalrollbackFor进行配置。

    3. 多线程下事务管理因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean。在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。一个使用了@Transactional的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务。

 

@Transactional实现原理:

  1. 事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令。 [不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]

  2. 事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象。

 

事务隔离级别:

  1. @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,不可重复读)基本不使用;

  2. @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读);

  3. @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读);

  4. @Transactional(isolation = Isolation.SERIALIZABLE):串行化;

 


参考学习资料和相关文章列表,请参照如下链接:

https://blog.csdn.net/moonlight821/article/details/116463513

你可能感兴趣的:(Spring框架专栏)