Spring注解整理

1.@Configuration

使用这个注解来告诉Spring这是一个配置类,来替代原来的

<bean id="xxx" class="xxxx">
    <property name ="xx" value = "xx"></property>
    <property name ="xx1" value = "xx1"></property>
</bean>

2.@Bean

使用这个注解将一个对象实例注册进IOC容器中作为一个bean,默认的bean id为访问名,也可以显示指定id以及init、destroy方法,如@Bean(name = "sunwukong",initMethod = "init",destroyMethod = "destroy")
除了可以在注解中指定init、destroy方法外,还可以通过实现InitializingBean与DisposableBean来实现bean的初始化与销毁逻辑。

3.@ComponentScan

在原来的Spring xml配置文件中需要配置

<context:component-scan base-package="com.xxx"></context>

来表示将这个包下的带有@Controller、@Service、@Repository、@Component的类都注册到IOC容器里面。
现在可以在@Configuration标注的配置类(相当于原xml配置文件)上标注@ComponentScan(value = "com.xxx")
里面可以添加excludeFilters = {} 、includeFilters = {}等用于过滤,@ComponentScans(value= {@ComponentScan})可以写多个。

4.@Filter

在上面说到的excludeFilters、includeFilters数组中可以添加@Filter注解的过滤器。如

excludeFilters = {
    @Filter(type=FilterType.Annotation,classes={Controller.class,Service.class},useDefaultFilters=false)
}
includeFilters = {
    @Filter(type=FilterType.Annotation,classes={Controller.class,Service.class})
}

5.@Scope

@Bean将实例注入IOC时,可以通过@Scope设置4种模式。
singleton(单例)、prototype(原型)、request(同一个请求一个实例)、session(同一个会话一个实例)。默认是单例,对象为启动时创建,为饿汉式。若设置为prototype则对象为每次获取时创建,为懒汉式。

6.@Lazy

单实例bean可以标注此注解,用于将默认的饿汉式变为懒加载即第一次获取对象时创建。

7.@Conditional

这个注解根据条件来注册bean,可以放在方法上或者类上(满足条件类中的所有都会生效),下面是例子:

// 配置类
@Configuration
public class MainConfig {
    @Bean("sunwukong")
    @Conditional({LinuxCondition.class})
    public Person persion01(){
        return Person.builder().name("孙悟空").age(18).build();
    }

    @Bean("zhubajie")
    @Conditional({WindowsCondition.class})
    public Person persion02(){
        return Person.builder().name("猪八戒").age(17).build();
    }
}
// 自定义condition条件
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment env = ctx.getEnvironment();
        String osName = env.getProperty("os.name");
        return osName.contains("Windows");
    }
}
// 自定义condition条件
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment env = ctx.getEnvironment();
        String osName = env.getProperty("os.name");
        return osName.contains("Linux");
    }
}

8. @Import

自己写的组件使用@Component``@Controller``@Service``@Dao来标注
第三方的组件使用@Bean来注册
如果嫌写@Bean麻烦,可以使用@Import直接导入组件,默认id为全类名。@Import({A.class,B.class})参数可以是数组同时导入多个类。

image.png

9.@Import通过ImportSelector注册bean

可以自定义导入什么样的组件。

// 自定义importSelect
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // annotationMetadata参数可以获取注解等值
        // 不要返回null
        return new String[]{"com.zmz.al.bean.Red","com.zmz.al.bean.Yellow"};
    }
}
// 配置类中注解导入
@Import({Color.class, MyImportSelector.class})
image.png

10.@Import通过BeanDefinitions手动注册

自定义一个MyBeanDefinationRegistrar

public class MyBeanDefinationRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 判断是否含有黄色以及红色,有的话添加绿色
        boolean hasYellow = registry.containsBeanDefinition("com.zmz.al.bean.Yellow");
        boolean hasRed = registry.containsBeanDefinition("com.zmz.al.bean.Red");
        if (hasYellow && hasRed) {
            // 创建一个beanDefination
            RootBeanDefinition green = new RootBeanDefinition(Green.class);
            // 可以设置bean的name
            registry.registerBeanDefinition("green",green);
        }
    }
}

配置类@import中加入MyBeanDefinationRegistrar.class

@Import({Color.class, MyImportSelector.class, MyBeanDefinationRegistrar.class})
image.png

11.通过BeanFatory注册

// 新建一个类实现一个FacotryBean的接口
public class ColorFactoryBean implements FactoryBean<Color> {

    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    @Override
    public boolean isSingleton() {
        // true为单例,false为原型
        return true;
    }
}
// 将这个新的类注册到IOC容器
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
// 测试类
    @Test
    public void registryTest(){
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
    }

可以看到,使用ioc可以根据name获取bean,如果有&符号表示获取的是工厂本身,如果不加&符号则获取的是具体的对象。

image.png

12. @PostConstruct

bean的生命周期的控制注解,在bean创建之后,赋值之后,进行调用。

13. @PreDestroy

bean的生命周期的控制注解,在bean的销毁之前进行调用。

@Component
public class Dog {
    private String name;
    private Integer age;

    @PostConstruct
    public void postConstruct(){
        System.out.println("dog postConstruct ...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("dog preDestroy...");
    }
}

14.使用BeanPostProcessor控制bean的生命周期

// 新建一个dog类
public class Dog {
    private String name;
    private Integer age;

    @PostConstruct
    public void postConstruct(){
        System.out.println("dog postConstruct ...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("dog preDestroy...");
    }

    public void init(){
        System.out.println("dog init...");
    }
    public void destroy(){
        System.out.println("dog destroy...");
    }
}
// 配置类中@Bean注册dog bean并标明init、destroy方法
    @Bean(name = "dog",initMethod = "init",destroyMethod = "destroy")
    public Dog dog(){
        return new Dog();
    }
// 构建beanProcessor类
@Component
public class MyBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化前处理beanName:"+beanName+",bean:"+bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化后处理beanName:"+beanName+",bean:"+bean);
        return bean;
    }
}

image.png

最后的执行顺序为postProcessBeforeInitializationpostConstructinitpostProcessAfterInitializationpreDestroydestroy

15.@Value

给bean赋值,1)可以是基本数值;2)可以使用Spring表达式SPEL,#{};3)使用${}读取配置文件中的值或者运行环境变量中的值。可以放在全局变量或者方法参数上。

16.PropertySource(value="classpath:person.properties")

使用该注解加载外部配置文件保存到运行的环境变量中,然后再使用@Value读取配置文件。

<context:property-placeholder location="classpath:person.properties" />

17. @Autowired@Qualifier

默认按照类型去寻找IOC容器中的组件。如何同一类型的找到了多个,那么默认就根据属性名去寻找,如private ADAO aDao;就根据aDao这个名称去寻找。还可以显示的指定使用@Qualifier("aDao")注解来指定具体的名字。如何IOC中没有这个组件的情况下,可以在@Autowired(required=false)标明是非必须的。
@Autowired可以标注在方法上,表示其中的参数会通过类型去IOC中找到对应的bean。如set方法,有参构造器方法。
还可以标注在参数上public void aaa(@Autowired A a){},标在有参构造器上如果参数为1个时,可以省略。
@Bean时方法中的参数也可以省略@autowired注解,自动会去IOC容器中寻找bean。

18.@Primary

注册bean时,同类型多个bean中可以在其中一个标明此注解,来作为一个首选项,这样依赖注入@Autowired的时候就可以优先拿到首选的bean了。注意@Qualifier优先级大于@Primary

19.@Resource

是Java规范的依赖注入的注解,而@Autowired是Spring的注解。
@Resource默认按照组件名称进行装配。或者显示的@Resource(name="aDao")来指定具体的bean。不能结合@Qualifier@Primary注解,且不能使用required=false属性。

20.@Inject

是Java规范的依赖注入的注解,但是使用时需要引入一个javax.inject的包。

21.Spring底层Aware的使用

自定义组件想要使用Spring底层的组件时,可以实现xxxAware接口再进行使用。


image.png

22.@Profile

可以添加在方法上或者类上。
给组件添加标识,满足条件的bean才能被注册到IOC容器之中。默认@Profile("default")的会被注册。
1)使用配置文件中的spring.prifiles.active=xxx来指定现在使用的环境标识,
2)通过JVM参数-Dspring.prifiles.active=xxx来指定
3)使用编码来指定

// 先准备两个环境的Bean
@Configuration
public class ProfileConfig {

    @Profile("eat")
    @Bean
    public Person baigujing(){
        return new Person("白骨精",17);
    }

    @Profile("mary")
    @Bean
    public Person nverguoguowang(){
        return new Person("女儿国国王",18);
    }

    @Profile("mary")
    @Bean
    public Person kongquegongzhu(){
        return new Person("孔雀公主",16);
    }
}
// 测试类中
public class ProfileTest {
    @Test
    public void profileTest(){
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext();
        ioc.getEnvironment().setActiveProfiles("mary");
        ioc.register(ProfileConfig.class);
        ioc.refresh();

        String[] names = ioc.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}
image.png

可以看到“eat组”的白骨精没有注册进来,“mary组”的孔雀公主、女儿国国王注册进来了。

23. aop相关注解

1.切面类与被切的业务逻辑类都应该使用IOC容器管理
2.给切面类一个注解@Aspect标记为切面类
3.切面类中使用注解@Pointcut("execution(public int com.zmz.al.util.MathUtil.*(..))")编写切入点表达式来赋值切入点的位置
4.@Before("pointCut()")@After("com.zmz.al.log.LogAspect.pointCut()")@AfterReturning(value="pointCut()",returning = "ret")@AfterThrowing(value="pointCut()",throwing = "e")
来编写切入点的前、后、返回后、异常后的生命周期自定义函数。
5.自定义的生命周期函数可以使用JoinPoint jp来获取切点相关的参数,如函数名称、函数参数列表等,这个参数一定要写在参数列表的第一位,还可以在注解中传入value、returning、throwing等属性值。
6.@EnableAspectJAutoProxy // 开启切面自动代理,一般在配置类中开启切面自动代理功能

@Aspect
public class LogAspect {
    // 切入点表达式
    @Pointcut("execution(public int com.zmz.al.util.MathUtil.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void logStart(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println(jp.getSignature().getName()+"@Before运行,参数列表是:"+ Arrays.asList(args));
    }

    @After("com.zmz.al.log.LogAspect.pointCut()")
    public void logEnd(JoinPoint jp){
        System.out.println(jp.getSignature().getName()+"@After运行");
    }

    @AfterReturning(value="pointCut()",returning = "ret")
    public void logReturn(JoinPoint jp,Object ret){
        System.out.println(jp.getSignature().getName()+"@AfterReturning运行正常返回,结果是:"+ret);
    }

    @AfterThrowing(value="pointCut()",throwing = "e")
    public void logException(JoinPoint jp,Exception e){
        System.out.println(jp.getSignature().getName()+"@AfterThrowing运行异常,异常为:"+e);
    }
}
image.png

24.声明式事务注解

1.将DataSource的事务管理器注册到IOC容器中,整合mybatis时会自动注册。
2.@Transactional在方法上添加、@EnableTransactionManagement在配置类上添加开启事务功能。
3.当方法中报异常时,会调用DataSource的事务管理器自动回滚数据库相关操作。

FAQ

1.在测试@Value注解的时候,报了Could not resolve placeholder '' in value "${}"这样的错

进一步检查发现配置文件没有打包进入到target目录,查找之后是因为自己在pom.xml文件中配置了这个导致的。

<packaging>pom</packaging>

而这这个标签是用来指示idea将该模块打包成pom,所以,项目在启动编译时没有把配置文件同步到target目录下,项目运行时也拿不到配置文件的信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355