Spring高级注解-Day2

学习目标

  • 学会Spring高级注解
  • 对应项目
    day3_anno_dependsOn
    day3_anno_lazy
    day3_anno_conditional
    day3_anno_profile
    day4_anno_basic
    day4_anno_jsr
    day4_anno_primary
    day4_anno_lifecycle

学习笔记

1 注入时机和设定注入条件的注解

1.1 @DependsOn

1.1.1 作用

  • 用于指定某个类的创建依赖的bean对象先创建。spring中没有特定bean的加载顺序,使用此注解则可指定bean的加载顺序。(在基于注解配置中,是按照类中方法的书写顺序决定的)

1.1.2 属性

  • value:用于指定bean的唯一标识。被指定的bean会在当前bean创建之前加载

1.1.3 基本使用

@Configuration
@ComponentScan("com.example")
public class SpringConfiguration {
}

SpringConfiguration.java

@Component
@DependsOn("eventListener")
public class EventSource {

    public EventSource(){
        System.out.println("事件源对象创建");
    }
}

EventSource.java

@Component
public class EventListener {

    public EventListener(){
        System.out.println("监听器创建");
    }
}

EventListener.java

public class SpringDependsOnTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        ac.start();
    }
}

SpringDependsOnTest.java

  • EventSource依赖于EventListener,所以先创建EventListener,后创建EventSource

1.2 @Lazy

1.2.1 作用

  • 用于指定单例bean对象的创建时机。在没有使用此注解时,单例bean的生命周期与容器相同。但是当使用了此注解之后,单例对象的创建时机变成了第一次使用时创建。注意:这不是延迟加载思想(因为不是每次使用时都创建,只是第一次创建的时机改变了)

1.2.2 属性

  • value: 指定是否采用延迟加载。默认值为true,表示开启

1.2.3 基本使用

@Configuration
@ComponentScan("com.example")
public class SpringConfiguration {
}

SpringConfiguration.java

@Component
@Lazy
public class LogUtil {
    public LogUtil(){
        System.out.println("LogUtil创建了");
    }
    public void printLog(){
        System.out.println("记录日志");
    }
}

LogUtil.java

public class SpringLazyTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        LogUtil logUtil = ac.getBean("logUtil", LogUtil.class);
        logUtil.printLog();
    }
}

SpringLazyTest.java

1.3 @Conditional

1.3.1 作用

  • 它的作用是根据条件选择注入的bean对象。

1.3.2 属性

  • value: 用于提供一个Condition接口的实现类,实现类中需要编写具体代码实现注入的条件。

1.3.3 基本使用

  • 自定义Condition实现bean的选择性注册
@Configuration
@Import(JdbcConfig.class)
@PropertySource({"classpath:jdbc.properties","classpath:linuxjdbc.properties"})
public class SpringConfiguration {
}

SpringConfiguration.java

/**
 * 自定义注册bean的条件,windows操作系统注入
 */
public class WindowsCondition implements Condition {
    /**
     * 是否注册到ioc容器中的和新方法
     *
     * @param conditionContext
     * @param annotatedTypeMetadata
     * @return 是true表示注册,否则不注册
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获取ioc使用的BeanFactory对象
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //获取类加载器
        ClassLoader classLoader = beanFactory.getBeanClassLoader();
        //获取环境信息,取出当前操作系统信息
        Environment environment = conditionContext.getEnvironment();
        //输出所有的系统环境信息
        if (environment instanceof StandardEnvironment) {
            //转换环境信息
            StandardEnvironment standardEnvironment = (StandardEnvironment) environment;
            Map<String, Object> map = standardEnvironment.getSystemEnvironment();
            for (Map.Entry<String, Object> me : map.entrySet()) {
                System.out.println(me.getKey() + " " + me.getValue());
            }
        }
        //获取bean定义信息的注册器
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //获取当前系统的名称
        String os = environment.getProperty("os.name");
        //判断是否包含Windows规则
        if (os.contains("Windows")) {
            //需要注册到ioc容器
            return true;
        }
        //不需要注册到ioc容器
        return false;
    }
}

WindowsCondition.java

public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获取环境信息,取出当前操作系统信息
        Environment environment = conditionContext.getEnvironment();
        //获取当前系统的名称
        String os = environment.getProperty("os.name");
        //判断是否包含Windows规则
        if (os.contains("Linux")) {
            //需要注册到ioc容器
            return true;
        }
        //不需要注册到ioc容器
        return false;
    }
}

LinuxCondition.java

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")
    @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("windows");
        return dataSource;
    }

    /**
     * 创建linux系统测试的数据源
     * @return
     */
    @Bean("dataSource")
    @Conditional(LinuxCondition.class)
    public DataSource createLinuxDataSource(@Value("${linuxjdbc.driver}") String linuxDriver,
                                       @Value("${linuxjdbc.url}") String linuxUrl,
                                       @Value("${linuxjdbc.username}") String linuxUsername,
                                       @Value("${linuxjdbc.password}") String linuxPassword){
        //创建数据源
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        //给数据源填充属性
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        System.out.println("linux");
        return dataSource;
    }
}

JdbcConfig.java

public class SpringConditionalTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        DataSource dataSource = ac.getBean("dataSource", DataSource.class);
        System.out.println(dataSource);
    }
}

SpringConditionalTest.java

1.4 @Profile

1.4.1 作用

  • @Profile注解是spring提供的一个用来标明当前运行环境的注解。我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次,尤其是从测试到上线这个环节,让测试的也不敢保证改了哪个配置之后能不能在线上运行。为了解决上面的问题,我们一般会使用一种方法,就是针对不同的环境进行不同的配置,从而在不同的场景中跑我们的程序。
  • 而spring中的@Profile注解的作用就体现在这里。在spring使用DI来注入的时候,能够根据当前制定的运行环境来注入相应的bean。最常见的就是使用不同的DataSource了。

1.4.2 基本使用

@Configuration
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfiguration {
}

SpringConfiguration.java

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;

    /**
     * 开发环境的数据源
     * @return
     */
    @Bean("dataSource")
    @Profile("dev")
    public DruidDataSource createDevDataSource(){
        //创建数据源对象
        DruidDataSource dataSource = new DruidDataSource();
        //设置属性
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        //开发环境的最大活动连接数
        dataSource.setMaxActive(5);

        return dataSource;
    }

    /**
     * 测试环境的数据源
     * @return
     */
    @Bean("dataSource")
    @Profile("test")
    public DruidDataSource createTestDataSource(){
        //创建数据源对象
        DruidDataSource dataSource = new DruidDataSource();
        //设置属性
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        //测试环境的最大活动连接数
        dataSource.setMaxActive(50);

        return dataSource;
    }

    /**
     * 生产环境的数据源
     * @return
     */
    @Bean("dataSource")
    @Profile("pro")
    public DruidDataSource createProDataSource(){
        //创建数据源对象
        DruidDataSource dataSource = new DruidDataSource();
        //设置属性
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        //生产环境的最大活动连接数
        dataSource.setMaxActive(150);

        return dataSource;
    }
}

JdbcConfig.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
@ActiveProfiles("pro")
public class SpringProfileTest {

    @Autowired
    private DruidDataSource druidDataSource;

    @Test
    public void testDataSource(){
        System.out.println(druidDataSource.getMaxActive());
    }
}

SpringProfileTest.java

2 用于创建对象的注解

2.1 @Component和三个衍生注解@Controller @Service @Repository

2.1.1 作用

  • 这四个注解都是用于修饰类的。是用于把当前类创建一个对象,存入spring的ioc容器中。在实例化时,首选默认无参构造函数。同时支持带参构造,前提是构造函数的参数依赖必须要有值。否则抛异常

2.1.2 属性

  • value:用于指定存入容器时bean的id。当不指定时,默认值为当前类的名称

3 用于注入数据的注解

3.1 @Autowired

3.1.1 作用

  • 自动按照类型注入。当ioc容器中有且只有一个类型匹配时可以直接注入成功。当有超过一个匹配时,则使用变量名称(写在方法上就是方法名称)作为bean的id,在符合类型的bean中再次匹配,能匹配上就可以注入成功。当匹配不上时,是否报错要看required属性的取值。

3.1.2 属性

  • required:是否必须注入成功。默认值是true,表示必须注入成功。当取值为true的时候,注入不成功会报错。

3.1.3 @Qualifier

3.1.3.1 作用
  • 当使用自动按类型注入时,遇到有多个类型匹配的时候,就可以使用此注解来明确注入哪个bean对象。注意它通常情况下都必须配置@Autowired注解一起使用
3.1.3.2 属性
  • value:用于指定bean的唯一标识。
3.1.3.3 问题
  • 必须要从容器中自动注入JdbcTemplate,否则在运行时会报空指针异常
@Autowired
private JdbcTemplate jdbcTemplate;
  • 自动注入的前提是JdbcTemplate已经注入到容器中
@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
    return new JdbcTemplate(dataSource);
}
  • 如果容器中有多个JdbcTemplate,优先找到与变量名与Bean的id相匹配的注入,如果不匹配,则会因为在容器中找到多个JdbcTemplate而报错
  • 可以在@Autowired后使用@Qualifier指定唯一标识解决多个JdbcTemplate的问题

3.3 @Resource

3.3.1 作用

  • 此注解来源于JSR规范(Java Specification Requests),其作用是找到依赖的组件注入到应用来,它利用了JNDI(Java Naming and Directory Interface Java命名目录接口 J2EE规范之一)技术查找所需的资源。
    默认情况下,即所有属性都不指定,它默认按照byType的方式装配bean对象。如果指定了name,没有指定type,则采用byName。如果没有指定name,而是指定了type,则按照byType装配bean对象。当byName和byType都指定了,两个都会校验,有任何一个不符合条件就会报错。

3.3.2 属性

  • name:资源的JNDI名称。在spring的注入时,指定bean的唯一标识。
  • type:指定bean的类型。
  • lookup:引用指向的资源的名称。它可以使用全局JNDI名称链接到任何兼容的资源。
  • authenticationType:指定资源的身份验证类型。它只能为任何受支持类型的连接工厂的资源指定此选项,而不能为其他类型的资源指定此选项。
  • shareable:指定此资源是否可以在此组件和其他组件之间共享。
  • mappedName:指定资源的映射名称。
  • description:指定资源的描述。

3.3.3 基本使用

@Service
public class AccountServiceImpl implements AccountService {

    @Resource(type = AccountDao.class,name = "accountDaoImplOne")
    private AccountDao accountDao;

    @Override
    public void save() {
        accountDao.save();
    }
}

AccountServiceImpl.java

@Repository
public class AccountDaoImplOne implements AccountDao {
    @Override
    public void save() {
        System.out.println("1保存");
    }
}

AccountDaoImplOne.java

@Repository
public class AccountDaoImplTwo implements AccountDao {
    @Override
    public void save() {
        System.out.println("2保存");
    }
}

AccountDaoImplTwo.java

public class SpringJsrTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        AccountService accountService = ac.getBean("accountServiceImpl", AccountService.class);
        accountService.save();
    }
}

SpringJsrTest.java

3.4 @Inject

3.4.1 作用

  • 它也是用于建立依赖关系的。和@Resource和@Autowired的作用是一样,没有属性

3.4.2 基本使用

@Service
public class AccountServiceImpl implements AccountService {

    //@Resource(type = AccountDao.class,name = "accountDaoImplOne")
    @Inject
    @Named("accountDaoImplOne")
    private AccountDao accountDao;

    @Override
    public void save() {
        accountDao.save();
    }
}

AccountServiceImpl.java

3.5 几种注入数据注解的区别

  • @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装配。

3.6 @Primary

3.6.1 作用

  • 用于指定bean的注入优先级。被@Primary修饰的bean对象优先注入,无属性

3.6.2 基本使用

  • 如果注入时没有指定名称,则优先使用@Primary注解的AccountDaoImplTwo
@Repository
@Primary
public class AccountDaoImplTwo implements AccountDao {
    @Override
    public void save() {
        System.out.println("2保存");
    }
}

AccountDaoImplTwo.java

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

4.1 @Scope

4.1.1 作用

  • 用于指定bean对象的作用范围。

4.1.2 属性

  • value:指定作用范围的取值。在注解中默认值是""。
    但是在spring初始化容器时,会借助ConfigurableBeanFactory接口中的类成员:
    String SCOPE_SINGLETON = “singleton”;
  • scopeName:它和value的作用是一样的。
  • proxyMode:它是指定bean对象的代理方式的。指定的是ScopedProxyMode枚举的值
    DEFAULT:默认值。(就是NO)
    NO:不使用代理。
    INTERFACES:使用JDK官方的基于接口的代理。
    TARGET_CLASS:使用CGLIB基于目标类的子类创建代理对象。

4.2 @PostConstruct

4.2.1 作用

  • 用于指定bean对象的初始化方法。无属性

4.3 @PreDestroy

4.3.1 作用

  • 用于指定bean对象的销毁方法。无属性

4.3.2 基本使用

@Component
public class LogUtil {
    public LogUtil(){
        System.out.println("对象创建");
    }

    @PostConstruct
    public void init(){
        System.out.println("对象初始化");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("对象销毁");
    }
}

LogUtil.java

public class SpringLifecycleTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
        LogUtil logUtil = ac.getBean("logUtil", LogUtil.class);
        System.out.println(logUtil);
        //关闭容器
        ac.close();
    }
}

SpringLifecycleTest.java

  • 对于单例会随着Spring容器的关闭销毁单例对象

上一篇:【二进制拆分 随机化】【GDOI2017 day2】凡喵识图


下一篇:【DP】【GDOI2017 day2】小学生语文题