Beginning Spring学习笔记——第2章(一)Spring IoC容器
来源:互联网 发布:php curl下载远程图片 编辑:程序博客网 时间:2024/05/17 09:36
配置元数据(Configuration Metadata)
即我们常说的配置文件,用于实例化Bean和指定如何对Bean进行装配(接口的实现方式)。可基于Java或者基于XML配置。
作用示意
在庞大项目中常常把不同层的Bean配置放到不同的配置文件中,比如以下若干种配置文件
- beans-web.xml或ConfigurationForWeb.class对应Web层/表现层Bean配置
- beans-service.xml或ConfigurationForService.class对应服务层/业务层Bean配置
- beans-dao.xml或ConfigurationForDao.class对应数据访问层Bean配置
基于Java的Bean配置
首先创建一个maven项目,用quick start模板就好
展示一下目录结构
我的Maven包依赖如下,这些包基本可以满足这本书的非web应用
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework.samples</groupId> <artifactId>JdbcTemplateAndLambdaExpression</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <!-- Generic properties --> <java.version>1.6</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!-- Spring --> <spring-framework.version>4.3.10.RELEASE</spring-framework.version> <!-- Hibernate / JPA --> <hibernate.version>5.2.10.Final</hibernate.version> <!-- Logging --> <logback.version>1.2.3</logback.version> <slf4j.version>1.7.25</slf4j.version> <!-- Test --> <junit.version>4.12</junit.version> <!-- Database --> <h2.version>1.4.196</h2.version> </properties> <dependencies> <!-- Spring and Transactions --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-framework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring-framework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring-framework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring-framework.version}</version> </dependency> <!-- Logging with SLF4J & LogBack --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> <scope>runtime</scope> </dependency> <!-- Hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <!-- Test Artifacts --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring-framework.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${h2.version}</version> </dependency> </dependencies> </project>
首先,我们在src/main/java路径下创建一个com.wiley.beginning.spring.ch2程序包
先在该包下创建Account数据类
package com.wiley.beginningspring.ch2;import java.util.Date;public class Account { private long id; private String ownerName; private double balance; private Date accessTime; private boolean locked; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getOwnerName() { return ownerName; } public void setOwnerName(String ownerName) { this.ownerName = ownerName; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } public Date getAccessTime() { return accessTime; } public void setAccessTime(Date accessTime) { this.accessTime = accessTime; } public boolean isLocked() { return locked; } public void setLocked(boolean locked) { this.locked = locked; }}
该类(上一章提到的典型POJO!)中包含了Account数据类型的几个属性以及他们的getter和setter方法(由于没有定义含参构造方法,我们可以断定这个Bean是setter注入!)
为了完成Account数据的增改删查业务,我们创建AccountDao接口
package com.wiley.beginningspring.ch2;import java.util.List;public interface AccountDao { public void insert(Account account); public void update(Account account); public void update(List<Account> accounts); public void delete(long accountId); public Account find(long accountId); public List<Account> find(List<Long> accountIds); public List<Account> find(String ownerName); public List<Account> find(boolean locked);}
接下来是实现AccountDao接口的实现类的创建,为了不涉及数据库知识的方便起见,我们将数据直接储存在内存中,因此该实现类取名为AccounDaoInMemoryImpl
package com.wiley.beginningspring.ch2;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class AccountDaoInMemoryImpl implements AccountDao { private Map<Long,Account> accountsMap = new HashMap<>(); { Account account1 = new Account(); account1.setId(1L); account1.setOwnerName("John"); account1.setBalance(10.0); Account account2 = new Account(); account2.setId(2L); account2.setOwnerName("Mary"); account2.setBalance(20.0); accountsMap.put(account1.getId(), account1); accountsMap.put(account2.getId(), account2); } @Override public void insert(Account account) { accountsMap.put(account.getId(), account); } @Override public void update(Account account) { accountsMap.put(account.getId(), account); } @Override public void update(List<Account> accounts) { for(Account account:accounts) { update(account); } } @Override public void delete(long accountId) { accountsMap.remove(accountId); } @Override public Account find(long accountId) { return accountsMap.get(accountId); } @Override public List<Account> find(List<Long> accountIds) { List<Account> accounts = new ArrayList<>(); for(Long id:accountIds) { accounts.add(accountsMap.get(id)); } return accounts; } @Override public List<Account> find(String ownerName) { List<Account> accounts = new ArrayList<>(); for(Account account:accountsMap.values()) { if(ownerName.equals(account.getOwnerName())) { accounts.add(account); } } return accounts; } @Override public List<Account> find(boolean locked) { List<Account> accounts = new ArrayList<>(); for(Account account:accountsMap.values()) { if(locked == account.isLocked()) { accounts.add(account); } } return accounts; }}
该类将Account数据都储存在一个HashMap中,通过对HashMap中元素的查找和访问来完成Account数据的增改删查。
然后我们创建AccountService接口定义用户对账户可以进行的操作——转账和存钱以及查找账户
package com.wiley.beginningspring.ch2;public interface AccountService { public void transferMoney(long sourceAccountId, long targetAccountId, double amount); public void depositMoney(long accountId, double amount) throws Exception; public Account getAccount(long accountId);}
接着,创建利用之前创建好的数据访问接口来实现服务层类AccountServiceImpl
package com.wiley.beginningspring.ch2;public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transferMoney(long sourceAccountId, long targetAccountId, double amount) { Account sourceAccount = accountDao.find(sourceAccountId); Account targetAccount = accountDao.find(targetAccountId); sourceAccount.setBalance(sourceAccount.getBalance() - amount); targetAccount.setBalance(targetAccount.getBalance() + amount); accountDao.update(sourceAccount); accountDao.update(targetAccount); } @Override public void depositMoney(long accountId, double amount) throws Exception { Account account = accountDao.find(accountId); account.setBalance(account.getBalance() + amount); accountDao.update(account); } @Override public Account getAccount(long accountId) { return accountDao.find(accountId); } }
为了确定主程序中的接口使用的是哪种实现,我们创建Ch2BeanConfiguration来配置Bean
package com.wiley.beginningspring.ch2;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class Ch2BeanConfiguration { @Bean public AccountService accountService() { AccountServiceImpl bean = new AccountServiceImpl(); bean.setAccountDao(accountDao()); return bean; } @Bean public AccountDao accountDao() { AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl(); //depedencies of accountDao bean will be injected here... return bean; }}
其中@Configuration是配置类的标志,@Bean是工厂方法的标志
很明显的可以看出针对AccountService返回的为AccountServiceImpl实例,针对AccountDao返回的是AccountDaoInMemoryImpl实例,工厂方法的返回类型为接口而不是实现类可以方便我们更换接口的实现方式,这也就是IoC的优势,依赖对象管理的方便。
比如我们如果想用JDBC来实现DAO层就只要在配置文件中把返回值改为AccountDaoJdbcImpl(假设我们实现了这个类),服务层装配的类就变成了后者,而并不需要修改任何服务层代码,实现了层与层之间的去耦合。
最后创建Main函数测试服务层的功能
package com.wiley.beginningspring.ch2;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Ch2BeanConfiguration.class); AccountService accountService = applicationContext.getBean("accountService", AccountService.class); System.out.println("Before money transfer"); System.out.println("Account 1 balance :" + accountService.getAccount(1).getBalance()); System.out.println("Account 2 balance :" + accountService.getAccount(2).getBalance()); accountService.transferMoney(1, 2, 5.0); System.out.println("After money transfer"); System.out.println("Account 1 balance :" + accountService.getAccount(1).getBalance()); System.out.println("Account 2 balance :" + accountService.getAccount(2).getBalance()); }}
Main函数通过语句
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Ch2BeanConfiguration.class);
引入了配置文件后,实例都从上下文对象(Spring容器)applicationContext中通过Spring的工厂获取,ApplicationContext.getBean()方法执行Bean查找,返回对象的类型由配置类决定,该程序打印了两个账户转账前后的余额,运行得到的结果为
功能实现正确
基于XML的配置
将基于Java配置的项目中的配置类删除,用一个ch2-beans.xml文件来代替之即可。新的目录结构如下:
ch2-beans.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountService" class="com.wiley.beginningspring.ch2.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <bean id="accountDao" class="com.wiley.beginningspring.ch2.AccountDaoInMemoryImpl"> </bean></beans>
可以从中辨别出针对accountService的对象,实例化指向的是AccountServiceImpl类,其中的accountDao属性被指向名为accountDao的bean,而accountDao的Bean在之后被定义指向AccountDaoInMemoryImpl实现类,从而指定了各个Bean的装配
修改了配置方法为XML配置后,Main函数中获取配置上下文的方法也要由
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Ch2BeanConfiguration.class);
改为
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/com/wiley/beginningspring/ch2/ch2-beans.xml");
基于注释的配置
除了以上两种在一个文件中包含Bean的装配方法外,也可以通过注释来指定Bean对象并通过@Autowired注解来自动装配Bean,此时配置XML中就只要指定Bean查找的包范围
此时Bean实现类都加上@Component注释或者扩展自它的注释表示该类为一个Bean,扫描Bean的时候就可以扫描到该类
@Repositorypublic class AccountDaoInMemoryImpl implements AccountDao {//Implementation}@Servicepublic class AccountServiceImpl implements AccountService { private AccountDao accountDao; @Autowired public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; }}
@Service和@Repository都扩展自@Component,前者仅仅表明该类为一个Bean,后者还与一些Spring数据访问功能相关。setAccountDao()方法上的@Autowired注释表明该处参数accountDao的实现会在配置文件指定的包中自动查找
此时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-4.0.xsd"> <context:component-scan base-package="com.wiley.beginningspring.ch2"/></beans>
指定了查找定义为Bean的类的包
注意,配置Bean的XML文件中xsi:schemaLocation指定时
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
和
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
都是可行的,后者没有指定版本会默认采用最新版本
- Beginning Spring学习笔记——第2章(一)Spring IoC容器
- Beginning Spring学习笔记——第3章(一)Spring MVC基础
- Beginning Spring学习笔记——第4章(一)Spring JDBC连接的配置
- Beginning Spring学习笔记——第6章(一)Spring事务管理基础
- Beginning Spring学习笔记——第2章(三)Spring的Bean管理
- Beginning Spring学习笔记——第5章(一)ORM和JPA基础
- Beginning Spring学习笔记——第1章
- Beginning Spring学习笔记——第9章 SpEL
- Beginning Spring学习笔记——第10章 缓存
- Beginning Spring学习笔记——第5章(二)Spring的JPA支持
- Beginning Spring学习笔记——第2章(二)依赖注入
- Spring 学习笔记2—— IoC容器
- Spring学习笔记——Spring IOC容器
- Spring揭秘 学习笔记一 (Spring的IoC容器 一)
- Spring揭秘 学习笔记一 (Spring的IoC容器 二)
- Beginning Spring学习笔记——第3章(二)表单处理
- Spring学习(一)IOC容器
- Beginning Spring学习笔记——第7章 使用Spring进行测试驱动开发
- UVA
- spring boot 利用分层结构输出简单的Hello world
- 测试方法总结
- 屏幕亮度调节—基于Android_6.0(代码源于Google)
- Vue笔记(一)
- Beginning Spring学习笔记——第2章(一)Spring IoC容器
- 线性筛法与欧拉函数
- maven与Tomcat包冲突问题
- Binary Tree
- 字符串排序 Java编程练习 学堂在线
- js面试题知识点全解(一原型和原型链1)
- Android-Intent传递数据(Bitmap)闪退问题
- Mysql之Copying to tmp table
- Java web学习笔记