目录
使用Spring的新注解,完全替代applicationContext.xml中的配置
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其实就依赖上了
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency>
2. 步骤二:创建对应的包结构,编写Java的类
UserService -- 接口 UserServiceImpl -- 具体的实现类
3. 步骤三:在src的目录下,创建applicationContext.xml的配置文件,然后引入约束。注意:因为现在想使用注解的方式,那么引入的约束发生了变化
* 需要引入context的约束,具体的约束如下
<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"> <!-- bean definitions here --> </beans>
4. 步骤四:在applicationContext.xml配置文件中开启组件扫描 * Spring的注解开发:组件扫描 <context:component-scan base-package="org.westos.demo1"/> * 注意:可以采用如下配置 <context:component-scan base-package="org.westos"/> 这样是扫描org.westos包下所有的内容 5. 步骤五:在UserServiceImpl的实现类上添加注解 * @Component(value="userService") -- 相当于在XML的配置方式中 <bean id="userService" class="..."> 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<Account> 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<Account> 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<Account> findAll() { List<Account> 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<Account> findAll() { List<Account> list = null; try { list = queryRunner.query("select * from account", new BeanListHandler<Account>(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>(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配置文件 引入约束 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans> 12.在applicationContext.xml配置文件中配置相应的类,以及注入 <!--配置dao--> <bean class="org.westos.dao.IAccountDaoImpl" id="accountDao"> <!--注入QueryRunner--> <property name="queryRunner" ref="queryRunner"></property> </bean> <!--配置service--> <bean class="org.westos.service.IAccountServiceImpl" id="accountService"> <!--注入dao--> <property name="dao" ref="accountDao"></property> </bean> <!--配置QueryRunner 注意配置成多例的--> <bean class="org.apache.commons.dbutils.QueryRunner" id="queryRunner" scope="prototype"> <!--注入数据源,只能使用构造方法注入--> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <!--配置数据源--> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <!--配置数据源的四个基本参数--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///spring_test"></property> <property name="user" value="root"></property> <property name="password" value="123456"></property> </bean> 13.编写测试类,测试我们的crud 操作 public class TestAccountDao { @Test public void testFindAll(){ //加载配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取dao对象 IAccountDao dao = context.getBean("accountDao", IAccountDao.class); //执行查询所有的操作 List<Account> list = dao.findAll(); for (Account account : list) { System.out.println(account); } } //其他测试略 留给学生测试 }
使用注解对上面的案例进行改造
1.使用注解的话,需要一个新的约束 <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"> </beans> 2.使用注解需要开启注解扫描 <context:component-scan base-package="org.westos"></context:component-scan> 3.可以删除 dao和service的配置 只留下下面的 <!--开启注解扫描--> <context:component-scan base-package="org.westos"></context:component-scan> <!--配置QueryRunner 注意配置成多例的--> <bean class="org.apache.commons.dbutils.QueryRunner" id="queryRunner" scope="prototype"> <!--注入数据源,只能使用构造方法注入--> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <!--配置数据源--> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <!--配置数据源的四个基本参数--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///spring_test"></property> <property name="user" value="root"></property> <property name="password" value="123456"></property> </bean> 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配置文件中的那行注解扫描,也使用注解的方式替代掉 <context:component-scan base-package="org.westos"></context:component-scan> 怎么替代上面这行注解扫描呢? 我们可以使用一个新的注解@ComponentScan 表示通过此注解指定spring在容器时要扫描的包 @ComponentScan(basePackages = "org.westos") @ComponentScan(value = "org.westos") 这里ComponentScan注解的属性 value和basePackages 意思是一样的,使用哪个都行 例子: @Configuration @ComponentScan(value = "org.westos") //注解扫描 public class SpringConfig { } 4.我们接下来再想办法,把applicationContext.xml中的下面这个配置,也想办法替换掉 <bean class="org.apache.commons.dbutils.QueryRunner" id="queryRunner" scope="prototype"> <!--注入数据源,只能使用构造方法注入--> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> 也就是把创建 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中数据源的配置 <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <!--配置数据源的四个基本参数--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///spring_test"></property> <property name="user" value="root"></property> <property name="password" value="123456"></property> </bean> 怎么替换上面的配置呢? 我们在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 中配置的内容就可以删了 <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"> 里面的配置,可以删掉,全用注解替代了 </beans> 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<Account> 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 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> * 步骤二:在具体的测试类上添加注解 @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<Account> list = dao.findAll(); for (Account account : list) { System.out.println(account); } } }