回顧:mysql
package cn.itcast.service;spring
public interface UserService {sql
/**數據庫
* 業務層:用戶保存apache
*/session
public void saveUser();app
}框架
package cn.itcast.service.impl;ide
import cn.itcast.service.UserService;單元測試
public class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("業務層:用戶保存...");
}
}
@Component("userService")
public class UserServiceImpl implements UserService{
public void saveUser() {
System.out.println("業務層:用戶保存...");
}
}
在src目錄下,建立applicationContext.xml的配置文件,引入約束。注意:由於如今想使用註解,那麼引入的約束髮生了變化,須要context的約束。同時還要引入log4j.properties.
【提示】:約束能夠從spring開發文檔中拷貝,也能夠從筆記裏拷貝。若是從spring開發文檔中拷貝,能夠參考spring開發文檔的6.9節
添加完context約束以後的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">
</beans>
在applicationContext.xml經過context:component-scan標籤開啓spring註解掃描,掃描時是以包範圍來掃描的:
<?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">
<!-- 開啓註解掃描 -->
<context:component-scan base-package="cn.itcast.service.impl"></context:component-scan>
</beans>
/**
* 測試註解
*/
public void test1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.saveUser();
}
Spring中用於管理bean的註解分爲四大類:
用於建立對象的有四個:@Component,@Controller,@Service,@Repository
做用:
把資源讓spring來管理。至關於在xml中配置一個bean。
屬性:
value:指定bean的id。若是不指定value屬性,默認bean的id是當前類的類名。首字母小寫。
他們三個註解都是針對一個的衍生註解,他們的做用及屬性都是如出一轍的。
他們只不過是提供了更加明確的語義化。
@Controller:通常用於表現層的註解。
@Service:通常用於業務層的註解。
@Repository:通常用於持久層的註解。
@Service的用法:修改UserServiceImpl類,把@Component改爲@Service
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService{
@Override
public void saveUser() {
System.out.println("業務層:用戶保存...");
}
}
@Repository的用法:
建立UserDao接口:
public interface UserDao {
public void save();
}
建立UserDao接口的實現類UserDaoImpl,在該類上加@Repository註解
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("持久層:用戶保存...");
}
}
注意:此處測試時,要把掃描的包定義爲cn.itcast,否則的其它包的註解就不能識別了
<?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">
<!-- 開啓註解掃描 -->
<context:component-scan base-package="cn.itcast"></context:component-scan>
</beans>
@Controller的用法:建立UserAction類,在該類上加@Controller註解
@Controller("userAction")
public class UserAction {
}
說明:這三個註解是爲了讓標註類自己的用途清晰
用於注入數據的註解有:
至關於:<property name="" ref="">
<property name="" value="">
做用:
注入基本數據類型和String類型數據的
屬性:
value:用於指定值
修改UserServiceImpl類,增長一個字符串屬性name,如今要經過@Value給name屬性注入值
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("張三")
private String name;
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
}
}
運行TestIOC中的test1方法,測試結果以下:
做用:
自動按照類型注入。當使用註解注入屬性時,set方法能夠省略。它只能注入其餘bean類型。當有多個類型匹配時,使用要注入的對象變量名稱做爲bean的id,在spring容器查找,找到了也能夠注入成功。找不到就報錯。
修改UserServiceImpl類,增長一個對象屬性userDao,如今經過@Autowired給userDao注入值
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("張三")
private String name;
@Autowired
private UserDao userDao;
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
userDao.save();
}
}
運行TestIOC中的test1方法,測試結果以下:
做用:
在自動按照類型注入的基礎之上,再按照Bean的id注入。它在給字段注入時不能獨立使用,必須和@Autowire一塊兒使用;可是給方法參數注入時,能夠獨立使用。
屬性:
value:指定bean的id。
@Repository("userDao2")
public class UserDaoImpl2 implements UserDao {
@Override
public void save() {
System.out.println("持久層:用戶保存2222...");
}
}
運行TestIOC中的test1方法,測試結果以下:
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("張三")
private String name;
@Autowired
@Qualifier("userDao2")
private UserDao userDao;
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
userDao.save();
}
}
再次運行TestIOC中的test1方法,發現UserServiceImpl中注入的是UserDaoImpl2;測試結果以下:
做用:
直接按照Bean的id注入。它也只能注入其餘bean類型。
屬性:
name:指定bean的id。
修改UserServiceImpl類,使用@Resource給userDao注入值。@Resource是按照bean的id來注入,只能注入對象類型
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("張三")
private String name;
// @Autowired
// @Qualifier("userDao2")
@Resource(name="userDao2")
private UserDao userDao;
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
userDao.save();
}
}
做用:
指定bean的做用範圍。
屬性:
value:指定範圍的值。
取值:singleton prototype request session globalsession
@Service("userService")
@Scope("prototype")
public class UserServiceImpl implements UserService{
@Value("張三")
private String name;
// @Autowired
// @Qualifier("userDao2")
@Resource(name="userDao2")
private UserDao userDao;
public UserServiceImpl() {
System.out.println("調用了無參構造方法...");
}
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
userDao.save();
}
}
@Test
public void test2(){
//建立ioc容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService) ac.getBean("userService");
UserService userService2 = (UserService) ac.getBean("userService");
System.out.println(userService1 == userService2);
}
至關於:<bean id="" class="" init-method="" destroy-method="" />
@PostConstruct加在方法上,指定bean對象建立好以後,調用該方法初始化對象,相似於xml的init-method方法。修改UserServiceImpl類,在其中增長一個init方法,在該方法上指定@PostConstruct註解
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("張三")
private String name;
// @Autowired
// @Qualifier("userDao2")
@Resource(name="userDao2")
private UserDao userDao;
public UserServiceImpl() {
System.out.println("調用了無參構造方法...");
}
@PostConstruct
public void init(){
System.out.println("調用了init方法...");
}
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
userDao.save();
}
}
運行TestIOC中的test1方法,測試結果以下:
@PreDestory加在方法上,指定bean銷燬以前,調用該方法,相似於xml的destory-method方法。修改UserServiceImpl類,在該類中增長一個destroy方法,在該方法上加@PreDestroy註解
@Service("userService")
public class UserServiceImpl implements UserService {
@Value("張三")
private String name;
// @Autowired
// @Qualifier("userDao2")
@Resource(name="userDao2")
private UserDao userDao;
public UserServiceImpl() {
System.out.println("調用了無參構造方法...");
}
@PostConstruct
public void init(){
System.out.println("調用了init方法...");
}
@Override
public void saveUser() {
System.out.println("業務層:用戶保存..." + name);
userDao.save();
}
@PreDestroy
public void destroy(){
System.out.println("調用了destroy方法...");
}
}
注意:要看到@PreDestory的效果,須要調用ClassPathXmlApplicationContext.close方法,同時scope的值要是singleton。因此,還得修改test1方法,顯示關閉ioc容器
@Test
public void test1(){
//建立ioc容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.saveUser();
((ClassPathXmlApplicationContext)ac).close();
}
測試結果以下:
註解的優點:
配置簡單,維護方便。(咱們找到了類,就至關於找到了配置)
XML的優點:
修改時,不用改源碼。不涉及從新編譯和部署。
Xml和註解的比較
須要導入的jar包有:spring ioc必要的六個jar包+c3p0+mysql驅動包。完成的jar包以下圖所示:
建立Customer實體類
建立CustomerService接口
public interface CustomerService {
/**
* 業務層:查詢全部客戶
* @return
*/
public List<Customer> findAllCustomer();
}
建立CustomerService接口的實現類CustomerServiceImpl
public class CustomerServiceImpl implements CustomerService {
private CustomerDao customerDao;
public void setCustomerDao(CustomerDao customerDao) {
this.customerDao = customerDao;
}
@Override
public List<Customer> findAllCustomer() {
List<Customer> list = customerDao.findAll();
return list;
}
}
建立UserDao接口
public interface CustomerDao {
public List<Customer> findAll();
}
建立UserDao接口的實現類
public class CustomerDaoImpl implements CustomerDao {
private QueryRunner queryRunner;//不須要實例化,經過spring依賴注入進來
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
@Override
public List<Customer> findAll() {
List<Customer> list = null;
try {
list = queryRunner.query("select * from cst_customer", new BeanListHandler<Customer>(Customer.class));
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
在src下建立spring的配置文件applicationContext.xml,把Service、Dao、QueryRunner、DataSource配置到Spring中
<?xml version="1.0" encoding="UTF-8"?>
<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">
<bean id="customerService" class="cn.itcast.service.impl.CustomerServiceImpl">
<property name="customerDao" ref="customerDao"></property>
</bean>
<bean id="customerDao" class="cn.itcast.dao.impl.CustomerDaoImpl">
<property name="queryRunner" ref="queryRunner"></property>
</bean>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<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/hibernate"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
注意:還得引入log4j.properties文件
建立單元測試類TestFind,在其中建立test1方法
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
CustomerService customerService = (CustomerService) ac.getBean("customerService");
List<Customer> customers = customerService.findAllCustomer();
for (Customer customer : customers) {
System.out.println(customer);
}
}
在這裏測試的時候,實體類的屬性名字要和數據庫的屬性名字一一對應,否則數據不能回顯;
測試結果以下:
能夠把第四章的工程裏直接改爲註解的形式
注意:註解開發須要額外導入spring-aop.jar包
修改CustomerServiceImpl類,在該類上加@Service註解。在customerDao上加@Autowired註解,表示給該屬性注入值
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerDao customerDao;
@Override
public List<Customer> findAllCustomer() {
List<Customer> list = customerDao.findAllCustomer();
return list;
}
}
修改CustomerDaoImpl類,在該類上加@Repository註解。在queryRunner上加@Autowired註解,表示給該屬性注入值。
@Repository("customerDao")
public class CustomerDaoImpl implements CustomerDao {
@Autowired
private QueryRunner queryRunner;
@Override
public List<Customer> findAll() {
List<Customer> list = null;
try {
list = queryRunner.query("select * from cst_customer", new BeanListHandler<Customer>(Customer.class));
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
在applicationContext.xml中開啓spring註解掃描。
注意:QueryRunner和DataSource這兩個bean暫時沒有辦法用註解來配置,由於屬於jar包裏的bean.
<?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">
<context:component-scan base-package="cn.itcast"></context:component-scan>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<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/hibernate"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
再次運行TestFind中的test1方法,結果和xml的配置是同樣的。
全註解整合的思路:要想實現全註解,得把applicationContext.xml的全部內容用一個類來配置。@Configuration註解能夠表示一個類是配置類;@ComponentScan註解能夠實現包掃描;還得想辦法把QueryRunner和DataSource這兩個bean用註解來配置。這個兩個bean都是來自於jar包中的,用傳統的註解配置方法是不可能實現的。得用@Bean註解,這個註解能夠把某個方法的返回值存放到spring容器中。咱們能夠寫兩個方法,這兩個方法分別返回QueryRunner對象和DataSource對象,而後在這兩個方法上加@Bean註解,就能夠保證spring容器中QueryRunner和DataSource這兩個對象了。
@Configuration//指定該類是spring的配置類,用來替換bean.xml
@ComponentScan("cn.itcast")//指定要掃描的包
public class SpringConfig {
/**
* Bean註解:把方法的返回值交給srping容器來管理
* @param ds
* @return
*/
@Bean(name="queryRunner")
public QueryRunner createQueryRunner(@Qualifier("dataSource") DataSource ds){
return new QueryRunner(ds);
}
@Bean(name="dataSource")
public DataSource createDataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/hibernate");
dataSource.setUser("root");
dataSource.setPassword("123456");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
}
由於已經用SpringConfig類來代替applicationContext.xml了,因此,得更換ApplicationContext接口的實現類爲AnnotationConfigApplicationContext
@Test
public void test1(){
// ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
CustomerService customerService = (CustomerService) ac.getBean("customerService");
List<Customer> customers = customerService.findAllCustomer();
for (Customer customer : customers) {
System.out.println(customer);
}
}
運行test1方法,發現結果和以前是一致的。
下面,能夠考慮優化SpringConfig配置類。在SpringConfig類中可能會出現不少方法,能夠考慮實現分離。建立另外一個配置類JdbcConfig,把createQueryRunner和createDataSource方法挪到JdbcConfig中:
public class JdbcConfig {
/**
* Bean註解:把方法的返回值交給srping容器來管理
* @param ds
* @return
*/
@Bean(name="queryRunner")
public QueryRunner createQueryRunner(@Qualifier("dataSource") DataSource ds){
return new QueryRunner(ds);
}
@Bean(name="dataSource")
public DataSource createDataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/hibernate");
dataSource.setUser("root");
dataSource.setPassword("123456");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
}
在SpringConfig中引入JdbcConfig
@Configuration//指定該類是spring的配置類,用來替換bean.xml
@ComponentScan("cn.itcast")//指定要掃描的包
@Import(JdbcConfig.class)
public class SpringConfig {
}
此時,咱們發現,數據庫的驅動類名、鏈接地址、用戶名、密碼都是直接寫死在類中的,不便於修改,能夠把與數據庫相關的信息寫到jdbc.properties中。在src下建立jdbc.properties文件:
jdbc.properties內容以下:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hibernate_itheima14
jdbc.username=root
jdbc.password=123456
修改JdbcConfig,定義四個成員變量,在每一個成員變量上加@value註解,爲該變量注入值,值來自於jdbc.properties.${}中寫的是jdbc.properties中的鍵。
public class JdbcConfig {
@Value("${jdbc.driverClass}")
private String driverClass;
@Value("${jdbc.url}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name="queryRunner")//把該方法的返回值放到ioc容器中,等價於<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
public QueryRunner createQueryRunner(@Qualifier("dataSource") DataSource ds){
return new QueryRunner(ds);
}
@Bean(name="dataSource")//把該方法的返回值放到ioc容器中, <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
public DataSource createDataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource .setDriverClass(driverClass);
dataSource .setJdbcUrl(jdbcUrl);
dataSource .setUser(username);
dataSource .setPassword(password);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return ds;
}
}
在SpringConfig中須要經過@PropertySource註解引入外部屬性文件jdbc.properties;還要在SpringConfig類中配置一個佔位符$的解析器,這樣才能解析${jdbc.username}這樣的表達式
@Configuration//指定該類是spring的配置類,用來替換bean.xml
@ComponentScan("cn.itcast")//指定要掃描的包
@Import(JdbcConfig.class)
@PropertySource("classpath:cn/itcast/config/jdbc.properties")
public class SpringConfig {
@Bean
public PropertySourcesPlaceholderConfigurer createPropertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
最後,再次運行test1方法進行測試。
爲了簡化了JUnit的測試,使用Spring框架也能夠整合測試。
要求:必須先有JUnit的環境(默認會使用Eclipse導入單元測試的環境)!!
能夠直接在spring4_day02中引入spring-test.jar包,把spring4_day02中的單元測試類所有改爲spring的寫法
注意:spring單元測試還得要有spring-aop.jar包,由於spring單元測試會用到註解
修改單元測試類TestIOC,在該類上添加@RunWith和@ContextConfiguration兩個註解。咱們須要測試的是UserService,在TestIOC中聲明一個UserService的屬性,並在該屬性上添加@Autowired註解,爲該屬性注入值。修改後的TestIOC以下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestIOC {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.saveUser();
}
}
spring4_day02_springAndDBUtilsFullAnnotation這個工程是一個全註解的工程,沒有applicaitonContext.xml文件,那麼@ContextConfiguration註解就沒辦法指定applicationContext.xml了,只能指定SpringConfig這個配置類了。@ContextConfiguration中的classes屬性指定配置類。修改TestFind單元測試類以下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SpringConfig.class)//指定加載配置類建立ioc容器
public class TestFind {
@Autowired
private CustomerService customerService;
@Test
public void test1(){
List<Customer> list = customerService.findAllCustomer();
for (Customer customer : list) {
System.out.println(customer);
}
}
}