扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
Spring是一个开源框架,为简化企业级应用开发而生。
创新互联建站是一家专业提供罗城企业网站建设,专注与成都网站设计、网站建设、外贸网站建设、H5开发、小程序制作等业务。10年已为罗城众多企业、政府机构等服务。创新互联专业网络公司优惠进行中。
Spring是一个 IOC 和 AOP 容器框架。
Spring 是轻量级的、面向切面编程、依赖注入的一站式框架。
导入
commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
IOC:反转控制器,思想是反转资源获取的方向,传统资源查找方式是组件向容器发起请求查找资源,IOC 则是容器主动的将资源推送给它所管理的组件。
DI:依赖注入,组件预定义的方式接受资源注入。
在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前,必须要对其进行实例化,只有实例化后,才可以从 IOC 容器中取出并使用。
Spring 提供了两种类型的 IOC:
BeanFactory:IOC 容器的基本实现。
ApplicationContext:提供了更多的特性,是 BeanFactory 的子接口,在初始化上下文阶段就实例化了所有单例Bean。
AppliactionContext 主要实现类
ClassPathXmlApplicationContext:从类路径下加载配置文件。
FileSystemXmlApplicationContext:从文件系统下加载配置文件。
ConfigurableApplicationContext:ApplicationContext 的扩展类,新增 refresh 和 close 方法,使其具有启动、刷新和关闭上下文的功能。
WebApplicationContext:Web 应用专属。
基于 XML 方式
基于注解的方式
Spring 能从 classpath 中自动扫描、侦测和实例化具有特定注解的组件,包括:
@Component: 基本注解,标识一个受 Spring 管理的组件。
@Respository:标识持久层组件。
@Service:标识业务层组件。
@Controller:标识表现层组件。
对于扫描到的组件,Spring 有特定的命名策略,当然可以在注解中通过 value 属性指定组件名。
当在组件中使用了注解后,还需要在配置文件中声明\
base-package:扫描组件基类包,Spring 容器将扫描该包下的所有类,当需要扫描多个包时,用逗号隔开。
resource-pattern:仅扫描基类中的特定类。
use-default-filters:默认为ture,扫描@Component、@Respository、@Service、@Controller 组件。
\
\
类别 | 说明 | |
---|---|---|
annotation | com.kernel.XxxAnnotation | 所有标注了XxxAnnotation的类,该类型采用目标类是否标注了某个注解进行过滤 |
assignable | com.kernel.XxxService | 所有继承或扩展XxxService的类,该类型采用了目标类是否继承或扩展某个特定类进行过滤 |
aspectj | com.kernel.*Service | 所有类名义Service结束的类及继承或扩展它们的类,该类型采用AspectJ表达式进行过滤 |
regex | com.kernel.anno.* | 所有com.kernel.anno包下的类。该类型采用正则表达式,根据类的类名进行过滤 |
custom | com.kernel.XxxTypeFilter | 采用XxxTypeFilter通过代码的方式定义过滤原则。该类必须实现org.springframework.core.type.TypeFilter接口 |
Spring 还可以进行组件装配
\
构造器、普通字段、具有参数的方法都可以应用 @Autowired。
默认,所有被设置为该注解的属性都要被设置,否则会抛出异常,可以使用该注解的 required 属性设为false。
当 IOC 中存在多个类型兼容的 Bean 时,自动装配失败,此时可以在 @Qualifier 注解中提供 Bean 的名称。
@Authwired 注解应用在数组类型的属性上,此时 Spring 将会把所有匹配的 Bean 进行自动装配。
@Authwired 注解应用在集合属性上, 此时 Spring 读取该集合的类型信息,然后自动装配所有与之兼容的 Bean。
@Authwired 注解用在 Map 上时, 若该 Map 的键值为 String,那么 Spring 将自动装配与之 Map 值类型兼容的 Bean,此时 Bean 的名称作为键值。
通过全类名
通过工厂方法
静态工厂
调用静态工厂创建 Bean 是将对象创建的过程封装到静态方法中,当调用者需要时,只需调用静态方法就可以返回对象。
要声明静态工厂创建 Bean,在 class 属性中指定静态工厂,同时在 \
实例工厂
调用实例工厂创建 Bean 是将对象封装到另一个对象的实例中,当调用者需要时,只需调用实例方法就可以返回对象。
要声明实例工厂创建 Bean,首先要声明一个实例工厂 Bean,然后在 \
Bean Factory
String 有两种 Bean,一种是普通的 Bean,另一种是 Bean Factory,它与普通的 Bean 不同,它返回的类型是是 getObject方法所返回的对象类型。
属性注入
200
构造器注入
当 Bean 实例仅仅给一个特定的属性使用时,可以将其声明为内部 Bean,内部 Bean 声明在
java.util.List 通过 \ 定义,标签里可以使用 \
java.util.Set通过 \ 一样。
java.util.Map 通过 \
java.util.Properties 通过 \
jdbc:MySQL:///test
使用基本的集合标签不能将集合作为单独的 Bean 定义,导致其他的 Bean 无法引入,可以使用 util schema 的集合标签定义独立的集合 Bean。
jdbc:mysql:///test
为了简化 XML 的配置,Spring2.5 开始引入了一个新的 p 命名空间,可以通过
可以使用
byName:根据名称装配,属性名和要装配 Bean 名称必须完全相同,否则装配失败。
byType:根据类型装配,如果存在两个类型相同的 Bean,则装配失败。
缺点:对于一个 Bean,不能选择只装配其中的某一个属性,不够灵活,再一个是要么选择 byName,要么选择 byType,两者不能混搭。
继承关系
Spring 允许继承 Bean 的配置,子 Bean 从父 Bean 继承配置,如果父 Bean 只想作为模板,可以将父 bean 设为 abstract,并不是所有的 Bean 配置都会被继承,如果父 Bean 的 class 属性没有配置,它必须为抽象 Bean。
依赖关系
Soring 允许设置前置依赖,可以通过 depends-on 属性设置依赖 Bean,多个 Bean 通过逗号或空格隔开,依赖 Bean 在本 Bean 实例化之间实例化。
在 Spring 中,允许在
singleton:在初始化 IOC 容器的时候就会创建一个实例,再次获取,都会返回这一个实例。
prototype:在初始化 IOC 容器的时候不会创建实例,每次获取都会返回一个不同的实例。
request:每次请求 HTTP 都会返回一个新的 Bean 实例,仅适用于 WebApplicationContext。
seesion:同一个 HTTP Session 使用一个 Bean 实例,仅适用于WebApplicationContext。
配置 Bean 时,有时候需要在配置文件中混入系统部署的细节信息,而这些部署文件要和 Bean 配置文件分离。Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器,这个处理器允许将部署信息存放到外置文件中,可以在 Bean 配置中使用 ${var} 的方式引入属性值。
Spring 表达式语言,支持运行时查询和操作对象的表达式语言。
以#{}作为定界符。
支持算数、比较、逻辑运算符,支持字符串拼接和 if、else。
通过 SpEL 可以实现:
引用 bean
调用方法以及引入对象的属性
计算表达式的值
正则表达式的匹配
Spring IOC 可以管理 Bean 的生命周期并允许在特定点执行定制任务。
Spring ICO 对 Bean 的生命周期管理的过程:
构造 Bean 实例。
为 Bean 设置属性。
初始化 Bean。
当容器关闭时,调用 Bean 的销毁方法。
在 Bean 的生命中可以通过 init-method 和 destroy-method 指定初始化和销毁方法。
Bean 的后置处理器允许在调用初始化方法之前对 Bean 进行额外处理。
Bean 的后置处理器对所有 Bean 实例逐一处理,对于 Bean 后置处理器来讲,必须实现 BeanPostProcessor,
Spring 将每个 Bean 传递给两个方法:
Object postProcessBeforeInitialization:在初始化方法调用前执行。
Object postProcessAfterInitialization:在初始化方法调用后执行。
添加 Bean 后置处理器后 Bean 的生命周期
构建 Bean 实例。
为 Bean 设置属性。
将 Bean 传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法。
初始化 Bean。
将 Bean 传递给 Bean 后置处理器的 postProcessAfterInitialization 方法。
当容器关闭时,调用 Bean 的销毁方法。
创建两个泛型类,并配置两者的依赖关系,对于继承这两个类的子类,如果泛型相同,则会继承这种依赖关系。
当程序在运行期间需要给程序的运行增加一些特定需求,例如日志,可以修改代码实现,但是要修改的函数过多,就会特别麻烦。可以使用动态代理实现:
使用一个代理将对象包裹起来,让代理对象代替原对象执行方法。
public class ArithmeticCalculatorProxy {
// 被代理对象
private ArithmeticCalculator target = null;
public void ArithmeticCalculator(ArithmeticCalculator target) {
this.target = target;
}
public ArithmeticCalculator getProxy(){
// 代理对象
ArithmeticCalculator proxy;
// 代理对象用哪个类加载器加载
ClassLoader classLoader = target.getClass().getClassLoader();
// 代理对象的类型
Class[] interfaces = new Class[]{ArithmeticCalculator.class};
// 当代理对象执行其中的方法时的逻辑
InvocationHandler h = new InvocationHandler() {
/**
* @param proxy 要返回的代理对象
* @param method 正在被调用的方法
* @param args 传参
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("The method" + method.getName() + "begins with" + Arrays.asList(args));
Object result = method.invoke(args);
return result;
}
};
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(classLoader, interfaces, h);
return proxy;
}
}
面向切面编程(AOP)是对面向对象编程(OOP)的一个补充,AOP 的主要关注对象是切面(aspect)。
面向切面编程时,仍需定义公共功能,但可以明确定义功能在哪里,以什么方式应用,并且无需更改受影响的类。
AOP 的好处是:
每个事物逻辑位于一个文件,代码不分散,便于维护和升级。
业务模块更简洁,只包含核心业务代码。
AOP 术语:
切面:公共功能,在上例中切面就是日志功能。
通知:切面必须要完成的工作,上例中通知就是加减乘除。
目标:被通知的对象。
代理:代理类。
连接点:程序执行的某个特定位置。
切点:每个类拥有多个连接点,AOP 通过切点定位到特定的连接点。
Java 社区中最流行的 AOP 框架。
基于注解方式,要在 IOC 容器中添加注解支持,只需要在配置文件中添加
要在 Spring 中声明切面,只需要将其声明为 Bean,当在 IOC 容器中声明切面类后,IOC 就会为那些与切面类匹配的类生成代理对象,切面是一个带有@Aspect 注解的 Java 类。
通知是标注有某种注解的 Java 方法。
Aspect 支持五种类型的通知注解:
@Before
/**
* 前置通知,方法执行前执行
*/
@Before("execution(* com.kernel.spring.aop.impl.ArithmeticCalculatorImpl.*(..) )")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List
@After
/**
* 后置通知,方法执行后执行
*/
@After("execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method:" + methodName + " end");
}
@AfterReturning
/**
* 返回值通知,方法执行成功后执行,可以访问到方法的返回值
*/
@AfterReturning(value = "execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method:" + methodName + " end with " + result);
}
@AfterThrowing
/**
* 异常通知,方法执行发生异常后执行,可以访问到异常对象,同时可以在出现特定异常后执行响应的逻辑
*/
@AfterThrowing(value = "execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs exception:" + ex);
}
@around
/**
* 环绕通知需要携带 proceedingJoinPoint 类型的参数
* 环绕通知相当于动态代理的全过程
* 环绕通知必须有返回值,返回值即为方法执行后的返回值
*/
@Around(value = "execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
try {
// 前置通知
System.out.println("The method:" + methodName + " begins with " + Arrays.asList(proceedingJoinPoint.getArgs()));
result = proceedingJoinPoint.proceed();
// 返回值通知
System.out.println("The method:" + methodName + " end with " + result);
} catch (Throwable e) {
// 异常通知
System.out.println("The method occurs exception:" + e);
}
// 后置通知
System.out.println("The method:" + methodName + " end");
return result;
}
优先级:可以通过 @order 注解设置切面类的优先级
基于 XML 方式
为了使 JDBC 更加易用,Spring 在 JDBC API 上建立了一个抽象层,缺点是毕竟是 Spring 提供了一个小工具,不支持级联操作。
使用 SQL 语句更新数据库
String sql = "UPDATE empoyees SET EMAIL = ? WHERE id = ?";
int update = jdbcTemplate.update(sql, "Tom@163.com", 1);
批量更新
String sql = "INSERT INTO empoyees(LAST_NAME, EMAIL,DEPT_ID) VALUES (?, ?, ?)";
List
查询单个记录
String sql = "SELECT ID, LAST_NAME lastName, EMAIL FROM empoyees where id = ?";
RowMapper rowMapper = new BeanPropertyRowMapper<>(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
查询多个记录
String sql = "SELECT ID, LAST_NAME lastName, EMAIL FROM empoyees";
RowMapper rowMapper = new BeanPropertyRowMapper<>(Employee.class);
List maps = jdbcTemplate.query(sql, rowMapper);
事务是企业级开发必不可少的技术,用来保证数据的一致性和完整性,事务是一系列的操作,要么都完成,要么都不完成。
原子性:事务中包含的逻辑不可再分。
一致性:事务执行前后,数据的完整性保持一致。
隔离性:事务在执行期间,不能受到其他事务的影响。
持久性:事务执行成功,应该持久化到磁盘上。
当事务方法被另一个事务调用时,必须指定事务的传播方式。
使用 @Transactional 注解中的 Propagation 指定事务的传播行为。
REQUIRED:如果有事务在运行,当前方法就在这个事务中运行,否则创建新事务。
REQUIRES_NEW:当前方法必须创建新事务,并在自己的事务中运行,原事务挂起。
脏读是指读到了别的事务回滚前的脏数据。
不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。
幻读是指同样一笔查询在整个事务过程中多次执行后,查询所得的结果集是不一样的。
Read Uncommitted 读未提交 ,引发脏读问题
Read Committed 读已提交,解决脏读,引发不可重复读问题(Oracle默认隔离级别)。
Repeatable Read 重复读,解决不可重复读,未解决幻读(MySQL默认隔离级别)
Serializable,可串行化 解决所有问题
隔离级别分类
按性能从高到低可划分为:读未提交>读已提交>重复读>可串行化
按拦截程序从高到低可划分为:可串行化>重复读>读已提交>读未提交
注解方式
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
// 使用Propagation指定事务的传播行为
// 使用Isolation指定事务的隔离级别
// 使用noRollbackFor指定事务不回滚的异常
// 使用readOnly指定事务是否只读
// 使用timeout指定强制回滚操作之前事务占用的时间
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, noRollbackFor = BookStockException.class)
@Override
public void purchase(String username, String isbn) throws Exception {
int bookPriceByIsbn = bookShopDao.findBookPriceByIsbn(isbn);
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(username, bookPriceByIsbn);
}
}
XML 方式
在 web.xml 中配置 Spring 配置文件的名称和路径,并且配置启动 IOC 容器的 ServletContextListener。
contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
获取 IOC 容器:
在 Web 应用中,我们用到的 IOC 容器是 WebApplicationContext,它继承自 ApplicationContext,它的初始化方式和 ApplicationContext 有所不同,因为 WebApplicationContext 需要 ServletContext 实例,所以我们必须在 web.xml 配置 Servlet 或者 Web 容器监听器。
Spring 提供了用于启动 WebApplicationContext 的 Servlet 和 Web 容器监听器:
org.springframework.web.context.ContextLoaderServlet
org.springframework.web.context.ContextLoaderListener
那么 ContextLoaderListener 做了什么呢?
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
继续追踪 WebApplicationContext,ContextLoader 将 WebApplicationContext 放到了 Servlet中,Servlert 是一个 MAP 容器,WebApplicationContext 的 KEY 是 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。
Spring提供了一个WebApplicationContextUtils类,可以方便的取出WebApplicationContext,只要把ServletContext传入就可以了。
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流