Spring框架——IOC注解方式

目录

Spring框架的IOC功能之注解的方式

Spring框架的IOC之注解方式的快速入门

Spring框架中Bean管理的常用注解

Bean的作用范围和生命周期的注解

案例演示

使用注解对上面的案例进行改造

使用Spring的新注解,完全替代applicationContext.xml中的配置

Spring框架整合JUnit单元测试


Spring框架的IOC功能之注解的方式


Spring框架的IOC之注解方式的快速入门

1. 步骤一:导入注解开发所有需要的jar包
    * 引入IOC容器必须的6个jar包
        com.springsource.org.apache.commons.logging-1.1.1.jar
        com.springsource.org.apache.log4j-1.2.15.jar
        spring-beans-5.0.2.RELEASE.jar
        spring-context-5.0.2.RELEASE.jar
        spring-core-5.0.2.RELEASE.jar
        spring-expression-5.0.2.RELEASE.jar
    * 多引入一个:Spring框架的AOP的jar包,spring-aop的jar包

如果使用了Maven直接添加Spring的jar包坐标即可,spring-aop的jar包的jar其实就依赖上了

        
                org.springframework
                spring-context
                5.0.2.RELEASE
        

2. 步骤二:创建对应的包结构,编写Java的类

​
​
    UserService         -- 接口
    UserServiceImpl     -- 具体的实现类
3. 步骤三:在src的目录下,创建applicationContext.xml的配置文件,然后引入约束。注意:因为现在想使用注解的方式,那么引入的约束发生了变化

* 需要引入context的约束,具体的约束如下

             
                    
                
            

4. 步骤四:在applicationContext.xml配置文件中开启组件扫描
    * Spring的注解开发:组件扫描
        
        
    * 注意:可以采用如下配置
         这样是扫描org.westos包下所有的内容
​
5. 步骤五:在UserServiceImpl的实现类上添加注解
    * @Component(value="userService")   -- 相当于在XML的配置方式中 
​
6. 步骤六:编写测试代码
    public class SpringDemo1 {
        @Test
        public void run1(){
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService us = (UserService) ac.getBean("userService");
            us.save();
        }
    }

Spring框架中Bean管理的常用注解

1. @Component:组件.(作用在类上)
​
2. Spring中提供@Component的三个衍生注解:(功能目前来讲跟@Component是一致的)
    * @Controller       -- 作用在WEB层
    * @Service      -- 作用在业务层
    * @Repository       -- 作用在持久层
    
    * 说明:这三个注解是为了让标注类本身的用途清晰,Spring在后续版本会对其增强
​
3. 属性注入的注解(说明:使用注解注入的方式,可以不用提供set方法)
    * 如果是注入的普通类型,可以使用value注解
        * @Value            -- 用于注入普通类型 作用在成员变量上面
        例如
        @Value(value="妹纸")
        private String name;   //setName 方法可以省略不写
    
    * 如果注入的是对象类型,使用如下注解
        * @Autowired        -- 默认按类型进行自动装配 注意这样的自动装配不好,我们应该按名称注入
            * 如果想按名称注入
            * @Qualifier    -- 强制使用名称注入
​
        例如:
        @Autowired
        @Qualifier(value = "cusDao")  //两个注解一起用
        private CustomerDao dao;    
    
    * @Resource             -- 相当于@Autowired和@Qualifier一起使用
        * 强调:Java提供的注解  简便写法
        * 属性使用name属性
        例如:
        @Resource(name = "cusDao")   //使用Java中的注解也可以,而且还少写一个
        private CustomerDao dao;

Bean的作用范围和生命周期的注解

1. Bean的作用范围注解  
    * 注解为@Scope(value="prototype"),作用在类上。值如下:
        * singleton     -- 单例,默认值
        * prototype     -- 多例
​
2. Bean的生命周期的配置(了解)作用在方法上
    * 注解如下:
        * @PostConstruct    -- 相当于init-method
        * @PreDestroy       -- 相当于destroy-method
​
        例如:
        @PostConstruct
        public void init(){
            System.out.println("serivce初始化方法执行了");
        }

案例演示

准备工作
1.创建Maven项目,在pom.xml文件中导入相应的jar包
    spring-context
    mysql-connector-java
    c3p0
    commons-dbutils
    junit 4.12 注意版本要用4.12
2.创建一张表,用于练习
    create table account(
    id int primary key auto_increment,
    name varchar(40),
    money float
)character set utf8 collate utf8_general_ci;
​
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
3.开始分包创建相应的类
  在org.westos.domain 包下创建一个实体类 Account.class
  
  public class Account implements Serializable {
    private Integer id;
    private String name;
    private Float money;
    //get set 方法略,自己补上
}
4.在org.westos.service 包下创建一个接口 IAccountService,提供crud的方法
​
public interface IAccountService {
    List findAll();//查询所有
    Account findAccountByID(Integer id);//根据id查询一个
    void saveAccount(Account account);//保存
    void updateAccount(Account account);//更新
    void deleteAccountByID(Integer id);//根据id删除
}
​
5. 写一个该接口的实现类IAccountServiceImpl,然后具体去实现crud的操作
​
6. 在org.westos.dao 包下创建一个IAccountDao 接口
   
   public interface IAccountDao {
    List findAll();//查询所有
​
    Account findAccountByID(Integer id);//根据id查询一个
​
    void saveAccount(Account account);//保存
​
    void updateAccount(Account account);//更新
​
    void deleteAccountByID(Integer id);//根据id删除
}
​
7.创建IAccountDao接口的实现类 IAccountDaoImpl
​
8.在IAccountServiceImpl中提供 IAccountDao dao 并提供set方法
 public class IAccountServiceImpl implements IAccountService {
    
    IAccountDao dao;
​
    public void setDao(IAccountDao dao) {
        this.dao = dao;
    }
    //其他方法 略
}
9. 在IAccountServiceImpl 中的方法中,调用dao中的方法
   public class IAccountServiceImpl implements IAccountService {
​
    IAccountDao dao;
​
    public void setDao(IAccountDao dao) {
        this.dao = dao;
    }
​
    public List findAll() {
        List list = dao.findAll();
        return list;
    }
​
    public Account findAccountByID(Integer id) {
        Account account = dao.findAccountByID(id);
        return account;
    }
​
    public void saveAccount(Account account) {
            dao.saveAccount(account);
    }
​
    public void updateAccount(Account account) {
            dao.updateAccount(account);
    }
​
    public void deleteAccountByID(Integer id) {
            dao.deleteAccountByID(id);
    }
}
​
10.在IAccountDaoImpl中提供 QueryRunner 的引用并提供set方法 使用他来完成crud操作
   public class IAccountDaoImpl implements IAccountDao {
    QueryRunner queryRunner;
​
    public QueryRunner getQueryRunner() {
        return queryRunner;
    }
​
    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }
​
    public List findAll() {
        List list = null;
        try {
            list = queryRunner.query("select * from account", new BeanListHandler(Account.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }
​
    public Account findAccountByID(Integer id) {
        Account account = null;
        try {
            account = queryRunner.query("select * from account where id=?", new BeanHandler(Account.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return account;
    }
​
    public void saveAccount(Account account) {
        try {
            queryRunner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
​
    public void updateAccount(Account account) {
        try {
            queryRunner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
​
    public void deleteAccountByID(Integer id) {
        try {
            queryRunner.update("delete from account where id=?",id);
        } catch (SQLException e) {
            
            e.printStackTrace();
        }
    }
    
}
​
11.在resources目录下 创建applicationContext.xml配置文件 引入约束
 
 
        
        
12.在applicationContext.xml配置文件中配置相应的类,以及注入
     
    
        
        
    
    
    
        
        
    
    
    
        
        
    
    
    
        
        
        
        
        
    
​
13.编写测试类,测试我们的crud 操作
public class TestAccountDao {
    @Test
    public void testFindAll(){
        //加载配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取dao对象
        IAccountDao dao = context.getBean("accountDao", IAccountDao.class);
        //执行查询所有的操作
        List list = dao.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }
   //其他测试略 留给学生测试    
}
​

使用注解对上面的案例进行改造

1.使用注解的话,需要一个新的约束

 
2.使用注解需要开启注解扫描
    
3.可以删除 dao和service的配置 只留下下面的
    
    
    
    
        
        
    
    
    
        
        
        
        
        
    
   
 4.在dao和service 上添加注解 并注入要使用的对象
 @Repository(value = "accountDao")
public class IAccountDaoImpl implements IAccountDao {
    @Autowired  //自动注入 采用注解注入set方法可以省略不写
    QueryRunner queryRunner;  
    ...
  }
  
@Service(value = "accountService")
public class IAccountServiceImpl implements IAccountService {
    @Autowired  //注入dao
    IAccountDao dao;
    ...
}  
​
5.测试

使用Spring的新注解,完全替代applicationContext.xml中的配置

通过上面的案例发现,我们不管是使用xml配置文件的方式,还是使用注解的方式,并没有完全脱离applicationContext.xml 这个配置文件。
那么我们想要完全脱离applicationContext.xml 这个配置文件中的配置,就需要使用一些新的注解。
1. 在org.westos.config 包下新创建一个类 SpringConfig 这个类就相当于要替代applicationContext.xml 这个配置文件。
​
2.在这个SpringConfig类上添加一个新的注解 @Configuration  代表这是一个配置类
@Configuration
public class SpringConfig {
​
}
​
3.我们再想办法把applicationContext.xml配置文件中的那行注解扫描,也使用注解的方式替代掉
 
 怎么替代上面这行注解扫描呢?
 我们可以使用一个新的注解@ComponentScan 表示通过此注解指定spring在容器时要扫描的包
 @ComponentScan(basePackages = "org.westos")
 @ComponentScan(value = "org.westos")
 这里ComponentScan注解的属性 value和basePackages 意思是一样的,使用哪个都行
 例子:
 @Configuration
@ComponentScan(value = "org.westos") //注解扫描
public class SpringConfig {
​
}
​
4.我们接下来再想办法,把applicationContext.xml中的下面这个配置,也想办法替换掉
  
        
        
    
也就是把创建 QueryRunner对象的操作使用注解替换掉
怎么做呢?我们在SpringConfig这个配置类中,提供一个方法,方法的返回值,就是返回一个QueryRunner对象
如下:
@Configuration
@ComponentScan(basePackages = "org.westos")
public class SpringConfig {
    public QueryRunner createQueryRunner(DataSource dataSource){//要一个数据源参数
        return new QueryRunner(dataSource); //new 一个QueryRunner对象返回
    }
}
如果我们仅仅只是提供了这么一个方法,方法返回一个QueryRunner对象,但是这个对象并没有放到spring容器中
所以我们还得在方法上使用一个注解 @Bean 表示把这个方法返回的这个对象,放到spring容器中
@Bean 这个注解有一个属性 name 表示用于指定bean的id,如果不写默认的值是当前方法的名称也就是 name的默认值是createQueryRunner 这个方法名
​
例子:
@Configuration
@ComponentScan(basePackages = "org.westos")
public class SpringConfig {
    @Bean(name = "queryRunner") 
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    
}
​
5.我们采用同样的方式,替换掉applicationContext.xml中数据源的配置
 
        
        
        
        
        
    
怎么替换上面的配置呢?
我们在SpringConfig这个配置类中,提供一个方法,方法的返回值是一个数据源对象
​
 //注意@Bean(name = "dataSource")中name的名字跟我们createQueryRunner(DataSource dataSource)方法中的参数名一致的话,这个dataSource对象就会自动注入到QueryRunner中
    @Bean(name = "dataSource") //把返回的数据源对象,放到spring容器中
    public DataSource createComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass("com.mysql.jdbc.Driver");
        ds.setJdbcUrl("jdbc:mysql:///spring_test");
        ds.setUser("root");
        ds.setPassword("123456");
        return ds;
    }
    
    
6.上面的操作做完之后,我们 applicationContext.xml 中配置的内容就可以删了

        里面的配置,可以删掉,全用注解替代了
 
​
7. 我们可以把applicationContext.xml这个配置文件删了,但是删了,我们要使用
    AnnotationConfigApplicationContex 这个类,来加载我们的SpringConfig这个类中的注解配置
8. 测试
     @Test
    public void testFindAll() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //传入我们配置类的字节码对象
        //加载配置文件 这种方式就可以注释掉了
        //ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取dao对象
        IAccountDao dao = context.getBean("accountDao", IAccountDao.class);
        //执行查询所有的操作
        List list = dao.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }
​
9.我们在使用xml文件配置的方式时,是把 QueryRunner配置成了一个多例的 所以我们把他配成一个多例的
     @Bean(name = "queryRunner")
    @Scope(value = "prototype") //配置成多例的
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
​
10.我们也可以设置多个配置类比如把SpringConfig这个类设置为一个主配置类,他里面比如可以设置一些公共的配置,然后我们在创建一个配置类比如 JDBCConfig这个配置类,然后我们把主配置类里面的配置拿到JDBCConfig这个配置类当中 如下
​
public class JDBCConfig {
    @Bean(name = "queryRunner")
    @Scope(value = "prototype") //配置成多例的
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
​
    @Bean(name = "dataSource")
    public DataSource createComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass("com.mysql.jdbc.Driver");
        ds.setJdbcUrl("jdbc:mysql:///spring_test");
        ds.setUser("root");
        ds.setPassword("123456");
        return ds;
    }
}
​
那么这时候我们主配置类中没有了配置
@Configuration
@ComponentScan(basePackages = "org.westos")
public class SpringConfig {
​
}
​
我们的测试代码,是加载的主配置类
 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //传入我们配置类的字节码对象
 
所以我们应该在主配置类中导入子配置类,怎么导入?使用一个 @Import(value = JDBCConfig.class) 的注解
注意该注解的value属性要的是一个 .class 类型,不是字符串类型
​
@Configuration
@ComponentScan(basePackages = "org.westos")
@Import(value = JDBCConfig.class) //导入子配置类的字节码对象
public class SpringConfig {
​
}
​
经过上面的配置说明Spring支持这种主配置类中可以导入子配置类的这种方式
​
最后我们在看一下哪里还有需要优化的地方,我们发现我们在创建数据源时,数据源的四个基本参数,在代码里写死了如下:
  @Bean(name = "dataSource")
    public DataSource createComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass("com.mysql.jdbc.Driver");
        ds.setJdbcUrl("jdbc:mysql:///spring_test");
        ds.setUser("root");
        ds.setPassword("123456");
        return ds;
    }
    
所以,我们把这四个基本参数抽离到一个配置文件中,那么我们在resources目录下创建一个jdbcConfig.properties 配置文件,内容如下
​
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_test
jdbc.username=root
jdbc.password=123456
​
然后我们再在JDBCConfig类中提供四个成员变量
然后在成员变量上面使用@Value注解来读取jdbcConfig.properties中的配置
然后在创建数据源的方法中直接使用四个成员变量
如下:
public class JDBCConfig {
    //注意这里的键的名称要和jdbcConfig.properties文件中的键名保持一致
    @Value(value = "${jdbc.driver}")
    private String driverClass;
    @Value(value = "${jdbc.url}")
    private String jdbcUrl;
    @Value(value = "${jdbc.username}")
    private String user;
    @Value(value = "${jdbc.password}")
    private String password;
    @Bean(name = "queryRunner")
    @Scope(value = "prototype") //配置成多例的
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
​
    @Bean(name = "dataSource")
    public DataSource createComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        //使用成员变量的值
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
}
​
最后要注意我们的jdbcConfig.properties配置文件要在主配置类里面读取进来,
怎么读取?可以使用一个注解 @PropertySource 来读取
这个classpath表类路径下的文件
@PropertySource(value = "classpath:jdbcConfig.properties")
​
代码如下:
@Configuration
@ComponentScan(basePackages = "org.westos")
@Import(value = JDBCConfig.class) //导入子配置类的字节码对象
@PropertySource(value = "classpath:jdbcConfig.properties") //读取类路径下的配置文件
public class SpringConfig {
​
}
​
​
11.最后一步测试即可
​
​
12.那么接下来再说最后一个小问题
 就是 createQueryRunner(DataSource dataSource)这个方法的要的这个数据源的参数名称和
    createComboPooledDataSource()方法上的 @Bean(name = "dataSource")配置的name的名称一样时,可以自动把这个数据源注入给QueryRunner,但是,如果说我们数据源的name名配置的和createQueryRunner(DataSource dataSource)不一样时该怎么办?或者说我有多个数据源对象,那到底把那个数据源注入给QueryRunner? 那么这个时候,我们可以在形参上使用一个注解@Qualifier(value = "ds"),让他按名称注入,而不是自动注入
    
  @Bean(name = "queryRunner")
    @Scope(value = "prototype") //配置成多例的
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
​
    @Bean(name = "dataSource")
    public DataSource createComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
    
    
  所以给  createQueryRunner(DataSource dataSource)方法的形参加上一个注解让他按名称注入
   @Bean(name = "queryRunner")
    @Scope(value = "prototype") //配置成多例的
    //@Qualifier(value = "ds")按照数据源的名称注入
    public QueryRunner createQueryRunner(@Qualifier(value = "ds") DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
​
   //@Bean(name = "dataSource")
    @Bean(name = "ds") //名字我改为ds了,让上面的方法安名称来注入这个数据源
    public DataSource createComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass(driverClass);
        ds.setJdbcUrl(jdbcUrl);
        ds.setUser(user);
        ds.setPassword(password);
        return ds;
    }
    
13.  最后再来测试    

Spring框架整合JUnit单元测试

1. 为了简化了JUnit的测试,使用Spring框架也可以整合测试
2. 具体步骤
    * 要求:必须先有JUnit的环境(即已经导入了JUnit4的开发环境)!!
     注意:如果用的是Spring5.x.x 版本,要求Junit的版本是Junit4.12版本及以上
    * 步骤一:在程序中引入:spring-test.jar
        如果是Maven项目则在pom.xml中引入spring-test.jar的坐标
        注意jar的依赖范围scope不要设置为test
       
            org.springframework
            spring-test
            5.0.2.RELEASE
        
         
    * 步骤二:在具体的测试类上添加注解
        @RunWith(SpringJUnit4ClassRunner.class)
        //如果我们使用的配置文件的方式,则加载类路径的配置文件
        //@ContextConfiguration("classpath:applicationContext.xml")
        //如果我们使用的是配置类的方式则加载配置类的字节码类型
        @ContextConfiguration(classes = SpringConfig.class)

代码如下:

    @RunWith(value = SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class TestAccountDao2 {
        @Autowired  //记得注入dao
        private IAccountDao dao;
        @Test
        public void testFindAll() {
            //执行查询所有的操作
            List list = dao.findAll();
            for (Account account : list) {
                System.out.println(account);
            }
        }
    }


你可能感兴趣的:(Spring,spring,java,后端)