初识Spring学习笔记---基于注解的IOC配置

一、Spring中基于注解的IOC配置

1.概述

基于注解的IOC配置即注解配置和xml配置要实现的功能都是一样,都是降低程序间的耦合。只是配置形式不一样。

2.环境搭建

在IDEA中创建maven项目,写入依赖。

    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>

3.学习内容

* 曾经xml的配置:
* <bean id="accountService"  class="com.wuhao.service.implement.AccountServiceImpl" scope="" init-method="" destroy-method="" >
*     <property name="" value="" | ref="" ></property>
* </bean>
*
* 用于创建对象的
*       他们的作用就和在xml配置文件中编写一个<bean>标签实现的功能是一样的
* 用于注入数据的
*       他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* 用于改变作用范围的
*       他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* 和生命周期相关
*       他们的作用就和在<bean>标签中使用init-method和destroy-method的作用是一样的

二、具体内容

1.用注解创建对象

*       @Component:
*           作用:用于把当前类对象存入spring容器中  (spring容器是map类型,存在key和value)
*           属性:
*                value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
*       @Controller :一般用在表现层
*       @Service    :一般用在业务层
*       @Repository :一般用在持久层
*       以上三个注解他们的作用和属性与Component是一模一样。
*       他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰

配置文件bean.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
     <!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束中-->
    <context:component-scan base-package="com.wuhao"></context:component-scan>
</beans>

 实现类AccountServiceImpl:

@Component(value = "accountService")  //当只有一个属性时可以不写value : @Component("accountService")
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

    public AccountServiceImpl(){
        System.out.println("对象已创建");
    }

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

}

 表现层实现:

    public static void main(String[] args) {

        //1.获取核心容器对象
        ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");

        //2.根据id获取Bean对象
        IAccountService as=(IAccountService)ac.getBean("accountService");
        IAccountDao adao=ac.getBean("accountDao",IAccountDao.class);
        System.out.println(as);
        System.out.println(adao);

        //as.saveAccount();
        

    }

2.用注解注入数据

1)Autowired

*       @Autowired:
*           *作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就注入成功。
*                  如果IOC容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
*                  如果IOC容器中有多个类型匹配时,首先按照数据类型找出匹配对象,然后使用变量名称作为bean的id与IOC容器中的key进行匹配,找出是否有对应的bean。
*           出现的位置:
*                 可以是变量上,也可以是方法上
*           细节:
*               在使用注解注入时,set方法就不是必须的

自动按照类型注入:

解释:  如果IOC容器中有多个类型匹配时,首先按照数据类型找出匹配对象,然后使用变量名称作为bean的id与IOC容器中的key进行匹配,找出是否有对应的bean。

存在两个IAccountDao接口的实现类:IAccountDaoImpl,IAccountDaoImpl2

@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {


    @Override
    public void saveAccount() {
        System.out.println("保存了账户1111");

    }
}

@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {


    @Override
    public void saveAccount() {
        System.out.println("保存了账户2222");

    }
}

 现在IAccountService接口的实现类IAccountServiceImpl调用IAccountDao接口的实现类:

@Component(value = "accountService")  //当只有一个属性时可以不写value : @Component("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao=null;

    public AccountServiceImpl(){
        System.out.println("对象已创建");
    }

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

}

 此时注:数据类型IAccountDao的变量名称为accountDao,而在数据类型为IAccountDao的两个实现类中声明对象为accountDao1和accountDao2。

执行表现层:

    public static void main(String[] args) {

        //1.获取核心容器对象
        ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");

        //2.根据id获取Bean对象
        IAccountService as=(IAccountService)ac.getBean("accountService");
//        IAccountDao adao=ac.getBean("accountDao",IAccountDao.class);
//        System.out.println(as);
//        System.out.println(adao);

        as.saveAccount();
        

    }

 报错:

报错原因就是找见了2个匹配的bean对象。

所以只要修改IAccountService接口的实现类就行:

将 @Autowired
    private IAccountDao accountDao=null;

改为 @Autowired
    private IAccountDao accountDao1=null;  或 

   @Autowired
    private IAccountDao accountDao2=null;

2)Qualifier

*       @Qualifier:
*           作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是再给方法参数注入时可以。
*           属性:
*               value:用于指定注入bean的id。
*           必须与@Autowired一起使用
@Component(value = "accountService")  //当只有一个属性时可以不写value : @Component("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    @Qualifier("accountDao1")
    private IAccountDao accountDao=null;

    public AccountServiceImpl(){
        System.out.println("对象已创建");
    }

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

}
*       以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
*       另外,集合类型的注入只能通过xml来实现

3)value
*       @Value:
*           作用:用于注入基本类型和String类型的数据
*           属性:
*                value:用于指定数据的值,它可以使用spring中SpEL(也就是spring的el表达式)
*                       SpEL的写法:${表达式}

3.用注解改变作用范围

*       Scope:
*           作用:用于指定bean的作用范围
*           属性:
*                value:指定范围的取值。 常用取值:singleton  prototype

IAccountService接口实现类:

public class AccountServiceImpl implements IAccountService {

//    @Autowired
//    @Qualifier("accountDao1")
    @Resource(name="accountDao1")
    private IAccountDao accountDao=null;

    public AccountServiceImpl(){
        System.out.println("对象已创建");
    }

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

}

 表现层:

    public static void main(String[] args) {

        //1.获取核心容器对象
        ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");

        //2.根据id获取Bean对象
        IAccountService as1=(IAccountService)ac.getBean("accountService");
        IAccountService as2=(IAccountService)ac.getBean("accountService");


        System.out.println(as1==as2);



    }

结果:

 修改IAccountService接口实现类,加入scope(prototype):

@Component(value = "accountService")  //当只有一个属性时可以不写value : @Component("accountService")
@Scope("prototype")
public class AccountServiceImpl implements IAccountService {

//    @Autowired
//    @Qualifier("accountDao1")
    @Resource(name="accountDao1")
    private IAccountDao accountDao=null;

    public AccountServiceImpl(){
        System.out.println("对象已创建");
    }

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

}

结果:

4.用注解修改生命周期

*       @PreDestroy
*           作用:用于指定销毁方法
*       @PostConstruct
*           作用:用于指定初始化方法

IAccountService接口实现类:

@Component(value = "accountService")  //当只有一个属性时可以不写value : @Component("accountService")
//@Scope("prototype")
public class AccountServiceImpl implements IAccountService {

//    @Autowired
//    @Qualifier("accountDao1")
    @Resource(name="accountDao1")
    private IAccountDao accountDao=null;

    public AccountServiceImpl(){
        System.out.println("对象已创建");
    }

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

    @PostConstruct
    public void init(){
        System.out.println("初始化方法执行了");

    }

    @PreDestroy
    public void destroy() {
        System.out.println("销毁方法执行了");

    }

}

表现层:

    public static void main(String[] args) {

        //1.获取核心容器对象
        ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取Bean对象
        IAccountService as1=(IAccountService)ac.getBean("accountService");

        as1.saveAccount();
        ac.close();

    }

结果:

三、Spring的新注解

Spring引入新注解的目的就是在基于注解的IOC配置中脱离XML文件。

未引入新注解时,用Spring连接数据库需创建XML文件,例如本实例中bean.xml:

    <!--告知Spring在创建容器时要扫描的包-->
    <context:component-scan base-package="com.wuhao"></context:component-scan>

    <!--配置QueryRunner-->
    <!--scope="prototype" 用来保证每次操作数据库都是一个新的对象,避免线程冲突-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds"  ref="dataSource"></constructor-arg>
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_study?serverTimezone=GMT"></property>
        <property name="user" value="root"></property>
        <property name="password" value="wh456159"></property>
    </bean>

数据库中表信息:

IAccountService接口的实现类AccountService:

@Scope("prototype")
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    @Resource(name = "accountDao")
    private IAccountDao accountDao;

    @Override
    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    @Override
    public Account findAcoountById(Integer accountId) {
        return accountDao.findAcoountById(accountId);
    }

    @Override
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);

    }

    @Override
    public void updateAccount(Account account) {
        accountDao.updateAccount(account);

    }

    @Override
    public void deleteAccount(Integer accountId) {
        accountDao.deleteAccount(accountId);

    }
}

 IAccountDao接口的实现类AccountDao:

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    private QueryRunner runner;


    @Override
    public List<Account> findAllAccount() {
        try {
            return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAcoountById(Integer accountId) {
        try {
            return runner.query("select * from account where id=?",new BeanHandler<Account>(Account.class),accountId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveAccount(Account account) {
        try {
            runner.update("insert into account(name,money)values(?,?)",account.getName(),account.getMoney());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateAccount(Account account) {
        try {
            runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteAccount(Integer accountId) {
        try {
            runner.update("delete from account where id=?",accountId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 1.configuration

*  @Configuration
*        作用:指定当前类是一个配置类
*        细节:当配置类作为AnnotationConfig的ApplicationContext对象创建的参数时,该注解可以不写。

2.ConponentScan


*  @ConponentScan
*        作用:用于通过注解指定spring在创建容器时要扫描的包
*        属性:
*            value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
*            我们使用此注解就等同于在xml中配置了:
*                 <context:component-scan base-package="com.wuhao"></context:component-scan>

有以上两个注解就可以去掉bean.xml文件中:  <context:component-scan base-package="com.wuhao"></context:component-scan>

我们创建一个配置类SpringConfiguration:

@Configuration
@ComponentScan("com.wuhao")
public class SpringConfiguration {  //主配置类



}

3.Bean


*  @Bean
*        作用:用于把当前方法的返回值作为bean对象存入Spring的ioc容器中
*        属性:
*             name:用于指定bean的id。 当不写时,默认值是当前方法的名称
*        细节:当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
*        查找的方法和Autowired注解的作用是一样的

然后我们在配置类中创建连接数据库所需的QueryRunner对象和数据源对象替换掉bean.xml文件中创建QueryRunner的操作:

    /**
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name = "queryRunner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }


    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        ComboPooledDataSource ds=new ComboPooledDataSource();
        try {
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring_study?serverTimezone=GMT");
            ds.setUser("root");
            ds.setPassword("wh456159");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

 此时需要修改测试类中获取bean对象的配置文件:

ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfiguration.class);
    @Test
    public void testFindAll(){
        //1.获取核心容器对象
//        ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfiguration.class);

        //2.根据id获取bean对象  (获取业务层对象)
        IAccountService as=ac.getBean("accountService",IAccountService.class);

        //3.执行方法
        List<Account> accounts=as.findAllAccount();
        for(Account account:accounts){
            System.out.println(account);
        }
    }
 

4.Import


*  @Import
*        作用:用于导入其他的配置类
*        属性:
*             value:用于指定其他配置类的字节码。
*                   当我们使用Import的注解之后,有Import注解的类就是父配置类,而导入的都是子配置类

当我们创建一个专门用来连接数据库的配置类JdbcConfig,我们就要在主配置类上加上注解Import,表示所引入的子类。(前提是引入的子类不出现在获取核心容器对象的方法参数中)

@Configuration
@ComponentScan("com.wuhao")
@Import(JdbcConfig.class)
public class SpringConfiguration {  //主配置类



}

5.PropertySource

*  @PropertySource
*        作用:用于指定properties文件的位置
*        属性:
*             value:指定文件的名称和路径。
*                     关键字:classpath,表示类路径

现在我们优化jdbcConfig配置类中:

            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring_study?serverTimezone=GMT");
            ds.setUser("root");
            ds.setPassword("wh456159");

创建一个jdbcConfig.properties文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_study?serverTimezone=GMT
jdbc.username=root
jdbc.password=wh456159

然后在主配置类上注明jdbcConfig.properties的路径:

@Configuration
@ComponentScan("com.wuhao")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {  //主配置类



}

JdbcConfig配置类修改:

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;


    /**
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name = "queryRunner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }


    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        ComboPooledDataSource ds=new ComboPooledDataSource();
        try {
//            ds.setDriverClass("com.mysql.jdbc.Driver");
//            ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring_study?serverTimezone=GMT");
//            ds.setUser("root");
//            ds.setPassword("wh456159");
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42070473/article/details/112497288
今日推荐