Spring
一、概述:
以下概述内容拷自w3cschool
Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。
Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。
Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。
Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。
Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
两大重点:
1.依赖注入(DI)
Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。
当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能的独立于其他的 Java 类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注入(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让它们保持独立。
到底什么是依赖注入?让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。
依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。由于依赖注入是 Spring 框架的核心部分,所以我将在一个单独的章节中利用很好的例子去解释这一概念。
2.面向方面的程序设计(AOP):
Spring 框架的一个关键组件是面向方面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。
在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是方面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。
Spring 框架的 AOP 模块提供了面向方面的程序设计实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
二、Ioc:
2.1spring Ioc容器(控制反转):
作用:减小类/程序间的耦合
个人理解:运行时,找对象的事变成了间接寻找。没有使用ioc时,是直接寻找,找不到,编译便报错,而使用后,spring会创建一个工厂,寻找的事交给了工厂来处理,由工厂来寻找所需要的类,这样便减少了类与类之间的耦合,进一步实现低耦合高内聚!但Ioc只能做到减小,无法做到真正的解耦。
2.2 两大容器:
2.2.1:BeanFactory 容器和 ApplicationContext 容器(常用):
BeanFactory 可以理解为含有bean集合的工厂类。而 ApplicationContext 是他的一个子接口(进行了扩展)。
BeanFactory 是一个基本的容器,他无法支持aop、web等功能。
BeanFactory 能做到的事, ApplicationContext 都能做到。
ApplicationContext: 单例对象适用 采用此接口
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
BeanFactory: 多例对象使用
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
2.2.2 ApplicationContext的三个常用实现类:
ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
AnnotationConfigApplicationContext: 读取注解创建容器
相关代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
xxx x = (xxx)ac.getBean("xxx"); //要强转类型
xxx x = ac.getBean("xxx",xxx.class);
bean.xml
2.3 spring对bean的管理细节
2.3.1 创建bean的三种方式
1.使用默认构造函数创建,在spring配置文件配id和class属性的bean
2.使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
3.使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
2.3.2 bean对象的作用范围
scope属性:
*singleton 单例(默认)
*prototype 多例
*request web应用的请求范围
*session web应用的会话范围
*global-session: 作用于集群环境的会话范围
2.3.3 bean对象的生命周期
单例对象
出生:容器创建时
活着:容器在便在
死亡:容器销毁,死亡
多例对象
出生:当我们使用spring框架为我们创建(同beanFactory)
活着:对象在使用,就活着
死亡:等着java垃圾回收机制回收
2.4 依赖注入(DI)
1.能注入的数据:
1.基本类型和String
2.其他bean类型
3.复杂类型/集合类型
2.注入方式:
1.使用构造函数注入
使用的标签:constructor-arg
标签内的属性:
type:指定注入的数据的数据类型,也是构造函数中某个或某些参数的类型
index:用于指定注入的数据给构造函数中指定索引位置赋值
name:用于指定给构造函数中指定名称的参数赋值
value:提供基本类型和String类型的数据
ref:指定其他的bean类型数据,即在Ioc核心容器中出现过的bean对象
2.使用set注入(常用)
标签:property
标签内属性:
name:注入时所调用的set方法名称
ref:指定其他的bean类型数据,即在Ioc核心容器中出现过的bean对象
使用注解注入
2.5注解:
在bean.xml中要先表明扫描的包(要加新的约束)
创建对象:
Component:将当前类对象存入spring容器,有个value属性:相当于bean的id(默认为当前类名第一个字母小写)
以下三个与上面作用一模一样
Controller:一般用于表现层
Service:一般用于业务层
Repository:一般用于持久层
注入数据:
作用同bean标签的
1.Autowired:按照类型自动注入,此时set方法不为必须的了
注入时,没有匹配类型则报错,如果多个匹配的类型,则以id来匹配
2.Qualifier:按照类中注入的基础之上再按照名称注入,注入类成员不能单独食用,给 方法参数可以,属性value:用于指定注入bean的id。
3.Resource:直接按照bean的id注入
以上3个只能注入其他bean类型,基本类型和String不能,另外,集合类型只能通过xml实现
4.Value:用于注入基本类型和String
指定作用范围:
Scope:
*singleton 单例
*prototype 多例
配置类注解:
作用同bean.xml
1.Congifuration:表面当前类是一个配置类
2.ComponentScan:通过注解表面spring创建容器时要扫描的包
3.Bean:用于把当前方法的返回值作为bean对象存入springIoc容器,属性name:指定bean的id,默认方法名称
4.Import(): 用于插入其他配置类
5.PropertySource: 扫描的配置文件(一般是jdbc.properties)
6.@EnableTransactionManagement:开启事务支持
2.6案例:
xml配置案例:
bean.xml:
AccountServiceImpl:
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public List findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer accountId) {
accountDao.deleteAccount(accountId);
}
}
AccountService:
public interface AccountService {
/*
查询所有
*/
List findAllAccount();
/*
根据id查找
*/
Account findAccountById(Integer accountId);
/*
保存
*/
void saveAccount(Account account);
/*
更新
*/
void updateAccount(Account account);
/*
删除
*/
void deleteAccount(Integer accountId);
}
AccountDao:
public interface AccountDao {
/*
查询所有
*/
List findAllAccount();
/*
根据id查找
*/
Account findAccountById(Integer accountId);
/*
保存
*/
void saveAccount(Account account);
/*
更新
*/
void updateAccount(Account account);
/*
删除
*/
void deleteAccount(Integer accountId);
}
AccountDaoImpl:
public class AccountDaoImpl implements AccountDao {
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List findAllAccount() {
try {
return runner.query("select *from account",new BeanListHandler(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
public Account findAccountById(Integer accountId) {
try {
return runner.query("select *from account where id = ?",new BeanHandler(Account.class),accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try {
runner.update("insert into account(name,money) value (?,?)",
account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try {
runner.update("update account set name=?,money=? where id=?",
account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void deleteAccount(Integer accountId) {
try {
runner.update("delete from account where id = ?",
accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
Test:
public class TestCurd {
@Test
public void TestFindAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService as = ac.getBean("accountService",AccountService.class);
List accounts = as.findAllAccount();
for (Account account : accounts) {
System.out.println(account);
}
}
@Test
public void TestFindOne() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService as = ac.getBean("accountService",AccountService.class);
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void TestSaveAccount() {
Account account = new Account();
account.setName("老王");
account.setMoney(1234f);
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService as = ac.getBean("accountService",AccountService.class);
as.saveAccount(account);
}
@Test
public void TestUpdateAccount() {
Account account = new Account();
account.setId(1);
account.setName("AAA");
account.setMoney(1234f);
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService as = ac.getBean("accountService",AccountService.class);
as.updateAccount(account);
}
@Test
public void TestDeleteAccount() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService as = ac.getBean("accountService",AccountService.class);
as.deleteAccount(4);
}
}
注解配置:
SpringConfiguration:
/**
* 相当于bean.xml
*/
@Configuration //表明他是一个配置类,会被被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
@ComponentScan("cn.zzz") //表面创建容器扫描的包
public class SpringConfiguration {
@Bean("runner")
@Scope("prototype")
public QueryRunner runner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean("dataSource")
public DataSource creatDatasource() {
ComboPooledDataSource cbp = new ComboPooledDataSource();
try {
cbp.setDriverClass("com.mysql.cj.jdbc.Driver");
cbp.setJdbcUrl("jdbc:mysql://localhost:3306/spring01?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC");
cbp.setUser("root");
cbp.setPassword("lin990306");
return cbp;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
AccountServiceImpl:
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public List findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer accountId) {
accountDao.deleteAccount(accountId);
}
}
AccountDaoImpl:
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner runner;
public List findAllAccount() {
try {
return runner.query("select *from account",new BeanListHandler(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
public Account findAccountById(Integer accountId) {
try {
return runner.query("select *from account where id = ?",new BeanHandler(Account.class),accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try {
runner.update("insert into account(name,money) value (?,?)",
account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try {
runner.update("update account set name=?,money=? where id=?",
account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void deleteAccount(Integer accountId) {
try {
runner.update("delete from account where id = ?",
accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
其他与xml配置案例一样
三、Aop
AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
相关术语:
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。
Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知/增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。
Target(目标对象): 代理的目标对象。
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect(切面): 是切入点和通知(引介)的结合
xml约束:
org.aspectj
aspectjweaver
1.8.7
四:spring中的事务管理
题外:使用jdbcTemplate时,可以在dao中继承JdbcDaoSupport,便可以省去定义和注入,使用时直接super.getJdbcTemplate().方法。
基于xml的事务管理:
bean.xml:
AccountServiceImpl:
public class AccountServiceImpl implements AccountService {
private AccoundDao accountDao;
public void setAccountDao(AccoundDao accoundDao) {
this.accountDao = accoundDao;
}
public Account findAccoundByName(String AccountName) {
return accountDao.findAccoundByName(AccountName);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public List findAll() {
return accountDao.findAll();
}
public void transfer(String outName, String inName, Float money) {
Account outAccount = accountDao.findAccoundByName(outName);
Account inAccount = accountDao.findAccoundByName(inName);
outAccount.setMoney(outAccount.getMoney()-money);
inAccount.setMoney(inAccount.getMoney()+money);
accountDao.updateAccount(outAccount);
// int i =1/0;
accountDao.updateAccount(inAccount);
}
}
Test:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
private AccountService as;
@Test
public void TestFindAll(){
List accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
@Test
public void TestFindAccountByName(){
Account account = as.findAccoundByName("AAA");
System.out.println(account);
}
@Test
public void TestTransfer(){
as.transfer("AAA","bbb",100f);
}
}
这种单元测试写法是为了让测试在Spring容器环境下执行。