Spring框架之bean的作用范围及生命周期的注解
Bean的作用范围注解
* 注解为@Scope(value="prototype"),作用在类上。值如下:
* singleton -- 单例,默认值
* prototype -- 多例
Bean的生命周期的配置(了解)作用在方法上
* 注解如下:
* @PostConstruct -- 相当于init-method
* @PreDestroy -- 相当于destroy-method
例如:
@PostConstruct
public void init(){
System.out.println("serivce初始化方法执行了");
}
案例演示
-
创建maven项目,在pom.xml中引入所需的依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <scope>compile</scope> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
2,创建一张account表
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.开始分包创建相应的类
在com.itheima.bean包中创建Account实体类 Account.class
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
//get set 方法略,自己补上
}
4.在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删除
}
-
写一个该接口的实现类IAccountServiceImpl,然后具体去实现crud的操作
-
在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;
}
//其他方法 略
}
- 在IAccountServiceImpl 中的方法中,调用dao中的方法
此处只展示一个
public List<Account> findAll() {
List<Account> list = dao.findAll();
return list;
}
10.在IAccountDaoImpl中提供 QueryRunner 的引用并提供set方法 使用他来完成crud操作,因为queryRunner只能采用构造方法的办法来提供注入依赖
QueryRunner queryRunner;
public QueryRunner getQueryRunner() {
return queryRunner;
}
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public List<Account> findAll() throws SQLException {
// System.out.println(queryRunner);
List<Account> list = queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
return list;
}
11.在resources目录下 创建applicationContext.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">
<!--配置数据源-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--配置四个属性值-->
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="jdbcUrl" value="jdbc:mysql:///spring_test"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
<!--配置QueryRunner-->
<bean class="org.apache.commons.dbutils.QueryRunner" id="queryRunner">
<!--注入数据源,只能采用构造的方法注入-->
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!--配置Dao-->
<bean class="com.itheima.dao.IAccountDaoImpl" id="accountDao">
<property name="queryRunner" ref="queryRunner"/>
</bean>
<!--配置service-->
<bean class="com.itheima.service.IAccountServiceImpl" id="accountService">
<!--注入Dao-->
<property name="accountDao" ref="accountDao"/>
</bean>
</beans>
13.编写测试类,测试我们的crud 操作
public class MyTest {
@Test
public void testFindAll() throws SQLException {
//读取配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContect.xml");
// Object accountService = context.getBean("accountService");
IAccountService accountService = context.getBean("accountService", IAccountService.class);
List<Account> list = accountService.findAll();
for (Account account : list) {
System.out.println(account);
}
}
}
开始使用注解的方式对上面的案例进行改造
- 使用注解的时候需要新的约束
<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="com.itheima">
</context:component-scan>
3.可以删除 dao和service的配置 只留下下面的
<!--使用注解需要开启注解扫描-->
<context:component-scan base-package="com.itheima">
</context:component-scan>
<!--配置数据源-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--配置四个属性值-->
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="jdbcUrl" value="jdbc:mysql:///spring_test"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
<!--配置QueryRunner 配置成多例的-->
<bean class="org.apache.commons.dbutils.QueryRunner" id="queryRunner" scope="prototype">
<!--注入数据源,只能采用构造的方法注入-->
<constructor-arg name="ds" ref="dataSource"/>
</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中的配置
-
在com.itheima.config 包下新创建一个类 SpringConfig这个类就相当于要替代applicationContext.xml 这个配置文件。
-
在这个SpringConfig类上添加一个新的注解 @Configuration代表这是一个配置类
@Configuration public class SpringConfig { }
-
我们再想办法把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 = "com.itheima")//注解扫描 public class SpringConfig { }
-
我们接下来再想办法,把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对象
如果我们仅仅只是提供了这么一个方法,方法返回一个QueryRunner对象,但是这个对象并没有放到spring容器中
所以我们还得在方法上使用一个注解 @Bean 表示把这个方法返回的这个对象,放到spring容器中
@Bean 这个注解有一个属性 name 表示用于指定bean的id,如果不写默认的值是当前方法的名称也就是 name的默认值是createQueryRunner 这个方法名
@Configuration
@ComponentScan(value = "com.itheima")//注解扫描
public class SpringConfig {
@Bean(name = "queryRunner")
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);//new 一个QueryRunner对象返回
}
5.我们采用同样的方式,替换掉applicationContext.xml中数据源的配置
我们在SpringConfig这个配置类中,提供一个方法,方法的返回值是一个数据源对象
@Bean(value = "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 中配置的内容就可以删了
- 我们可以把applicationContext.xml这个配置文件删了,但是删了,我们要使用
AnnotationConfigApplicationContex 这个类,来加载我们的SpringConfig这个类中的注解配置. - 测试
@Test
public void testFindAll() throws SQLException {
//传入我们配置类的字节码对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//获取dao对象
IAccountDao dao = context.getBean("accountDao", IAccountDao.class);
List<Account> list = dao.findAll();
for (Account account : list) {
System.out.println(account);
}
9.代码优化
创建数据源的时候发现四个基本参数是写死的,我们可以将参数抽离到一个配置文件中,那么我们在resources目录下创建一个jdbcConfig.properties 配置文件,内容如下
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_test
jdbc.username=root
jdbc.password=123456
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 = "ocom.itheima")
public class SpringConfig {
}
我们的测试代码,是加载的主配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //传入我们配置类的字节码对象
所以我们应该在主配置类中导入子配置类,怎么导入?使用一个 @Import(value = JDBCConfig.class) 的注解
注意该注解的value属性要的是一个 .class 类型,不是字符串类型
@Configuration
@ComponentScan(basePackages = "com.itheima")
@Import(value = JDBCConfig.class) //导入子配置类的字节码对象
public class SpringConfig {
}
然后我们再在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;
}
}
可以使用一个注解 @PropertySource 来读取
@PropertySource(value = "classpath:jdbcConfig.properties")
代码如下:
@Configuration
@ComponentScan(basePackages = "com.itheima")
@Import(value = JDBCConfig.class) //导入子配置类的字节码对象
@PropertySource(value = "classpath:jdbcConfig.properties") //读取类路径下的配置文件
public class SpringConfig {
}