Spring核心功能AOP
1.什么是AOP?
1.在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
2.AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
3.AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范
4.通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
5.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
6.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
2.Spring框架的AOP底层实现
1.Spring框架的底层采用的是代理技术,代理的方式分为两种
1.基于JDK的动态代理
必须面向接口,只有实现了具体接口的类才能生成代理对象
2.基于CDLIB动态代理
没有实现接口的类,也可以产生代理,产生这个类的子类
3.JDK动态代理
package org.best.util;
import org.best.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
/**
*
* @param userService 目标对象
* invoke中的三个参数
* 参数一:生成的代理对象
* 参数二:代理对象所调用的方法对象
* 参数三:代理对象所调用的方法对象中的参数对象数组
* @return
*/
public static UserService getProxy(final UserService userService){
UserService p =(UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
//equals中写的为方法名
if (method.getName().equals("say")) {
//这里我们可以执行代理对象所需要添加的功能
System.out.println("代理对象方法");
//执行目标对象中的方法
method.invoke(userService, args);
}
return invoke;
}
});
//返回代理对象
return p;
}
}
4.AOP相关术语
1. Joinpoint(连接点) -- 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
2. Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义 ,也就是我们想对那个方法做增强,这个方法叫做切入点
3. Advice(通知/增强) -- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
4. Introduction(引介) -- 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
5. Target(目标对象) -- 代理的目标对象
6. Weaving(织入) -- 是指把增强应用到目标对象来创建新的代理对象的过程,简单来说也就是生成代理对象的这个过程,称之为织入。
7. Proxy(代理) -- 一个类被AOP织入增强后,就产生一个结果代理类
8. Aspect(切面) -- 是切入点和通知的结合,切面是以后咱们自己来编写和配置的
5.使用配置文件实现AOP开发
1.创建项目,导入jar包
普通项目中
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.apache.log4j-1.2.15.jar
spring-beans-5.0.2.RELEASE.jar
spring-context-5.0.2.RELEASE.jar
spring-core-5.0.2.RELEASE.jar
spring-expression-5.0.2.RELEASE.jar
* 再引入Spring框架的AOP的开发包
* spring的传统AOP的开发的包
* spring-aop-5.0.2.RELEASE.jar
* com.springsource.org.aopalliance-1.0.0.jar 这个在依赖包中找
* aspectJ的开发包
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar 这个在依赖包中找
* spring-aspects-5.0.2.RELEASE.jar Spring整合AspectJ的jar包
Maven项目中,在Pom.xml文件中添加jar包坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2.创建配置文件,引入约束
<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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
3.创建实现类和接口
//接口
package org.best.service;
public interface UserService {
void login();
}
//实现类
package org.best.service;
public class UserServiceImpl implements UserService {
public void login(){
System.out.println("登录");
}
}
4.定义切面类
package org.best.aspect;
public class MyAspect {
public void checkPassword(){
System.out.println("校验密码");
}
}
5.配置applicationContext.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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="org.best"></context:component-scan>
<!--定义切面类-->
<bean class="org.best.aspect.MyAspect" id="aspect"></bean>
<!--引入切面类-->
<aop:config>
<aop:aspect ref="aspect">
<!--定义通知类型,切面类的方法和切入点表达式-->
<aop:before method="checkPassword"
pointcut="execution(public void org.best.dao.UserDaoImpl.login())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
6.测试
@org.junit.Test
public void test2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
/* UserService userService = context.getBean("userService", UserService.class);
userService.login();*/
UserDao userDao = context.getBean("userDao", UserDao.class);
userDao.login();
}
6.切入点表达式
1. 再配置切入点的时候,需要定义表达式,重点的格式如下:execution(public * *(..)),具体展开如下:
* 切入点表达式的格式如下:
* execution([修饰符] 返回值类型 包名.类名.方法名(参数))
* 修饰符可以省略不写,不是必须要出现的。 public 可以省略不写
* 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。 例如: public * org.westos.demo3.BookDaoImpl.save()
* 包名例如:org.westos.demo3.BookDaoImpl
* 首先org是不能省略不写的,但是可以使用 * 代替 例如:org.*.*.BookDaoImpl
* 中间的包名可以使用 * 号代替 例如:public void *.*.*.BookDaoImpl.save()
* 如果想省略中间的包名可以使用 *..* 例如: public void *..*.BookDaoImpl.save()
* 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl 意思是这个类以DaoImp结尾 例如:public void org.westos.demo3.*DaoImpl.save()
* 方法也可以使用 * 号代替
例如:public void org.westos.demo3.*DaoImpl.*() 通配所有方法名
例如: public void org.westos.demo3.*DaoImpl.save*() 通配方法以save开头
* 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
例如:public void org.westos.demo3.*DaoImpl.save(*) 统配一个参数
例如:public void org.westos.demo3.*DaoImpl.save(..) 统配任意个数的参数
7.AOP的通知类型
1. 前置通知 before
* 在目标类的方法执行之前执行。
* 配置文件信息:<aop:before method="before" pointcut-ref="myPointcut3"/>
* 应用:可以对方法的参数来做校验
2. 最终通知 after
* 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
* 在配置文件中编写具体的配置:<aop:after method="after" pointcut-ref="myPointcut3"/>
* 应用:例如像释放资源
3. 后置通知
* 方法正常执行后的通知。
* 在配置文件中编写具体的配置:<aop:after-returning method="afterReturning" pointcut-ref="myPointcut2"/>
* 应用:可以修改方法的返回值
4. 异常抛出通知
* 在抛出异常后通知
* 在配置文件中编写具体的配置:
throwing="e" 这个e的意思就是你切面类里面配置的afterThrowingLog(Exception e)这个增强的方法的形参的名称
//当你目标对象中delCustomer()方法中出现了异常,那么切面类里面配置的afterThrowingLog()这个增强的方法就会执行
<aop:after-throwing method="afterThrowingLog"
pointcut="execution(public * *..*.Customer*.delCustomer(..))" throwing="e"/>
//切面类中 抛出异常后通知 需要在增强的方法形参里面提供一个异常对象,当你切入点里面抛出你配置的这个异常类型
//这个增强的方法,就会执行
public void afterThrowingLog(Exception e) {
System.out.println("方法抛出异常后执行通知 日志记录");
}
* 应用:包装异常的信息
5. 环绕通知
* 方法的执行前后执行。
* 在配置文件中编写具体的配置:<aop:around method="around" pointcut-ref="myPointcut2"/>
* 要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。
/**
* 环绕通知:方法执行之前和方法执行之后进行通知,默认的情况下,目标对象的方法不能执行的。需要手动让目标对象的方法执行
*/
public void around(ProceedingJoinPoint joinPoint){ //ProceedingJoinPoint 固定的一个类 使用他然目标对象的方法执行.
System.out.println("环绕通知1...");
try {
// 手动让目标对象的方法去执行
joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("环绕通知2...");
}