回顾: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);
}
}
}