Spring家族-spring基础知识注解版

        现在基于spring开发都是使用注解方式,很少在去基于xml配置文件方式。到了springboot这种现象就更加突出了。估计对于大多数人来说(当然也包括我),有两个难点:

1)注解难点是注解太多了,根本记不住。这个问题其实很简单,那就是写博客,记笔记,用的查一下。

2)注解很多时候不如xml方式直观,比如说:spring事务的管理是基于AOP方式实现的,然而通过一个注解@Transactional就能实现,我们却不知道它的底层实现。这个问题并没有什么好的方式,只有查相关资料或者阅读源码了。

一、注解分类

        注解有这么几类:用于创建对象,用于注入属性的,用于配置的文件的,用于业务的比如事务,还有一些开关类注解。下面就从这几点罗列一下

二、创建bean的注解

注解名称

属性

备注

@Component

value - 指定bean id,如果没有指定value,则bean id默认值为类名字,且首字母是小写

1)只能用于类上

@Controller

同上

1)用于类上,代表表现层

2)@Component的派生注解

@Service

同上

1)用于类上,代表业务层

2)@Component的派生注解

@Repository

同上

1)用于类上,代表持久层

2)@Component的派生注解

@Configuration

value - 同@Component

proxyBeanMethods - 默认是true,代表是否生成代理对象,用于代理@Bean标注的对象

1)只能用于类上,也生成bean对象注入到spring容器中

2)@Component的派生注解

@Bean

name:代表bean id,默认为当前方法名字initMethod:定义bean的初始化方法

destroyMethod:定义bean的销毁方法

autowireCandidate:默认值是true,表示当前bean可用于属性注入

1)用于方法上,将返回值作为bean对象存入spring ioc容器

@Importvalue - 指定Class名

导入一个类,创建该bean对象注入到容器中

需要配合@Configuration @Component一起使用

@ImporResourcevalue - 指定配置文件名称导入一个spring的xml配置文件,

三、属性注入 

        上面是用于创建bean对象注解,接下来介绍属性注入(bean注入),属性分为基本类型,复杂类型(map,list,set)和自定义类类型。

注解名称

属性

备注

@Value

value - 表达式

支持SpEL表达是

@Value("${xxxx}")

@Value("#{xxxx}")

1)可注入,基本数据类型,数组,String类型,Date,map,list,set

2)通常需要配置文件properties文件,并且与@PropertySource一起使用

@PropertySourcevalue - 配置文件路径

1)加载配置文件,供@Value使用,例如:

@PropertySource("classpath:jdbcConfig.properties")

@Autowired

required - 默认值是true,如果找不到符合条件的bean则抛出异常

1)通过类型注入,可以用于构造函数,方法,类成员,方法参数

2)自动注册时,通过类型判断:

  1. 有且只有一个bean与要注入类型匹配,则自动注入成功。
  2. 如果通过类型匹配出多个bean对象,则会通过变量名称再次进行查找(匹配key),如果匹配成功则自动注入,否则失败。

注意:spring容器底层采用map保存bean对象。

@Qualifier

Value 用于指定bean id

1)通过名称进行注入

2)可用于类成员,方法,方法参数和类上

3)在修饰类成员变量时不能单独使用,必须与Autowired配合使用。

4)修饰方法形参的时候可单独使用

通过value指定bean id来解决Autowired场景下匹配出多个bean的问题

@Resource

Name用于指定bean id

1)先按照名称查找,如果没有找到则按照类型查找

2)这个注解是javax提供的,并非spring原生注解

对于@Value注解,这里有一篇专门介绍:《Spring常用注解-@Value @ConfigurationProperties

四、业务相关-AOP相关注解 

AOP有三元组:切面、切点、通知,所以需要有对应的注解,具体如下:

4.1、切面类

说明备注
@EnableAspectJAutoProxy表示启用AOP功能这个注解是必须的

@Aspect

修饰一个类,表示当前类是一个切面类,实际是代理类对象

1)用于类上

2)需要导入aspectjweaver包

4.2、切点

属性备注

@Pointcut

execution,切入点表达式,对待增强方法的描述

举例说明:

@Pointcut("execution(* com.example.service.AccountService.saveMoney(..))")

public void myPointCut(){ }

4.3、通知

通知有多种,各个通知的执行时机,可参考下面的例子

说明备注

@Before

前置通知

这些通知,需要一个切入点来支持有两种方式

1)直接定义execution表达式

例如:

@Before("execution(* com.example.service.AccountService.saveMoney(..))"

)

2)引用@Pointcut

@Before("myPointCut()")

@AfterReturning

后置通知

@AfterThrowing

异常通知

@After

最终通知

@Around

环绕通知

4.4、AOP完全注解例子

1)创建被代理类和代理类

package com.example.service;

import org.springframework.stereotype.Component;

/**
 * 通过@Componet注入一个bean
 *
 * 被代理类
 *
 */
@Component
public class AccountService {
    public int saveMoney(int money) {
        System.out.println("  >>>>   save money:"+money);
        return money;
    }
}
package com.example.proxy;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 通过@Component注入到容器
 * 通过@Aspect声明是一个切面,实际为代理类
 */
@Component
@Aspect
public class AccountServiceProxy {
    //前置通知
    @Before("execution(* com.example.service.AccountService.saveMoney(..))")
    public void beforeMethod() {
        System.out.println("2. advice before");
    }

    //后置通知
    @AfterReturning("execution(* com.example.service.AccountService.saveMoney(..))")
    public void afterReturning() {
        System.out.println("3. advice afterReturning");
    }

    //最终通知
    @After("execution(* com.example.service.AccountService.saveMoney(..))")
    public void afterMethod() {
        System.out.println("4. advice after");
    }

    //环绕通知  这个地方前后加了打印
    @Around("execution(* com.example.service.AccountService.saveMoney(..))")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("1. around advice before");
        Object o = proceedingJoinPoint.proceed();
        System.out.println("5. around advice after");
        return o;
    }

    //异常通知
    @AfterThrowing("execution(* com.example.service.AccountService.saveMoney(..))")
    public void afterThrowing() {
        System.out.println("3. advice afterThrowing...");
    }
}

2)创建Main类并且启动用代理

/**
 * 需要设置包扫描
 * 开启Aspecj动态代理
 */
@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AopApp {
    public static void main(String[] args) {
        ApplicationContext  context = new AnnotationConfigApplicationContext(AopApp.class);
        AccountService accountService = context.getBean("accountService", AccountService.class);
        accountService.saveMoney(10);
    }
}

输出结果(不同spring版本可能不一样,下面输出结果版本:5.3.12):

# 没有异常情况下:环绕通知前,前置通知,后置通知,最终通知,环绕通知后
1. around advice before
2. advice before
  >>>>   save money:10
3. advice afterReturning
4. advice after
5. around advice after

有异常的情况下,修改方法以及输出: 

@Component
public class AccountService {
    public int saveMoney(int money) {
        System.out.println("  >>>>   save money:"+money);
        int i = money / 0; //制造异常
        return money;
    }
}
# 有异常场景下,执行顺序:环绕通知前 前置通知 异常通知 最终通知
1. around advice before
2. advice before
  >>>>   save money:10
3. advice afterThrowing...
4. advice after
Exception in thread "main" java.lang.ArithmeticException: / by zero

通过测试结果可知:

1)环绕通知是最先执行的,接着是前置通知

2)无论是否有异常,最终通知一定会执行

3)至于后置通知和异常通知需要看具体情况 

五、其他注解

属性

说明

@ComponentScan

@ComponentScans

用于替换xml文件中context:component-scan标签

@Import

导入某个类,作为配置类使用

// Test 类   这里不需要任何注解(@Component、@Service)这些都不需要
public class Test {}


// MyConfig 类
@Configuration
@Import({Test.class})
public class AppConfig {}

当AppConfig类被处理的时候, 顺便就把Test类给注册成bean了。

@ContextConfiguration

locations:指定xml文件的位置,加上classpath关键字表示类路径下

classes:指定注解类所在位置

用于单元测试,spring和juint整合

@Ordervalue代表执行顺序值,数字越小优先级越高,默认是最小优先级

用于指定bean执行顺序

比如:两个代理类,A和B,代理同一个类C,那么就可以用该注解指定A、B的顺序

@Scope

属性value,用于指定bean的作用范围。具体取值为:singletion、prototype

用于写到类上面,多例模式下,对象回收靠jvm垃圾回收期

@PreDestroy

用于指定销毁方法,底层是通过BeanPostProcessors实现

具体是InitDestroyAnnotationBeanPostProcessor的before方法

@PostConstruct

用于指定初始化方法,底层是通过BeanPostProcessors实现

具体是InitDestroyAnnotationBeanPostProcessor的before方法

@Primary标识主bean

1)当一个类型在spring容器中存在多个bean对象,例如

@Bean

public User user1() { return new User()}

@Bean

public User user2() { return new User()}

2)@Autowired进行注入,例如:

@Autowired

private User  myUser;

spring运行就会报错,可以通过@Primary解决

解决方式:

@Bean

@Primary

public User user1() { return new User()}

当有冲突的时候用主Bean进行注入

六、举例说明

6.1、@Import

@Import 可以和接口ImportSelector或者ImportBeanDefinitionRegistrar接口一起作用,做一些定制化操作,这里import就是一个桥梁。

public class Teacher implements ImportBeanDefinitionRegistrar {
    private String name = "teacher is english";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(Student.class);
        registry.registerBeanDefinition("student123", beanDefinition);
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}

@Component
@Import(Teacher.class)
public class User {

}


注意通过Import注入了student,但是Teacher不会注入。 如果Teacher没有实现任何接口,就会注入Teacher

七、总结

Spring内容太多了,需要一点点积累,后续还要继续更新。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值