注意:
a、类当中所有对象的实例与基本数据类型的变量统称为成员也可以叫属性,如name、card
b、其中所有对象的实例又可以统称为实例名称或对象名称
public class Student { private String name;//string类型 private IdCard card;//java类对象 }
第一章——Spring简介
1、什么是JavaEE构架
三大核心、四大容器
a、JavaEE构架基于Java SE基础构建的,主要由容器、组件和服务三大核心部分组成
b、JavaEE一共提供了四种容器,Appelt Container、Application Client Container、Web Container和EJB Container
- Application程序
使用主方法运行的一种组件
- Web容器
运行JSP和Servlet组件
- EJB
提供的是一个业务中心,属于分布式开发
三层架构
在整个企业的应用环境中,JavaEE构架只是工作在中间层的一种组件
- 客户层:
分为内部用户和外部用户,客户端可以使用Web浏览器,也可以是Java编写的应用。
- 中间层:
为客户访问提供服务,使用JavaEE中的各种组件技术进行搭建,且各个容器之间
- 企业信息系统层:
例如保存数据的数据库
JavaEE的MVC设计模式
2、什么是spring
a、spring是轻量级JavaEE服务器端组件模型框架,便于创建性能好、易于测试、可重用的代码。
b、最新的技术有springboot、springcloud、ejb3
- 两个核心
以IoC控制反转(如DI依赖注入)和AOP面向切面编程为内核
- 设计模式
a、表现层:
提供SpringMVC和Struts的整合功能
b、业务逻辑层
可以管理事务、记录日志等
c、持久层
可以整合Mybatis、Hibernate、JdbcTemplate等技术
- 优点
a、将所有对象的创建和依赖关系的维护工作都交给了spring容器管理,降低了组件之间的耦合性
b、提供了对aop的支持,对安全、事务、日志等进行集中式处理,提高了程序的复用性
c、支持声明式事务处理,通过配置去完成而无需手动编程
d、提供对Junit的支持,方便测试
e、提供了对其他框架的支持(如Struts、hibernate、mybaties等)
f、对javaee的一些api进行封装,降低了使用难度
3、体系结构
Spring 框架共约 20 个模块,核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成
Core Conainer核心容器
- spring-core
模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能
- spring-beans
提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
- context
模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。
- spring-context-support
提供对第三方库的集成支持,如缓存、邮件服务等
- spring-expression
模块提供了强大的表达式语言,用于在运行时查询和操作对象图。
第二章——Spring IoC 容器
a、对象的创建与关系交给容器负责,并管理他们的整个生命周期从创建到销毁。
b、Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans
c、"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做
序号 | 容器 & 描述 |
---|---|
1 | Spring BeanFactory 容器 它是最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。 用于轻量级的应用程序,如移动设备或基于 applet 的应用程序,在数据量和速度上有优势 |
2 | Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文 该容器添加了更多的企业特定的功能,由 org.springframework.context.ApplicationContext 接口定义。 例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。 |
ApplicationContext 容器包括 BeanFactory 容器的所有功能,通常建议使用。BeanFactory 用于轻量级的应用程序
bean.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-3.0.xsd"> <bean id=" " class=" "> <property name=" " value=" "/> </bean> </beans>
t.java
ApplicationContext context = new FileSystemXmlApplicationContext("文件路径"); 类 对象 = (类) context.getBean("id");
1、Spring Bean 定义
bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象,由容器提供的配置元数据创建。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
name | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope | 作用域 |
constructor-arg | 它是用来注入依赖关系的 |
properties | 它是用来注入依赖关系的 |
parent | 继承,内容为父bean的Id值 |
<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-3.0.xsd"
<bean id="..." class="..."> </bean>
2、Spring Bean 作用域
当在 Spring 中定义一个 bean 时,必须声明该 bean 的作用域
作用域 | 描述 |
---|---|
singleton | 仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境 |
<?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-3.0.xsd">
<bean id=" " class=" " scope="singleton">
</bean>
</beans>
3、Spring Bean 生命周期
所谓生命周期,可以理解为当一个 bean 被实例化时,需要做一些初始化工作。当 bean 不再需要,需要做一些清除工作。
- 初始化:init-method
@PostConstruct:初始化方法的注解方式 等同与init-method
指定一个方法或者实现InitializingBean 接口使用默认的afterPropertiesSet方法,在实例化 bean 时,立即调用该方法。
public class ExampleBean implements InitializingBean { public void afterPropertiesSet() { // do some initialization work } }
bean.xml
<bean id="exampleBean" class="examples.ExampleBean" init-method="init"/>
ExampleBean .java
public class ExampleBean { public void init() { // do some initialization work } }
- 销毁:destroy-method
@PreDestroy: 销毁方法的注解方式 等同于destory-method
指定一个方法或者实现InitializingBean 接口使用默认的destroy方法,只有从容器中移除 bean 之后,才调用该方法。
public class ExampleBean implements DisposableBean { public void destroy() { // do some destruction work } }
bean.xml
<bean id="exampleBean" class="examples.ExampleBean" destroy-method="destroy"/>
ExampleBean .java
public class ExampleBean { public void destroy() { // do some destruction work } }
- 默认
设置默认的初始化和销毁方法
<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-3.0.xsd"
default-init-method="init"
default-destroy-method="destroy">
<bean id="..." class="..."> </bean>
</beans>
- 调用
a、在使用 AbstractApplicationContext 注册bean时需要调用 registerShutdownHook() 方法
b、它将确保正常关闭,并且调用相关的 destroy 方法
AbstractApplicationContext context = new ClassPathXmlApplicationContext("文件路径");
类 对象 = (类) context.getBean("id");
context.registerShutdownHook();//确保正常关闭,并且调用相关的 destroy 方法
4、Spring Bean 后置处理器
在调用初始化方法前后对 Bean 进行额外的处理,需继承BeanPostProcessor 接口
a、ApplicationContext 会自动检测由 BeanPostProcessor 接口的实现定义的 bean,注册这些 bean 为后置处理器
b、通过在容器中创建 bean,在适当的时候调用它。
com.tutorialspoint.Init
它在任何 bean 的初始化的之前和之后输入该 bean 的名称,实现更复杂的逻辑
public class Init implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;} public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;} }
beam.xml
注意这里扫描后置处理器的方式,不同于普通bean的定义方式,也和注解的扫描方式不一样
<?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-3.0.xsd"> <bean id=" " class=" " init-method="init" destroy-method="destroy"> </bean> <bean class="com.tutorialspoint.Init" /> </beans>
调用方式
自动注册这些 bean 为后置处理器,在适当的时候调用它
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); HelloWorld obj = (HelloWorld) context.getBean("helloWorld"); obj.getMessage(); context.registerShutdownHook();
输出:
BeforeInitialization : helloWorld Bean is going through init. AfterInitialization : helloWorld Your Message : Hello World! Bean will destroy now.
第三章——依赖注入
通过依赖注入的方式来管理Bean之间的依赖关系。
a、每个基于应用程序的 java 都有几个对象,依赖注入(或有时称为布线)有助于把这些类粘合在一起,同时保持他们独立。
b、基于XML的两种装配方式:构造注入和设置注入
c、ref 属性:传递引用,内容为另一个bean的id,通过bean的id引用另一个java对象
d、value 属性:传递值,内容为具体的数据,如字符串或8个基本数据类型的值
- 构造注入
通过向constructor方法传递参数完成注入,index为构造函数参数的下标,当index忽略时按照constructor方法参数顺序去注入
<bean id=" " class=" " >
<constructor-arg index="0" value=" "/>
<constructor-arg index="1" ref="id值"/>
</bean>
<bean id="id值" class=" "></bean>
- 设值注入
通过向set方法传递参数完成注入,name为java成员的名称
<?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-3.0.xsd">
<bean id=" " class=" ">
<property name=" " ref="id值"/>
<property name=" " value="值"/>
</bean>
<bean id="id值" class=" ">
</bean>
</beans>
- 内部注入
在内部定义bean,而不使用ref="id值"的方式去引用
<bean id=" " class="...">
<property name="target">
<bean id=" " class="..."/>
</property>
</bean>
- 集合注入
注意:
可以将引用和值混合在一起使用,前提是类集成员在声明时是没有泛型的
List addressList; Set addressSet; Map addressMap;
<property name="addressList">
<list>
<ref bean="id值"/>
<value>值</value>
</list>
</property>
<property name="addressMap">
<map>
<entry key=" " value="值"/>
<entry key=" " value-ref="id值"/>
</map>
</property>
第四章—— 自动装配
所谓自动装配,就是将一个Bean自动的注入到其他Bean当中
注意:
a、基本类型和字符串仍要手动配置,只能去完成引用。
b、对象名称必须与类命名保持一致,当然是不区分大小写的
- byName
由属性名称指定自动装配
<bean id=" " class="" autowire="byName">
<property name=" " value=" " />
</bean>
- byType
由属性类型指定自动装配
<bean id=" " class="" autowire="byType">
<property name=" " value=" " />
</bean>
- constructor
由构造函数自动装配
<bean id=" " class=" " autowire="constructor">
<constructor-arg value="值"/>
</bean>
第五章—— 基于Annotation的自动装配
在 XML 注入之前进行注解注入,因此与xml同时存在时,注解注入会被xml注入替代。
注意:
a、@Component 仅仅用来表示这是一个组件,用于层次区分,没有实际作用,不使用该注释也不会有任何影响
b、@Autowired、@Resource作用相同,可以用在成员或set方法上,通常写在成员上
c、仍要遵循第四章的两点注意事项
d、当命名不一致时,通过name和type属性去指定名称
@Autowired(name="card") private IdCard card;
e、< context:annotation-config />和 < context:component-scan>同时存在的时候,前者会被忽略。
注释 | 说明 |
---|---|
@Component | 表示一个组件(Bean),可以作用在任何层次 |
@Repository | 将数据访问层(DAO)的类标识为Spiring的Bean,作用与@Component相同 |
@Service | 将业务层(Service)的类标识为Spiring的Bean,作用与@Component相同 |
@Controller | 将控制层(Controller)的类标识为Spiring的Bean,作用与@Component相同 |
@Autowired | 注入依赖的注释 默认按照Bean的类型名称进行装配,如果匹配不成功再按照实例名称装配 也可以手动通过name和type属性进行装配 |
@Resource | 注入依赖的注释 默认按照Bean的实例名称进行装配,如果匹配不成功再按照类型名称装配 也可以手动通过name和type属性进行装配
|
- 非自动扫描
通过注释完成自动装配属性,而不通过<property>配置属性 ,但仍需通过id与class定义bean
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 使用 context 命名空间 ,在配置文件中开启相应的注解处理器 -->
<context:annotation-config/>
<bean id=" " class=" "></bean>
<bean id=" " class=" "></bean>
</beans>
- 自动扫描
对包路径下的所有Bean文件进行扫描,不再需要手动配置那么多bean
<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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--使用 context 命名空间 ,通知Spring扫描指定包下所有Bean类,进行注解解析-->
<context:component-scan base-package="路径" />
</beans>
第六章——Spring JDBC 框架
a、Spring JDBC 框架负责处理低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。
b、所以当从数据库中获取数据时,只需定义连接参数,指定要执行的 SQL 语句,
- JdbcTemplate 类
配置数据源并注入到 DAO 类中,同时在 DAO 类的数据源设值函数中实例化 JdbcTemplate对象
StudentJDBCTemplate.java
private DataSource dataSource; private JdbcTemplate jdbcTemplateObject; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.jdbcTemplateObject = new JdbcTemplate(dataSource); public void create(String name, Integer age) { String SQL = "insert into Student (name, age) values (?, ?)"; jdbcTemplateObject.update( SQL, name, age); return; } public Student getStudent(Integer id) { String SQL = "select * from Student where id = ?"; Student student = jdbcTemplateObject.queryForObject(SQL, new Object[]{id}, new StudentMapper()); return student; } public List<Student> listStudents() { String SQL = "select * from Student"; List <Student> students = jdbcTemplateObject.query(SQL, new StudentMapper()); return students; } public void delete(Integer id){ String SQL = "delete from Student where id = ?"; jdbcTemplateObject.update(SQL, id); return; } public void update(Integer id, Integer age){ String SQL = "update Student set age = ? where id = ?"; jdbcTemplateObject.update(SQL, age, id); return; } }
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-4.3.xsd"> <!-- 1配置数据源 --> <bean id="dataSource" class= "org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--数据库驱动 --> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <!--连接数据库的url --> <property name="url" value="jdbc:mysql://localhost:3306/spring" /> <!--连接数据库的用户名 --> <property name="username" value="root" /> <!--连接数据库的密码 --> <property name="password" value="root" /> </bean> <bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
- 配置数据源
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
- 数据访问对象(DAO)
支持多种数据访问技术,如 JDBC、Hibernate、JPA 或者 JDO。
accountDao
- 配置模板
也可配置JdbcTemplate而不在dao中去实例化
<?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-4.3.xsd">
<!-- 1配置数据源 -->
<bean id="dataSource" class=
"org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<!--连接数据库的url -->
<property name="url" value="jdbc:mysql://localhost:3306/spring" />
<!--连接数据库的用户名 -->
<property name="username" value="root" />
<!--连接数据库的密码 -->
<property name="password" value="root" />
</bean>
<bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
<!-- 2配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默认必须使用数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!--定义id为accountDao的Bean-->
<bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl">
<!-- 将jdbcTemplate注入到accountDao实例中 -->
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
第七章——Spring 框架的 AOP
面向切面编程AOP:采用横向抽取机制,通常用于声明式事务、日志记录等操作
通过类似拦截的方式在执行一个方法时,可以在方法执行之前或之后添加额外的功能。
a、将分散在各个方法中的重复代码抽取出来
b、然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方
- 术语
项 | 描述 |
---|---|
Aspect切面 | 插入的(如事务、日志等)类 |
Join point连接点 | 程序执行过程中的每个阶段点 它实际上是对象的一个操作,如方法的调用或异常的抛出 |
Pointcut切入点
| 通常在程序中,切入点指的是类或者方法名称 |
Advice通知/增强处理 | 可以理解为切面类中的方法,它是切面的的具体实现 |
Target object目标对象 | 是指所有被通知的对象 |
Proxy代理 | 将通知的应用到目标对象之后,被动态创建的对象 |
Weaving织入 | 将切面代码插入到目标对象身上,从而生成代理对象的过程 |
- 通知类型
具体顺序与配置文件的申明顺序有关
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在方法调用之前和之后,执行通知。 |
椭圆中不区分顺序
a、环绕通知-前置通知-doSomething-环绕通知-后置通知-最终通知
b、前置通知-环绕通知-doSomething-环绕通知-后置通知-最终通知
c、前置通知-环绕通知-doSomething-环绕通知-最终通知-后置通知
- 主流AOP框架
a、SpringAOP使用纯 Java实现,通过代理方式向目标类织入增强的代码
b、AspectJ是基于Java语言的Aop框架,并扩展了Java语言,提供了转梦的编译器
1、基于XML的声明式AspectJ
pointcut、advisor、aspect必须按照顺序执行
通过xml文件来定义切面、切入点、及通知,所有的切面、切入点和通知都必须在<aop:config>元素内。
xml
<aop:config> <!-- 切面 --> <aop:aspect id="log" ref="logging"> <!-- 切入点表达式--> <aop:pointcut id="selectAll" expression="execution(* com.tutorialspoint.*.*(..))"/> <!-- 前置通知 --> <aop:before pointcut-ref="selectAll" method="beforeAdvice"/> <!-- 最终通知 --> <aop:after pointcut-ref="selectAll" method="afterAdvice"/> <!-- 后置通知 --> <aop:after-returning pointcut-ref="selectAll" returning="retVal" method="afterReturningAdvice"/> <!-- 异常通知 --> <aop:after-throwing pointcut-ref="selectAll" throwing="ex" method="AfterThrowingAdvice"/> </aop:aspect> </aop:config>
配置切面
a、配置切面使用<aop:aspect>元素,将一个已定义好的Spring Bean转换成切面Bean
b、在Spring的配置文件中,切入点是通过<aop:pointcut>元素来定义的。
属性 | 描述 |
---|---|
id: | 用于定义该切面的唯一标识名 |
ref: | 用于引用普通的Spring Bean |
配置切入点
execution(<访问修饰符><返回类型><路径><方法名>(<参数>)<异常>),<访问修饰符><路径><异常>可省略
返回类型后面有空格
a、execution(* cn.itcast.aop.Book.add(..))
b、execution(* cn.itcast.aop.Book.*(..))
c、execution(* *.*(..))
d、 匹配所有save开头的方法 execution(* save*(..))
- 全局切入点
当<aop:pointcut>为<aop:config>元素的子元素时,表示该切入点可被多个切面所共享;
- 一般切入点
当<aop:pointcut>为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效
配置通知
属性名称 | 描述 |
pointcut | 该属性指定一个切入点表达式,Spring将在匹配该表达式的连接点织入通知 |
point-ref | 该属性指定一个存在的切入点名称。通常pointcut和point-ref两个属性只使用其中一个。 |
method | 概述性指定一个方法名,指定将切面Bean中的该方法转换为增强处理 |
throwing | 该属性只对<after-throwing>元素有效,它用于指定一个形参名,异常通知可以通知该形参名访问目标所抛出的异常。 |
returning | 该属性只对<after-returning>属性有效,它用于指定一个形参名,后置通知可以通过该形参名访问目标的返回值。 |
举个栗子
UserDaoImp
public class UserDaoImp implements UserDao{ @Override public void doSomething() { System.out.println("doSomething"); } }
MyAspect
public class MyAspect { public void myBefore(JoinPoint joinpoint) { System.out.println("前置通知"); } public void myAfterReturning(JoinPoint joinpoint, Object returnVal) { System.out.println("后置通知"); } public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable { System.out.println("环绕通知:前"); Object object = proceedingJoinPoint.proceed(); System.out.println("环绕通知:后"); return object; } public void myAfterThrowing(JoinPoint joinpoint, Throwable e) { System.out.println("异常:" + e.getMessage()); } public void myAfter() { System.out.println("最终通知"); } }
Test
public class Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationAspectJ.xml"); UserDao userDao = (UserDao) applicationContext.getBean("userDao"); userDao.doSomething(); } }
applicationAspectJ.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="userDao" class="springAspectJ.UserDaoImp"></bean> <bean id="myAspect" class="springAspectJ.MyAspect"></bean> <aop:config> <aop:aspect ref="myAspect"> <aop:pointcut expression="execution(* springAspectJ.*.*(..))" id="myPointCut" /> <aop:before method="myBefore" pointcut-ref="myPointCut" /> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/> <aop:after method="myAfter" pointcut-ref="myPointCut"/> <aop:around method="myAround" pointcut-ref="myPointCut" /> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> </aop:aspect> </aop:config> </beans>
2、基于注释的声明式AspectJ
举个栗子
@Aspect @Component public class MyAspect { // 定义切入点表达式 @Pointcut("execution(* com.itheima.jdk.*.*(..))") // 使用一个返回值为void、方法体为空的方法来命名切入点 private void myPointCut(){} // 前置通知 @Before("myPointCut()") public void myBefore(JoinPoint joinPoint) { System.out.print("前置通知 :模拟执行权限检查...,"); System.out.print("目标类是:"+joinPoint.getTarget() ); System.out.println(",被织入增强处理的目标方法为:" +joinPoint.getSignature().getName()); } // 后置通知 @AfterReturning(value="myPointCut()") public void myAfterReturning(JoinPoint joinPoint) { System.out.print("后置通知:模拟记录日志...," ); System.out.println("被织入增强处理的目标方法为:" + joinPoint.getSignature().getName()); } // 环绕通知 @Around("myPointCut()") public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 开始 System.out.println("环绕开始:执行目标方法之前,模拟开启事务..."); // 执行当前目标方法 Object obj = proceedingJoinPoint.proceed(); // 结束 System.out.println("环绕结束:执行目标方法之后,模拟关闭事务..."); return obj; } // 异常通知 @AfterThrowing(value="myPointCut()",throwing="e") public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("异常通知:" + "出错了" + e.getMessage()); } // 最终通知 @After("myPointCut()") public void myAfter() { System.out.println("最终通知:模拟方法结束后的释放资源..."); } }
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 指定需要扫描的包,使注解生效 --> <context:component-scan base-package="com.itheima" /> <!-- 与自动扫描bean不同,需加入下面语句,启动基于注解的声明式AspectJ支持 --> <aop:aspectj-autoproxy /> </beans>
举个栗子
//把LoggingAspect类声明为切面 /* 1、把该类放入IOC容器 2、用@Aspect声明切面 */ @Order(2) @Aspect @Component public class LoggingAspect { /* 切入点表达式写法:"execution(控制权限 包名.类名.方法名(参数类型))" 例如:@Before(public com.demo.sshtest.aop2.annotationNotification.AOPoperations.addC(String,String)) 其中,控制权限、类名、方法名、参数类型可以用"*"代替,如:(* com.demo.sshtest.aop2.annotationNotification.*.*(*,*)) 另外,参数类型还可以用".."代表任意类型和任意数量的参数参数类型 */ //声明前置通知:在目标方法开始之前执行 @Before("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()") public void beforeMethod(JoinPoint joinPoint){ //通过在@Before标注的方法的参数中添加JoinPoint来获取当前调用的方法名及其传入的值 //获取方法名 :joinPoint.getSignature().getName() //获取传入方法的值 :joinPoint.getArgs() String methodname = joinPoint.getSignature().getName(); List<Object> values = Arrays.asList(joinPoint.getArgs()); System.out.println("Begin run method "+methodname+" and the value is:"+values); } //声明后置通知:在目标方法执行之后执行,无论是否发生异常 //后置通知不能获取方法返回的值 @After("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()") public void afterMethod(JoinPoint joinPoint){ //通过在@After标注的方法的参数中添加JoinPoint来获取当前调用的方法名及其传入的值 //获取方法名 :joinPoint.getSignature().getName() //获取传入方法的值 :joinPoint.getArgs() String methodname = joinPoint.getSignature().getName(); List<Object> values = Arrays.asList(joinPoint.getArgs()); System.out.println("End run method "+methodname+" and the value is:"+values); } //声明返回通知:在目标方法返回结果之后执行 @AfterReturning(value="com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()",returning="result") public void afterReturnMethod(JoinPoint joinPoint,Object result){ //通过在@AfterReturning标注的方法的参数中添加JoinPoint来获取当前调用的方法名及其返回的值 //获取方法名 :joinPoint.getSignature().getName() //获取传入方法的值 :joinPoint.getArgs() String methodname = joinPoint.getSignature().getName(); System.out.println("run method "+methodname+" and Return:"+result); } //声明异常返通知:在目标方法抛出异常之后执行 //可以访问异常对象,且指定出现指定异常的执行通知代码,即afterThrowing()中的Exception //配置中@AfterThrowing的throwing值要和afterThrowing中Exception的参数名一致 //如果想要只在@AfterThrowing中输出定制的异常信息,则需要在对应方法里面直接使用throw来抛出异常,而不是用try catch,因为catch会完成完整的异常捕获,异常捕获完成后@AfterThrowing不会运行 @AfterThrowing(value="com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()",throwing="exception") public void afterThrowingMethod(JoinPoint joinPoint,Exception exception){ String methodname = joinPoint.getSignature().getName(); System.out.println(methodname+" throw an exception:"+exception.getMessage()); } //声明环绕通知:在前置通知执行前后运行,后置通知前执行,在异常通知执行后,则不执行 //环绕通知需要携带ProceedingJoinPoint类型的参数 //环绕通知类似于动态代理全过程:ProceedingJoinPoint可以决定是否执行目标方法 //环绕通知必须有返回值,返回值为目标方法返回值 @Around("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()") public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){ Object result = null; try{ String methodname = proceedingJoinPoint.getSignature().getName(); System.out.println(methodname+" aroundMethod...before note...."); result = proceedingJoinPoint.proceed(); System.out.println(methodname+" aroundMethod...after note...."+result); }catch(Throwable e){ throw new RuntimeException(); } return result; } }
//关于切点优先级,可以使用@Order()来设定,括号中的数值越小,优先级越高 @Order(1) @Aspect @Component public class LoggingAspect2 { //声明前置通知:在目标方法开始之前执行 @Before("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()") public void beforeMethod2(JoinPoint joinPoint){ String methodname = joinPoint.getSignature().getName(); List<Object> values = Arrays.asList(joinPoint.getArgs()); System.out.println("LoggingAspect2-->Begin run method "+methodname+" and the value is:"+values); } //声明后置通知:在目标方法执行之后执行,无论是否发生异常 @After("com.demo.sshtest.aop2.annotationNotification.PointCut.pointcutexpression()") public void afterMethod2(JoinPoint joinPoint){ String methodname = joinPoint.getSignature().getName(); List<Object> values = Arrays.asList(joinPoint.getArgs()); System.out.println("LoggingAspect2-->End run method "+methodname+" and the value is:"+values); } }
public class PointCut { //关于可重用的切点表达式 //1、先创建一个void类型的空方法,方法名任意,不需要写任何参数和代码 //2、为步骤一中的方法添加@Pointcut()注解 //3、把需要重用的切点表达式写到@Pointcut()中 //4、调用时,在需要调用的注解标签内写上该方法的包名+类名+方法名,如果跟该方法在同一个包类,则可以省略包名,如果在同一个类里面,则可以省略包名和类名 //调用方法例子:@Before("com.demo.sshtest.aop2.frontnotification.PointCut.pointcutexpression()") @Pointcut("execution(* com.demo.sshtest.aop2.annotationNotification.AOPoperations.*(..))") public void pointcutexpression(){} }
第八章——Spring 事务管理
1、什么是事务
事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性。
- 事务提交(commit)
数据库向用户提供保存当前程序状态的方法
- 事务回滚(rollback)
当事务执行过程中,使数据库忽略当前的状态并回到前面保存的状态的方法
声明式事务
a、Spring声明式事务让我们从复杂的事务处理中得到解脱。无需去处理获得连接、关闭连接、事务提交和回滚等这些操作。
b、无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。
四大特性
事务具备ACID四种特性:ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)
- 原子性(Atomicity)
a、事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。
b、 事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(Consistency)
a、事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。
b、如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。
c、如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
- 隔离性(Isolation)
a、指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。
b、由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。
c、事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态
- 持久性(Durability)
a、指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。
b、即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
1、事务管理接口
三个核心接口PlatformTransactionManager、TransactionDefinition、TransactionStatus
- PlatformTransactionManager
事务管理接口,主要用于管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器
序号 | 方法 & 描述 |
---|---|
1 | TransactionStatus getTransaction(TransactionDefinition definition) 用于获取事务状态信息 |
2 | void commit(TransactionStatus status) 用于提交事务 |
3 | void rollback(TransactionStatus status) 用于回滚事务 |
- TransactionDefinition
核心接口,定义了事务规则,并提供了获取事务相关信息的方法
序号 | 方法 & 描述 |
---|---|
1 | int getPropagationBehavior() 获取事务的传播行为 |
2 | int getIsolationLevel() 获取事务的隔离级别 |
3 | String getName() 获取事务的对象名称 |
4 | int getTimeout() 获取事务的超时时间 |
5 | boolean isReadOnly() 获取该事务是否只读。 |
传播行为
spring的事务传播行为是:REQUIRED
隔离级别
mysql的默认的事务处理级别是:REPEATABLE-READ, 也就是可重复读
Oracle的默认的事务处理级别是:READ COMMITTED, 也就是读已提交
序号 | 传播 & 描述 |
---|---|
1 | READ-UNCOMMITTED: 读未提交 --> 容易导致脏读,不被确认的结果也可能被读取; 即没有提交的情况下,别人也可以看到未提交的更新的记录 |
2 | READ-COMMITTED: 读提交--> 读别人确认的数据,可能导致不可重复读的问题,可能多次读的结果不一样;
|
3 | REPEATABLE-READ: 可重复读--> 导致幻读的问题,如别的事务已经修改过数据,但是看到的还是旧数据; |
4 | SERIALIZABLE: 串行化,隔离度最高;只有对方的事务结束(要么commit,要么rollback),另一窗口才能执行对同一表格的操作 |
- TransactionStatus
描述了某一时间点上事务的状态信息
序号 | 方法 & 描述 |
---|---|
1 | boolean hasSavepoint() 获取是否存在保存点 |
2 | boolean isCompleted() 获取事务是否完成 |
3 | boolean isNewTransaction() 获取是否是新事物 |
4 | boolean isRollbackOnly() 获取是否回滚 |
5 | void setRollbackOnly() 设置事务回滚 |
二、基于Aspectj AOP声明式事务管理
- 基于XML方式的声明式事务
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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 事务管理器,依赖于数据源 -->
<bean id="transactionManager" class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- name:*表示任意方法名称 -->
<tx:method name="*" propagation="REQUIRED"
isolation="DEFAULT" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- 编写aop,让spring自动对目标生成代理,需要使用AspectJ的表达式 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* com.itheima.jdbc.*.*(..))"
id="txPointCut" />
<!-- 切面:将切入点与通知整合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
</beans>
- 基于Annotation方式的声明式事务
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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 事务管理器,依赖于数据源 -->
<bean id="transactionManager" class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 注册事务管理器驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
java
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
属性名 | 说明 |
---|---|
name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT。 |
read-only | 指定事务是否为只读事务,默认值为 false; 为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
第九章——Spring 中的事件处理
由于 Spring 的事件处理是单线程的,所以如果一个事件被发布,除非所有的接收者得到的该消息,若该进程被阻塞流程将不会继续。
a、Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。
b、当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布
c、通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。
d、如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
序号 | Spring 内置事件 & 描述 |
---|---|
1 | ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。 |
2 | ContextStartedEvent 当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。 你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。 |
3 | ContextStoppedEvent 当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。 你可以在接受到这个事件后做必要的清理的工作。 |
4 | ContextClosedEvent 当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。 一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。 |
5 | RequestHandledEvent 这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。 |
HelloWorld
public class HelloWorld { private String message; public void setMessage(String message){ this.message = message; } public void getMessage(){ System.out.println("Your Message : " + message); } }
CStartEventHandler
public class CStartEventHandler implements ApplicationListener<ContextStartedEvent>{ public void onApplicationEvent(ContextStartedEvent event) { System.out.println("ContextStartedEvent Received"); } }
CStopEventHandler
public class CStopEventHandler implements ApplicationListener<ContextStoppedEvent>{ public void onApplicationEvent(ContextStoppedEvent event) { System.out.println("ContextStoppedEvent Received"); } }
MainApp
public class MainApp { public static void main(String[] args) { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); // Let us raise a start event. context.start(); HelloWorld obj = (HelloWorld) context.getBean("helloWorld"); obj.getMessage(); // Let us raise a stop event. context.stop(); } }
输出:
ContextStartedEvent Received Your Message : Hello World! ContextStoppedEvent Received