Spring注解详解

一、组件添加

1、@Configuration&@Bean

@Configuration:标注在类上,告诉Spring这是一个配置类(配置类==配置文件)

@Bean:标注在方法上,给容器中注册一个Bean,类型为返回值的类型id,默认是用方法名作为id,也可以通过value属性指定id

@Configuration
public class MainConfig {

    @Bean("person")
    public Person person01() {
        return new Person("lisi", 20);
    }
}
    @Test
    public void test01() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);// Person [name=lisi, age=20]
        String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String name : beanNamesForType) {
            System.out.println(name);// person
        }
    }

2、@ComponentScan

在配置类上添加@ComponentScan注解,value属性值为需要扫描的包名

@ComponentScan(value = "com.hand")

excludeFilters属性指定扫描的时候按照什么规则排除哪些组件

//排除标注了@Controller和@Service注解的类
@ComponentScan(value = "com.hand", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})})

includeFilters属性指定扫描的时候只包含哪些组件,需要使用将useDefaultFilters属性值置为false来禁用默认的规则

//只保留标注了@Controller注解的类
@ComponentScan(value = "com.hand", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
        useDefaultFilters = false)

@ComponentScans指定多个扫描规则

@ComponentScans(value = {
        @ComponentScan(value = "com.hand", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
                useDefaultFilters = false)
})

3、自定义TypeFilter指定过滤规则

FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则表达式
//只保留BookService这种类型的类
@ComponentScans(value = {
        @ComponentScan(value = "com.hand", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})},
                useDefaultFilters = false)
})
FilterType.CUSTOM:自定义过滤规则
@ComponentScan(value = "com.hand", includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})},
        useDefaultFilters = false
)
public class MyTypeFilter implements TypeFilter {
    //metadataReader:读取到的当前正在扫描的类的信息
    //metadataReaderFactory:可以获取到其他任何类信息的
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类型信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("className:" + className);
        //获取当前类资源信息(类的路径)
        Resource resource = metadataReader.getResource();
        if (className.contains("Service")) {
            //扫描到的类名只要包含Service,就添加到IOC容器中(即使类上没有标注@Service等注解也会被添加进IOC容器中)
            return true;
        }
        return false;
    }
}

4、@Scope设置组件作用域

    /*
     * ConfigurableBeanFactory#SCOPE_PROTOTYPE:多实例的,IOC容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象
     * ConfigurableBeanFactory#SCOPE_SINGLETON:单实例的(默认值),IOC容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器中拿
     * org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST:同一次请求创建一个实例
     * org.springframework.web.context.WebApplicationContext#SCOPE_SESSION:同一个session创建一个实例
     */
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean
    public Person person() {
        return new Person("张三", 25);
    }

5、@Lazy-bean懒加载

    /*
     * 单实例bean:默认在容器启动的时候创建对象
     * 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化
     */
    @Lazy
    @Bean
    public Person person() {
        System.out.println("初始化person对象");
        return new Person("张三", 25);
    }

6、@Conditional按照条件注册Bean

标注在方法上:

    /*
     * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
     * 如果是linux系统,给容器中注册("linus")
     */
    @Conditional(LinuxCondition.class)
    @Bean("linus")
    public Person person2() {
        return new Person("Linus", 48);
    }
//判断是否linux系统
public class LinuxCondition implements Condition {
    /**
     * ConditionContext:判断条件能使用的上下文(环境)
     * AnnotatedTypeMetadata:注释信息
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1、能获取到IOC使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2、获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //3、获取当前环境信息
        Environment environment = context.getEnvironment();
        //4、获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        String property = environment.getProperty("os.name");
        //可以判断容器中的bean注册情况,也可以给容器中注册bean
        boolean definition = registry.containsBeanDefinition("person");
        if (property.contains("linux")) {
            return true;
        }
        return false;
    }

}

标注在类上:

//类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig2 {

7、@Import给容器中快速导入一个组件

@Configuration
//@Import导入组件,id默认是组件的全类名
@Import(value = {Color.class, Red.class})
public class MainConfig2 {

8、ImportSelector

@Configuration
//@Import导入组件,id默认是组件的全类名
@Import(value = {Color.class, Red.class, MyImportSelector.class})
public class MainConfig2 {
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
    //返回值,就是导入到容器中的组件全类名
    //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.hand.bean.Blue", "com.hand.bean.Yellow"};
    }
}

9、ImportBeanDefinitionRegistrar

@Import(value = {Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /*
     *  AnnotationMetadata:当前类的注解信息
     *  BeanDefinitionRegistry:BeanDefinition注册类,把所有需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanDefinition手工注册
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean flag1 = registry.containsBeanDefinition("com.hand.bean.Red");
        boolean flag2 = registry.containsBeanDefinition("com.hand.bean.Blue");
        if (flag1 && flag2) {
            //指定Bean定义信息
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            //注册一个Bean,指定bean名
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }
}

10、使用FactoryBean注册组件

    @Bean
    public ColorFactoryBean colorFactoryBean(){
        //注册实际上是Color对象
        return new ColorFactoryBean();
    }
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
    //返回一个Color对象,这个对象会添加到容器中
    public Color getObject() throws Exception {
        return new Color();
    }

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

    //是否单例
    public boolean isSingleton() {
        return true;
    }
}
    @Test
    public void test06() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        Object bean1 = applicationContext.getBean("colorFactoryBean");
        System.out.println("bean1的类型:"+bean1.getClass());//bean1的类型:class com.hand.bean.Color
        Object bean2 = applicationContext.getBean("&colorFactoryBean");
        System.out.println("bean2的类型:"+bean2.getClass());//bean2的类型:class com.hand.bean.ColorFactoryBean
    }

11、给容器中添加Bean的几种方式

给容器中注册组件:
1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2)、@Bean[导入的第三方包里面的组件]
3)、@Import[快速给容器中导入一个组件]
	1)@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
	2)ImportSelector:返回需要导入的组件的全类名数组;
	3)ImportBeanDefinitionRegistrar:手动注册bean到容器中
4)、使用Spring提供的 FactoryBean(工厂Bean)
	1)默认获取到的是工厂bean调用getObject创建的对象
	2)要获取工厂Bean本身,我们需要给id前面加一个&

二、Bean生命周期

1、@Bean指定初始化和销毁方法

/*
 * Bean的生命周期:bean创建-->初始化-->销毁的过程
 * 容器管理bean的生命周期
 *
 * 构造(对象创建)
 * 单实例:在容器启动的时候创建对象
 * 多实例:在每次获取的时候创建对象
 *
 * 初始化:对象创建完成,并赋值好,调用初始化方法
 * 销毁:
 *  单实例:容器关闭的时候
 *  多实例:容器不会管理这个bean,容器不会调用销毁方法
 *
 * 可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用自定义的初始化和销毁方法
 * 指定初始化和销毁方法;通过@Bean指定init-method和destroy-method
 */
@Configuration
public class MainConfigOfLifeCycle {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Car car() {
        return new Car();
    }
}
public class Car {
    public Car(){
        System.out.println("car constructor...");
    }

    public void init(){
        System.out.println("car init...");
    }

    public void destroy(){
        System.out.println("car destroy...");
    }
}

2、InitializingBean和DisposableBean

通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)

@Component
public class Cat implements InitializingBean, DisposableBean {
    public Cat(){
        System.out.println("cat constructor...");
    }

    public void destroy() throws Exception {
        System.out.println("cat destroy...");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("cat afterPropertiesSet...");
    }
}

3、@PostConstruct&@PreDestroy

可以使用JSR250:
 @PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法
 @PreDestroy:在容器销毁bean之前通知我们进行清理工作
@Component
public class Dog {
    public Dog(){
        System.out.println("dog constructor...");
    }

    //对象创建并赋值之后调用
    @PostConstruct
    public void init(){
        System.out.println("dog @PostConstruct...");
    }

    //在容器移除对象之前
    @PreDestroy
    public void destroy(){
        System.out.println("dog @PreDestroy...");
    }
}

4、BeanPostProcessor后置处理器

BeanPostProcessor【interface】:bean的后置处理器
 在bean初始化前后进行一些处理工作
 postProcessBeforeInitialization:在初始化之前工作
 postProcessAfterInitialization:在初始化之后工作
/*
 * 后置处理器:初始化前后进行处理工作
 * 将后置处理器加入到容器中
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " postProcessBeforeInitialization...");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " postProcessAfterInitialization...");
        return bean;
    }
}

5、小结

Bean的生命周期:bean创建-->初始化-->销毁的过程
容器管理bean的生命周期

构造(对象创建)
 单实例:在容器启动的时候创建对象
 多实例:在每次获取的时候创建对象

初始化:对象创建完成,并赋值好,调用初始化方法
销毁:
 单实例:容器关闭的时候
 多实例:容器不会管理这个bean,容器不会调用销毁方法

可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用自定义的初始化和销毁方法
1)、指定初始化和销毁方法;通过@Bean指定init-method和destroy-method
2)、通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)
3)、可以使用JSR250:
 @PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法
 @PreDestroy:在容器销毁bean之前通知我们进行清理工作
4)、BeanPostProcessor【interface】:bean的后置处理器
 在bean初始化前后进行一些处理工作
 postProcessBeforeInitialization:在初始化之前工作
 postProcessAfterInitialization:在初始化之后工作

三、组件赋值

1、@Value赋值

public class Person {
    //使用@Value赋值
    //1、基本数值
    //2、可以写SpEL:#{}
    @Value("张三")
    private String name;
    @Value("#{20-2}")
    private Integer age;

2、@PropertySource加载外部配置文件

配置文件person.properties:

person.nickName=张三123
@Configuration
//使用@PropertySource读取外部配置文件中的属性
@PropertySource(value = {"classpath:/person.properties"})
public class MainConfigOfPropertyValues {
    @Bean
    public Person person(){
        return new Person();
    }
}
public class Person {
    //使用@Value赋值
    //1、基本数值
    //2、可以写SpEL:#{}
    //3、可以写${},取出配置文件中的值(在运行环境变量里面的值)
    @Value("张三")
    private String name;
    @Value("#{20-2}")
    private Integer age;
    @Value("${person.nickName}")
    private String nickName;
    @Test
    public void test02() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String property = environment.getProperty("person.nickName");
        System.out.println(property);
    }

2、自动装配

自动装配:Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
1)、@Autowired:自动注入
 1)默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
 2)如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean("bookDao")
 3)@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
 4)自动装配默认一定要将属性赋值好,没有就会报错
  可以使用@Autowired(required=false)
 5)@Primary:让Spring进行自动装配的时候,默认使用首选的bean
  也可以继续使用@Qualifier指定需要装配的bean的名字

2)、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[Java规范的注解]
 @Resource:
  可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的
  没有能支持@Primary功能没有支持@Autowired(reqiured=false)
 @Inject:
  需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能
 
 @Autowired:Spring定义的; @Resource、@Inject都是Java规范

3)、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
 1)[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
 2)[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
 3)放在参数位置

3、@Profile

配置文件dbconfig.properties:

db.user=root
db.password=123456
db.driverClass=com.mysql.jdbc.Driver
/*
 * Profile:
 * Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能
 * 开发环境、测试环境、生产环境
 * 数据源:(/A)(/B)(/C)
 *
 * @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
 * 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
 * 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
 * 3)、没有标注环境标识的bean在,任何环境下都是加载的
 */
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${db.user}")
    private String user;

    private StringValueResolver valueResolver;

    private String driverClass;

    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/cms");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }


    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/web_shop");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("prod")
    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/imooc-demo");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }

}
    //1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
    //2、代码的方式激活某种环境
    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext();
        //1、创建一个applicationContext
        //2、设置需要激活的环境
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //3、注册主配置类
        applicationContext.register(MainConfigOfProfile.class);
        //4、启动刷新容器
        applicationContext.refresh();
        String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String string : namesForType) {
            System.out.println(string);
        }
        applicationContext.close();
    }
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邋遢的流浪剑客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值