- 一、spring框架的概述
- 二、IOC的概念和原理
- 三、 AOP 面向切面编程
一、spring框架的概述
1.Spring的概述
是一个开放源代码的J2EE应用程序框架,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。目的是可以解决企业应用开发的复杂性。
2.Spring的两个核心部分+
(1)IOC:控制反转,把创建对象过程交给 Spring 进行管理
(2)Aop:面向切面,不修改源代码进行功能增强
3.Spring的特点
(1)方便解耦,简化开发 (IOC)
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低 API 开发难度
二、IOC的概念和原理
1.什么是IOC
1.1 IOC:把创建对象的权利交给框架,也就是指将对象的创建、对象的存储、对象的管理(调用)交给了Spring容器。Spring容器是用于管理对象,底层可以理解为是一个Map集合。
1.2 IOC 目的:为了耦合度降低
2.IOC底层原理
2.1 xml 解析、工厂模式、反射
解析Bean的xml配置文件,获得配置类的信息,通过反射实现工厂模式,实现对Bean对象的管理。
2.2 IOC底层图解:
原始方式:如果UserDao修改路径,则需要修改UserService文件,耦合度太高
工厂模式“虽然UserDao修改路径后,UserService文件不许修改,但是要求UserFactroy类修改和不能改变路径。
IOC方式:只需要修改Bean的xml配置文件即可,进一步的降低耦合度。
3.IOC的BeanFactor接口(容器实现方式)
3.1 IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
3.2 Spring 提供 IOC 容器实现两种方式:(两个接口)
1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人
员进行使用
加载配置文件时候就会把在配置文件对象进行创建
3.3 ApplicationContext 接口有实现类
4.IOC操作Bean管理
4.1 什么是 Bean 管理(Bean 管理指的是两个操作 )
4.2 Bean 管理操作有两种方式
(1)基于 xml 配置文件方式实现
(2)基于注解方式实现
5.基于 xml 配置文件方式实现 Bean管理
5.1 基于 xml 方式创建对象

(1)在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
(2)在 bean 标签有很多属性,介绍常用的属性
id 属性:唯一标识
class 属性:类全路径(包类路径)
(3)创建对象时候,默认也是执行无参数构造方法完成对象创建
5.2 基于xml方式注入属性
DI:依赖注入,就是注入属性,有两种方式set注入(P指令注入属于set注入)、构造器注入
(1)使用set方法注入
必须有注入属性的set方法,使用property属性标签注入属性
a.创建类,定义属性和其属性的set方法
b.在 spring 配置文件配置对象创建,配置属性注入

(2)使用构造器注入
必须有参构造器,使用constructor-arg标签注入。
a.创建类,定义属性,创建属性对应有参数构造方法
b.在 spring 配置文件中进行配置
(3)p名称空间注入(实质是使用set方法注入,了解)
必须有注入属性的set方法,使用p:name注入,可以简化基于 xml 配置方式。
a.第一步 添加 p 名称空间在配置文件中
b.进行属性注入,在 bean 标签里面进行操作
5.3 注入的属性的类型
(1)注入属性-字面量(特殊的字面量)
a.null值
b.属性值包含特殊符号

(2)注入属性-外部Bean
a.创建两个类 service 类和 dao 类
b.在 service 调用 dao 里面的方法
c.在 spring 配置文件中进行配置
(3)注入属性-内部 bean
在property的标签内建立一个bean,id和属性名一致
<!--内部 bean--><bean id="emp" class="com.atguigu.spring5.bean.Emp"><!--设置两个普通属性--><property name="ename" value="lucy"></property><property name="gender" value="女"></property><!--设置对象类型属性--><property name="dept"><bean id="dept" class="com.atguigu.spring5.bean.Dept"><property name="dname" value="安保部"></property></bean></property></bean>
(4)注入属性-级联赋值
a.第一种写法 ,使用外部bean
<!--级联赋值--><bean id="emp" class="com.atguigu.spring5.bean.Emp"><!--设置两个普通属性--><property name="ename" value="lucy"></property><property name="gender" value="女"></property><!--级联赋值--><property name="dept" ref="dept"></property></bean><bean id="dept" class="com.atguigu.spring5.bean.Dept"><property name="dname" value="财务部"></property></bean>
b.第二种写法,使用name.name2
需要注入的属性有get方法
使用属性点(.)来赋值
<!--级联赋值--><bean id="emp" class="com.atguigu.spring5.bean.Emp"><!--设置两个普通属性--><property name="ename" value="lucy"></property><property name="gender" value="女"></property><!--级联赋值--><property name="dept" ref="dept"></property><property name="dept.dname" value="技术部"></property></bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"><property name="dname" value="财务部"></property></bean>
5.4 xml注入集合属性
(1)注入数组类型、List集合、Map集合、Set集合属性
a.创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {//1 数组类型属性private String[] courses;//2 list 集合类型属性private List<String> list;//3 map 集合类型属性private Map<String,String> maps;//4 set 集合类型属性private Set<String> sets;public void setSets(Set<String> sets) {this.sets = sets;}public void setCourses(String[] courses) {this.courses = courses;}public void setList(List<String> list) {this.list = list;}public void setMaps(Map<String, String> maps) {this.maps = maps;} }
b.在 spring 配置文件进行配置
<!--1 集合类型属性注入--><bean id="stu" class="com.atguigu.spring5.collectiontype.Stu"><!--数组类型属性注入--><property name="courses"><array><value>java 课程</value><value>数据库课程</value></array></property><!--list 类型属性注入--><property name="list"><list><value>张三</value><value>小三</value></list></property><!--map 类型属性注入--><property name="maps"><map><entry key="JAVA" value="java"></entry><entry key="PHP" value="php"></entry></map></property><!--set 类型属性注入--><property name="sets"><set><value>MySQL</value><value>Redis</value></set></property></bean>
(2)在集合里设置对象属性
<!--创建多个 course 对象--><bean id="course1" class="com.atguigu.spring5.collectiontype.Course"><property name="cname" value="Spring5 框架"></property></bean><bean id="course2" class="com.atguigu.spring5.collectiontype.Course"><property name="cname" value="MyBatis 框架"></property></bean><!--注入 list 集合类型,值是对象--><bean name="student" class="com.atguigu.spring5.collectiontype.Student"><property name="courseList"><list><ref bean="course1"></ref><ref bean="course2"></ref></list></property></bean>
(3)把集合注入部分提取出来
a.在 spring 配置文件中引入名称空间 util
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/utilhttps://www.springframework.org/schema/util/spring-util.xsd"></beans>
b.使用 util 标签完成 list 集合注入提取
<!--1 提取 list 集合类型属性注入--><util:list id="bookList"><value>易筋经</value><value>九阴真经</value><value>九阳神功</value></util:list><!--2 提取 list 集合类型属性注入使用--><bean id="book" class="com.atguigu.spring5.collectiontype.Book"><property name="list" ref="bookList"></property></bean>
5.5 FactroyBean
(1)Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
普通 bean:在配置文件中定义 bean 类型就是返回类型
工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
a.创建类,让这个类作为工厂 bean,实现接口 FactoryBean
b.实现接口里面的方法,在实现的方法中定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> {//定义返回 bean@Overridepublic Course getObject() throws Exception {Course course = new Course();course.setCname("abc");return course;}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {return false;}}//spring的xml配置文件<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>//测试代码 返回Course的对象,非MyBean的对象@Testpublic void test3() {ApplicationContext context =new ClassPathXmlApplicationContext("bean3.xml");Course course = context.getBean("myBean", Course.class);System.out.println(course);}
5.6 Bean的作用域
(1)在 Spring 里面,设置创建 bean 实例是单实例还是多实例
(2)在 Spring 里面,默认情况下,bean 是单实例对象
(3)如何设置单实例还是多实例
a.在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
b.scope 属性值
第一个值默认值,singleton,表示是单实例对象
第二个值 prototype,表示是多实例对象 
c.singleton 和 prototype 区别
第一 singleton 单实例,prototype 多实例
第二设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用
getBean 方法时候创建多实例对象
5.7 bean的生命周期
(1)生命周期
(2)bean 生命周期
a.通过构造器创建 bean 实例(无参数构造)
b.为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
c.调用 bean 的初始化的方法(需要进行配置初始化的方法)
d.bean 可以使用了(对象获取到了)
e.当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
(3)演示 bean 生命周期
a.定义订单类
public class Orders {private String oname;//无参数构造public Orders() {System.out.println("第一步 执行无参数构造创建 bean 实例");}//属性的set方法public void setOname(String oname) {this.oname = oname;System.out.println("第二步 调用 set 方法设置属性值");}//创建执行的初始化的方法public void initMethod() {System.out.println("第三步 执行初始化的方法");}//创建执行的销毁的方法public void destroyMethod() {System.out.println("第五步 执行销毁的方法");} }
b.配置spring配置
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"><property name="oname" value="手机"></property></bean>
c.演示执行步骤
@Testpublic void testBean3() {ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("bean4.xml");Orders orders = context.getBean("orders", Orders.class);System.out.println("第四步 获取创建 bean 实例对象");System.out.println(orders);//手动让 bean 实例销毁context.close();}
(4)添加bean 的后置处理器,bean 生命周期有七步
a.通过构造器创建 bean 实例(无参数构造)
b.为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
c.把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
d.调用 bean 的初始化的方法(需要进行配置初始化的方法)
e.把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
f.bean 可以使用了(对象获取到了)
g.当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
(5)演示添加后置处理器效果
a.创建类,实现接口 BeanPostProcessor,创建后置处理器
public class MyBeanPost implements BeanPostProcessor {//前置处理器@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {System.out.println("在初始化之前执行的方法");return bean;}//后置处理器@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {System.out.println("在初始化之后执行的方法");return bean;} }
b.配置spring文件(实现类实现BeanPostProcessor,spring会自动识别,不需要在配置文件种配置什么)
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
5.8 xml 自动装配
自动装配:根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
(1)根据属性名称自动注入
要求Bean的ID名唯一,且和注入的属性名一致。
<!--实现自动装配bean 标签属性 autowire,配置自动装配autowire 属性常用两个值:byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样byType 根据属性类型注入--><bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"><!--<property name="dept" ref="dept"></property>--></bean><bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
(2)根据属性类型自动注入
要求配置文件有且只要一个此类型的bean,且Bean的类型与注入属性的类型一致。
<!--实现自动装配bean 标签属性 autowire,配置自动装配autowire 属性常用两个值:byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样byType 根据属性类型注入--><bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"><!--<property name="dept" ref="dept"></property>--></bean><bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
5.9 外部属性文件
a.定义外部文件(.property)
b.使用context 名称空间,引入外部文件(.property)。
c.使用${}和property里的key值使用。例如:${prop.username}
(1)引入德鲁伊连接池依赖 jar 包
(2)配置德鲁伊连接池(原始方式)
<!--直接配置连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url"value="jdbc:mysql://localhost:3306/userDb"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean>
(3)创建外部属性文件,properties 格式文件,写数据库信息
(4)把外部 properties 属性文件引入到 spring 配置文件中,替换掉相应的参数
a.引入引入 context 名称空间
b.替换掉相应的参数
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd<!--context指令-->http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--引入外部属性文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${prop.driverClass}"></property><property name="url" value="${prop.url}"></property><property name="username" value="${prop.userName}"></property><property name="password" value="${prop.password}"></property></bean>
三、 AOP 面向切面编程
1.什么是AOP
(1)定义
AOP(Aspect Orient Programming),面向切面编程。通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,aop也是一个规范, 基于动态代理,使用jdk动态代理和cglib动态代理方式,实现动态代理的一个规范化,一个标准。
(2)目的
将交叉业务代码从业务逻辑代码中划分出来,在不改变源码的情况下增加功能。
(3)作用
功能于动态代理类似:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离
(4)AOP的理解(AOP三要素)
1)需要在分析项目功能时,找出切面(切面)。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)(通知 advice)
3)合理的安排切面执行的位置,在哪个类,哪个方法增加增强功能(切入点 pointcut)
2.AOP底层原理
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理。
2.1 不使用动态代理
先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要再写两个非 业务方法。非业务方法也称为交叉业务逻辑
StudentDao接口
public interface StudentDao {//两数相加public int add(int a,int b);//返回idpublic String update(String id);}
StudentDaoImpl实现类
public class StudentDaoImpl implements StudentDao{@Overridepublic int add(int a, int b) {System.out.println("add方法");return a+b;}@Overridepublic String update(String id) {System.out.println("update方法");return id;}}
(1)交叉业务与业务代码混合
交叉业务与业务代码混合在一起,不便于交叉业务的修改和专注于业务代码。
public class StudentDaoImpl implements StudentDao{@Overridepublic int add(int a, int b) {//交叉业务System.out.println("交叉业务");//业务System.out.println("add方法");return a+b;}@Overridepublic String update(String id) {//交叉业务System.out.println("交叉业务");//业务System.out.println("update方法");return id;}}
(2)交叉业务封装后与业务代码混合
虽然减少了一定的耦合度,但交叉业务与主业务还是深度耦合在一起。交叉代码和业务代码还是混合在一起。
a.封装的交叉业务
public class Utils {public static void show(){System.out.println("交叉业务");}}
b.交叉业务的调用与业务代码的混和
public class StudentDaoImpl implements StudentDao{@Overridepublic int add(int a, int b) {//交叉业务Utils.show();//业务System.out.println("add方法");return a+b;}@Overridepublic String update(String id) {//交叉业务Utils.show();//业务System.out.println("update方法");return id;}}
(3)使用代理模式,使交叉代码与业务代码分离
代理模式实现了交叉代码和业务代码的分离,使耦合度大大降低。但如果有多个类的方法要实现同一个交叉代码,则十分麻烦,且不利于日后交叉代码的修改。
代理类代码:
public class StudentDaoProxy implements StudentDao{StudentDaoImpl studentDao = new StudentDaoImpl();@Overridepublic int add(int a, int b) {//交叉业务System.out.println("交叉业务");//业务代码int add = studentDao.add(1, 2);return add;}@Overridepublic String update(String id) {//交叉业务System.out.println("交叉业务");//业务代码String update = studentDao.update("001");return update;}}
2.2 使用动态代理
使用动态代理不仅可以分离交叉代码和业务代码,还可以提高交叉业务代码的重复利用和便于日后维护。
(1)动态代理
两种 实现方式:
jdk动态代理:使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
要求:目标类必须实现接口
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。
要求:目标类不能是final的, 方法也不能是final的
(2)JDK动态代理的实现
a.实现InvocaitonHanderl接口
public class ProxyHandle implements InvocationHandler {private Object object;public ProxyHandle(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//交叉业务System.out.println("交叉业务");//业务代码Object invoke = method.invoke(object, args);return invoke;}}
b.调用
@Testpublic void test01(){//1.创建studentDao的实现类对象StudentDaoImpl studentDao = new StudentDaoImpl();//2.生成代理类StudentDao proxy = (StudentDao)Proxy.newProxyInstance(studentDao.getClass().getClassLoader(),//类加载器studentDao.getClass().getInterfaces(), //被代理类的所有接口newProxyHandle(studentDao));//代理处理器//3.调用int add = proxy.add(1, 3);System.out.println("----------------------------------------");String update = proxy.update("001");}
c.运行结果
交叉业务add方法----------------------------------------交叉业务update方法
3.AOP 编程术语
3.1 切面(Aspect)
切面泛指交叉业务逻辑。常用的事务处理、日志处理就可以理解为切面。常用的切面 是通知(Advice)。实际就是对主业务逻辑的一种增强。
切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
3.2 连接点(Joinpoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
3.3 切入点(Pointcut)
切入点指实际被切面植入的具体方法。即多个连接点方法的集合。(执行位置)
3.4 目标类(Target)
3.5 通知(Advice)
4.AspectJ 对 AOP 的实现(重点)
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。 在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 简介
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现 。
4.1 AspectJ 的切入点表达式
(1) 表达式的定义
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
解释:
共四部分,? 表示可选的部分 ,红色代表必须有的部分。
[modifiers-pattern]? 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern]? name-pattern(param-pattern) 包名类名 、方法名(参数类型和参数个数)
[throws-pattern]? 抛出异常类型
以上表达式共 4 个部分。 execution(访问权限 方法返回值 方法声明(参数) 异常类型)
(2)表达式的通配符
通配符的含义:
*:表示0至多个任意字符。访问权限、包名、类名、方法名都可用
.. :用在方法参数种,表示任意多个参数。用在包名后面。表示当前包及其子包路径。
+(不常用):用在类名后,表示当前类及其子类。用在接口后,表示当前接口及其实现类
举例:
execution(public (..))
指定切入点为:任意公共方法。
execution( set(..))
指定切入点为:任何一个以“set”开始的方法。
execution( com.xyz.service..*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution( com.xyz.service...*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后 面必须跟“”,表示包、子包下的所有类。
**execution( ..service..(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution( .service..(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution( .ISomeService.(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution( ..ISomeService.(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution( com.xyz.service.IAccountService.(..))
指定切入点为:IAccountService 接口中的任意方法。
execution( com.xyz.service.IAccountService+.(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意 方法;若为类,则为该类及其子类中的任意方法。
execution( joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参 数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用 全限定类名,如 joke( java.util.List, int)。
execution( joke(String,)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类 型,如joke(String s1,String s2)和joke(String s1,double d2)都是,但joke(String s1,double d2,String s3)不是。
execution( joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且 参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3) 都是。
execution( joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob) 是,但,joke(String s)与 joke(User u)均不是。
execution( joke(Object+))) *
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。 不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
4.2 AspectJ的通知类型
(1)@Beforee前置通知- 方法有 JoinPoint 参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。
不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该 参数。
示例:
(2)@AfterReturning 后置通知-注解有 returning 属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。 
(3)@Around 增强方法有 ProceedingJoinPoint 参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法 的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
接口增加方法: 
接口方法的实现: 
定义切面:
(4)@After 最终通知
(5)@AfterThrowing 异常通知-注解中有 throwing 属性
在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。 当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的 名称,表示发生的异常对象。
增加业务方法: 
方法实现: 
定义切面:
4.3 AspectJ的环境配置
(1)导包
a.原始方式,引入AOP相关依赖
b.maveb方式
依赖<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.5.RELEASE</version></dependency>插件<build><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>
(2)AOP的Bean文件配置
使用标签
proxy-target-class为的值:
true则优先使用cglib
false为默认值,使用JDK的接口动态代理
示例:
<?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:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/utilhttps://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--创建一个代理类--><bean name="student" class="com.ahpu.spring.dao.StudentDaoImpl"></bean><!--创建AOP的切面类--><bean name="aop" class="com.ahpu.spring.aop.Aop"></bean><!--开启代理类自动生成--><aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy></beans>
4.4 @Aspect和@Pointcut注解的使用
(1)Aspect注解可以标注一个类为切面类
@Aspectpublic class Aop {}
(2)Pointcut注解可以把相同的切入点表达式提取出来,然后通过其标注的方法,进行调用
//定义公共切入点@Pointcut(value="execution(public * *..*.add(..))")private void pointcut(){//因为是一个公共切入点,所以没有代码}/***Before前置通知* 在切入的方法前执行* 可以有一个JoinPoint类型参数,可以获取方法名和参数*///使用公共切点@Before(value = "pointcut()")public void Before(JoinPoint joinPoint){System.out.println("我是一个前置通知");}

