spring随笔(二) AOP

AOP(Aspect-Oriented Programming 面向切面编程)

aop(Aspect-Oriented Programming)也就是面向切面编程,作为面向对象编程的一种补充。aop从程序运行角度考虑程序的流程,提取业务处理过程的切面。

1,AOP中的概念

Aspect(切面):          是通知(Advice)和切入点(Pointcut)的结合,通知和切入点共同定义了关于切面的全部内容---何时、何地、做什么。

Advice(通知):          所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能,通知定义了切面的”何时”和”做什么”)

Pointcut(切入点):    所谓切入点是指我们要对那些joinpoint进行拦截的定义.   切入点就定义了”何地”.
JoinPoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Target(目标对象):   代理的目标对象
Weaving(织入):       是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
Introduction(引入):  在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
AOP代理:springAOP创建的代理对象。也可以说是对目标对象的加强。spring中的代理可以是jdk的动态代理,也可以是CGLIB代理。前者为实现接口的对象进行代理,后者是为了不实现接口的对象的代理。spring会根据具体的类是否有接口来以不同的方式处理代理过程。

2,spring中的两种代理(内部实现)

1,JDK的动态代理

JDK的动态代理主要的核心方法是:java.lang.reflect.Proxy类的 public static Object  newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  throws IllegalArgumentException该方法。

其中参数 :loader - 定义代理类的类加载器   、interfaces - 代理类要实现的接口列表、h - 指派方法调用的调用处理程序 。

其中的InvocationHandler为java.lang.reflect下面的接口

返回:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

InvocationHandler接口只有一个方法:Object invoke(Object proxy, Method method, Object[] args) throws Throwable

代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

如下要在一个CustmerService的保存更新操作上面加上事务处理。

ICustomeService接口:

package com.xiaohui.proxy;
public interface ICustomeService {
	void save(Customer c);
	void update(Customer c);
}

CustomerServiceImplCustomerServiceImpl类

package com.xiaohui.proxy;
public class CustomerServiceImpl implements ICustomeService{
	public void save(Customer customer) {
		System.out.println("CustomerServiceImpl......save..."+customer.getName());
	}
	public void update(Customer c) {
		System.out.println("CustomerServiceImpl......update..."+c.getName());
	}
}

MyInvocationHandlerFactoryMyInvocationHandlerFactory类

package com.xiaohui.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandlerFactory implements InvocationHandler {
	//目标对象
	private Object target;
	/**
	 *被代理的对象
	 * @param target
	 * @return 代理对象
	 */
	public Object getProxyInstance(Object target){
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//proxy参数一般不用
		//前置的处理逻辑程序
		System.out.println("session.beginTranscation()");
		//回调目标对象的方法  类似于filter中的chain.doFilter(request,response);
		Object obj = method.invoke(target, args);
		//后续的处理逻辑程序
		System.out.println("session.getTranscation().commit()");
		return obj;
	}
}

测试类:

	@Test
	public void testProxy() throws Exception {
		ICustomeService service = new CustomerServiceImpl();
		Customer customer = new Customer();
		customer.setName("张珊");
		MyInvocationHandlerFactory factory = new MyInvocationHandlerFactory();
		ICustomeService serviceProxy =(ICustomeService) factory.getProxyInstance(service);
		serviceProxy.save(customer);
		System.out.println("-------------------------------");
		serviceProxy.update(customer);
	}

 打印结果:

session.beginTranscation()
CustomerServiceImpl......save...张珊
session.getTranscation().commit()
-------------------------------
session.beginTranscation()
CustomerServiceImpl......update...张珊
session.getTranscation().commit()

2,使用CGLIB代理

在使用Spring的CGLIB代理过程中主要使用到的类为:net.sf.cglib.proxy.Enhancer。其核心方法为:Object hancer.create();

代码如下:

MyCallbackFactory类,需要注意的是 该类实现的接口和JDK实现的接口名字完全一样,接口结构也一致,单spring对其做了另外的调整。所以开发者们不能实现错接口。

package com.xiaohui.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
public class MyCallbackFactory implements  InvocationHandler {
	private Object target;
	public Object getInstance(Object target){
		this.target = target;
		Enhancer hancer = new Enhancer(); 
		hancer.setClassLoader(target.getClass().getClassLoader());
		hancer.setSuperclass(target.getClass());
		hancer.setCallback(this);
		return hancer.create();
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("your before logic progream.....");
		Object obj =  method.invoke(target, args);
		System.out.println("your after logic progream.....");
		return obj;
	}
}

测试类:

@Test
	public void testProxy2() throws Exception {
		CustomerServiceImpl service = new CustomerServiceImpl();
		Customer customer = new Customer();
		customer.setName("张珊");
		MyCallbackFactory factory = new MyCallbackFactory();
		CustomerServiceImpl proxy =  (CustomerServiceImpl) factory.getInstance(service);
		proxy.update(customer);
		System.out.println(proxy.getClass().getSuperclass().getName());
	}

打印结果:

your before logic progream.....
CustomerServiceImpl......update...张珊
your after logic progream.....
com.xiaohui.proxy.CustomerServiceImpl
由此可以看出该代理对象为真实对象的子类。需要代理的对象实不实现接口都无所谓,都可以被CGLIB代理。

3,通过实现org.aopalliance.intercept.MethodInterceptor接口,使用spring的org.springframework.aop.framework.ProxyFactoryBean类配置xml获取代理,内部机制就是上面的两种。

TranscationInterceptor类:

package com.xiaohui.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class TranscationInterceptor implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("your Transcation...logic progream...Before....");
		Object obj = invocation.proceed();
		System.out.println("your Transcation...logic progream...After....");
		return obj;
	}
}

ICustomeService接口:

package com.xiaohui.aop;
public interface ICustomeService {
	void save(Customer c);
	void update(Customer c);
}

CustomerServiceImpl类:

package com.xiaohui.aop;
public class CustomerServiceImpl implements ICustomeService{
	public void save(Customer customer) {
		System.out.println("CustomerServiceImpl......save..."+customer.getName());
	}
	public void update(Customer c) {
		System.out.println("CustomerServiceImpl......update..."+c.getName());
	}
}

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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<bean id="transcationInteceptor" class="com.xiaohui.aop.TranscationInterceptor" />
	<bean id="customerService" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="proxyInterfaces" value="com.xiaohui.aop.ICustomeService" />
		<property name="target">
			<bean class="com.xiaohui.aop.CustomerServiceImpl"/>
		</property>
		<property name="interceptorNames">
			<list>
				<value>transcationInteceptor</value>
			</list>
		</property>
	</bean>
</beans>

使用spring的这种配置,被代理的类如果没有实现接口就不用在ProxyFactoryBean中配<property name="proxyInterfaces" value="pacgake.xxxx" />,这样没有接口的代理会通过CGLIB创建代理。有接口的会通过JDK动态代理来创建。
测试代码:

@Test
	public void testProxy2() throws Exception {
		Customer customer = new Customer();
		customer.setName("张珊");
		ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
		ICustomeService service = ctx.getBean("customerService",ICustomeService.class);
		service.save(customer);
	}

打印结果:

your Transcation...logic progream...Before....
CustomerServiceImpl......save...张珊
your Transcation...logic progream...After....

3,spring使用@AspectJ注解添加AOP功能。

AspectJ是一门专门用于Java AOP编程的Java扩展语言。在Spring中,可以通过使用@AspectJ注释,来添加AOP功能。
要使用@AspectJ注释编程,首先要在Spring的配置文件中引入aop命名空间:并加入 标签<aop:aspectj-autoproxy/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
        <aop:aspectj-autoproxy/>
</beans>

使用@AspectJ注解来功能来获取代理首先需要定义一个切面:(定义一个类,使用@Aspect声明)
 
如下切面类TransCationService:
package com.xiaohui.aop;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TransCationService {

	@Pointcut("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")
	public void pointcut() {
	}

	@Before("pointcut()")
	public void beginTranscation(JoinPoint jp) {
		System.out.println("目标类:" + jp.getTarget().getClass().getName());
		System.out.println("参数: " + Arrays.toString(jp.getArgs()));
		System.out.println("方法: " + jp.getSignature().getName());
		System.out.println("session.brgintranscation()........");
	}

	@AfterReturning("pointcut()")
	public void commit() {
		System.out.println("session.getTranscation().commit()");
	}

	@AfterThrowing(pointcut = ("execution(* com.xiaohui.aop.*ServiceImpl.*(..))"), throwing = "e")
	public void rollback(Throwable e) {
		System.out.println("出现异常......" + e.getMessage());
		System.out.println("session.rollback....");
	}

	@After("pointcut()")
	public void closeSession() {
		System.out.println("session.close()");
	}
}
上面的
@Pointcut("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")
	public void pointcut(){}
其定义了一个切入点表达式:其中第一个‘*’ 表示返回值为任何类型,第二个‘*’表示在包com.xiaohui.aop下面的以ServiceImpl结尾的类,第三个‘*’表示所有方法()里的.. 表示任意参数类表。,最终的意思为,在com.xiaohui.aop包下面返回值为任何类型,且类名以ServiceImpl结尾的参数类表不限所有方法。
 
@Before("pointcut()"):前置通知,在目标代理对象执行方法之前调用。直接引用了上面已经定义好的切入点,使用方法名。
@AfterReturning("pointcut()"):后置通知,在目标代理对象成功执行方法之后调用。
@After("pointcut()"):最终通知,在目标代理对象执行方法之后,无论目标对象方法成功调用与否,都会执行。
@AfterThrowing(pointcut=("execution(* com.xiaohui.aop.*ServiceImpl.*(..))"),throwing="e"):异常通知:在一个方法抛出异常后执行。throwing,表示抛出的异常。其切入点为自己重新定义的切入点表达式。
 
applicationContext.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<bean  id="advice" class="com.xiaohui.aop.TransCationService"/>
	<bean id="customerService" class="com.xiaohui.aop.CustomerServiceImpl"/>
	<aop:aspectj-autoproxy/>
</beans>

ICustomerService接口和测试类和2.3中的代码一致,为了测试有异常抛出的情况,我们对CustomerServiceImpl类的save方法进行一点点修改。save方法如下:
public void save(Customer customer) {
		System.out.println("CustomerServiceImpl......save..."+customer.getName());
		if(new Random().nextInt(10)>5){
			throw new RuntimeException("保存失败.....");
		}
	}

这样就可以出现抛出异常的情况。没有异常的打印结果如下:

目标类:com.xiaohui.aop.CustomerServiceImpl
参数: [com.xiaohui.aop.Customer@1700391]
方法: save
session.brgintranscation()........
CustomerServiceImpl......save...张珊
session.getTranscation().commit()
session.close()

有异常抛出的打印结果如下:(在测试的过程中发现如果在切面类中将最终通知的方法 定义在 异常通知的方法 上面,则会先打印session.close(),后打印session.rollback....,有点不解)
目标类:com.xiaohui.aop.CustomerServiceImpl
参数: [com.xiaohui.aop.Customer@118223d]
方法: save
session.brgintranscation()........
CustomerServiceImpl......save...张珊
出现异常......保存失败.....
session.rollback....
session.close()
 出过这几个通知外,还有一个环绕通知:@Around("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")
 
TransCationService切面类:
package com.xiaohui.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class TransCationService {
	public void pointcut() {
	}

	public void beginTranscation(JoinPoint jp) {
		System.out.println("目标类:" + jp.getTarget().getClass().getName());
		System.out.println("参数: " + Arrays.toString(jp.getArgs()));
		System.out.println("方法: " + jp.getSignature().getName());
		System.out.println("session.brgintranscation()........");
	}

	public void commit() {
		System.out.println("session.getTranscation().commit()");
	}

	public void closeSession() {
		System.out.println("session.close()");
	}

	public void rollback(Throwable e) {
		System.out.println("出现异常......" + e.getMessage());
		System.out.println("session.rollback....");
	}

	@Around("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")
	public Object around(ProceedingJoinPoint point) {
		this.beginTranscation(point);
		try {
			Object obj = point.proceed();
			this.commit();
			return obj;
		} catch (Throwable t) {
			this.rollback(t);
		} finally {
			this.closeSession();
		}
		return null;
	}
}

 测试的结果和上面的测试结果一致。

4,在applicationContext.xml中使用aop命名空间配置代理。

xml中同样需要引入aop的命名空间。但这次不需要打开<aop:aspectj-autoproxy/>

同样,也不需要使用注解来声明切面类和使用advice声明方法。

xml中配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<aop:config>
		<aop:pointcut expression="execution(* com.xiaohui.aop.*ServiceImpl.*(..))" id="ponit"/>
		<!-- 配置切面 -->
		<aop:aspect ref="advice">
			<aop:before method="beginTranscation" pointcut-ref="ponit"/>
			<aop:after-throwing method="rollback" pointcut-ref="ponit" throwing="e"/>
			<aop:after-returning method="commit" pointcut-ref="ponit"/>
			<aop:after method="closeSession" pointcut-ref="ponit"/>
		</aop:aspect>
	</aop:config>
	<!-- 定义切面对象 -->
	<bean  id="advice" class="com.xiaohui.aop.TransCationService"/>
	<!-- 定义目标代理对象 -->
	<bean id="customerService" class="com.xiaohui.aop.CustomerServiceImpl"/>
</beans>


测试代码和ICustomerService接口以及CustomerServiceImpl类都和3中的一样,至于TransCationService,有无注解都没关系,因为在xml中 没有开启<aop:aspectj-autoproxy/>,所以不影响测试。

测试结果也和上面3中的一致。遇到同样的问题是在xml中配置<aop:after-throwing> 在<aop:after>之后时打印结果顺序和配置他的顺序一致,有所不解。 before无论位置在哪都先执行没问题。 

 

你可能感兴趣的:(spring,AOP,aspectj,面向切面编程)