Spring 是一个java 项目开发的框架技术
所谓框架可以看成一个项目的半成品,已经具备了一个项目项目的基本骨架部分,需要自己实现一些具体的内容
Spring官网
Spring技术的优势:
1、能够简化开发:
2、能够整合框架
Spring家族
Spring发展到今天已经形成了一套非常完整丰富的框架,市场上很多项目都基于Spring框架完成
初学Spring应该重点关注一下几种技术
Spring Framework 是Spring生态圈其他项目的基础
Spring4.0 系统架构
编写项目的时候因为实现功能往往需要new对象,导致耦合度偏高(如DAO层原来的实现类有问题,现在重新编写了一个,在Service层实现时,需要调用DAO层对象,这时就需要new一个新的DAO实现类对象,这样代码就改变了,需要重新编译,重新部署)
org.springframework
spring-context
5.2.10.RELEASE
public class APP2 {
public static void main(String[] args) {
// 获取 IoC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// // 获取bean
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.SaveAll();
}
}
public class BookServiceImpl implements BookService {
// 1 删除原来new 出来的Dao对象
private BookDao dao;
@Override
public void SaveAll() {
// BookDao dao = new BookDaoImpl();
dao.save();
System.out.println("BookServiceImpl …… All");
}
//2 提供对象set方法
public void setBookDao(BookDao bookDao){
this.dao = bookDao;
}
}
4、 可以重新运行APP2程序,输出一样的结果
spring bean对象的创建其实是用反射实现的,反射内部调用的是spring无参的构造方法
上面的案例使用反射机制 使用构造方法实例化bean对象的;创建实例化对象还可以采用静态工厂的方式创建,步骤如下
// 静态工厂
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
// Dao接口
public interface OrderDao {
public void order();
}
// Dao实现
public class OrderDaoImpl implements OrderDao {
@Override
public void order() {
System.out.println("order dao ...");
}
}
class指向工厂类对象,factory-menthod 指向 获取对象的方法。
public class APPForInstanceOrder {
public static void main(String[] args) {
// OrderDao orderDao = OrderDaoFactory.getOrderDao();
// orderDao.order();
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao dao = (OrderDao) ctx.getBean("orderDao");
dao.order();
}
}
实例工厂创建bean对象(工厂对象得到对象采用实例方法,不是静态方法,即去掉上述案例中public static OrderDao getOrderDao() 的 static) 这样要得到对象就必须创建工厂对象,然后再用工厂对象得到想要的对象
public class OrderDaoFactoryBean implements FactoryBean<OrderDao>{
public UserDao getObject() throws Exception{
return new OrderDaoImpl();
}
public Class<?> getObjectType(){
return UserDao.class;
}
public boolean isSingleton(){
return false;//如果是true或者默认不写这个函数,为单例
}
}
-如此可以简化伤处bean为
dao init...
service->dao set...
service init(afterPropertionSet)...
dao function...
service destroy...
dao destroy...
依赖注入的方式
setter 注入
setter注入:引用类型(最开始学的就是引用雷勇的依赖注入)
- 在bean中定义引用数据类型并提供对应的set方法
- 在配置文件中 使用property标签 ref 属性注入引用对象,多个引用就创建多个property标签
setter注入:简单类型
举例在 BookDaoImp中定义了基本类型的属性 ,并提供了setter方法
public class BookDaoImpl implements BookDao{
private int connectionNum;
private String databaseName;
public void book(){
System.out.println("Dao book...")
}
public void setConnectionNum(int connectionNum){
this.connectionNum=connectionNum;
}
public void setDatabaseName(String databaseName){
this.databaseName=databaseName;
}
}
这样的话在配置文件中,配置bean如下
构造器注入(使用构造方法注入)
构造方式的选择:
按类型
按名称(不推荐)
按构造方法(不推荐)
100
200
300
value1
以阿里的 druid 数据库连接池对象为例
在pom文件中引入依赖
com.alibaba
druid
1.1.16
在xml文件中管理相应的对象
在APP方法里面执行
public class APP3 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource ds = (DataSource) ctx.getBean("dataSource");
System.out.println(ds);
}
}
步骤1:准备properties配置文件
resources下创建一个jdbc.properties文件,并添加对应的属性键值对
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root
步骤2:开启context
命名空间
在applicationContext.xml中开context
命名空间
在配置文件中使用context
命名空间下的标签来加载properties配置文件
使用${key}
来读取properties配置文件中的内容并完成属性注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
@Component("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ..." );
}
}
<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">
<context:component-scan base-package="com.itheima"/>
beans>
当前类名首字母小写
,所以也可以按照名称获取,如@Controller
、@Service
、@Repository
SpringConfig
public class SpringConfig {
}
@Configuration
注解,将其标识为一个配置类,替换applicationContext.xml
@Configuration
public class SpringConfig {
}
在配置类上添加包扫描注解@ComponentScan
替换
@Configuration
@ComponentScan("com.taotao")
public class SpringConfig {
}
创建一个新的运行类AppForAnnotation
public class AppForAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
@Repository // 表示Dao层 的bean对象
//@Scope设置bean的作用范围
@Scope("prototype")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
@PostConstruct //在构造方法之后执行,替换 init-method
public void init() {
System.out.println("init ...");
}
@PreDestroy //在销毁方法之前执行,替换 destroy-method
public void destroy() {
System.out.println("destroy ...");
}
}
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao1 = ctx.getBean(BookDao.class);
BookDao bookDao2 = ctx.getBean(BookDao.class);
System.out.println(bookDao1);
System.out.println(bookDao2);
ctx.close(); //关闭容器
}
}
@Autowired
注解 即可@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
// public void setBookDao(BookDao bookDao) {
// this.bookDao = bookDao;
// }
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
@Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
为什么setter方法可以删除呢?
@Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ..." );
}
}
@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao {
public void save() {
System.out.println("book dao save ...2" );
}
}
@Qualifier
来指定注入哪个名称的bean对象。@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
@Value
注解,将值写入注解的参数中就行了 @Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("itheima")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
上述 value属性 会有一种感觉就是这个注解好像没什么用,跟直接赋值是一个效果,还没有直接赋值简单,所以这个注解存在的意义是什么?
@Value
一般会被用在从properties配置文件中读取内容进行使用,具体如何实现?
1、resource下准备properties文件
jdbc.properties
name=itheima888
2、使用注解加载properties配置文件,
在配置类上添加@PropertySource
注解
@Configuration
@ComponentScan("com.itheima")
@PropertySource("jdbc.properties")
public class SpringConfig {
}
3、使用@Value读取配置文件中的内容
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("${name}")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
前面定义bean的时候都是在自己开发的类上面写个注解就完成了,但如果是第三方的类,这些类都是在jar包中,我们没有办法在类上面添加注解,这个时候该怎么办?
遇到上述问题,我们就需要有一种更加灵活的方式来定义bean,这种方式不能在原始代码上面书写注解,一样能定义bean,这就用到了一个全新的注解==@Bean==。
在上述环境中完成对Druid
数据源的管理,具体的实现步骤为:
com.alibaba
druid
1.1.16
@Bean
注解 @Configuration
public class SpringConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
注意:不能使用DataSource ds = new DruidDataSource()
因为DataSource接口中没有对应的setter方法来设置属性。
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);
}
}
为了避免SpringConfig 配置类此类的外部配置项太多,一般还是要单独写一个配置类,对于数据源的bean,我们新建一个JdbcConfig
配置类,并把数据源配置到该类下。
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
现在的问题是,这个配置类如何能被Spring配置类加载到,并创建DataSource对象在IOC容器中?
针对这个问题,有两个解决方案:
方案一 :
方案二:
@Import
引入 @Configuration
//@ComponentScan("com.itheima.config")
@Import({JdbcConfig.class})
public class SpringConfig {
}
优化上述配置代码
public class JdbcConfig {
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("password")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
public class JdbcConfig {
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("password")
private String password;
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
第一件事是:Spring要管理MyBatis中的SqlSessionFactory
第二件事是:Spring要管理Mapper接口的扫描
具体该如何实现,具体的步骤为:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.0version>
dependency>
//配置类注解
@Configuration
//包扫描,主要扫描的是项目中的AccountServiceImpl类
@ComponentScan("com.itheima")
public class SpringConfig {
}
在配置类中完成数据源的创建
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}
public class MybatisConfig {
//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
//设置模型类的别名扫描
ssfb.setTypeAliasesPackage("com.itheima.domain");
//设置数据源
ssfb.setDataSource(dataSource);
return ssfb;
}
//定义bean,返回MapperScannerConfigurer对象
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
说明:
SqlSessionFactoryBean是前面我们讲解FactoryBean的一个子类,在该类中将SqlSessionFactory的创建进行了封装,简化对象的创建,我们只需要将其需要的内容设置即可。
方法中有一个参数为dataSource,当前Spring容器中已经创建了Druid数据源,类型刚好是DataSource类型,此时在初始化SqlSessionFactoryBean这个对象的时候,发现需要使用DataSource对象,而容器中刚好有这么一个对象,就自动加载了DruidDataSource对象。
使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
在运行类中,从IOC容器中获取Service对象,调用方法获取结果
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService accountService = ctx.getBean(AccountService.class);
Account ac = accountService.findById(1);
System.out.println(ac);
}
}
支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:
整合Junit与整合Druid和MyBatis差异比较大,为什么呢?Junit是一个搞单元测试用的工具,它不是我们程序的主体,也不会参加最终程序的运行,从作用上来说就和之前的东西不一样,它不是做功能的,看做是一个辅助工具就可以了。
这块环境,大家可以直接使用Spring与Mybatis整合的环境即可。当然也可以重新创建一个,因为内容是一模一样,所以我们直接来看下项目结构即可:
在上述环境的基础上,我们来对Junit进行整合。
pom.xml
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.10.RELEASEversion>
dependency>
在test\java下创建一个AccountServiceTest,这个名字任意
//设置类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//设置Spring环境对应的配置类
@ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件
public class AccountServiceTest {
//支持自动装配注入bean
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
System.out.println(accountService.findById(1));
}
@Test
public void testFindAll(){
System.out.println(accountService.findAll());
}
}
注意:
@ContextConfiguration(classes = 配置类.class)
@ContextConfiguration(locations={配置文件名,...})
SpringJUnit4ClassRunner