概述:
Spring针对于业务层(逻辑分析,控制),以IOC和AOC为核心,提供给业务层和持久层的业务关系,目的减少层次的依赖性。
目的是(重要):解决企业开发应用的复杂性,使现有的技术更加容易。它是一个融合剂,乱七八糟的东西都可以交给它。
当前趋势
下载:如何在官网下载以前版本的文件。
在路径上修改你想下载的版本号
点击链接
复制打开这个地址
所有的版本
GitHub上下载,就是被github开源托管了。
如图带这个小猫的都是被GitHub开源的
点击小猫
如图点击下载的版本
引入spring的时候,导入spring web mvc东西最全 ,maven带这个功能就可以把需要的东西一起带进来。
当整合mybatis时候就可以使用这个依赖
IOC
Interval Of Control即控制反转,它不是技术而是一种设计思想。传统的创建对象是通过new关键字,而Spring通过IOC容器来帮助我们创建对象,也就是说我们将把创建对象的控制权交给IOC容器,从而消减程序中的耦合,也就是降低代码中的依赖关系。
AOP
Aspect Oriented Programming 即面向切面编程,它是面向对象编程的一种延伸。
优势
1、方便解耦,简易开发
2、支持AOP开发
3、事务的支持
4、方便程序的测试
以上的话比较官方,自己的理解:
IOC设计思想,通过bean配置文件中的class属性指明需要创建对象的实现类后,IOC容器会自动给我们创建出对象并由它接手。有了对象而不是我们new出来的,所以说叫控制反转。
那么如何操作IOC创建对象:
IOC容器 = bean.xml(配置文件) + ApplicationContext容器类
IOC 控制反转(重要)
配置bean标签,相当于建一个对象,spring是怎么创建出来的?
官网约束。
所以容器就指的是bean.xml配置文件中的标签,里面配置的一个个子标签就是一个个对象。注入数据的标签,value属性是相当于set值。 ref属性是引用中的对象。
演示一下:
创建包
pom.xml添加jar包
如图建立包结构
添加bean文件约束
配置bean文件的实现类
spring需要跟MyBatis建立联系
service层保存数据的方法
dao层接口保存数据
service实现类
配置bean.xml文件
获取IOC容器,并在容器中得到对象
核心容器就是为了存储我们需要的对象,根据id的标识,通过getBean方法获取对应的对象。
BeanFactory spring的顶级接口
可以查看该接口及子接口的关系
两个接口的区别
spring IO容器中默认一个类只产生一个对象,也就是默认是单例模式。
ApplicationContext
1、适用于单例模式 ,也就是说只有一个对象时用这个接口
2、创建核心容器时,创建对象的方式采用立即加载。立即加载就是读取配置 文件就立即创建出该配置文件的对象。
BeanFactory
1、使用于多例对象
2、采用延时加载的方式,什么时候需要对象再创建出来。
ApplicationContext 子接口
ApplicationContex接口是BeanFactory常用的子接口,有以下三个常用是实现类:
ClassPathXmlApplicationContext
基于xml文件方式 ,类路径的全限定类名,对应<bean>
中的class属性(最常用)
FileSystemXmlApplicationContext
基于xml文件,针对磁盘中任意路径,可以加载本地磁盘中的文件路径(需要访问权限,不常用)
AnnotationConfigApplicatonContext
基于注解,读取用注解创建容器的信息
bean.xml配置文件(重要)
<bean>
标签是核心
作用:为IOC容器配置对象信息。
属性:
id : 唯一标识,通过标识找到容器中的对象。
class : 指明需要创建对象的类(全限定类名),应用到反射技术。
默认调用的是无参的构造方法。
scope : 指定对象的作用域
singleton 默认单例对象 prototype 多例对象
request域 session域 globalsession 全局
init-method : 指定类中初始化方法的名字
destory-method :指定类中销毁方法的名字
spring的IOC对bean对象管理的细节:
创建对象的方式(重要)
1、使用无参构造方法(默认 编译器自动调用)
以上spring演示的方式就是这种。
弊端:如果类存在于jar包,其中无源码只有class文件的话,无法操作构造方法
<!--默认的无参构造方法-->
<!--<bean id="accountService" class="com.neuedu.service.impl.AccountServiceImpl">
</bean>-->
创建一个类的对象,并给类中的属性注入值
有参构造方法可以直接通过参数名(记住这个就可以了)
起别名(配置关于bean对象的信息)
applicationContext.xml
这个是一个总的容器。
比如
当创建出两个bean.xml的文件,可以把两个文件配置的对象合并到一起
合并
总结
2、用类中的方法创建对象,存储到spring容器中(常用)
<!--非静态方法获取对象-->
<!--<bean id="instanceFactory" class="com.neuedu.factory.InstanceFactory">
</bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService">
</bean>-->
3、使用工厂中的静态方法创建对象,存储到spring容器中
<!--静态方法获取对象-->
<bean id="accountService" class="com.neuedu.factory.StaticFactory" factory-method="getAccountService">
</bean>
作用域分为单例和多例。
生命周期(重要)
bean对象的生命周期
单例:
1、创建期:被容器创建时对象产生
2、存在期:只要容器存在对象就存在
3、销毁期:容器销毁即销毁
多例:
1、创建期:当需要使用对象时,Spring框架为我们创建
2、存在期:只要对象被使用的过程中,对象就一直存在
3、销毁期:当对象长时间不用的时候,且没有对象的引用,由java垃圾回收器进行回 收
Spring 依赖注入
Spring给我们提供了管理机制,也就所有的依赖关系由Spring DI管理。
DI就是依赖注入:(独自见解)
我们普通的创建对象可以通过有参的构造方法直接附值给类中属性、或者再通过对象
调用set方法。而spring DI中注入方式也给我们提供了对应构造方法和set方法实现注
入,提供了constructor-arg标签向对象中注入数据的方式.而在创建对象的过程中
Spring可以依据配置对象的属性进行设置,这个过程称之为依赖注入,也即DI。
管理依赖注入
依赖注入的意思:
给类中这些熟悉进行注入,设置get/set方法
可以实现依赖注入的数据分为三类:
1、基本数据类型和String
2、Bean类型(bean.xml中配置的对象,自己封装的对象),或者注解配置过的Bean对象。
3、复杂数据类型,集合类型。
依赖注入三种方式
1、构造方法提供注入:
创建对象的同时把数据注入对象,相当于初始化属性。
利用<constructor-arg>标签,位置在<bean>标签内部
type
:该属性用于指定注入数据的数据类型,构造方法中的参数的类型。
index
:该属性给构造方法中指定索引位置参数赋值,从0位置开始。
标签内常用的属性
name
:给构造方法中指定名称的参数赋值,最常用。
value
:用于提供基本类型和String类型的数据
ref
: 指定bean类型等其他的数据,比如自己封装的,集合,日期。
优劣势
优势:在获取bean对象时,在创建对象的同时可以做到立刻有对应的属性数据
劣势:如果改变了bean对象的实例化方式,那么在创建对象时只有一种有参数的构造方
法,那么也必须提供该参数去创建对象(方法重载的话太麻烦不使用)。
注意
实际开发中,我们要求如果无法避免必须使用构造的注入,那么不要轻易使用构造注入
给对象注入基本数据类型和String类型。
演示
新建工程
加入jar包,百度搜索maven复制粘贴
在实现类中写构造方法
依赖注入给属性赋值
拿到对象调用方法
其他数据类型
一个bean标签代表创建了一个对象,只不过一个bean标签中的对象在另个bean中当做属性存在 如下图
注入
<!--配置service-->
<bean id="accountService" class="com.neuedu.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="哈哈"></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>
测试
2、通过set方法实现注入(重要)
使用最多的依赖注入方式,常用的方式。
前提:必须有set方法,在实现类中。
property
标签,书写位置在bean
标签中。
<bean id="accountService2" class="com.neuedu.service.impl.AccountServiceImpl2">
<property name="name" value="呵呵"></property>
<property name="age" value="20"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
set注入方式对比构造方法注入方式的比较?
set注入优势和劣势
优势:创建对象时候,没有参数限制,首先利用无参的构造方法创建对象,然后注入对
应属性信息,符合JavaBean的格式,所以我们更常用的是set方式注入。
劣势:某一个成员必须有值,如果我们没有给对应的值,有可能造成后续的操作问题。
3、通过注解实现注入
专门讲注解的时候统一说明
复杂类型注入
(set方式、构造方式都一样)
1、List结构集合注入数据的标签
List结构集合:数据存储为一列结构的,就是数组注入也可以使用list的标签
<list> <array> <set>
数组:
<bean id="accountService3" class="com.neuedu.service.impl.AccountServiceImpl3">
<property name="myStrs">
<list>
<value>QQQ1</value>
<value>QQQ2</value>
<value>QQQ3</value>
</list>
</property>
<bean>
List集合:
<property name="myList">
<list>
<value>QQQ1</value>
<value>QQQ2</value>
<value>QQQ3</value>
</list>
</property>
set集合:
<property name="mySet">
<set>
<value>QQQ1</value>
<value>QQQ2</value>
<value>QQQ3</value>
</set>
</property>
2、Map结构集合注入数据的标签(双列结构的)
<map>
<props>
<property name="myMap">
<map>
<entry key="111" value="AAA"></entry>
<entry key="222" value="BBB"></entry>
</map>
</property>
<property name="myPros">
<props>
<prop key="111">CCC</prop>
<prop key="222">DDD</prop>
</props>
</property>
Spring注解
概念:
注解替代了xml中标签的使用
以上xml文件中的bean标签配置的格式是这样的:
<bean id="accountService" class="com.neuedu.service.impl.AccountServiceImpl"
scope="" init-method="" destroy-method="">
<!--set方式的依赖注入-->
<property name="" value=""></property>
</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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.neuedu"></context:component-scan>
演示一下注解的操作方式
新建工程
配置pom文件
把上一个spring工程的src文件替换过来,否则还得建包结构
如红线所示添加注解的约束
开启注解
注意 小知识点 如图所示
用注解配置
创建对象
@Component创建对象的注解
作用与xml文件中配置bean标签实现是一致的
属性: value
用于指定bean的id,如果不写,value默认的值是当前的类名(首字母必须是小写)
实际中有衍生版的注解,通过Component实现的,在不同层次都是创建对象的
@Controller 表现层
@Service 业务逻辑层
@Repositroy 持久层
上述三个注解作用和属性和@Component一模一样的
单例注解
@Configuration
这种方式不需要spring的xml配置对象,而是java的注解。也就是纯Java配置bean。springboot源码就是用了这个注解实现的
类
配置文件
整合多个配置文件的注解
相当于之前的import标签
总结
@Autowired
用于注入数据的注解,等价于<property>
标签。
作用:自动按照类型注入。
要求:容器中有唯一的一个bean对象类型和要注入的变量类型匹配
弊端:如果ioc容器没有bean的类型和要注入的类型匹配(匹配不上),报错。
所以利用注解实现依赖注入的操作习惯:
先利用类型查找,如果是同类型,再用变量名查找。
注意:使用该注解,set方法不是必须存在的
这些注解可以存在位置:
可以放在变量上
可以放在方法上
service层注解
dao层注解
@Qualifier
作用:在按照类型中注入的基础上,再次按照名称注入,就是先根据类型找,类型多
个的时候再根据变量名找,然后注入数据。
注意:它在给类的成员注入时不能单独使用,给方法参数注入时可以单独使用
属性:value 用于指定注入的bean的id
@Resource
作用:直接按照bean的id查找然后注入,它是可以独立使用。
属性:name 用于指定bean的id。
注解注入一个对象操作流程:(图解)
上面三个注解只能注入其他bean类型(我们自己写的或者官方认可的)的数据
注意:
1、基本数据类型(Integer、char…)和String类型无法使用上面的注解实现的
(但有单独的注解配合使用)
2、集合类型注入只能通过xml方式实现
@Value
作用:注入基本数据类型和String类型的
属性:value 用于指定数据的值,它可以使用Spring中EL表达式(缩写SpEL)
格式:${表达式}
@Scope注解
用于改变作用范围(作用域)
实现与bean标签中的scope属性是一样
属性:value 指定取值范围 singleton prototype(单例和多例)
和生命周期相关(了解)
@PreDestroy
destroy-method=""
用于销毁方法
@PostConstruct
init-method=""
用于初始化方法
注意:正常默认是单例模式
如果是多例对象,不负责销毁
学习方式 note.md文件
idea是可以建立.md文件,写笔记,不如经常用的约束,依赖,注解。
Spring AOP
代理模式(底层代码原理):
23中设计模式的一种;包括单例模式
目的:真实对象之间纯粹的做一件事情,其他乱七八糟的事情交给代理对象。
以下是AOP的底层原理
静态代理
你,租房的人 就是客户(由他去访问代理角色)
代码
代理模式的模拟真实案例
一个接口或抽象方法即抽象对象
实现类即真实对象
真实对象
真实对象
这是实际的实现
需求:实现的每一步操作都需要打印日志。
前提:所有的程序都不希望在源码上更改,那么在不改变源码情况下给方法
加四个日志。
实现:通过代理对象实现。
缺点:代码量翻倍。
创建一个代理对象
思路图解
原有的业务流程不改动,而是使用代理横切进去,实现增加的需求。
动态代理
动态代理的由来,我们即要使用代理对象,还要不使代码量增加,那么动态代理来了。
个人理解:动态代理通过反射动态的加载一些类
代码实现:
抽象对象
真实对象
代理对象请求处理类
目的: 得到代理类,执行真正执行的方法(租房子)
模拟客户
getProxy()方法就是通过反射动态生成的。
以上就搭建好了动态代理
现在我们想程序中纵向切入两个方法,在方法执行前和之后。
重要(理解这个就可以了)
以上推导过程理解即可
生成不同代理对象需要去修改代理类。抽取请求处理类使之更灵活,直接使用即可。
请求处理类:代理谁 + 生成代理类 + 执行方法
最终实现的需求是执行操作前添加日志
通过反射实现
添加日志的方法,改造
总结,请求处理类相当于一个工具类,需求不同的代理对象,直接调即可。都是利用反射实现的,我们不需要写,只是理解。
利用spring框架来实现AOP
方式1 利用spring API 主要接口实现
创建类 抽象对象和真实对象
打印日志的方法,
建立联系,把打印日志的方法(通知)在作用在insert等方法(切点)上,这个过程叫做织入。
表达式:访问修饰符 包名.包名.包名…类名.方法名(参数列表)
* com.neuedu.service.impl.*.*(..)
调用,注意动态代理代理的是一个接口即抽象对象。
简单 对象之间互不干扰。
通过以上例子图解
总结:静态代理代理的是实现类(真实对象),动态代理代理的是接口(抽象对象)。抽象对象spring给我们生成,我们只需要在容器中拿出来使用。
方式2 自定义实现AOP 主要面向切面定义(建议)
织入自定义的方法
利用了切面的概念,其实就是一个类,我们自定义的一个类,类中是通知的方法。
方式3 利用注解方式实现AOP
注意:需要开启注解支持
拓展
了解增强方法的执行顺序
以下为自己的理解整理
面向切面编程,是面向对象的延续。通过预编译方式和运行期间动态代理实现程序功能,
从而进行统一维护的一种技术.
通俗的说,将程序重复代码抽取出来,需要执行的时候使用动态代理的方式。
实现不修改源码的基础上,对已有的方法实现增强。
个人理解:
代码需要完善,但是不修改代码,所以使用AOP面向切面编程。面向切面就是切一个
面,在面上找到点就可以把需要的功能动态的放到方法里面,从而实现功能,也就是说
增强了原来的方法。
1)连接点(JoinPoint):指的是那些被拦截的点,在spring中这些点就是方法。
2)切入点(Pointcut):对哪些连接点进行拦截,增强某个方法需要用切入点实现。
3)通知(增强 Advice): 指的是拦截到连接点(JoinPoint)后要做的事情,也就是对方法
的操作。
4)(了解)引介(Introduction) 一种特殊的通知在不修改类代码的前提下,可以在运
行期间为类动态的添加一些方法或者属性。
5)织入 Weaving 指增强应用到目标对象来创建新的代理对象的过程 spring采用动态织
入。
6)代理 Proxy 一个类被AOP织入增强后,就会产生一个结果代理类。
7)切面 Aspect 是切入点和通知的结合(结合的是引进)。
通知类型:
前置通知、后置通知、异常通知、最终通知、(这四个为基本通知)。
环绕通知。
了解:
动态代理技术:字节码随时使用随时创建,并且随时加载。
两种方式:
1、基于接口动态代理 利用官方JDK实现的(了解)
2、基于子类的动态代理 利用第三方插件CGlib(了解)
整合mybatis
这种方式回顾mybatis,
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
注意 需要用到这个依赖 mybatis-spring,连接数据源需要spring-jdbc
1 新建实体类
2 mabatis的核心配置文件(起名应该是mybatisConfig.xml,不用理会)
接口
对应接口的xml配置
测试
我们开始利用在spring框架上整合mybatis。
方式一:重要
spring的bean.xml中这三个配置文件是固定的
整合后不需要再配置mybatis的xml配置文件,而是写一个实现类在里面实现。然后在spring的bean.xml中给它配置对象和sqlSessionTemplate模版对象。
bean.xml中配置实现类对象查数据
总结:多了一个实现类,里面去实现mybatis的事情
方式二:
直接继承SqlSessionDaoSupport类,即可得到sqlSessionTemplate对象,省去注入sqlSessionTemplate的过程
事务
现在有一个需求,增加一条数据然后再删除这条数据,这个需求在一个方法中,必须都执行成功。
目的:通过AOP配置事务,使我们在执行增删改查的时候自动将事务织入到程序中。
在上一个bean.xml增加
以下是根据王xx总结
Spring开发流程
两个过程:
开发过程(自己写代码)
1、编写核心业务代码(程序的主线),程序员编写(要求我们需要了解业务逻辑需
求)。
2、将公用的代码抽取出来,写成通知,开发最后阶段实现 程序人员来做。
3)配置文件:负责切入点和通知的关系说明,我就将它叫做切面。
运行过程(Spring框架为我们完成)
Spring框架运行原理:Spring框架监控切入点方法的执行,一旦监控到切入点被运行,
使用代理机制,动态地创建对象,同时根据通知类型在代理对象的对应位置,将通知对
应的功能织入,从而完成对代码逻辑的增强。
实际结构搭建(重要)
1、基于XML方式的AOP的搭建
1)在核心配置文件中,添加解析切入点表达式jar
<!--解析切入点表达式-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
2)开始配置AOP的bean.xml
1、先将通知Bean交给Spring IOC来管理
<bean id="accountService" class="com.neuedu.service.impl.AccountServiceImpl">
</bean>
2、使用aop:config标签表明开始AOP的配置
<!--配置AOP-->
<aop:config>
</aop:config>
3、使用aop:aspect标签声明配置的切面
id属性:是提供给切面的一个唯一标识
ref属性:指定和通知类对应的bean标签中id属性建立连接
<!--配置AOP-->
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
</aop:aspect>
</aop:config>
4、在aop:aspect标签内部使用对应的标签来配置通知的类型
让printLog方法在切入点方法执行之前执行,我们实现的是一个前置通知
aop:before:配置前置通知
method:用于指定Logger类(切面)中哪个方法是前置通知
pointcut:指定切点表达式,指的是对应业务层中哪些方法实现增强
pointcut属性放的是切点表达式
切点表达式格式
关键字 execution(表达式)
表达式:访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
切点表达式的演化过程:
public void com.neuedu.service.impl.AccountServiceImpl.saveAccount()
太长了,太麻烦
实际中一般我们不写成全路径的信息的切点表达式
* *..*.*(..) 太简单 没有一定标识含义
* void com.neuedu.service.impl.AccountServiceImpl.saveAccount()
* * *.*.*.*.AccountServiceImpl.saveAccount()
* *..AccountServiceImpl.saveAccount()
* *..*.*(..)
* com.neuedu.service.impl.*.*(..)
开始动手操作
补充
执行结果
四种通知配置
<!--配置AOP-->
<aop:config>
<aop:pointcut id="ptl" expression="execution(* com.neuedu.service.impl.*.*(..))"/>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置前置通知-->
<aop:before method="beforePrintLog" pointcut-ref="ptl"></aop:before>
<!--配置后置通知-->
<aop:after-returning method="afterPrintLog" pointcut-ref="ptl"></aop:after-returning>
<!--配置异常通知
注意:异常通知和后置通知只能有一个
-->
<!--<aop:after-throwing method="exceptionPrintLog" pointcut="execution(* com.neuedu.service.impl.*.*(..))"></aop:after-throwing>-->
<!--配置最终通知,无论切入点方法是否正确都正常执行-->
<aop:after method="finallyPrintLog" pointcut-ref="ptl"></aop:after>
</aop:aspect>
</aop:config>
代码演示:
创建工程demo05
补充
可以简化每个通知的切点表达式
<aop:pointcut id="pt1"
expression="execution(*com.neuedu.service.impl.*.*(..))"></aop:pointcut>
配置切入点表达式 aop:pointcut
id属性:用于指定表达式的唯一标识。
expression属性:用于指定表达式内容
注意:此标签写在aop:aspect标签内部只能当前切面使用。还可以写在
aop:aspect外面,此时就变成了所有切面可用
环绕通知配置(特殊)
概述:
Spring 通知 环绕通知
与之前的四个通知互斥的 (前置、后置、异常、最终)
通知都是为了增强切入点的方法——事务处理使用
<!--配置环绕通知-->
<aop:around method="aroundPrintLog" pointcut-ref="ptl">
</aop:around>
代码运行出现问题:
当使用环绕通知后,切入点方法没有执行,只是执行了增加的功能(通知的方法)。
为什么?
动态代理的环绕通知有明确的切入点方法调用,而我们在代码中是没有调用的。
解决:spring为我们提供了一个接口
ProceedingJoinPoint,接口中有一个方法proceed(),当我们调用该方法时,相当于明确
了切入点方法。
/**
* 环绕通知
* */
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try {
Object[] args = pjp.getArgs();//得到方法执行所需要的参数
//模拟前置通知
System.out.println("beforePrintLog,记录了日志...");
//参数是一个Object[] 类型 明确调用业务逻辑方法(切入点方法)
pjp.proceed(args);
//模拟后置通知
System.out.println("afterPrintLog,记录了日志...");
return args;
} catch (Throwable throwable) {//如果使用exception捕获,是无法捕获到异常
//模拟异常通知
System.out.println("exceptionPrintLog,记录了日志...");
throw new RuntimeException();
}finally {
//模拟最终通知
System.out.println("finallyPrintLog,记录了日志...");
}
}
基于XML实现的AOP,并且实现了切入点方法的增强(通知:4个基本通知),而环绕通知一般用于注解形式的AOP
2、基于注解的AOP配置
bean文件
<!--配置Spring针对IOC的配置,扫描包中的注解-->
<context:component-scan base-package="com.neuedu"></context:component-scan>
<!--针对AOP 配置spring开启AOP注解的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
环绕通知通过注解实现的AOP–不要与四种基本通知一起使用
演示过程:利用之前的工程
包结构
通知(加强的文件)
bean.xml
注意:通常情况下,环绕通知都是独立使用的。
如果使用注解,建议使用环绕通知。
不使用XML的配置方式
@Configuration
作用: 用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。获取
容器时需要使用AnnotationApplicationContext(有@Configuration注解的类.class)。
属性: value:用于指定配置类的字节码
@Configuration
public class SpringConfiguration { }
@ComponentScan
作用: 用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中
的: <context:component-scan base-package="com.itheima"/>是一样的。
属性: basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。
/**
*spring的配置类,相当于bean.xml文件
*/
@Configuration
@ComponentScan("com.neuedu")
public class SpringConfiguration { }
新建一个类
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring中的新注解
* Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
*
* ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
* 我们使用此注解就等同于在xml中配置了:
* <context:component-scan base-package="com.neuedu"></context:component-scan>
*/
@Configuration
//简写@ComponentScan("com.neuedu")
@ComponentScan(basePackages = "com.neuedu")
public class SpringConfiguration {
//...
}
@Component("Logger")
@EnableAspectJAutoProxy
@Aspect//表示当前类是一个切面类
public class Logger {...}
测试类:
package com.neuedu.test;
import com.neuedu.service.AccountService;
import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AOPTest {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2.获取对象
AccountService as = (AccountService)ac.getBean("accountService");
//3.执行方法
as.saveAccount();
}
}