spring_day02-spring-aop编程与aspectj编程

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑和责任(例如事务管理,日志管理,权限管理等封装起来),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么springaop就会使用jdk动态代理去创建代理对象;而对于那些没有实现接口的对象,就无法使用jdk动态代理,转而使用cglib动态代理,生成一个被代理的对象的子类来作为管理。
在项目的开发中我们也可以使用aspectj,springAOP中已经集成了aspectj

1. spring jdk代理(手动设置代理的方法)

通过自定义配置获取代理对象 进行调用方法
1. 目标类 = 接口+实现类

 	public interface UserService {
 		public void addUser();
 	}
   	public class UserServiceImpl implements UserService{
   	@Override
   	public void addUser() {
   		// TODO Auto-generated method stub
   		System.out.println("add User sofency");
   	}
  1. 切面类(增强方法)即在上面的每一个方法前执行前置方法 在后面执行后置方法
public class InsertDemo{
	public void before() {
		System.out.println("插入前");
	}
	public  void after() {
		System.out.println("插入后");
	}
 		}
  1. 生成代理对象的类
    import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//整合目标类和切面类
public class MyBeanFactory {
   public static UserService createService() {
   	//目标类
   	final UserService userService = new UserServiceImpl();
   	//切面类
   	final InsertDemo insert = new InsertDemo();
   	//必须使用final修饰
   	/**
   	 * 3.代理类 将目标类和切面类 结合   
   	 *           Proxy.newProxyInstance
   	 *           参数1: loader  类加载器  动态代理类  运行时创建 任何类都需要将其加载到内存
   	 *           一般情况下  当前类.class.getClassLoader();  也可以目标实例类
   	 * 			 参数2:interfaces  代理类需要实现的所有接口
   	 * 				方式1:目标类实例.getClass().getInterfaces()  只能获得当前类中的接口  不能获得父元素的接口
   	 * 			 参数3:InvocationHandler  处理类 接口  必须进行实现类 一般采用您名内部类  
   	 * 			 提供invoke()方法  代理类中的每个方法执行时 都将调用一次invoke()
   	 *            参数31  Obejct 代理对象
   	 *            参数32  Method  代理对象当前执行的方法的描述对象(反射)
   	 *            			过滤时 method.getName()  
   	 *            参数33  object[] args 方法实际参数
   	 */
   	UserService proxyUser = (UserService)Proxy.newProxyInstance(
   			MyBeanFactory.class.getClassLoader(),//也可以是目标类的类加载器
   			userService.getClass().getInterfaces(),//目标类里面的所有接口
   			new InvocationHandler() {
   				@Override
   				public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
   					insert.before();//前方法
   					Object object = method.invoke(userService);//生成代理对象
   					insert.after();//后置方法
   					return object;//返回代理对象赋值给proxyUser
   				}
   			});
   	return proxyUser;//返回代理对象
   }
}
  1. 测试类
import org.junit.Test;
public class testDemo {
  @Test
  public void  test() {
  	UserService userService = MyBeanFactory.createService();//获取代理对象
  	userService.addUser();//调用方法
  }
}

2. spring cglib代理的设置

cglib的设置和jdk动态代理的本质区别就是 jdk代理是通过接口的方式实现代理对象的生成
而cglib是通过继承的方式实现动态代理的生成。

  1. 目标类 = 实体类
public class UserServiceImpl {
	public void addUser() {
		System.out.println("cglib addUser");
	}
	public void deleteUser() {
		System.out.println("cglib deleteUser");
	}
}
  1. 切面类
public class MyAspect {
	public void before() {
		System.out.println("前方法");
	}
	public void after() {
		System.out.println("后方法");
	}
  }
  1. 代理对象生成类
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class MyBeanFactory {
	public static UserServiceImpl createProxy() {
		//目标类
		final UserServiceImpl  user = new UserServiceImpl();
		//切面类
		final MyAspect myAspect = new MyAspect();
		//核心类  增强
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(user.getClass());//设置将目标类设置为父类  让子类继承父类目标类)
		//设置回调函数  MethodInterceptor 接口等效于jdk  InvocationHandler接口
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			//intercep拦截的意思  将方法拦截后再进行增强
			public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
				//上面的参数和jdk动态代理一样
				myAspect.before();//增强前方法
				Object object = method.invoke(user, args);
				myAspect.after();//增强后方法
				return object;//将子类返回到enhance里面
			}
		});
		UserServiceImpl userProxy = (UserServiceImpl) enhancer.create();//调用create方法创建(子类)代理类
		return userProxy;
	}
}
  1. 测试类
public class testDemo {
	public static void main(String[] args) {
		UserServiceImpl userService = MyBeanFactory.createProxy();
		userService.addUser();
		userService.deleteUser();
	}
}

3.基于xml的代理对象生成

  1. 目标类=接口+实现类
    和上面的一样就不再写了

  2. 切面类

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 切面类确定通知 需要实现不同接口 接口就是规范 从而确定方法名称
* 采用环绕通知 MethodIntercpetor
* @author sofency
*
*/
public class MyAspect implements MethodInterceptor{
   @Override
   public Object invoke(MethodInvocation method) throws Throwable {
   	System.out.println("前方法");
   	//手动执行方法  method就是拦截到的方法
   	Object obj = method.proceed();
   	//切入点  即增强的方法  切面类和目标类整合到一起让spring去整合	
   	System.out.println("后方法");
   	return null;
   }
}
  1. 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.xsd">
	<!-- 目标类 -->
	<bean id="UserServiceId" class="com.sofency.factory_bean.UserService"></bean><!--接口类-->
	<bean id="userServiceImplId" class="com.sofency.factory_bean.UserServiceImpl"></bean><!--实现类-->
	
	<!-- 创建切面类 -->
	
	<bean id="myAspectId" class="com.sofency.factory_bean.MyAspect"></bean>
	
	<!-- 创建代理类 -->
	<!-- 用户创建代理工厂Bean  
     引入代理工厂必须实现代理对象bean

     生成特殊的代理对象 -->
	
	<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
 	    <!-- 确定接口类 通过array可以设置多个值   
 	    只有一个值时   value=""也可以用value 
			注意下面属性的name属性的值要一定 不能改变
		-->
		<property name="interfaces" ref="UserServiceId"></property>
		<!-- target :确定目标类   -->
		<property name="target" ref="userServiceImplId"></property>
		<!-- 切面类 -->
		<property name="interceptorNames" value="myAspectId"></property>
		<!-- 强制使用cglib -->
		<property name="optimize" value="true"></property>
	</bean>
</beans>

4.测试类

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testDemo {
	@Test
	public  void test() {
		String xmlPath="com/sofency/factory_bean/beans.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		UserService user = (UserService)applicationContext.getBean("proxyServiceId");
		user.addUser();	
	}
}

4. 基于xml的AspectJ编程

需要依赖的jar包
spring-aop-4.1.3.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
1.目标类省略 和前面的一样
2. 切面类的配置

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectjXML {
	//前置通知
	public void before(JoinPoint joinPoint) {
		System.out.println("前置方法"+joinPoint.getSignature().getName());
	}
	//后置通知
	public void after(Object object) {//有参数的情况
//		System.out.println("后置方法"+joinPoint.getSignature().getName());
		System.out.println("后置方法"+object);
	}
	//环绕通知 必须是ProceedJoinPoint 类型 try
	public Object MyAround(ProceedingJoinPoint joinPoint) throws Throwable{
		
		System.out.println("前置通知");
		Object obj = joinPoint.proceed();//将返回值赋给Object
		System.out.println("后置通知"+obj);
		return obj;
	}
	//抛出异常通知  catch
	public void MyThrowable(JoinPoint joinPoint,Throwable e) {
		System.out.println("捕获到异常");
		System.out.println(e.getMessage());
	}
	//最终通知finally
	public void MyFinally() {
		System.out.println("最终通知");	
	}
}

3.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.xsd">
 		
 	<!--目标类-->	
 	<bean id="UserServiceId" class="com.sofency.AspectjXML.UserServiceImpl"></bean>
 	<!--切面类-->
 	<bean id="MyAspectId" class="com.sofency.AspectjXML.MyAspectjXML"></bean>
     <!--aop配置-->
     <aop:config>
		<aop:aspect ref="MyAspectId" >
		<!-- 切入点引入  即要增强的方法  公共声明      -->
			<aop:pointcut expression="execution(* com.sofency.AspectjXML.UserServiceImpl.*(..))" id="MyPointCut"/>
		<!-- 	1.前置通知的方法的配置
			method 方法 即方法名字  
			pointcut-ref  对里面的方法的前置方法都执行  method里面的方法
			<aop:before method="before" pointcut-ref="MyPointCut"/>
			前置通知可以或的方法的名字
				 <aop:after method="after" pointcut-ref="MyPointCut"/>
			//带返回值后置通知
			    后置方法或得方法的返回值  如何获取哪  通过后置方法的第二个参数获取
			    public void after(JoinPoint joinPoint,Object object)
			             参数1:连接点描述
			             参数2: 返回值
			    xml的配置
				<aop:after-returning method="after" returning="object(与方法的返回值一样)" pointcut-ref="MyPointCut"/>
				环绕通知
				使用<aop:around method="" point-ref="MyPointCut"></aop:around>
			 -->
			
			<aop:around method="MyAround" pointcut-ref="MyPointCut"></aop:around>
			<!-- 异常通知
				public void MyThrowable(JoinPoint joinPoint,Throwable e);
				method设置异常的捕获的方法
				pointcut-ref 切面类的id
				throwing 抛的位置
				 -->
			
			<aop:after-throwing method="MyThrowable" pointcut-ref="MyPointCut" throwing="e"/>
			
		<!-- 	最终通知 -->
			<aop:after method="MyFinally" pointcut-ref="MyPointCut"/>
		</aop:aspect>
	</aop:config>
	
</beans>

注意 前置通知是before
后置通知是after-returning
还有抛出异常通知 after-throwing
最终通知是after
类似于try catch finally
环绕通知 可以替代掉前置通知和后置通知

4.测试类(测试的时候将上面xml中的五种通知一个一个测试 )

public class testAspectjXML {
	@Test
	public void testDemo() {
		String xmlPath="com/sofency/AspectjXML/beans.xml";
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
		UserService userService = (UserService) applicationContext.getBean("UserServiceId");
		userService.addUser();
		userService.deleteUser();
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值