Spring框架
Spring概述
1.Spring框架是什么?
Spring框架是一个开放源代码的J2EE应用程序框架,是针对bean的生命周期进行管理的轻量级容器。它的核心是IOC、AOP和Web MVC。它可以应用于桌面应用程序以及小应用程序之中。
2.Spring的优点
①轻量:jar包小,Spring框架运行时占用的资源少,效率搞
②针对于接口编程,解耦合:spring提供ioc控制反转,由容器管理对象,对象的依赖关系。对象的创建方式交由容器自主完成,
③支持aop编程:支持aop煸程,相对于传统的oop编程,aop编程可以使开发人员从繁杂的事务管理代码中解脱出来,通过声明式方式进行事务的管理,提高开发的效率和质量。
④方便集成优秀的框架:不排除优秀的开源框架。spring框架可以降低各种框架的使用难度。
3.Spring的体系结构
模块:
Core Container(核心容器):
①Beans:提供了BeanFactory,Spring将管理对象称为Bean。
②Core:提供了Spring框架的基本组成部分,包括IOC(控制反转)和DI(依赖注入)功能。
③Context:建立在Core和Beans模块的基础之上,它是访问定义和配置的任何对象的媒介。
④SpEL:Spring3.0后新增的模块,是运行时查询和操作对象强大的表达式模块。
Data Access/Integration(数据访问/集成) :
①JDBC:提供了一个JDBC的抽象层,大幅度的减少了在开发过程中对数据库操作的编码。(这里的不是sun公司推出的JDBC,这里指的是DButil的JDBC)
②ORM:对 对象关系映射的API,包括JPA(sun公司推出的持久化的接口)、JDO和Hibmate提供了集成层支持。
③OXM:提供了一个支持对象/XML映射的抽象层实现,如JAXB、Castor、XMLBeans、JiBX和XStream。
④JMS:指Java消息传递服务,包含使用和产生信息的特性,自4.1版本后支持与Spring-message模块的集成。
⑤Transcations:支持对实现特殊接口以及所有POJO类的编程和声明式的事务管理。
Web:
①Web:提供了基本的Web开发集成特性:如多文件上传、使用Servlet监听器来初始化IOC容器以及Web应用的上下文。
②Portlet:提供了在porlet环境中使用MVC实现,类似于Servlet模块的功能。
③servlet:提供了web应用的model_view_controller(mvc)的实现
其他模块:
①AOP:提供了面向切面编程的实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,已降低耦合性。
②Aspects:提供了与AspectJ的集成功能,AspectJ是一个功能强大且成熟的面向切面编程(AOP)框架。
③Instrumentation:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
④Messaging:spring4.0以后新增的模块,他提供了对消息传递体系结构和协议的支持。
⑤Test:提供了对单元测试和集成测试的支持。
4.Spring框架常用的一些注解:
IOC(控制反转)
1.控制反转是什么?
(Ioc,Inversion of Control),是一个概念,一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。
2.依赖是什么?
比如A类中存在B类的实例,在A类中可以调用B类的方法完成的功能,即A类对B类有依赖。
3.什么是依赖注入?
依赖注入俗称DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。依赖注入DI是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
4.Spring容器是什么?
Spring容器是一个超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。Spring 容器管理着容器中Bean之间的依赖关系,Spring 使用“依赖注入”的方式来管理Bean之间的依赖关系。使用loC实现对象之间的解耦合。
案例:(第一个spring程序)
项目结构:
resources文件夹只能放.xml结尾的文件
pom文件:
4.0.0
org.example
Demo1
1.0-SNAPSHOT
8
8
org.springframework
spring-context
6.0.11
junit
junit
4.13.2
test
pom文件讲解一
1.pom文件的作用:
pom文件定于了一个maven项目的maven配置,一般pom文件的放在项目或者模块的根目录下。
2.根元素和必要配置:
project是pom文件的根元素,project下有modelVersion、groupId、artifactId、version、packaging等重要的元素。其中,groupId、artifactId、version三个元素用来定义一个项目的坐标,也就是说,一个maven仓库中,完全相同的一组groupId、artifactId、version,只能有一个项目。
project:整个pom配置文件的根元素,所有的配置都是写在project元素里面的;
modelVersion:指定了当前POM模型的版本,对于Maven2及Maven 3来说,它只能是4.0.0;
groupId:这是项目组的标识。它在一个组织或者项目中通常是唯一的。
artifactId:这是项目的标识,通常是工程的名称。它在一个项目组(group)下是唯一的。
version:这是项目的版本号,用来区分同一个artifact的不同版本。
packaging:这是项目产生的构件类型,即项目通过maven打包的输出文件的后缀名,包括jar、war、ear、pom等。(如果不写该标签,maven默认打成jar包)
pojo类
package com.hh.pojo;
//User实例
public class User {
private String name;
private String age;
public User() {
System.out.println("学生");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + ''' +
", age='" + age + ''' +
'}';
}
}
测试类
package com.hh;
import com.hh.pojo.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//测试
public class TestSpring {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User)applicationContext.getBean("userid");
System.out.println("user="+user);
}
}
加载配置文件的方法:
我们加载配置文件时候有两种方法。
方法一:
FileSystemXmlApplicationContext:从硬盘绝对路径下加载配置文件
@Test
public void testBeanFactory1(){
//通过绝对路径加载配置文件
ApplicationContext context = new FileSystemXmlApplicationContext("D:\workspace\spring\spring-01-IOC\src\applicationContext-bean.xml");
}
方法二:
ClassPathXmlApplicationContext:从类路径下加载配置文件
@Test
public void testBeanFactory2(){
ApplicationContext context2 = new ClassPathXmlApplicationContext("applicationContext-bean.xml");
}
当多个配置文件需要同时加载时,我们有两种方法进行加载
第一种:在解析配置文件时一起编写
new ClassPathXmlApplicationContext("application1.xml",”application2.xml”);
第二种::在一个配置文件中导入另一个配置文件
配置文件
配置文件讲解一:
配置文件的头可以直接粘贴使用
标签讲解:
: 是根标签beans内部必须包含的标签,它是用于声明具体的类 的对象!
属性讲解:
Property | 属性解释 |
---|---|
id | id是bean对象的唯一标识,不能添加特别字符 |
name | name是bean对应对象的一个标识 |
class | 指定bean对应类的全路径 |
scope | 执行bean对象创建模式和生命周期 |
lazy-init | 是否延时加载 默认值:false |
init-method | 对象初始化方法 |
destory-method | 对象销毁方法 |
依赖注入的方式:
第一种:
set注入:
①基本类型值注入
通过Property标签给属性赋值,参数 name:属性名 value:属性值
②引入类型值注入
通过Property标签给属性赋值,参数 name:属性名 ref:另一个bean的id
运行时输出的结果:
第二种:
构造注入
①单个参数构造器注入
通过constructor-arg标签给属性赋值,参数 name:属性名 value:属性值
②index属性注入
出现名字相同时,但是参数位置不一样
通过constructor-arg标签给属性赋值,参数 name:属性名 value:属性值 index:参数在构造器的下标位置,下标是从0开始的
③type属性注入
参数名和位置一致,但类型不一致时
④p名称空间注入
导入p名称空间: 放在开头中beans标签里
xmlns:p="http://www.springframework.org/schema/p"
使用p:属性名 完成注入,走set方法
- 基本类型值: p:属性名="值"
- 引入类型值: P:属性名-ref="bean的id"
⑤spel表达式注入
spring Expression Language:spring表达式语言
注解讲解一:
注解的使用步骤:
加入maven的依赖spring-context,在你加入spring-context的同时,间接加入spring-aop的依赖。使用注解必须使用spring-aop依赖
在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置。
注解讲解:
@Autowired :使用 @Autowired
自动装配对象类型的属性和方法
@Service: 用于服务层
@Repository :用于dao层
@Controller :用于控制层
@Component:通用的注解,可标注任意类为 Spring
组件 ,当你不知道该类属于那一层时,可以使用该注解
案例:
配置文件:
dao层
package com.hh.dao;
public interface UserDao {
void write();
}
package com.hh.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void write() {
System.out.println("我叫赵子龙");
}
}
service层
package com.hh.service;
import com.hh.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void write(){
userDao.write();
}
}
pom文件
4.0.0
org.example
Demo2
1.0-SNAPSHOT
8
8
org.springframework
spring-context
5.0.2.RELEASE
junit
junit
4.13.2
test
AOP
1. Aop介绍
AOP底层,就是采用动态代理模式实现的。采用了两种代理: JDK的动态代理,与CGLIB的动态代理。
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方法,使用动态代理)。 切面:给你的目标类增加的功能,就是切面,什么日志等(切面的特点:一般都是非业务方法,独立使用的。)
2. AOP核心概念
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
3.AOP的两种代理方式:
第一种:JDK 的动态代理:针对实现了接口的类产生代理。
案例:
配置文件
增强的方法有5种:
①before:前置增强 在目标方法前织入增强处理
②after-returning:后置增强 在目标方法正常执行(不出现异常)后织入增强处理
③after-throwing:异常增强 在目标方法抛出异常后织入增强处理
④after:最终增强 不论方法是否抛出异常,都会在目标方法最后织入增强处理
⑤around:环绕增强 在目标方法的前后都可以织入增强处理
接口
package com.hh.aop.service;
public interface UserService {
public void add();
public void update();
public void delete();
public void find();
}
接口的实现类
package com.hh.aop.service.impl;
import com.hh.aop.service.UserService;
public class UserServiceImpl implements UserService {
public void add(){
System.out.println("执行增加的方法");
}
public void update(){
System.out.println("执行修改的方法");
}
public void delete(){
System.out.println("执行删除的方法");
}
public void find(){
System.out.println("执行查询的方法");
}
}
增强类
package com.hh.aop;
/*
1.我们需要确定目标是谁 Target=UserServiceImpl
2.确定增强类是谁 advice=Log
3.确定被增强的方法有哪些 Pointcut=UserServiceImpl.add() 使用我们的表达式来匹配
4.确定增强位置 (在目标方法执行的前面/后面)
*/
public class Log {
public void writeLog(){
System.out.println("执行日志功能");
}
}
启动项
package com.hh.aop;
import com.hh.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContent.xml");
UserService userServiceImpl =(UserService) applicationContext.getBean("userServiceImpl");
userServiceImpl.add();
}
}
pom文件
4.0.0
org.example
Demo3
1.0-SNAPSHOT
8
8
org.springframework
spring-context
5.0.2.RELEASE
org.aspectj
aspectjweaver
1.8.10
注解写法:
接口:
package com.hh.aop3.service;
public interface UserService {
public void add();
public void update();
public void delete();
public void find();
}
接口实现类:
package com.hh.aop3.service.impl;
import com.hh.aop3.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
public void add(){
System.out.println("执行增加的方法");
}
public void update(){
System.out.println("执行修改的方法");
}
public void delete(){
System.out.println("执行删除的方法");
}
public void find(){
System.out.println("执行查询的方法");
}
}
增强类:
package com.hh.aop3;
/*
1.我们需要确定目标是谁 Target=UserServiceImpl
2.确定增强类是谁 advice=Log
3.确定被增强的方法有哪些 Pointcut=UserServiceImpl.add() 使用我们的表达式来匹配
4.确定增强位置 (在目标方法执行的前面/后面)
*/
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect//标注此类为增强类
@Component
public class Log {
// 日志的工具类
private Logger logger=Logger.getLogger(Log.class);
@Before("execution(public * com.hh.aop3.service.impl.UserServiceImpl.delete())")
public void writeLog(JoinPoint joinPoint){
logger.info("日志工具类");
System.out.println("目标类:"+joinPoint.getTarget());
System.out.println("目标类的方法:"+joinPoint.getSignature().getName());
System.out.println("目标参数:"+joinPoint.getArgs());
}
}
启动项:
package com.hh.aop3;
import com.hh.aop3.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@ComponentScan
@EnableAspectJAutoProxy //产生代理 帮你实现增强
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(App.class);
UserService userServiceImpl =(UserService) annotationConfigApplicationContext.getBean("userServiceImpl");
userServiceImpl.delete();
}
}
加载注解的方法:AnnotationConfigApplicationContext
补充一点儿:
-
AnnotationConfigApplicationContext :加载注解
-
ClassPathXmlApplicationContext :加载配置文件(通过.xml文件方式)
-
FileSystemXmlApplicationContext :加载配置文件(通过文件路径,可以加载多个配置文件)
楼上的三种加载方法都是ApplicationContext的子类
注解讲解二:
@Aspect: 标注此类为增强类,写在类上
@ComponentScan:作用就是根据定义的扫描路径,把符合扫描规则带注解的类装配到spring容器 。写在启动项上
@EnableAspectJAutoProxy:作用是应用切面编程自动生产代理对象,写在启动项上
-
CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类的子类对象
在配置文件上加载这个属性,默认是false
Spring-JDCB数据访问
1.SpringJDBC的概念
Spring JDBC是Spring所提供的持久层技术,它的主要目标是降低使用JDBC API的门槛,以一种更直接,更简介,更简单的方式使用JDBC API, 在Spring JDBC里,仅需做那些与业务相关的DML操作,而将资源获取,Statment创建,资源释放以及异常处理等繁杂而乏味的工作交给Spring JDBC。
事务管理
1.什么是事务?
事务就是逻辑上一组操作,要么全都成功,要么全都失败。比如银行转账,要么转账成功要么转账失败。
2.为什么要用事务?
数据一致性:在一个事务中,所有的操作要么全部成功,要么全部失败回滚。这样可以保证数据的一致性,避免出现部分操作成功,部分操作失败的情况。
数据完整性:在一个事务中,所有的操作要么全部执行,要么全部回滚。这样可以保证数据的完整性,避免出现部分操作执行,部分操作未执行的情况。
并发控制:在多用户并发访问数据库的情况下,使用事务可以避免出现数据冲突和竞争条件,提高系统的并发性能。
错误恢复:在出现错误的情况下,使用事务可以保证数据的恢复性。如果某个操作失败,整个事务会回滚到初始状态,避免数据的损坏和丢失。
3.事务的特性有哪些?
ACID(关系型数据设计事务的原则)
原子性(atomicity):事务不可分割【要么都成功/要么都失败】
一致性(Consistency):事务执行的前后,数据完整性保持一致.【更新前后数据的和不变】
隔离性(Isolation):一个事务执行的时候,不应该受到其他事务的打扰
持久性(Durability):一旦结束,数据就永久的保存到数据库.
4.事务的并发问题(数据库的并发问题)
①脏读(Dirty Read)
一个事务读取到了另一个事务未提交的数据操作结果。如果后续事务回滚,读取到的数据就是无效的,可能会导致错误的结果。
②不可重复读(虚读)(NonRepeatable Read)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
③幻读(Phantom Read)
事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据,这是因为在两次查询过程中有另外一个事务插入数据造成的。
④更新丢失(Lost Update)
两个事务同时读取同一数据,然后分别修改该数据,并尝试将修改后的数据写回数据库。由于并发执行,只有一个事务的修改能够成功写回,另一个事务的修改将丢失。
5.事务的隔离级别(Spring定义的隔离级别)
①读未提交(不推荐,并发性最好,即性能最好,隔离性最差,安全性最差)
Read uncommitted:最低级别,以上情况均无法保证。
②读已提交(oracle默认值)
Read committed:可避免脏读情况发生。(不可重复读及幻读都可以发生)
③可重复读(mysql默认值)
Repeatable read:可避免脏读、不可重复读情况的发生。不可以避免幻读。
④串行化读 Serializable(隔离性最好,安全性最好,并发性最差,性能最差):
事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重
6.事务的传播机制
事务的传播行为:解决业务层之间的调用的事务的关系.
Propagation_require :支持当前事务,如果不存在 就新建一个
* A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
Propagation_supports :支持当前事务,如果不存在,就不使用事务
* A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
Propagation_mandatory :支持当前事务,如果不存在,抛出异常
* A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.
Propagation_requires_new :如果有事务存在,挂起当前事务,创建一个新的事务
* A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
Propagation_not_supported: 以非事务方式运行,如果有事务存在,挂起当前事务
* A,B 非事务的方式运行,A有事务,就会挂起当前的事务.
Propagation_never :以非事务方式运行,如果有事务存在,抛出异常
Propagation_nested :如果当前事务存在,则嵌套事务执行
* 基于SavePoint技术.保存点
* A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.如B事务有异常的话,用户需要自己设置事务提交还是回滚.
7.TransactionStatus:事务状态
是否有保存点
是否一个新的事务
事务是否已经提交
三者的关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态:状态由TransactionStatus记录.
8.Spring的事务管理
Spring的事务管理分成两类:
①编程式事务管理:[手动编写代码完成事务管理.]
pom文件
4.0.0
org.example
Demo4
1.0-SNAPSHOT
8
8
org.springframework
spring-context
5.0.2.RELEASE
org.springframework
spring-jdbc
5.0.2.RELEASE
junit
junit
4.13.2
test
org.aspectj
aspectjweaver
1.8.10
com.alibaba
druid
1.2.18
com.mysql
mysql-connector-j
8.0.32
属性文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/shop
jdbc.username=root
jdbc.password=123456
配置文件:
pojo类:
package com.hh.pojo;
import java.util.Date;
public class Account {
private Integer id;
private String name;
private Double 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 Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + ''' +
", money=" + money +
'}';
}
}
mapper接口
package com.hh.mapper;
public interface AccountMapper {
int inMoney(String inName,double money);
int outMoney(String outName,double money);
}
接口实现类:
package com.hh.mapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionTemplate;
public class AccountMapperImpl implements AccountMapper {
// 使用JdbcTemplate模板
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int inMoney(String inName, double money) {
// 编写sql语句
String sql="update account set money=money+? where name=inName";
// 收集参数
Object [] params={money,inName};
// 调用方法
return jdbcTemplate.update(sql,params);
}
@Override
public int outMoney(String outName, double money) {
// 编写sql语句
String sql="update account set money=money+? where name=outName";
// 收集参数
Object [] params={money,outName};
// 调用方法
return jdbcTemplate.update(sql,params);
}
}
service接口:
package com.hh.service;
public interface AccountService {
boolean moneyService(String outName,String inName,double money);
}
接口实现类:
package com.hh.service;
import com.hh.mapper.AccountMapper;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
public class AccountServiceImpl implements AccountService{
private AccountMapper accountMapper;
private TransactionTemplate transactionTemplate;
public void setAccountMapper(AccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public boolean moneyService(String outName, String inName, double money) {
transactionTemplate.execute(new TransactionCallback() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
accountMapper.outMoney(outName,money);
accountMapper.inMoney(inName,money);
return null;
}
});
return true;
}
}
启动项:
package com.hh;
import com.hh.service.AccountService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService serviceImpl = (AccountService)applicationContext.getBean("accountServiceImpl");
serviceImpl.moneyService("张三","李四",100);
}
}
②声明式事务管理:[不需要手动编写代码,配置.]
修改编程式事务代码
配置文件:
aop:config与tx:advice的区别?
aop:config和tx:advice 但是两者并不冲突, aop:config面向切面编程的切点,选择对应的方法进行切入,而tx:adivce是设置事务的相关的属性和描述,换句话说,aop:config选择了对应的切入点,tx:advice是在这些切入点上根据 method name属性再次进行筛选!!!
service接口实现类
package com.hh.service;
import com.hh.mapper.AccountMapper;
public class AccountServiceImpl implements AccountService{
private AccountMapper accountMapper;
public void setAccountMapper(AccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
@Override
public boolean moneyService(String outName, String inName, double money) {
accountMapper.outMoney(outName,money);
accountMapper.inMoney(inName,money);
return true;
}
}
Spring中常用的注解总结:
1、声明bean的注解
@Component 组件,没有明确的角色
@Service 在业务逻辑层使用(service层)
@Repository 在数据访问层使用(dao层)
@Controller 在展现层使用,控制器的声明(C)
2、注入bean的注解
@Autowired:由Spring提供
@Inject:由JSR-330提供
@Resource:由JSR-250提供
都可以注解在set方法和属性上,推荐注解在属性上(一目了然,少写代码)。
3、java配置类相关注解
@Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
@Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
@ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
@WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解
4、切面(AOP)相关注解
Spring支持AspectJ的注解式切面编程。
@Aspect 声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
@After 在方法执行之后执行(方法上) @Before 在方法执行之前执行(方法上) @Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
5、@Bean的属性支持
@Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean) 其设置类型包括:
Singleton (单例,一个Spring容器中只有一个bean实例,默认模式), Protetype (每次调用新建一个bean), Request (web项目中,给每个http request新建一个bean), Session (web项目中,给每个http session新建一个bean), GlobalSession(给每一个 global http session新建一个Bean实例)
@StepScope 在Spring Batch中还有涉及
@PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod
@PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod
6、@Value注解
@Value 为属性注入值(属性上) 支持如下方式的注入: 》注入普通字符
》注入操作系统属性
》注入表达式结果
》注入其它bean属性
》注入文件资源
》注入网站资源
》注入配置文件
注入配置使用方法: ① 编写配置文件(test.properties)
book.name=《三体》
② @PropertySource 加载配置文件(类上)
③ 还需配置一个PropertySourcesPlaceholderConfigurer的bean。
7、环境切换
@Profile 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。(类或方法上)
@Conditional Spring4中可以使用此注解定义条件话的bean,通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。(方法上)
8、异步相关
@EnableAsync 配置类中,通过此注解开启对异步任务的支持,叙事性AsyncConfigurer接口(类上)
@Async 在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)
9、定时任务相关
@EnableScheduling 在配置类上使用,开启计划任务的支持(类上)
@Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)
10、@Enable*注解说明
这些注解主要用来开启对xxx的支持。 @EnableAspectJAutoProxy 开启对AspectJ自动代理的支持
@EnableAsync 开启异步方法的支持
@EnableScheduling 开启计划任务的支持
@EnableWebMvc 开启Web MVC的配置支持
@EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持
@EnableJpaRepositories 开启对SpringData JPA Repository的支持
@EnableTransactionManagement 开启注解式事务的支持
@EnableTransactionManagement 开启注解式事务的支持
@EnableCaching 开启注解式的缓存支持
@EnableAspectJAutoProxy:作用是应用切面编程自动生产代理对象,写在启动项上
11、测试相关注解
@RunWith 运行器,Spring中通常用于对JUnit的支持
@ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
面试题
1.IOC和DI是什么,它们之间有什么关系
IOC是控制反转,DI是依赖注入
DI是IOC的技术实现,Spring使用的是反射机制
2.Spring加载配置文件的方式的区别(ClassPath和FileSystem)
前者是通过类路径加载
后者是绝对路劲加载
3.Spring加载bean的两种方式的区别(BeanFactory和ApplicationContext)
加载时机不同:
ApplicationContext是一次性立刻加载,比较消耗资源但是后续读取非常快,会将spring中所有的bean进行初始化,全部实例化到spring中。
Beanfactory是一个用来管理bean对象的工厂,加载bean的时候不会立刻一次性加载,使用的是惰性加载,只有执行调用get bean方法才会加载对应的bean。
功能不同:
Spring有两个顶级接口,BeanFactory和ApplicationContext接口,其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子接口,除了实现了BeanFactory接口之外,还增强了自己的功能,支持国际化资源绑定、和消息处理事件发布和监听、AOP(面向切面编程)、支持Web应用程序。
4.Spring创建的bean的生命周期:
①Spring中的bean的生命周期主要包含四个阶段:实例化Bean --> Bean属性填充 --> 初始化Bean -->销毁Bean
②首先是实例化Bean,当客户向容器请求一个尚未初始化的bean时,容器就会调用doCreateBean()方法进行实例化,实际上就是通过反射的方式创建出一个bean对象
③Bean实例创建出来后,接着就是给这个Bean对象进行属性填充,也就是注入这个Bean依赖的其它bean对象
④属性填充完成后,进行初始化Bean操作,初始化阶段又可以分为几个步骤:
A:执行Aware接口的方法
Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的些资源。如实现
BeanNameAware接口可以获取到BeanName,实现BeanFactoryAware接口可以获取到工厂对象BeanFactory等
B:执行BeanPostProcessor的前置处理方法postProcessBeforelnitialization(),对Bean进行一些自定义的前置处理
判断Bean是否实现了InitializingBean接口,如果实现了,将会执行lnitializingBean的afterPropertiesSet()初始化方法;
执行用户自定义的初始化方法,如init-method等;
执行BeanPostProcessor的后置处理方法postProcessAfterinitialization()
⑤初始化完成后,Bean就成功创建了,之后就可以使用这个Bean, 当Bean不再需要时,会进行销毁操作,
首先判断Bean是否实现了DestructionAwareBeanPostProcessor接口,如果实现了,则会执行DestructionAwareBeanPostProcessor后置处理器的销毁回调方法
其次会判断Bean是否实现了DisposableBean接口,如果实现了将会调用其实现的destroy()方法
最后判断这个Bean是否配置了dlestroy-method等自定义的销毁方法,如果有的话,则会自动调用其配置的销毁方法;
5.在Spring中如何注入基本数据类型
6.@Component注解的作用
7.@Resource注解和@Autowired注解的作用和区别
@Resource注解和@Autowired注解的最大区别在于它们的查找顺序不同。@Resource注解会先按照名称来查找要注入的Bean或者资源,如果找不到再按照类型来查找。而@Autowired注解则是按照类型来查找要注入的Bean,如果找不到再按照名称来查找。
另外,@Resource注解是Java EE标准中定义的注解,而@Autowired注解是Spring框架中定义的注解。因此,如果您的应用程序需要与Java EE容器进行集成,则应该使用@Resource注解。如果您的应用程序只使用Spring框架,则可以使用@Autowired注解。
总之,@Resource注解和@Autowired注解都是Spring框架中用于依赖注入的注解,它们可以将一个Bean注入到另一个Bean中,从而实现Bean之间的依赖关系。
8.Spring如何集成JUnit
导入spring-test-4.2.8.jar包
//创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class RunWithTest {
//将名为user的对象注入到u变量中
@Resource(name="person")
private Person p;
@Test
public void testCreatePerson(){
System.out.println(p);
}
}
9.什么是AOP,AOP有什么作用
10.在Spring中什么地方使用了AOP
事务管理:通过AOP可以将事务管理逻辑从业务逻辑中分离出来,使得事务的控制更加灵活和集中化。
安全性检查:通过AOP可以实现对方法或者资源的访问控制,例如权限检查、身份验证等。
日志记录:通过AOP可以在方法执行前后进行日志记录,方便跟踪和调试。
性能监控:通过AOP可以对方法的执行时间进行监控,以便分析和优化系统性能。
异常处理:通过AOP可以捕获方法抛出的异常,并进行统一的处理和转换。
缓存管理:通过AOP可以实现方法级别的缓存,提高系统的性能和响应速度。
11.在项目中什么时候用到了AOP,举例说明
日志记录:在项目中,我们经常需要记录方法的执行日志,以便跟踪和排查问题。通过AOP,我们可以在方法执行前后插入日志记录的逻辑,例如记录方法名称、参数信息、执行时间等。这样可以避免在每个方法中都进行日志记录的重复代码,提高代码的可维护性。
事务管理:在涉及数据库操作的项目中,事务管理是非常重要的一部分。通过AOP,我们可以将事务管理逻辑从业务逻辑中分离出来,以便实现事务的统一管理。例如,在一个订单处理的项目中,可以通过AOP将订单保存、库存更新等操作划分为一个事务,保证这些操作要么全部成功,要么全部回滚。
安全性检查:许多项目需要进行权限控制和身份验证。通过AOP,我们可以在方法执行前进行安全性检查,例如检查用户是否有权限执行某个操作,或者验证用户的身份信息。这样可以避免在每个方法中都进行权限检查的重复代码。
缓存管理:在需要频繁读取和计算的项目中,使用缓存可以提高系统的性能和响应速度。通过AOP,我们可以在方法执行前先检查缓存中是否存在结果,如果存在则直接返回缓存数据,避免重复计算。如果缓存中不存在结果,则执行方法并将结果存入缓存中。
12.名词解释:切面、切入点、连接点、织入、通知策略
13.简述SpEL的作用和在项目中的使用
SpEL(Spring Expression Language)是Spring框架自3.0版本后新增的模块,它是一种表达式语言,用于在运行时查询和操作对象。SpEL提供了一种灵活且强大的方式来访问和操作对象的属性、方法和其他相关信息。
在项目中,SpEL可以用于以下方面:
在Spring配置文件中使用SpEL表达式来配置bean属性值,例如使用SpEL表达式来设置bean的属性值,或者使用SpEL表达式来计算bean的属性值。
在注解中使用SpEL表达式来注入bean,例如使用@Value注解来注入bean的属性值,或者使用@Conditional注解来条件化地注入bean。
在Spring AOP中使用SpEL表达式来定义切点和通知,例如使用SpEL表达式来定义切点表达式,或者使用SpEL表达式来计算通知参数值。
在Spring MVC中使用SpEL表达式来处理请求参数和响应结果,例如使用SpEL表达式来获取请求参数值,或者使用SpEL表达式来计算响应结果值。
总之,SpEL可以在Spring框架的各个模块中使用,它提供了一种灵活且强大的方式来访问和操作对象的属性、方法和其他相关信息,使开发者能够更轻松地实现各种功能。
14.Spring JDBC、Hibernate、JPA的优缺点
Spring JDBC、Hibernate和JPA都是用于在Java应用程序中进行数据库访问的框架和技术。它们各自具有一些优点和缺点,下面是它们的简要比较:
Spring JDBC:
优点:
相对简单易用,直接使用JDBC API,不需要额外的配置和学习成本
提供了简化和模板化的JDBC操作,减少了样板代码的编写。
更接近底层数据库,可以更好地控制和优化SQL查询。
轻量级,适合小型项目和对性能要求较高的场景。
缺点:
需要手动处理数据库连接、事务管理等底层细节。
缺乏对象关系映射(ORM)能力,需要手动映射和处理结果集。
对于复杂的查询和关联操作,需要编写更多的SQL语句和代码。
Hibernate:
优点:
提供了强大的ORM功能,将Java对象与数据库表进行映射,减少了手动编写SQL的工作。
自动处理数据库连接、事务管理等底层细节,简化了开发流程。 - 支持缓存、延迟加载等性能优化功能。
提供了丰富的查询语言(HQL、Criteria API),方便进行复杂查询和关联操作。
缺点:
学习曲线较陡峭,需要掌握复杂的配置和映射规则。
生成的SQL语句可能不够优化,需要开发人员进行调优。
对于大量数据的批量操作,性能可能不如Spring JDBC。
JPA (Java Persistence API): -
优点:
是Java EE标准的ORM规范,提供了统一的API和注解,可以方便地切换不同的ORM实现(如Hibernate、EclipseLink等)。
提供了面向对象的查询语言(JPQL)和Criteria API,支持复杂的查询和关联操作。
自动处理数据库连接、事务管理等底层细节,简化了开发流程。
缺点:
学习曲线较陡峭,需要掌握复杂的注解和API。
对于一些高级特性和性能调优,可能需要使用底层ORM实现的特定功能。
总结来说,Spring JDBC适合简单的数据库操作和对性能要求较高的场景;Hibernate适合需要强大ORM功能和复杂查询的场景;JPA适合需要标准化的ORM规范 和可移植性的场景。选择合适的框架取决于项目的需求和开发团队的经验。
15.如何使用Spring进行事务管理
在Spring框架中,可以使用以下几种方式来进行事务管理:
基于XML配置的声明式事务管理:通过在Spring配置文件中配置事务管理器和事务通知,可以声明事务的开始、提交或回滚,并将其应用于特定的方法或类。这种方式可以使用XML配置文件或注解来定义事务的属性,如隔离级别、传播行为等。
基于注解的声明式事务管理:使用@Transactional注解可以将事务的属性直接应用于方法或类。通过在方法或类上添加@Transactional注解,可以指定事务的开始、提交或回滚,并设置事务的属性。
编程式事务管理:通过编写代码来显式地管理事务的开始、提交或回滚。在编程式事务管理中,可以使用TransactionTemplate或PlatformTransactionManager等Spring提供的API来执行事务操作。
无论使用哪种方式,事务管理的关键是配置事务管理器和事务通知。事务管理器负责管理底层的事务资源,如数据库连接,而事务通知则定义了事务的开始、提交或回滚的时机和行为。
16.什么是动态代理?什么是静态代理?
动态代理是一种在运行时创建代理对象的机制,它允许在不事先知道被代理对象的具体类型的情况下,通过使用反射来动态地创建代理对象。动态代理通常用于在运行时为目标对象创建代理,以提供额外的功能或控制。
静态代理是一种在编译时创建代理对象的机制,它通过手动编写代理类来实现对目标对象的代理。在静态代理中,代理类和被代理类在编译时就已经确定,代理类负责将方法调用转发给被代理对象,并可以在方法调用前后执行一些额外的操作。