3、spring-AOP

spring-AOP

AOP

什么是AOP,切面编程?

AOP为Aspect Oriented Programming的缩写,意为面向切面编写。通过预编译的方式和运行期间动态代理实现程序功能的同一维护的一种技术。AOP是OOP的延续,是函数编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑的各个部分之间的耦合度降低,提高程序的重用性,同时提高代码的开发效率。-解耦

传统的开发模型:纵向的编程

面向切面编程:纵横配合编程

编程的思想:

AOP的作用及优势:

作用:在编译期间不作编码,在运行期间,不对源代码动态的已有的方法进行增强。

优势:

​ 减少代码的重复,提高开发效率、维护方便。

AOP实现方式:

​ 通过动态代理模式

通过模拟事务处理引出问题

模拟事务处理类

/**
 * 模拟处理事务
 * @author lgx
 *
 */
public class TransactionManagerHandler {
	public void begin() {
		System.out.println("开启事务");
	}
	public void commit() {
		System.out.println("提交事务");
	}
	public void rollback() {
		System.out.println("回滚事务");
	}
	public void close() {
		System.out.println("释放资源");
	}
}

service层

public class UserServiceImpl implements UserService {

	@Override
	public void insert() {
		try {
			System.out.println("开启事务");
			System.out.println("从dao插入了一条数据");
			System.out.println("提交事务");
		} catch (Exception e) {
			System.out.println("回滚事务");
		}finally {
			System.out.println("释放资源");
		}
	}

	@Override
	public void udpate() {
		try {
			System.out.println("开启事务");
			System.out.println("从dao更新了一条数据");
			System.out.println("提交事务");
		} catch (Exception e) {
			System.out.println("回滚事务");
		}finally {
			System.out.println("释放资源");
		}
		
	}
}

存在的问题

上面的代码问题就是:每一个方法都有重复对事务处理的逻辑代码,一个类就有那么多,这么多代码的重复大大降低了我们的开发效率。

解决上述问题方案

使用代理技术:

  1. JDK的动态代理 -Java官方
  2. CGLIB的动态代理 -第三方组织开源
  3. Spring的AOP技术(底层就是JDK动态代理和CGLIB代理技术)

动态代理技术:

动态代理中,使用者使用的不是真实的对昂,而是使用一个代理对象,这个代理对象中含有真实的对象,代理对象在真实的对象上对原有的功能方法进行封装新功能。

动态代理对象不是在编译期间产生的,而是在运行期间,JVM动态的生成。

JDK动态代理

JDK动态代理是Java官方的代理

  1. 通过Proxy类创建代理对象(java.lang.reflect.Proxy 类:)
  2. 代理对象必须要有一个代理处理类(实现InvocationHandler的类)

代码:

/**
 * 用来创建代理对象的类
 * 使用JDK的代理对象的方式
 * @author lgx
 *
 */
public class DynamicProxyHandler {
	
	//被代理的对象
	private Object target;
	//所要增强的东西
	private TransactionManagerHandler txMannager;
	
	//生成代理对象的方法
	public Object getProxyObject() {
		/*
		 * 介绍:
		 * 		ClassLoader:类加载器,一个JVM只有一个
		 * 方式一:
		 * 		通过当前线程的getContextClassLoader()
		 * ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		 * 方式二:
		 * 		通过字节码文件
		 * ClassLoader classLoader = target.getClass().getClassLoader();
		 * 
		 * --------------------------------------------------------
		 * Class<?>[] interfaces:是一个接口类型的,通过反射得到一个接口类型的
		 * Class<?>[] interfaces = target.getClass().getInterfaces();
		 * 
		 * --------------------------------------------------------
		 * InvocationHandler:是具体实现增强的事情
		 * 
		 */
		
		ClassLoader loader = this.getClass().getClassLoader();
		Class<?>[] interfaces = target.getClass().getInterfaces();
		//调用Proxy类生成代理对象的方法
		Object newProxyInstance = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
			/**
			 * 写具体实现代理的功能
			 * @param proxy:要代理的对象
			 * @param method:所要调用的方法
			 * @param args:所写的参数
			 * @return:方法调用完返回的值
			 * @throws Throwable
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Object result = null;
				try {
					//增强前:开启事务
					txMannager.begin();
					//调用真实对象的方法	
					result = method.invoke(target, args);
					//增强后:提交事务
					txMannager.commit();
				} catch (Exception e) {
					//回滚事务
					txMannager.rollback();
				}finally{
					//释放资源
					txMannager.close();
				}
				//返回结果
				return result;
			}
		});
		return  newProxyInstance;
	}
	public void setTarget(Object target) {
		this.target = target;
	}
	public void setTxMannager(TransactionManagerHandler txMannager) {
		this.txMannager = txMannager;
	}
}

缺陷:

​ jdk动态代理只能通过代理接口来对实现类进行增强。不能对不需要增强的方法进行拦截,比如toString,hashCode等父类的方法。

CGLIB动态代理

CGLIB是一个开源的。与jdk动态代理一样都是动态代理,但是CGLIB可以代理没有接口的类。CGLIB代理没有接口的类的时候,在JVM中动态的产生一个子类,在子类中对真实对象的方法进行增强,然后通过父类来调用子类已增强的方法。

Spring已经为CGLIB代理集成了,不需要导入其他架包。

案例:

/**
 * 用来创建代理对象的类 使用CGLIB的代理对象的方式
 * 
 * @author lgx
 *
 */
public class CGLIBDynamicProxyHandler {
	// 被代理的对象
	private Object target;
	// 所要增强的东西
	private TransactionManagerHandler txMannager;
	// 生成代理对象的方法
	public Object getProxyObject() {
		Enhancer enhancer = new Enhancer();
		// 加载类
		enhancer.setClassLoader(this.getClass().getClassLoader());
		// 设置被代理对象
		enhancer.setInterfaces(target.getClass().getInterfaces());
		// 设置回调函数
		enhancer.setCallback(new org.springframework.cglib.proxy.InvocationHandler() {
			/**
			 * 写具体实现代理的功能
			 * 
			 * @param proxy:要代理的对象
			 * @param method:所要调用的方法
			 * @param args:所写的参数
			 * @return:方法调用完返回的值
			 * @throws Throwable
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Object result = null;
				try {
					// 增强前:开启事务
					txMannager.begin();
					// 调用真实对象的方法
					result = method.invoke(target, args);
					// 增强后:提交事务
					txMannager.commit();
				} catch (Exception e) {
					// 回滚事务
					txMannager.rollback();
				} finally {
					// 释放资源
					txMannager.close();
				}
				// 返回结果
				return result;
			}
		});
		//创建代理对象
		Object object = enhancer.create();
		return object;
	}
	public void setTarget(Object target) {
		this.target = target;
	}
	public void setTxMannager(TransactionManagerHandler txMannager) {
		this.txMannager = txMannager;
	}
}

缺陷:

​ 不能对不需要增强的方法进行拦截,比如toString,hashCode等父类的方法。

解决的方式:使用AOP切面编程。

AOP切面编程

能够解决JDK动态代理和CGLIB动态代理的缺陷。

底层原理还是JDK动态代理和CGLIB动态代理,只不过spring可以对不需要增强的方法进行拦截。

AOP相关的术语

Joinpoint(连接点):指的是被拦截的点,在spring中指的是方法类型

Pointcut(切入点): 设置拦截方法的规则,我们需要对哪些方法增强,哪些方法不需要都是通过切入点定义的。

Advice(通知/增强):就是方法执行之前拦截,或者方法之后进行拦截,可以写增强的代码。

Aspect(切面):我们拦截处理的类,切入点与通知的结合。

Weaving(织入):把切面家兔对象中,并创建出代理对象的过程,(交给spring完成)

常用标签

需要导入aop的约束。

  1. <aop:config>:开启aop的配置
    
  2. <aop:aspect>:配置切面,用于之定义配置,属性:id:为切面配置,ref:引用通知类bean的id
    
  3. <aop:porintcut>:配置切入点,用来配置切入点的表达式:expression:用于定义切入点表达式。id:用于给切入点表达式提供一个唯一标识。
    
    execution(* cn.zj.spring.service..*.*(..)
    
  4. <aop:before>:前置通知
    
  5. <aop:after-returning>:后置通知,如果出现异常就不执行
    
  6. <aop:after-throwing>:异常通知,出现异常或就会调用
    属性:
    	method:指定通知中方法的名称。
    	pointct:定义切入点表达式
    	pointcut-ref:指定切入点表达式的引用
    
  7. <aop:after>:最终通知,无论如何都会执行,用于释放资源
    
  8. <aop:around>:环绕通知,可以自己自定义通知的执行步骤
    属性:
    	method:指定通知中方法的名称。
    	pointct:定义切入点表达式
    	pointcut-ref:指定切入点表达式的引用
    

代码:

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.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    
    <!-- 被代理的对象 -->
    <bean id="userService" class="cn.zj.spring.service.impl.UserServiceImpl"/>
    <!-- 增强的对象 -->
    <bean id="txMannager" class="cn.zj.spring.util.TransactionManagerHandler"/>
    <!-- AOP的实现 -->
    <aop:config>
     <!-- 切入点: when? -->
            <aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" id="pt" />
        <!-- 增强对象:what? -->
        <aop:aspect ref="txMannager">
            <!-- 切面 : where?-->
            <!-- 通知前(开启事务) -->
            <aop:before method="begin" pointcut-ref="pt"/>
            <!-- 通知后 -->
            <aop:after-returning method="commit" pointcut-ref="pt"/>
            <!-- 异常后 -->
            <aop:after-throwing method="rollback" pointcut-ref="pt"/>
            <!-- 最后都会执行 -->
            <aop:after method="close" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
    
</beans>

切面类:

/**
 * 模拟处理事务
 * @author lgx
 *
 */
public class TransactionManagerHandler {
	public void begin() {
		System.out.println("开启事务");
	}
	public void commit() {
		System.out.println("提交事务");
	}
	public void rollback() {
		System.out.println("回滚事务");
	}
	public void close() {
		System.out.println("释放资源");
	}
}

全注解的方式:

@Configuration
@ComponentScan("cn.zj.spring")
@EnableAspectJAutoProxy//开启aop注解
public class SpringConfig {

}

切面类:

/**
 * 模拟处理事务
 * @author lgx
 *
 */
@Component
@Aspect//此类就有了AOP功能
public class TransactionManagerHandler {
	
	//切面
	@Pointcut("execution(* cn.zj.spring.service..*.*(..))")
	public void pointcut() {}
	
	@Before("pointcut()")
	public void begin() {
		System.out.println("开启事务");
	}
	@AfterReturning("pointcut()")
	public void commit() {
		System.out.println("提交事务");
	}
	@AfterThrowing("pointcut()")
	public void rollback() {
		System.out.println("回滚事务");
	}
	@After("pointcut()")
	public void close() {
		System.out.println("释放资源");
	}
}

around:

<?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.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    
    <!-- 被代理的对象 -->
    <bean id="userService" class="cn.zj.spring.service.impl.UserServiceImpl"/>
    <!-- 增强的对象 -->
    <bean id="txMannager" class="cn.zj.spring.util.TransactionManagerHandler"/>
    <!-- AOP的实现 -->
    <aop:config>
     <!-- 切入点: when? -->
            <aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" id="pt" />
        <!-- 增强对象:what? -->
        <aop:aspect ref="txMannager">
            <aop:around method="allInOne" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
    
</beans>


切面类:

/**
 * 模拟处理事务
 * @author lgx
 *
 */
public class TransactionManagerHandler {
	public void allInOne(ProceedingJoinPoint jp){
		try {
			System.out.println("开启事务");
			jp.proceed();//环绕业务层的方法
			System.out.println("提交事务");
		} catch (Throwable e) {
			System.out.println("回滚事务");
		}finally {
			System.out.println("释放资源");
		}
	}	
}

posted @ 2022-04-07 19:16  站着说话不腰疼  阅读(43)  评论(0)    收藏  举报