什么是AOP?
AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP的作用:
不修改源码的情况下,程序运行期间对方法进行功能增强
好处:
1、减少代码的重复,提高开发效率,便于维护。
2、专注核心业务的开发。
核心业务和服务性代码混合在一起
开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。
01
—
代理模式
一、代理模式
AOP底层是动态代理 。
代理模式分为静态代理与动态代理 /
代理模式作为23种经典设计模式之一,其比较官方的定义为“为其他对象提供一种代理以控制对这个对象的访问”, 简单点说就是,之前A类自己做一件事,在使用代理之后,A类不直接去做,而是由A类的代理类B来去做。代理类其实是在之前类的基础上做了一层封装。java中有静态代理、JDK动态代理、CGLib动态代理的方式。静态代理指的是代理类是在编译期就存在的,相反动态代理则是在程序运行期动态生成的
二、静态代理
1、原有方式:核心业务与服务方法编写在一起
public class TeamService {
public void add(){
try {
System.out.println("开始事务");
System.out.println("TeamService---- add----");// 核心业务
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("回滚事务");
}
}
}
2、基于类的静态代理
缺点:代理类只能代理一个类
public class ProxyTest {
@Test
public void test(){
UserService userService = new UserService();
Proxy proxy = new Proxy(userService);
proxy.show();
}
}
class UserService{
public void show(){
System.out.println("Output information -->");
}
}
class Proxy extends UserService{
private UserService userService;
public Proxy(UserService userService) {
this.userService = userService;
}
@Override
public void show() {
try {
System.out.println("开始事务");
super.show();
System.out.println("提交事务");
}catch (Exception e){
System.out.println("异常事务-->回滚");
}finally {
System.out.println("结束事务");
}
}
}
3、基于接口的静态代理
要求:代理类和被代理类都实现了同一个接口
public class ProxyTest {
@Test
public void test(){
UserService userService1 = new UserServiceImpl();
UserService userService2 = new StudentServiceImpl();
Proxy proxy1 = new Proxy(userService1);
proxy1.show();
Proxy proxy2 = new Proxy(userService2);
proxy2.show();
}
}
interface UserService{
void show();
}
class UserServiceImpl implements UserService{
public void show(){
System.out.println("UserServiceImpl Output information -->");
}
}
class StudentServiceImpl implements UserService{
public void show() {
System.out.println("StudentServiceImpl Output information -->");
}
}
class Proxy implements UserService{
private UserService userService;
public Proxy(UserService userService) {
this.userService = userService;
}
public void show() {
try{
System.out.println("开始事务");
userService.show();
System.out.println("提交事务");
}catch (Exception e){
System.out.println("异常-->回滚事务");
}finally {
System.out.println("结束事务");
}
}
}
4、提取出切面代码,作为AOP接口
public class ProxyTest {
@Test
public void test(){
UserService userService1 = new UserServiceImpl();
UserService userService2 = new StudentServiceImpl();
AOP tranAOP = new TranAOP();
ProxyService proxy1 = new ProxyService(userService1,tranAOP);
proxy1.show();
ProxyService proxy2 = new ProxyService(userService2,tranAOP);
proxy2.show();
}
}
interface UserService{
void show();
}
class UserServiceImpl implements UserService{
public void show(){
System.out.println("UserServiceImpl Output information -->");
int a = 1/0;
}
}
class StudentServiceImpl implements UserService{
public void show() {
System.out.println("StudentServiceImpl Output information -->");
}
}
interface AOP{
void before();
void after();
void exception();
void myFinally();
}
class TranAOP implements AOP{
public void before() {
System.out.println("开始事务-->");
}
public void after() {
System.out.println("提交事务-->");
}
public void exception() {
System.out.println("异常--回滚事务-->");
}
public void myFinally() {
System.out.println("结束事务-->");
}
}
class ProxyService implements UserService{
private UserService userService;
private AOP aop;
public ProxyService(UserService userService, AOP aop) {
this.userService = userService;
this.aop = aop;
}
public void show() {
try{
aop.before();
userService.show();
aop.after();
}catch (Exception e){
aop.exception();
}finally {
aop.myFinally();
}
}
}
三、动态代理
基于动态代理有两种方式:
基于JDK的动态代理
基于CGLIB的动态代理
1、基于jdk的动态代理
static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//定义一个接口
interface IService {
void show();
}
//定义一个类
class UserService implements IService{
@Override
public void show() {
System.out.println("Output information -->");
}
}
//使用JDK的动态代理添加事务
public class ProxyTest {
public static void main(String[] args) {
//目标对象(被代理的对象)
UserService userService = new UserService();
//返回代理的对象 基于JDK的代理
IService proxyService = (IService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() { //句柄(回调函数)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
System.out.println("开始事务");
Object invoke = method.invoke(userService, args);//核心方法
System.out.println("提交事务");
return invoke;
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
throw e;
}
}
}
);
proxyService.show();
}
}
代理对象不需要实现接口,但是目标对象一定要实现接口,否则无法使用JDK的动态代理。
2、基于CGLIB的动态代理
CFLIB代理,也叫子类代理,在内存中构建一个子类对象从而实现对目标对象功能的扩展
static Object create(Class type, Callback callback) {
Enhancer e = new Enhancer();
e.setSuperclass(type);
e.setCallback(callback);
return e.create();
}
//创建被代理对象
class UserService {
public void show() {
System.out.println("Output information -->");
}
}
//使用CGLIB代理事务
public class ProxyTest {
public static void main(String[] args) {
//目标对象(被代理对象) 无实现接口
UserService userService = new UserService();
//创建代理对象,使用CGLIB代理
UserService proxyService = (UserService) Enhancer.create(
userService.getClass(),
userService.getClass().getInterfaces(),
new MethodInterceptor() { //回调函数编写代理规则
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
System.out.println("开始事务");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("提交事务");
return invoke;
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
throw e;
}
}
});
proxyService.show();
}
}
02
—
Spring-AOP
一、Spring-AOP
Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
二、AOP的相关术语:
Target(目标对象) 要被增强的对象,一般是业务逻辑类的对象。
Proxy(代理) 一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect(切面) 表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法(一般是类中的业务方法),因为 Spring只支持方法类型的连接点。
Pointcut(切入点) 切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
Advice(通知/增强) 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时 间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
切入点定义切入的位置,通知定义切入的时间。
Weaving(织入). 是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编 译期织入和类装载期织入。
三、切面的三个关键因素
1、切面的功能--切面能干啥
2、切面的执行位置--使用Pointcut表示切面执行的位置
3、切面的执行时间--使用Advice表示时间,在目标方法之前还是之后执行。
四、AspectJ 对 AOP 的实现
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。
AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式 AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。
五、AspectJ的通知类型 AspectJ 中常用的通知有5种类型
-
前置通知
-
后置通知
-
环绕通知
-
异常通知
-
最终通知
六、AspectJ的切入点表达式
AspectJ 定义了专门的表达式用于指定切入点。
表达式的原型如下:
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(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。
所以,execution 表达式中就是方法的签名。
PS:表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
符号 意义
* 0-多个任意字符
.. 用在方法参数中,表示任意个参数;用在包名后,表示当前及其子包路径
+ 用在类名后,表示当前及其子类;用在接口后,表示当前接口及其实现类 示例:execution(* com.kkb.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.kkb.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。
“..”出现在类名中时,后面必须跟 “*”,表示包、子包下的所有类。
execution(* com.kkb.service.IUserService+.*(..))
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类, 则为该类及其子类中的任意方法。