• 9. Spring高级注解驱动开发入门


    Spring注解驱动开发入门

    1、写在最前

    spring在2.5版本引入了注解配置的支持,同时从Spring 3版本开始,Spring JavaConfig项目提供的许多特性成为核心Spring框架的一部分。因此,可以使用Java而不是XML文件来定义应用程序类外部的bean。在这里面官方文档为我们提供了四个基本注解@Configuration,@Bean,@Import,@DependsOn

    2、注解驱动入门案例介绍

    1.需求:
    	实现保存一条数据到数据库。
    2.表结构:
    	create table account(
    		id int primary key auto_increment,
    		name varchar(50),
    		money double(7,2)
    	);
    3.要求:
    	使用spring框架中的JdbcTemplate和DriverManagerDataSource
    	使用纯注解配置spring的ioc
    

    3、案例实现

    3.1、导入坐标

    <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.1.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.45</version>
            </dependency>
        </dependencies>
    

    3.2、编写配置类

    /**
     * spring的配置类
     * 用于替代xml配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbc.properties")
    public class SpringConfiguration {
    }
    
    
    /**
     * 连接数据库的配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class JdbcConfig {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
    
        @Bean("jdbcTemplate")
        public JdbcTemplate createJdbcTemplate(DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
    
    
        @Bean("dataSource")
        public DataSource createDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    

    3.3、编写配置文件

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_day01
    jdbc.username=root
    jdbc.password=1234
    

    3.4、编写测试类

    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class SpringAnnotationDrivenTest {
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            //1.获取容器
            ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            //2.根据id获取对象
            JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate",JdbcTemplate.class);
            //3.执行操作
            jdbcTemplate.update("insert into account(name,money)values(?,?)","test",12345);
        }
    }
    

    IOC的常用注解分析

    1、用于注解驱动的注解

    1.1、@Configuration

    1.1.1、源码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
    
    	/**
    	 * Explicitly specify the name of the Spring bean definition associated with the
    	 * {@code @Configuration} class. If left unspecified (the common case), a bean
    	 * name will be automatically generated.
    	 * <p>The custom name applies only if the {@code @Configuration} class is picked
    	 * up via component scanning or supplied directly to an
    	 * {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
    	 * is registered as a traditional XML bean definition, the name/id of the bean
    	 * element will take precedence.
    	 * @return the explicit component name, if any (or empty String otherwise)
    	 * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
    	 */
    	@AliasFor(annotation = Component.class)
    	String value() default "";
    }
    
    1.1.2、说明

    作用:
    它是在spring3.0版本之后加入的。此注解是spring支持注解驱动开发的一个标志。表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的ioc容器。
    属性:
    value:
    用于存入spring的Ioc容器中Bean的id。
    使用场景:
    在注解驱动开发时,用于编写配置的类,通常可以使用此注解。一般情况下,我们的配置也会分为主从配置,@Configuration一般出现在主配置类上。例如,入门案例中的SpringConfiguration类上。值得注意的是,如果我们在注解驱动开发时,构建ioc容器使用的是传入字节码的构造函数,此注解可以省略。但是如果传入的是一个包,此注解则不能省略。

    1.1.3、示例
    	在注解驱动的入门案例中,由于没有了applicationContext.xml,就没法在xml中配置spring创建容器要扫描的包了。那么,我们自己写的一些类,通过注解配置到ioc容器中也无法实现了。此时就可以使用此注解来代替spring的配置文件。
    /**
     * spring的配置类
     * 用于替代xml配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbc.properties")
    @ComponentScan("com.itheima")
    public class SpringConfiguration {
    }
    

    1.2、@ComponentScan

    1.2.1、源码
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
    	/**
    	 */
    	@AliasFor("basePackages")
    	String[] value() default {};
    
      	/**
    	 */
    	@AliasFor("value")
    	String[] basePackages() default {};
    
      	/**
    	 */
    	Class<?>[] basePackageClasses() default {};
    
      	/**
    	 */
    	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    
      	/**
    	 */
    	Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    
      	/**
    	 */
    	ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    
      	/**
    	 */
    	String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
    
      	/**
    	 */
    	boolean useDefaultFilters() default true;
    
      	/**
    	 */
    	Filter[] includeFilters() default {};
    
      	/**
    	 */
    	Filter[] excludeFilters() default {};
    
      	/**
    	 */
    	boolean lazyInit() default false;
    
    
    	/**
    	 * Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
    	 * include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
    	 */
    	@Retention(RetentionPolicy.RUNTIME)
    	@Target({})
    	@interface Filter {
    		/**
    		 */
    		FilterType type() default FilterType.ANNOTATION;
    
    		/**
    		 */
    		@AliasFor("classes")
    		Class<?>[] value() default {};
          
    		/**
    		 */
    		@AliasFor("value")
    		Class<?>[] classes() default {};
          
    		/**
    		 */
    		String[] pattern() default {};
    	}
    }
    
    1.2.2、说明
    作用:
    	用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则
    属性:
    	value:
    		用于指定要扫描的包。当指定了包的名称之后,spring会扫描指定的包及其子包下的所有类。
    	basePackages:
    		它和value作用是一样的。
    	basePackageClasses:
    		指定具体要扫描的类的字节码。
    	nameGenrator:
    		指定扫描bean对象存入容器时的命名规则。详情请参考第五章第4小节的BeanNameGenerator及其实现类。
    	scopeResolver:
    		用于处理并转换检测到的Bean的作用范围。
    	soperdProxy:
    		用于指定bean生成时的代理方式。默认是Default,则不使用代理。详情请参考第五章第5小节ScopedProxyMode枚举。
    	resourcePattern:
    		用于指定符合组件检测条件的类文件,默认是包扫描下的  **/*.class
    	useDefaultFilters:
    		是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的。
    	includeFilters:
    		自定义组件扫描的过滤规则,用以扫描组件。
    		FilterType有5种类型:
                ANNOTATION, 注解类型 默认
                ASSIGNABLE_TYPE,指定固定类
                ASPECTJ, ASPECTJ类型
                REGEX,正则表达式
                CUSTOM,自定义类型
            详细用法请参考第五章第6小节自定义组件扫描过滤规则
    	excludeFilters:
    		自定义组件扫描的排除规则。
    	lazyInit:
    		组件扫描时是否采用懒加载 ,默认不开启。
    使用场景:
    	在注解驱动开发时,我们自己编写的类都使用注解的方式进行配置,要想让spring添加到ioc容器中,就需要使用此注解来实现组件的扫描。
    细节:
    	在spring4.3版本之后还加入了一个@ComponentScans的注解,该注解就是支持配置多个@ComponentScan。
    
    1.2.3、示例
    	在入门案例中,如果我们加入了dao或者记录日志的工具类,这些使用了@Component或其衍生注解配置的bean,要想让他们进入ioc容器,就少不了使用@ComponentScan
    
    package com.itheima.dao.impl;
    import com.itheima.dao.AccountDao;      
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao{
    	//持久层开发(此处没有考虑mybatis代理dao方式或者其他持久层技术,因为不希望和其他框架技术关联)
    }      
    
    /**
     * spring的配置类
     * 用于替代xml配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbc.properties")
    @ComponentScan("com.itheima")
    public class SpringConfiguration {
    }
    

    1.3、@Bean

    1.3.1、源码
    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.beans.factory.annotation.Autowire;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.core.annotation.AliasFor;
    
    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Bean {
    	/**
    	 */
    	@AliasFor("name")
    	String[] value() default {};
      
    	/**
    	 */
    	@AliasFor("value")
    	String[] name() default {};
    
      	/**
    	 */
    	@Deprecated
    	Autowire autowire() default Autowire.NO;
    
      	/**
    	 */
    	boolean autowireCandidate() default true;
    
      	/**
    	 */
    	String initMethod() default "";
    
      	/**
    	 */
    	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
    }
    
    1.3.2、说明
    作用:
    	它写在方法上,表示把当前方法的返回值存入spring的ioc容器。
    	同时还可以出现在注解上,作为元注解来使用。		
    属性:
    	name:
    		用于指定存入spring容器中bean的标识。支持指定多个标识。当不指定该属性时,默认值是当前方法的名称。
    	value:
    		此属性是在4.3.3版本之后加入的。它和name属性的作用是一样的。
    	autowireCandidate:
    		用于指定是否支持自动按类型注入到其他bean中。只影响@Autowired注解的使用。不影响@Resource注解注入。默认值为true,意为允许使用自动按类型注入。
    	initMethod:
    		用于指定初始化方法。
    	destroyMethod:
    		用于指定销毁方法。
    使用场景:
    	通常情况下,在基于注解的配置中,我们用于把一个类存入spring的ioc容器中,首先考虑的是使用@Component以及他的衍生注解。但是如果遇到要存入容器的Bean对象不是我们写的类,此时无法在类上添加@Component注解,这时就需要此注解了。
    示例:
    	例如,在我们配置JdbcTemplate使用Spring内置数据源DriverManagerDataSource时,数据源类是spring-jdbc这个jar包中类,此时我们无法编辑,在上面加注解,此时就可以使用@Bean注解配置。
    
    1.3.3、示例代码
    @Bean("jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
    

    1.4、@Import

    1.4.1、源码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    	/**
    	 */
    	Class<?>[] value();
    }
    
    1.4.2、说明
    作用:
    	该注解是写在类上的,通常都是和注解驱动的配置类一起使用的。
      其作用是引入其他的配置类。使用了此注解之后,可以使我们的注解驱动开发和早期xml配置一样,分别配置不同的内容,使配置更加清晰。同时指定了此注解之后,被引入的类上可以不再使用@Configuration,@Component等注解。
    属性:
    	value:
    		用于指定其他配置类的字节码。它支持指定多个配置类。
    		关于ImportSelector和ImportBeanDefinitionRegistrar请参考第五章第7小节@Import注解的高级分析。
    使用场景:
    	当我们在使用注解驱动开发时,由于配置项过多,如果都写在一个类里面,配置结构和内容将杂乱不堪,此时使用此注解可以把配置项进行分门别类进行配置。
    
    1.4.3、示例
    	在入门案例中,我们使用了SpringConfiguration做为主配置类,而连接数据库相关的配置被分配到了JdbcConfig配置类中,此时使用在SpringConfiguration类上使用@Import注解把JdbcConfig导入进来就可以了。
     /**
     * spring的配置类
     * 用于替代xml配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbc.properties")
    @ComponentScan("com.itheima")
    public class SpringConfiguration {
    }
    
    /**
     * 连接数据库的配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class JdbcConfig {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
    
        @Bean("jdbcTemplate")
        public JdbcTemplate createJdbcTemplate(DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
    
    
        @Bean("dataSource")
        public DataSource createDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    

    1.5、@PropertySource

    1.5.1、源码
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Repeatable(PropertySources.class)
    public @interface PropertySource {
    
    	/**
    	 */
    	String name() default "";
    
    	/** 
    	 */
    	String[] value();
    
    	/** 
    	 */
    	boolean ignoreResourceNotFound() default false;
    
    	/**
    	 */
    	String encoding() default "";
    
    	/**
    	 */
    	Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
    
    }
    
    1.5.2、说明
    作用:
    	用于指定读取资源文件的位置。注意,它不仅支持properties,也支持xml文件,并且通过YAML解析器,配合自定义PropertySourceFactory实现解析yml配置文件(详情请参考第五章第8小节自定义PropertySourceFactory实现YAML文件解析)。
    属性:
    	name:
    		指定资源的名称。如果没有指定,将根据基础资源描述生成。
    	value:
    		指定资源的位置。可以是类路径,也可以是文件路径。
    	ignoreResourceNotFound:
    		指定是否忽略资源文件有没有,默认是false,也就是说当资源文件不存在时spring启动将会报错。
    	encoding:
    		指定解析资源文件使用的字符集。当有中文的时候,需要指定中文的字符集。
    	factory:
    		指定读取对应资源文件的工厂类,默认的是PropertySourceFactory。
    使用场景:
    	我们实际开发中,使用注解驱动后,xml配置文件就没有了,此时一些配置如果直接写在类中,会造成和java源码的紧密耦合,修改起来不方法。此时一些配置可以使用properties或者yml来配置就变得很灵活方便。
    
    1.5.3、示例
    在入门案例中,我们连接数据库的信息如果直接写到JdbcConfig类中,当需要修改时,就面临修改源码的问题,此时使用@PropertySource和SpringEL表达式,就可以把配置放到properties文件中了。
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_day01
    jdbc.username=root
    jdbc.password=1234
    
    /**
     * 连接数据库的配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class JdbcConfig {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    }
    
    /**
     * spring的配置类
     * 用于替代xml配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource(value = "classpath:jdbc.properties")
    @ComponentScan("com.itheima")
    public class SpringConfiguration {
    }
    

    2、注解驱动开发之注入时机和设定注入条件的注解

    2.1、@DependsOn

    2.1.1、源码
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DependsOn {
    	String[] value() default {};
    }
    
    2.1.2、说明
    作用:
    	用于指定某个类的创建依赖的bean对象先创建。spring中没有特定bean的加载顺序,使用此注解则可指定bean的加载顺序。(在基于注解配置中,是按照类中方法的书写顺序决定的)
    属性:
    	value: 
    		用于指定bean的唯一标识。被指定的bean会在当前bean创建之前加载。
    使用场景:
    	在观察者模式中,分为事件,事件源和监听器。一般情况下,我们的监听器负责监听事件源,当事件源触发了事件之后,监听器就要捕获,并且做出相应的处理。以此为前提,我们肯定希望监听器的创建时间在事件源之前,此时就可以使用此注解。
    
    2.1.3、示例
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Component
    public class CustomerListener {
    
        public CustomerListener(){
            System.out.println("监听器创建了。。。");
        }
    }
    
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Component
    @DependsOn("customerListener")
    public class Customer {
    
        public Customer(){
            System.out.println("事件源创建了。。。");
        }
    }
    

    2.2、@Lazy

    2.2.1、源码
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Lazy {
    	boolean value() default true;
    }
    
    2.2.2、说明
    作用:
    	用于指定单例bean对象的创建时机。在没有使用此注解时,单例bean的生命周期与容器相同。
      但是当使用了此注解之后,单例对象的创建时机变成了第一次使用时创建。注意:这不是延迟加载思想(因为不是每次使用时都创建,只是第一次创建的时机改变了)。
    属性:
    	value:
    		指定是否采用延迟加载。默认值为true,表示开启。
    使用场景:
    	在实际开发中,当我们的Bean是单例对象时,并不是每个都需要一开始都加载到ioc容器之中,有些对象可以在真正使用的时候再加载,
      当有此需求时,即可使用此注解。值得注意的是,此注解只对单例bean对象起作用,当指定了@Scope注解的prototype取值后,此注解不起作用。
    
    2.2.3、示例
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Component
    @Lazy
    //@Lazy(value = false)
    //@Scope("prototype")
    public class LazyBean {
    
        public LazyBean(){
            System.out.println("LazyBean对象创建了");
        }
    }
    
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class SpringAnnotationDrivenTest {
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
          //1.获取容器
          ApplicationContext ac = new AnnotationConfigApplicationContext("config");
          //2.根据id获取对象  
          LazyBean lazyBean = (LazyBean)ac.getBean("lazyBean");
          System.out.println(lazyBean);
        }
    
    }
    

    2.3、@Conditional

    2.3.1、源码
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
    
    	/**
    	 * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
    	 * in order for the component to be registered.
    	 */
    	Class<? extends Condition>[] value();
    }
    
    2.3.2、说明
    作用:
    	它的作用是根据条件选择注入的bean对象。
    属性:
    	value:
    		用于提供一个Condition接口的实现类,实现类中需要编写具体代码实现注入的条件。
    使用场景:
    	当我们在开发时,可能会使用多平台来测试,例如我们的测试数据库分别部署到了linux和windows两个操作系统上面,现在根据我们的工程运行环境选择连接的数据库。
      此时就可以使用此注解。同时基于此注解引出的@Profile注解,就是根据不同的环境,加载不同的配置信息,详情请参考第五章第9小节@Profile的使用。
    
    2.3.3、示例
    /**
     * 连接数据库的配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class JdbcConfig {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
    
       
    
        /**
         * linux系统注入的数据源
         * @param lDriver
         * @param lUrl
         * @param lUsername
         * @param lPassword
         * @return
         */
        @Bean("dataSource")
        @Conditional(LinuxCondition.class)
        public DataSource createLinuxDataSource(@Value("${linux.driver}") String lDriver,
                                                @Value("${linux.url}")String lUrl,
                                                @Value("${linux.username}")String lUsername,
                                                @Value("${linux.password}")String lPassword){
            DriverManagerDataSource dataSource = new DriverManagerDataSource(lUrl,lUsername,lPassword);
            dataSource.setDriverClassName(lDriver);
            System.out.println(lUrl);
            return dataSource;
        }
    
        /**
         * windows系统注入的数据源
         * @return
         */
        @Bean("dataSource")
        @Conditional(WindowsCondition.class)
        public DataSource createWindowsDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            System.out.println(url);
            return dataSource;
        }
    }
    
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class LinuxCondition implements Condition {
    
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //获取ioc使用的beanFactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            //获取类加载器
            ClassLoader classLoader = context.getClassLoader();
            //获取当前环境信息
            Environment environment = context.getEnvironment();
            //获取bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
    
            //获得当前系统名
            String property = environment.getProperty("os.name");
            //包含Windows则说明是windows系统,返回true
            if (property.contains("Linux")){
                return true;
            }
            return false;
        }
    }
    
    
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class WindowsCondition implements Condition {
    
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //获取ioc使用的beanFactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            //获取类加载器
            ClassLoader classLoader = context.getClassLoader();
            //获取当前环境信息
            Environment environment = context.getEnvironment();
    
            /**
             * 获取所有系统环境变量
             */
            if(environment instanceof StandardEnvironment){
                StandardEnvironment standardEnvironment = (StandardEnvironment)environment;
                Map<String,Object> map = standardEnvironment.getSystemProperties();
                for(Map.Entry<String,Object> me : map.entrySet()){
                    System.out.println(me.getKey()+","+me.getValue());
                }
            }
    
            //获取bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
    
            //获得当前系统名
            String property = environment.getProperty("os.name");
            //包含Windows则说明是windows系统,返回true
            if (property.contains("Windows")){
                return true;
            }
            return false;
        }
    }
    
    linux.driver=com.mysql.jdbc.Driver
    linux.url=jdbc:mysql://localhost:3306/ssm
    linux.username=root
    linux.password=1234
    -------------------------------------------------------------------------------------
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_day01
    jdbc.username=root
    jdbc.password=1234
    

    3、用于创建对象的注解

    3.1、@Component和三个衍生注解

    3.1.1、源码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Indexed
    public @interface Component {
    
    	/**
    	 * The value may indicate a suggestion for a logical component name,
    	 * to be turned into a Spring bean in case of an autodetected component.
    	 * @return the suggested component name, if any (or empty String otherwise)
    	 */
    	String value() default "";
    }
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Controller {
    
    	/**
    	 * The value may indicate a suggestion for a logical component name,
    	 * to be turned into a Spring bean in case of an autodetected component.
    	 * @return the suggested component name, if any (or empty String otherwise)
    	 */
    	@AliasFor(annotation = Component.class)
    	String value() default "";
    
    }
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Service {
    
    	/**
    	 * The value may indicate a suggestion for a logical component name,
    	 * to be turned into a Spring bean in case of an autodetected component.
    	 * @return the suggested component name, if any (or empty String otherwise)
    	 */
    	@AliasFor(annotation = Component.class)
    	String value() default "";
    
    }
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Repository {
    
    	/**
    	 * The value may indicate a suggestion for a logical component name,
    	 * to be turned into a Spring bean in case of an autodetected component.
    	 * @return the suggested component name, if any (or empty String otherwise)
    	 */
    	@AliasFor(annotation = Component.class)
    	String value() default "";
    
    }
    
    3.1.2、说明
    作用:
    	这四个注解都是用于修饰类的。是用于把当前类创建一个对象,存入spring的ioc容器中。在实例化时,首选默认无参构造函数。同时支持带参构造,前提是构造函数的参数依赖必须要有值。否则抛异常	
    属性:
    	value:
    		用于指定存入容器时bean的id。当不指定时,默认值为当前类的名称。
    使用场景:
    	当我们需要把自己编写的类注入到Ioc容器中,就可以使用以上四个注解实现。以上四个注解中@Component注解通常用在非三层对象中。而@Controller,@Service,@Repository三个注解一般是针对三层对象使用的,提供更加精确的语义化配置。
    	需要注意的是,spring在注解驱动开发时,要求必须先接管类对象,然后会处理类中的属性和方法。如果类没有被spring接管,那么里面的属性和方法上的注解都不会被解析。
    
    3.1.3、示例
    正确的方式1:使用默认构造函数
    	/**
         * 用于记录系统日志
         * @author 黑马程序员
         * @Company http://www.itheima.com
         */
        @Component
        public class LogUtil {
            /**
             * 默认无参构造函数
             */
          	public LogUtil(){
    		}
          //可以使用aop思想实现系统日志的记录
        }
    
    正确的方式2:在构造函数中注入一个已经在容器中的bean对象。
      	/**
         * 此处只是举例:使用JdbcTemplate作为持久层中的操作数据库对象
         * @author 黑马程序员
         * @Company http://www.itheima.com
         */
        @Repository("userDao")
        public class UserDaoImpl implements UserDao{
    
            private JdbcTemplate jdbcTemplate ;
            /**
             * 此时要求容器中必须有JdbcTemplate对象
             * @param jdbcTemplate
             */
            public UserDaoImpl(JdbcTemplate jdbcTemplate){
                this.jdbcTemplate = jdbcTemplate;
            }
        }
    
    正确的方式3:在构造函数中注入一个读取配置文件获取到的值。
        /**
         * 用于记录系统日志
         * @author 黑马程序员
         * @Company http://www.itheima.com
         */
        @Component
        public class LogUtil {
            /**
             * 构造时,注入日志级别
             * @param logLevel
             */
            public LogUtil(@Value("${log.level}")String logLevel){
                System.out.println(logLevel);
            }
          	//可以使用aop思想实现系统日志的记录
        }
    
    错误的方式:由于logLevel没有值,所以运行会报错。
       /**
         * 用于记录系统日志
         * @author 黑马程序员
         * @Company http://www.itheima.com
         */
        @Component
        public class LogUtil {
            /**
             * 构造时,注入日志级别
             * @param logLevel
             */
            public LogUtil(String logLevel){
                System.out.println(logLevel);
            }
          	//可以使用aop思想实现系统日志的记录
        }
    

    4、用于注入数据的注解

    4.1、@Autowired

    4.1.1、源码
    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    
    	/**
    	 * Declares whether the annotated dependency is required.
    	 * <p>Defaults to {@code true}.
    	 */
    	boolean required() default true;
    
    }
    
    4.1.2、说明
    作用:
    	自动按照类型注入。当ioc容器中有且只有一个类型匹配时可以直接注入成功。当有超过一个匹配时,则使用变量名称(写在方法上就是方法名称)作为bean的id,在符合类型的bean中再次匹配,能匹配上就可以注入成功。当匹配不上时,是否报错要看required属性的取值。
    属性:
    	required:
    		是否必须注入成功。默认值是true,表示必须注入成功。当取值为true的时候,注入不成功会报错。
    使用场景:
    	此注解的使用场景非常之多,在实际开发中应用广泛。通常情况下我们自己写的类中注入依赖bean对象时,都可以采用此注解。
    
    4.1.3、示例
    /**
     * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao{
    	@Autowired
        private JdbcTemplate jdbcTemplate ;
    }
    

    4.2、@Qualifier

    4.2.1、源码
    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Qualifier {
    
    	String value() default "";
    
    }
    
    4.2.2、说明
    作用:
    	当使用自动按类型注入时,遇到有多个类型匹配的时候,就可以使用此注解来明确注入哪个bean对象。注意它通常情况下都必须配置@Autowired注解一起使用
    属性:
    	value:
    		用于指定bean的唯一标识。
    使用场景:
    	在我们的项目开发中,很多时候都会用到消息队列,我们一ActiveMQ为例。当和spring整合之后,Spring框架提供了一个JmsTemplate对象,它既可以用于发送点对点模型消息也可以发送主题模型消息。如果项目中两种消息模型都用上了,那么针对不同的代码,将会注入不同的JmsTemplate,而容器中出现两个之后,就可以使用此注解注入。当然不用也可以,我们只需要把要注入的变量名称改为和要注入的bean的id一致即可。
    
    4.2.3、示例
    @Component("fooFormatter")
    public class FooFormatter implements Formatter {
        public String format() {
            return "foo";
        }
    }
    
    @Component("barFormatter")
    public class BarFormatter implements Formatter {
        public String format() {
            return "bar";
        }
    }
    
    @Component
    public class FooService {
        @Autowired
        private Formatter formatter;
    
        //todo 
    }
    
    // 如果我们尝试将 FooService 加载到我们的上下文中,Spring 框架将抛出 NoUniqueBeanDefinitionException。这是因为 Spring 不知道要注入哪个 bean。为了避免这个问题,有几种解决方案
    
    
    
    @Component
    public class FooService {
        @Autowired
        @Qualifier("fooFormatter")
        private Formatter formatter;
    
        //todo 
    }
    

    4.3、@Resource

    4.3.1、源码
    @Target({TYPE, FIELD, METHOD})
    @Retention(RUNTIME)
    public @interface Resource {
        String name() default "";
        String lookup() default "";
        Class<?> type() default java.lang.Object.class;
        enum AuthenticationType {
                CONTAINER,
                APPLICATION
        }
        AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
        boolean shareable() default true;
        String mappedName() default "";
        String description() default "";
    }
    
    4.3.2、说明
    作用:
    	此注解来源于JSR规范(Java Specification Requests),其作用是找到依赖的组件注入到应用来,它利用了JNDI(Java Naming and Directory Interface Java命名目录接口 J2EE规范之一)技术查找所需的资源。
    	默认情况下,即所有属性都不指定,它默认按照byType的方式装配bean对象。如果指定了name,没有指定type,则采用byName。如果没有指定name,而是指定了type,则按照byType装配bean对象。当byName和byType都指定了,两个都会校验,有任何一个不符合条件就会报错。
    属性:
    	name:
    		资源的JNDI名称。在spring的注入时,指定bean的唯一标识。
    	type:
    		指定bean的类型。
    	lookup:
    		引用指向的资源的名称。它可以使用全局JNDI名称链接到任何兼容的资源。
    	authenticationType:
    		指定资源的身份验证类型。它只能为任何受支持类型的连接工厂的资源指定此选项,而不能为其他类型的资源指定此选项。
    	shareable:
        	指定此资源是否可以在此组件和其他组件之间共享。
        mappedName:
        	指定资源的映射名称。
        description:
        	指定资源的描述。 
    使用场景:
    	当我们某个类的依赖bean在ioc容器中存在多个的时候,可以使用此注解指定特定的bean对象注入。当然我们也可以使用@Autowired配合@Qualifier注入。
    
    4.3.3、示例

    4.4、@Value

    4.4.1、源码
    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Value {
    
    	/**
    	 * The actual value expression: for example {@code #{systemProperties.myProp}}.
    	 */
    	String value();
    
    }
    
    4.4.2、说明
    作用:
    	用于注入基本类型和String类型的数据。它支持spring的EL表达式,可以通过${} 的方式获取配置文件中的数据。配置文件支持properties,xml和yml文件。
    属性:
    	value:
    		指定注入的数据或者spring的el表达式。
    使用场景:
    	在实际开发中,像连接数据库的配置,发送邮件的配置等等,都可以使用配置文件配置起来。此时读取配置文件就可以借助spring的el表达式读取。
    
    4.4.3、示例
    /**
     * 连接数据库的配置
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    public class JdbcConfig {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
    
        /**
         * windows系统注入的数据源
         * @return
         */
        @Bean("dataSource")
        public DataSource createWindowsDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    
    }
    
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_day01
    jdbc.username=root
    jdbc.password=1234
    

    4.5、@Inject

    4.5.1、源码
    @Target({ METHOD, CONSTRUCTOR, FIELD })
    @Retention(RUNTIME)
    @Documented
    public @interface Inject {
    }
    
    4.5.2、说明
    作用:
    	它也是用于建立依赖关系的。和@Resource和@Autowired的作用是一样。在使用之前需要先导入坐标:
    	<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
    	但是他们之前也有区别:
    		@Autowired:来源于spring框架自身。
    					默认是byType自动装配,当配合了@Qualifier注解之后,由@Qualifier实现byName装配。它有一个required属性,用于指定是否必须注入成功。
    		@Resource:来源于JSR-250规范。
    				  在没有指定name属性时是byType自动装配,当指定了name属性之后,采用byName方式自动装配。
    		@Inject:来源于JSR-330规范。(JSR330是Jcp给出的官方标准反向依赖注入规范。)
    				 它不支持任何属性,但是可以配合@Qualifier或者@Primary注解使用。
    				 同时,它默认是采用byType装配,当指定了JSR-330规范中的@Named注解之后,变成byName装配。
    			
    属性:
    	无
    使用场景:
    	在使用@Autowired注解的地方,都可以替换成@Inject。它也可以出现在方法上,构造函数上和字段上,但是需要注意的是:因为JRE无法决定构造方法注入的优先级,所以规范中规定类中只能有一个构造方法带@Inject注解。
    
    4.5.3、示例
    /** 
     * 第一种写法: 写在字段上
     * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("userDao")
    public class UserDaoImpl implements UserDao{
    	
      	@Inject
        private JdbcTemplate jdbcTemplate ;
      
    }
    
    /**
     * 第二种写法:写在构造函数上
     * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao{
    	
        private JdbcTemplate jdbcTemplate ;
        /**
         * 此时要求容器中必须有JdbcTemplate对象
         * @param jdbcTemplate
         */
        @Inject
        public AccountDaoImpl(JdbcTemplate jdbcTemplate){
            this.jdbcTemplate = jdbcTemplate;
        }
    }
    
    /**
     * 第三种写法:配合@Named注解使用
     * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao{
    	
      	@Inject
      	@Named("jdbcTemplate")
        private JdbcTemplate jdbcTemplate ;
    }
    

    4.6、@Primary

    4.6.1、源码
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Primary {
    
    }
    
    4.6.2、说明
    作用:
    	用于指定bean的注入优先级。被@Primary修饰的bean对象优先注入
    属性:
    	无
    使用场景:
    	当我们的依赖对象,有多个存在时,@Autowired注解已经无法完成功能,此时我们首先想到的是@Qualifier注解指定依赖bean的id。但是此时就产生了,无论有多少个bean,每次都会使用指定的bean注入。但是当我们使用@Primary,表示优先使用被@Primary注解的bean,但是当不存在时还会使用其他的。
    
    4.6.3、示例
    /**
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
    
        @Autowired
    //    @Qualifier("accountImpl1")
        private AccountDao accountDao;
    
        public void save(){
            System.out.println(accountDao);
        }
    }
    
    
    /**
     * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("accountDaoImpl1")
    public class AccountDaoImpl implements AccountDao{
    
        @Override
        public String toString() {
            return "accountDaoImpl1";
        }
    }
    
    /**
     * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
     * @author 黑马程序员
     * @Company http://www.itheima.com
     */
    @Repository("accountDaoImpl2")
    @Primary
    public class AccountDaoImpl2 implements AccountDao{
    
        @Override
        public String toString() {
            return "accountDaoImpl2";
        }
    }
    

    5、和生命周期以及作用范围相关的注解

    5.1、@Scope

    5.1.1、源码
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Scope {
    
    	/**
    	 * Alias for {@link #scopeName}.
    	 * @see #scopeName
    	 */
    	@AliasFor("scopeName")
    	String value() default "";
    
    	/**
    	 * Specifies the name of the scope to use for the annotated component/bean.
    	 * <p>Defaults to an empty string ({@code ""}) which implies
    	 * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
    	 * @since 4.2
    	 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
    	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON
    	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
    	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
    	 * @see #value
    	 */
    	@AliasFor("value")
    	String scopeName() default "";
    
    	/**
    	 * Specifies whether a component should be configured as a scoped proxy
    	 * and if so, whether the proxy should be interface-based or subclass-based.
    	 * <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
    	 * that no scoped proxy should be created unless a different default
    	 * has been configured at the component-scan instruction level.
    	 * <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
    	 * @see ScopedProxyMode
    	 */
    	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
    
    }
    
    5.1.2、说明
    作用:
    	用于指定bean对象的作用范围。
    属性:
    	value: 
    		指定作用范围的取值。在注解中默认值是""。
    		但是在spring初始化容器时,会借助ConfigurableBeanFactory接口中的类成员:
    		String SCOPE_SINGLETON = "singleton";
    	scopeName:
    		它和value的作用是一样的。
    	proxyMode:
    		它是指定bean对象的代理方式的。指定的是ScopedProxyMode枚举的值
                DEFAULT:默认值。(就是NO)
                NO:不使用代理。
                INTERFACES:使用JDK官方的基于接口的代理。
                TARGET_CLASS:使用CGLIB基于目标类的子类创建代理对象。
    使用场景:
    	在实际开发中,我们的bean对象默认都是单例的。通常情况下,被spring管理的bean都使用单例模式来创建。但是也有例外,例如Struts2框架中的Action,由于有模型驱动和OGNL表达式的原因,就必须配置成多例的。
    
    5.1.3、示例

    5.2、@PostConstruct

    5.2.1、源码
    @Documented
    @Retention (RUNTIME)
    @Target(METHOD)
    public @interface PostConstruct {
    }
    
    5.2.2、说明
    作用:
    	用于指定bean对象的初始化方法。
    属性:
    	无
    使用场景:
    	在bean对象创建完成后,需要对bean中的成员进行一些初始化的操作时,就可以使用此注解配置一个初始化方法,完成一些初始化的操作。
    
    5.2.3、示例

    5.3、@PreDestroy

    5.3.1、源码
    @Documented
    @Retention (RUNTIME)
    @Target(METHOD)
    public @interface PreDestroy {
    }
    
    5.3.2、说明
    作用:
    	用于指定bean对象的销毁方法。
    属性:
    	无
    使用场景:
    	在bean对象销毁之前,可以进行一些清理操作。
    
    5.3.3、示例
  • 相关阅读:
    Spark学习笔记1
    Scala学习笔记
    Scala实现网站流量实时分析
    使用Docker搭建Spark集群(用于实现网站流量实时分析模块)
    使用Docker搭建Hadoop集群(伪分布式与完全分布式)
    Docker配置阿里云镜像源
    从centos7镜像到搭建kubernetes集群(kubeadm方式安装)
    来做一个简单的成绩查询!(输入输出)
    String类的知识点(不断更新)
    人机猜拳游戏Java
  • 原文地址:https://www.cnblogs.com/weijiqian/p/16394278.html
Copyright © 2020-2023  润新知