- Spring的初衷:
1、JAVA EE开发应该更加简单。
2、使用接口而不是使用类,是更好的编程习惯。Spring将使用接口的复杂度几乎降低到了零。
3、为JavaBean提供了一个更好的应用配置框架。
4、更多地强调面向对象的设计,而不是现行的技术如JAVA EE。
5、尽量减少不必要的异常捕捉。
6、使应用程序更加容易测试。
- 核心容器
是Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。BeanFactory是工厂模式的一个实现,它使用IoC将应用配置和依赖说明从实际的应用代码中分离出来。
-
获取核心容器
- ApplicationContext的三个常用类
- ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要去配置文件必须在类路径下,不在的话,加载不了。
- FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(但是必须要有访问权限)
- AnnotationConfigApplication:它是用于读取注解创建容器的
- 两个接口
- applicationContext采用立即加载,读完配置文件立即创建对象
- beanFactory采用延时加载,什么时候ud获取了对象,什么时候才真正的创建对象
- ApplicationContext的三个常用类
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
- 使用spring会使用到的相关依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
- Spring的配置
spring配置的前面部分的信息可以去官网下载,下载后通过查询并粘贴到xml文件中
打开core.html后可通过Ctrl+F进行查询
< bean>标签之前即为spring的配置,根据自己需要复制粘贴到自己项目中的配置文件中
- 基于xml配置的配置文件
<!--把对象的创建交给Spring来管理-->
<!--spring对bean的管理细节
1.创建bean的三种方式
1.1方式一 使用默认构造函数创建,在spring的配置文件中使用bean标签,配以id和class属性,
没有其他属性,采用的就是默认构造函数创建bean对象,此时若类中没有默认构造函数,则对象无法创建-->
<!-- <bean id = "accountService" class="service.impl.AccountServiceImpl"></bean>-->
<bean id="accountDao" class="dao.impl.AccountDao"></bean>
<!--1.2方式二 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)-->
<!-- <bean id="instanceFactory" class="factory.InstanceFactory"></bean>-->
<!-- <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->
<!--1.3方式三 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器中-->
<!-- <bean id="accountService" class="factory.StaticFactory" factory-method="getAccountService"></bean>-->
<!--2.beam的作用范围调整
bean标签的scope属性
作用:用于指定bean的作用范围
取值:singleton 单例的(常用)
prototype 多例的(常用)
request 作用于web应用的请求范围
session web用于的会话范围
global-session 集群环境的会话范围
-->
<!-- <bean id="accountService" class="service.impl.AccountServiceImpl" scope="singleton"></bean>-->
<!-- bean对象的声明周期
单例对象
出生:容器创建对象出生,即解析配置文件时创建对象
活着:只要容器未被销毁,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时Spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:对象长时间不使用且没有别的对象引用时,由java垃圾回收期回收
-->
<!-- <bean id="accountService" class="service.impl.AccountServiceImpl"-->
<!-- scope="prototype" init-method="init" destroy-method="destory"></bean>-->
<!--Spring 中的依赖注入(dependency injection
IOC的作用降低耦合(依赖关系)
依赖关系的管理:交给spring来维护
当前类需要用到其他类的对象,由spring为我们提供,我们只需啊哟在配置文件中说明
依赖关系的维护成为依赖注入
能注入的数据:基本类型和String
其他bean类型(在配置文件或注解配置过的bean)
复杂类型/集合类型
注入的方式:使用构造函数提供
使用set方法提供
使用注解提供
-->
<!-- 构造函数注入
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性 type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引未知的额参数赋值。索引位置从0开始
name:用于指定给构造函数中指定名称的参数赋值
========上面三个方法用于指定给构造函数中的那个参数赋值
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。他值得就是在Spring的IOC核心容器中出现过的bean
优势:获取bean对象时。注入的数据是必须的操作,否则对象无法创建成功
弊端:改变了bean对象实例化方式,创建对象时,如果用不到这些数据,也必须提供
-->
<!-- 此时类中没有默认空参构造器-->
<bean id="accountService" class="service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
<!-- set方法的注入
设计的标签property
出现的位置bean标签的内部
标签的属性
name:用于指定给构造函数中指定名称的参数赋值
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。他指的就是在Spring的IOC核心容器中出现过的bean-->
<!--复杂类型的注入-->
<!-- 用于给List结构集合注入数据的标签:list array set
用于给map结构集合注入数据的标签:map props
结构相同标签可以互换-->
</beans>
- 基于注解的配置文件,要注意基于注解的bean.xml的头部和基于xml的不相同,这个也可以去刚才那个html中查找
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--告诉spring在创建容器时要扫描的包,配置所需要的标签不是在bean的约束中,
而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.boogie"></context:component-scan>
</beans>
基于注解的相关注解
- 用于创建对象的
- 与xml中标签功能相同
- @Component:把当前类对象存入spring容器中
- 属性:value 指定bean的id,不写时默认是当前类名第一个字母改小写
- 以下三个注解的作用和Component的作用一样,是spring框架为我们明确的三层使用的注解,让我们对三层对象更加清晰
- @Controller:一般在表现层
- @Service:一般在业务层
- @Repository:一般在持久层
- 用于注入数据的
- 与xml文件中bean标签中标签作用相同
- @Autowired:自动按照类型注入。只要容器中有唯一一个bean对象类型和要注入的变量类型匹配,就可以注入成功
- 如果IOC容器中没有任何bean类型和要注入的变量类型匹配,则报错
- 如果IOC容器中有多个类型匹配时:先按照类型找到匹配类型,再按照所定义的变量名称去找,若都不相同且有多个与之匹配的,则报错
- 出现位置:变量或方法
- 细节:使用注解时set方法不是必须的
- @Qualifier:在按照类注入的基础上在按照名称注入,他在给类成员注入时不能单独使用但是在给方法注入参数时可以
- @Resource:直接按照bean的id注入,可以独立使用
- 属性:name:用于指定bean的id
- 以上三个注解只能注入其他bean类型的数据,二基本类型和String类型无法使用上述注解实现
- 另外,集合类型的注入只能通过xml来实现
- @Value:用于注入基本类型和String类型的数据
* 属性:用于指定数据的值,它可以使用spring中的el表达式
* SpEL的写法:${表达式}
- 用于改变作用范围的
- 与xml文件中bean标签使用的scope属性功能相同
- @Scope:用于指定bean的作用范围
- 属性:singleton prototype分别对应单例和多例
- 默认情况是单例的
- 和生命周期相关
- 与xml文件中bean标签中init-method和destory-method作用相同
- @PreDestory:用于指定销毁方法
- @postConstruct:用于指定初始化方法
//一个小例子,可以体会一下各个注解的使用
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
// @Autowired
// @Qualifier("accountDao")//它不能独立使用
@Resource(name = "accountDao")
private IAccountDao accountDao;
public AccountServiceImpl(){
System.out.println("创建对象");
}
@PostConstruct
public void init(){
System.out.println("初始化...");
}
@PreDestroy
public void destroy(){
System.out.println("销毁...");
}
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
下面附上一个spring的实例,可以更好地理解Spring的容器的概念
- 首先创建与数据库相关实体类
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
- 创建dao接口类以及实现类
public interface IAccountDao {
List<Account> findAllAcount();
Account findAccountById(Integer id);
void saveAccount(Account account);
void updateAccount(Account account);
void deleteAccount(Integer id);
Account findAccountByName(String name);
}
@Repository("accountDao")
public class AccountDao implements IAccountDao {
// private IAccountDao accountDao;
private QueryRunner ruuner;
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public void setRuuner(QueryRunner ruuner) {
this.ruuner = ruuner;
}
@Override
public List<Account> findAllAcount() {
try {
return ruuner.query(connectionUtils.getThreadConnection(),"select * from account",new BeanListHandler<Account>(Account.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer id) {
try {
return ruuner.query(connectionUtils.getThreadConnection(),"select * from account where id=?",new BeanHandler<Account>(Account.class),id);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void saveAccount(Account account) {
try {
ruuner.update(connectionUtils.getThreadConnection(),"insert into account(id,name,money) values(?,?,?)",account.getId(),account.getName(),account.getMoney());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void updateAccount(Account account) {
try {
ruuner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer id) {
try {
ruuner.update(connectionUtils.getThreadConnection(),"delete from account where id=?",id);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String name) {
try {
List<Account> accounts= ruuner.query(connectionUtils.getThreadConnection(),"select * from account where name=?",new BeanListHandler<Account>(Account.class),name);
if (accounts==null||accounts.size()==0){
return null;
}
if (accounts.size()>1){
throw new RuntimeException("结果集不唯一,数据有问题");
}
return accounts.get(0);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
- 创建服务层接口以及实现类
public interface IAccountService {
List<Account> findAllAcount();
Account findAccountById(Integer id);
void saveAccount(Account account);
void updateAccount(Account account);
void deleteAccount(Integer id);
void transfer(String sourceName,String targetName,Float money);
}
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao=accountDao;
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteAccount(Integer id) {
accountDao.deleteAccount(id);
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
//转账
//2.1.根据名称查询转出账户
Account source= accountDao.findAccountByName(sourceName);
//2.2.根据名称转入账户
Account target=accountDao.findAccountByName(targetName);
//2.3.转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4.转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5.更新转出账户
accountDao.updateAccount(source);
//2.6.更新转入账户
accountDao.updateAccount(target);
}
@Override
public List<Account> findAllAcount() {
List<Account> accounts =accountDao.findAllAcount();
return accounts;
}
@Override
public Account findAccountById(Integer id) {
Account account =accountDao.findAccountById(id);
return account;
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
}
- 创建工具类
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程的连接
* @return
*/
public Connection getThreadConnection(){
//1.先从ThreadLocal上获取
Connection conn=tl.get();
//2.判断当前线程上是否有连接
try {
if(conn==null){
//3.从数据源中获取一个连接,并存入ThreadLocal中
conn=dataSource.getConnection();
tl.set(conn);
}
//4.返回当前线程上的连接
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void removeConnection(){
tl.remove();
}
}
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 释放事务
*/
public void release(){
try {
connectionUtils.getThreadConnection().close();//还回线程池
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 创建代理类
public class BeanFactory {
private IAccountService accountService;
public final void setAccountService(IAccountService accountService) {
this.accountService = accountService;
}
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* 获取Service代理对象
* @return
*/
public IAccountService getAccountService(){
return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader()
, accountService.getClass().getInterfaces()
, new InvocationHandler() {
/**
* 添加事务的支持
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue=null;
try {
//1.开启事务
transactionManager.beginTransaction();
// 2.执行操作
returnValue= method.invoke(accountService,args);
// 3.提交事务
transactionManager.commit();
// 4.返回结果
return returnValue;
} catch (Exception e) {
//5.回滚操作
transactionManager.rollback();
throw new RuntimeException(e);
} finally {
//6.释放连接
transactionManager.release();
}
}
});
}
}
- 配置
<!--配置代理的service-->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
<!--配置beanFactory-->
<bean id="beanFactory" class="com.boogie.factory.BeanFactory">
<!--注入service-->
<property name="accountService" ref="accountService"></property>
<!--注入事务管理器-->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!--配置业务层对象Service-->
<bean id="accountService" class="com.boogie.service.impl.AccountServiceImpl">
<!--注入Dao对象-->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置持久层对象Dao-->
<bean id="accountDao" class="com.boogie.dao.impl.AccountDao">
<!--配置QueryRunner-->
<property name="ruuner" ref="runner"></property>
<!--注入connectionutils-->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></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/数据库名称"></property>
<property name="user" value="root"></property>
<property name="password" value="数据库密码"></property>
</bean>
<!--配置Connection的工具类ConnectionUtils-->
<bean id="connectionUtils" class="com.boogie.utils.ConnectionUtils">
<!--注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="com.boogie.utils.TransactionManager">
<!--注入connectionutils-->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
</beans>
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
@Qualifier("proxyAccountService")
private IAccountService as;
@Test
public void testFindAll() {
List<Account> accounts=as.findAllAcount();
for(Account account:accounts){
System.out.println(account);
}
}
@Test
public void testFindOne() {
Account account=as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave() {
Account account= new Account();
account.setId(5);
account.setName("哈哈");
account.setMoney((float) 5000.0);
as.saveAccount(account);
}
@Test
public void testUpdate() {
Account account =as.findAccountById(5);
account.setMoney((float) 6000.0);
as.updateAccount(account);
}
@Test
public void testDelete() {
as.deleteAccount(5);
}
@Test
public void testTraansfer(){
as.transfer("aaa","bbb",100f);
}
}