Spring---面向切面AOP代理模式(下)

一、拦截器模式下的AOP

spring拦截器标准下实现代理模式(将spring源码从底层抽取出,重新实现):
把方法执行的时机(亦称为通知)抽取5个节点 :

  1. 方法执行之前 如果方法之前,做拦截,拦截方法如果返回为true,才允许后面流程执行
  2. 方法执行(包围)方法如果能被调用,那么只能在包围方法中调用
    result = interceptor.aroundInvoke(method, args, ori, proxy);方法执行(包围)
  3. 方法执行之后 能够获取方法返回的结果
  4. 当方法发生了异常
  5. 最终

注:将执行的逻辑交给拦截器去做,最后用的是拦截器的逻辑,而非代理类的逻辑,这样可以对代理类再次进行代理,再次代理见下面ProxyMain.java类的 责任链模式代码块

接口 interceptor

public interface Interceptor {

	/**
	 * 
	 * <b>Description: 在代理类方法执行之前被执行,如果返回为真,则继续执行,如果为假,则方法不被执行</b><br>
	 * 
	 * @param method 原始类方法
	 * @param args   执行原始类方法的参数
	 * @param ori    原始对象
	 * @param proxy  代理对象
	 */
	public Boolean beforeInvoke(Method method, Object[] args, Object ori, Object proxy);
	//方法执行(包围)
	public Object aroundInvoke(Method method, Object[] args, Object ori, Object proxy)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
	//方法执行之后,有返回值result
	public void afterInvoke(Object result, Method method, Object[] args, Object ori, Object proxy);
	//方法发生异常
	public void exceptionInvoke(Exception e, Method method, Object[] args, Object ori, Object proxy);
	//最终
	public void finallyInvoke(Method method, Object[] args, Object ori, Object proxy);

}

接口的子实现类 Interceptor1.java

public class Interceptor1 implements Interceptor {

	
	public Boolean beforeInvoke(Method method, Object[] args, Object ori, Object proxy) {
		System.out.println("interceptor1");
		return true;
	}

	public Object aroundInvoke(Method method, Object[] args, Object ori, Object proxy)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		return method.invoke(ori, args);
	}

	public void afterInvoke(Object result, Method method, Object[] args, Object ori, Object proxy) {
		System.out.println("after: " + result);
	}

	public void exceptionInvoke(Exception e, Method method, Object[] args, Object ori, Object proxy) {
		System.out.println(e.getMessage());
	}
	
	public void finallyInvoke(Method method, Object[] args, Object ori, Object proxy) {
		System.out.println("finally");
	}
}

创建代理类

public class JDKProxyProcessor implements InvocationHandler {

	private Object ori;
	private String inteceptorName;//为了拦截器的可配置,将拦截器名抽取出来成为一个变量

	public JDKProxyProcessor(Object ori) {
		this.ori = ori;
	}
	public JDKProxyProcessor(Object ori, String inteceptorName) {
		this.ori = ori;
		this.inteceptorName = inteceptorName;
	}

	/*
	 * 目标类中的所有方法,都会被该方法的逻辑增强 不一定是逻辑变多(non-Javadoc)
	 * 
	 * 方法执行的参数,方法执行的结果 谁再什么时候,用什么参数,调用了什么方法,得到了什么结果 批量增强某些类中的方法
	 * 
	 * 要求原始类必须有接口
	 * 
	 * CGLIB的实现 通过字节码增强 性能比JDK的实现要慢一些
	 * 
	 * 拦截器规范
	 * 
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
	 * java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null;

		if (inteceptorName != null) {	//判断如果有拦截器的名字传进来,则做逻辑增强,否则原始方法输出;
			//通过拦截器的全限定名(反射)创建出拦截器
			Interceptor interceptor = (Interceptor) Class.forName(inteceptorName).newInstance();
			try {
				// 方法执行之前 如果方法之前,做拦截,拦截方法如果返回为true,才允许后面流程执行
				if (interceptor.beforeInvoke(method, args, ori, proxy)) {
					// 方法执行(包围)方法如果能被调用,那么只能在包围方法中调用
					result = interceptor.aroundInvoke(method, args, ori, proxy);
					// 方法执行(包围)
					interceptor.afterInvoke(result, method, args, ori, proxy);
				} else {
					return null;
				}
				// 方法执行之后 能够获取方法返回的结果
			} catch (Exception e) {
				// TODO: handle exception
				// 当方法发生了异常
				interceptor.exceptionInvoke(e, method, args, ori, proxy);
			} finally {
				// 最终
				interceptor.finallyInvoke(method, args, ori, proxy);
			}
		} else {
			result = method.invoke(ori, args);
		}

		/*
		 * if (method.getName().equals("add")) { System.out.println(new Date() +
		 * " 使用参数 :" + Arrays.toString(args) + " 执行了方法 " + method.getName() + " 得到了: " +
		 * result + "结果"); }
		 */
		return result;
	}
}

此时在JDKProxyFactory类中添加方法:

public class JDKProxyFactory {
	public static Object bind(Object object) {
		// 被代理的类必须有接口,jdk会根据代理类的所有接口,创建一个具有相同实现关系的代理类,原始类和代理类之间相当于兄弟关系
		// 第三个参数,要如何增强原始类,将所有增强的逻辑写在InvocationHandler的invoke中
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
				new JDKProxyProcessor(object));
	}
	//通过拦截器创建代理类,传入原始类对象和接口名字(全限定名)
	public static Object bind(Object object, String interceptorName) {
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
				new JDKProxyProcessor(object, interceptorName));
	}
}

代理测试类ProxyMain.java:

public class ProxyMain {
	public static void main(String[] args) {
		Service service1 = new Service1();
		service1.service();
		System.out.println(service1.add(1));
		System.out.println("-----");

		//责任链模式
		//用Interceptor1这个拦截器去拦截service1这个原始类,得到代理类proxyService2
		Service proxyService2 = (Service) JDKProxyFactory.bind(service1, "net.seehope.spring.demo.aop.Interceptor1");
		//将代理类作为原始类,再用Interceptor1进行再次拦截
		Service proxyService3 = (Service) JDKProxyFactory.bind(proxyService2,
				"net.seehope.spring.demo.aop.Interceptor1");
		proxyService3.service();
	}
}

                      分隔线


导入切面依赖的jar包

<!-- 切面依赖 -->
		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.9.4</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.4</version>
		</dependency>

二、通过配置文件&注解方式实现AOP

切面类示例:MyAspect.java**(面)**

joinpoint(连接点):目标方法称之为连接点
advice(通知): 拦截方法
aspect(切面):存放通知的类
pointcut(切点):所有连接点的集合

注:Spring 中的切面 只能作用于bean
在Spring中切面用来拦截bean中的方法(点),将这种行为成为“切”(面跟点之间的关系),一个bean是一个组件或单独的节点。spring利用一下类的这些拦截方法(advice),去拦截一系列的方法,成为切面编程。

public class MyAspect {

	public void pointCut() {
	}

	public boolean myBefore(JoinPoint jp) {//JionPoint:连接点
		System.out.println("mybefore");
		return true;
	}

	public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
		Object result = pjp.proceed();
		return result;
	}

	public void myAfter(Object result) {		//使用配置,不能加JoinPoint jp参数
		System.out.println("myafter: " + result);
	}

	public void myException(Exception e) {
		System.out.println(e.getMessage());
	}

	public void myFinally(JoinPoint jp) {
		System.out.println("myAfter");
	}
}

在src/main/resources目录下创建的applicationContext.xml文件中,点击NameSpaces(命名空间),将AOP的约束导进来。
在这里插入图片描述该图片供切点表达式分析使用

AOP在Spring容器中的配置----基于文件的切面配置方式(比之前的切面优秀)

切点表达式:execution()表示它是一个切点表达式,然后根据方法签名进行匹配
*                 表示该方法返回值任意
* net.xikee            表示该方法在net.xikee这个包中
* net.xikee…*            表示包后面可以有任意层级
* net.xikee…* .service.impl      表示后面层级为service.impl
* net.xikee…* .service.impl.*.*    表示该包下的所有类的所有方法
* net.xikee…* .service.impl.*.* (··) ()表示所有方法,“··”表示任意参数

如果切点表达式匹配成功,表示切点切中(则Spring中的bean为代理类),不使用原始类而使用代理类

<!-- 将切面组件注册到Spring容器中 -->
	<bean id="myAspect" class="net.xikee.druid.aop.MyAspect"></bean>

	<!-- 使用切面中的哪些通知去拦截哪些连接点 -->
	<!-- pointcut切点,所有连接点的集合 -->
	<aop:config proxy-target-class="false">	//aop的配置标签
		<aop:aspect ref="myAspect">		//根据已注册的组件id(=myAspect)决定对哪个组件进行配置
			<aop:pointcut
				expression="execution(* net.seehope..*.service.impl.*.*(..))"
				id="pc" />
			<aop:before method="myBefore" pointcut-ref="pc" />	//让myBefore这个通知通知pc这个切点
			<aop:around method="myAround" pointcut-ref="pc" />
			<aop:after-returning method="myAfter"
				pointcut-ref="pc" returning="result" />	//给myAfter配置个result的参数,才能通过反射将值注入
			<aop:after-throwing method="myException"
				pointcut-ref="pc" throwing="e" />	//同上
			<aop:after method="myFinally" pointcut-ref="pc" />
		</aop:aspect>
	</aop:config>

proxy-target-class="false"表示使用默认的JDK代理实现,true则表示使用CGLIB(在字节码文件中对原始类进行增强,不要求原始类有接口,但效率比JDK低)
其中最重要的通知是:Around 和 Exception

AOP在Spring容器中的配置----基于注解的AOP实现

@Component:将MyAspect配置成Spring中的组件
@Aspect: 表示是一个切面
通过注解配置,这个bean与Spring中的其他组件没有耦合,通过以上两个注解将MyAspect插入Spring容器中,成为一个bean组件。如果对项目中的类进行增强,则写个切面,即插即用,对项目源码没有任何影响

@Component
@Aspect
public class MyAspect {

	//注册一个名叫“pointCut()”的切点,并将切点表达式封装到方法中
	@Pointcut("execution(* net.seehope..*.service.impl.*.*(..))")
	public void pointCut() {

	}

	@Before("pointCut()")	//将方法配置成通知,将切点名插入
	public boolean myBefore(JoinPoint jp) {//JionPoint:连接点
		System.out.println("mybefore");
		return true;
	}

	@Around("pointCut()")
	public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
		Object result = pjp.proceed();
		return result;
	}

	@AfterReturning(pointcut = "pointCut()", returning = "result")	//配置参数名
	public void myAfter(Object result) {		//使用配置,不能加JoinPoint jp参数
		System.out.println("myafter: " + result);
	}

	@AfterThrowing(pointcut = "pointCut()", throwing = "e")
	public void myException(Exception e) {
		System.out.println(e.getMessage());
	}

	@After("pointCut()")
	public void myFinally(JoinPoint jp) {
		System.out.println("myAfter");
	}
}

最后需在applicationContext.xml文件中开启AOP注解支持功能:

<!-- 开启对AOP注解支持 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值