Sping aop XML与注解方式

版权声明:Arno https://blog.csdn.net/yu342107056/article/details/86021268

spring的 aop:

aop:面向方面的编程,面向切面的编程,面向横切的编程

        方面就是新的业务功能,
        把新的方面的功能横切到老的业务的前或后

spring的aop的底层用的是动态代理

       如果目标类有接口就默认用jdk动态代理
       如果目标类没有接口就用默认cglib动态代理
       如果目标类有接口且不是final的,还想用cglib动态代理则需要单独配置

spring aop中定义个一套独有的表达式规则用于指定某些业务方法横切新功能

实现spring aop的步骤:

1.创建一个工程 工程是基础

2.导入jar包 spring的功能基础

spring的基础jar

   spring-context.jar
   spring-core.jar
   spring-beans.jar
   spring-expression.jar
   commons-logging.jar

spring aop的jar

  spring-aop.jar
  aspectjweaver-1.8.7.jar
  aopalliance-1.0.jar

3.创建java类

老的业务类:

     UserDao.java   UserDaoImpl.java
     UserService.java    UserServiceImpl.java

新的业务功能/新方面的功能/新的切面的功能,简称为方面/切面
由动态代理创建代理类用于调用接口方法,在接口方法中耦合切面和老业务功能

4.创建spring的配置文件

springaop的xml版本
springaop的annotation版本
无论是xml版本还是注解版本
     a.实例化老业务和新业务的对象(ioc)
     b.根据业务需求,设定对象的注入关系(di)
     c.写spring的特有的表达式规则,用于指定哪些方法需要横切新的功能<aop:XXX>

5.启动spring的容器读取spring的配置文件

6.从spring容器中取出对应的对象备用

xml版本:

新业务功能:

TransactionManager.java

 	/**
 	 * 此类是一个新功能类,用于给原有业务添加事务处理的功能
 	 * 符合单一职责原则,此类只做事务管理
 	 * @author Administrator
 	 *
 	 */
 	public class TransactionManager {
 		/**
 		 * 事务的开启
 		 * 在前置通知方法中得到目标的参数及其他信息
 		 */
 		public void begin(JoinPoint joinPoint){
 			System.out.print("事务开启");
 			System.out.println("目标方法的参数:"+joinPoint.getArgs()[0].getClass());
 			User user=(User)joinPoint.getArgs()[0];
 			System.out.println("目标方法的参数的值:"+user.getName()+"  "+user.getPassword());
 			System.out.println("目标方法的名称:"+joinPoint.getSignature().getName());
 			System.out.println("调用目标方法的目标对象"+joinPoint.getTarget().getClass());
 			
 		}
 		/**
 		 * 事务提交
 		 * 在后置通知中得到目标方法的参数及其他信息,还可以得到目标方法的返回值
 		 * 注意:如果有JoinPoint joinPoint参数一定要放参数的第一个位置
 		 */
 		public void commit(JoinPoint joinPoint,Object returnValue){
 			System.out.println("事务提交");
 			System.out.println("目标方法的返回值:"+returnValue);
 		}
 		/**
 		 * 事务的回滚
 		 * 在异常通知中得到目标方法的参数及其他信息,不能得到目标方法的返回值,但能得到异常信息
 		 */
 		public void rollback(JoinPoint joinPoint,Throwable ex){
 			System.out.println("事务回滚");
 			System.out.println("目标方法的异常信息:"+ex.getMessage());
 		}
 		public void finalMethod(JoinPoint joinPoint){
 			System.out.println("finally");
 		}
 		/**
 		 * 环绕通知,可以完全替换前4个通知
 		 * 用环绕通知有一个最大的好处,能够控制目标方法的执行
 		 * @param pjp
 		 * @return
 		 * @throws Throwable
 		 */
 		public Object around(ProceedingJoinPoint pjp) throws Throwable {
 		    Object retVal =null;
 		    try{
 		    	//前置通知
 		    	System.out.println("前置通知");
 		    	System.out.println("目标方法的参数:"+pjp.getArgs()[0].getClass());
 				User user=(User)pjp.getArgs()[0];
 				System.out.println("目标方法的参数的值:"+user.getName()+"  "+user.getPassword());
 				System.out.println("目标方法的名称:"+pjp.getSignature().getName());
 				System.out.println("调用目标方法的目标对象"+pjp.getTarget().getClass());
 		    	
 				retVal = pjp.proceed();//调用目标方法
 		    	//后置通知
 		    	System.out.println("后置通知");
 		    	System.out.println("目标方法的返回值:"+retVal);
 		    }catch(Exception e){
 		    	//异常通知
 		    	System.out.println("异常通知");
 		    	e.printStackTrace();
 		    }finally{
 		    	//最终通知
 		    	System.out.println("最终通知");
 		    }	    
 		    return retVal;
 		}
 	}

spring的配置文件 spring.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"
			xmlns:tx="http://www.springframework.org/schema/tx"
			xmlns:p="http://www.springframework.org/schema/p"
			xmlns:util="http://www.springframework.org/schema/util" 
			xmlns:context="http://www.springframework.org/schema/context"
			xmlns:mvc="http://www.springframework.org/schema/mvc"
			xsi:schemaLocation="
		        http://www.springframework.org/schema/beans
		        file:///c:/schema/beans/spring-beans-4.3.xsd
		        http://www.springframework.org/schema/aop 
		        file:///c:/schema/aop/spring-aop-4.3.xsd
		        http://www.springframework.org/schema/tx 
		        http://www.springframework.org/schema/tx/spring-tx.xsd
		        http://www.springframework.org/schema/util 
		        http://www.springframework.org/schema/util/spring-util.xsd
		        http://www.springframework.org/schema/context
		        file:///c:/schema/context/spring-context-4.3.xsd
		        http://www.springframework.org/schema/mvc
		        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
			
		   <!-- 老的业务需要实例化对象和注入 -->
		   <bean id="userDao" class="com.arno.dao.impl.UserDaoImpl"></bean>
		   <bean id="userService" class="com.arno.service.impl.UserServiceImpl">
		   	    <property name="userDao" ref="userDao"></property>
		   </bean>   
		   <!-- 新的业务/切面实例化对象和注入 /-->
		   <bean id="tm" class="com.arno.other.TransactionManager">
		   </bean>
		   
		   <!-- spring的独有的配置用于做横切 
		       <aop:config  代表的一个spring的aop配置
		       proxy-target-class="true"  告知spring用cglib生成代理对象
		       proxy-target-class="false" 告知spring用jdk生成代理对象
		   -->
		   <aop:config proxy-target-class="true">
		        <!-- <aop:aspect  代表一个切面,一个spring的aop配置可以包含很多个切面 -->
		        <aop:aspect id="myTransactionAspect" ref="tm">
		            <!-- <aop:pointcut 切点,且在方法上,由execution表达式限定哪些方法需要切面 ,可以写多个切点 -->
		        	<aop:pointcut id="businessService"
		        	              expression="execution(* com.arno.service..*.*(..))"/>
		        	<aop:pointcut id="businessService1"
		        	              expression="execution(* com.arno.service..*.*(..)) and args(user)"/>
		        	<!-- 前置通知/前置功能 
		        		aop:before 切在前面
		        		pointcut-ref   切在哪个方法上
		        		method:切的什么功能
		        		 arg-names="user"  参数
		        	-->
		<!--         	<aop:before -->
		<!-- 		        pointcut-ref="businessService" -->
		<!-- 		        method="begin"/> -->
				    <!-- 后置通知/后置功能 -->
		<!-- 		    <aop:after-returning -->
		<!-- 		        pointcut-ref="businessService" -->
		<!-- 		        method="commit"  -->
		<!-- 		        returning="returnValue"/> -->
				    <!-- 异常通知/异常功能 -->
		<!-- 		    <aop:after-throwing -->
		<!-- 		        pointcut-ref="businessService" -->
		<!-- 		        method="rollback" -->
		<!-- 		        throwing=""/> -->
				    <!-- 最终通知 -->
		<!-- 		    <aop:after -->
		<!-- 		        pointcut-ref="businessService" -->
		<!-- 		        method="finalMethod"/> -->
		            <!-- 环绕通知 -->
		            <aop:around
				        pointcut-ref="businessService"
				        method="around"/>
		        </aop:aspect>
		        
		   </aop:config>  
		</beans>

有关于spring aop中的那些让你发狂的几个概念解释:

切面 aspect:
			切面是一个类,是一个新业务的功能类;
            切面类中有很多个通知方法
        	比如:TransactionManager.java
连接点 joinpoint:
			连接点就是用代理对象调用目标方法的那句话
           只有用代理对象调用了目标方法,才有可能把新的业务横切到老的业务上
           没有连接点,老的业务和新的业务永远不会耦合
        比如:userService.addUser(new  User());//userService是代理对象
通知 advice:
			通知就是一些功能方法,每个功能就是一个通知,通知存在于切面类中
            可以有很多个通知的方法
     比如: begin方法  前置通知
                commit方法   后置通知
                rollback方法   异常通知
                finalMethod方法   最终通知
                around方法             环绕通知
      环绕通知能完全替代上面的4个通知,环绕通知能控制目标方法的执行
      环绕通知和前4中不要同时使用
切入点 pointcut:
				切入点是把切面中的通知,切在什么地方,一般指的老业务中的方法上
                切入点是且在方法上
                切入点一般要配合execution表达式和within表达式一同使用
execution表达式
        修饰符   返回值  包名.类名.方法名(参数) throws 异常类型
spring的官方案例:
			§ the execution of any public method:匹配任意公有方法
			公有的 任意返回类型,任意包任意类,任意方法名称,任意参数
			execution(public * *(..))
			§ the execution of any method with a name beginning with "set":
			任意返回类型,任意包,任意类,任意以set开头的方法,任意参数
			execution(* set*(..))
			§ the execution of any method defined by the AccountService interface:
			任意返回类型,com.xyz.service.AccountService接口中的任意方法,任意参数
			execution(* com.xyz.service.AccountService.*(..))
			§ the execution of any method defined in the service package:
			任意返回类型,com.xyz.service包下任意类或接口,任意方法,任意参数
			execution(* com.xyz.service.*.*(..))
			§ the execution of any method defined in the service package or a sub-package:
			任意返回类型,com.xyz.service包及其子包中的任意类,任意方法,任意参数
			execution(* com.xyz.service..*.*(..))
winthin表达式:
spring官方案例:
			any join point (method execution only in Spring AOP) within the service package:
		   com.xyz.service包中的任意接口或类
		   within(com.xyz.service.*)
			any join point (method execution only in Spring AOP) within the service package or a sub-package:
			com.xyz.service包及其子包中的任意类
		   within(com.xyz.service..*)
总结execution控制粒度细,控制到方法 winthin控制粒度粗,控制类

目标对象 targetObject:是老业务类的对象 比如UserServiceImpl的对象

aop代理:

用spring的aop的底层用的是动态代理,aop代理就是用springaop生成的代理对象
织入weaving:把新的功能织入到老的功能上去,引申义就是横切/切入

springaop的注解版本:

TransactionManager.java

/**
 * 此类是一个新功能类,用于给原有业务添加事务处理的功能
 * 符合单一职责原则,此类只做事务管理
 * @author Administrator
 *
 */
@Component("tm")
@Aspect
public class TransactionManager {
	@Pointcut("execution(* com.arno.service..*.*(..))")
	public void myTransactionPointCut() {}
	/**
	 * 事务的开启
	 * 在前置通知方法中得到目标的参数及其他信息
	 */
	//@Before(value="myTransactionPointCut()")
	public void begin(JoinPoint joinPoint){
		System.out.print("事务开启");
		System.out.println("目标方法的参数:"+joinPoint.getArgs()[0].getClass());
		User user=(User)joinPoint.getArgs()[0];
		System.out.println("目标方法的参数的值:"+user.getName()+"  "+user.getPassword());
		System.out.println("目标方法的名称:"+joinPoint.getSignature().getName());
		System.out.println("调用目标方法的目标对象"+joinPoint.getTarget().getClass());
		
	}
	/**
	 * 事务提交
	 * 在后置通知中得到目标方法的参数及其他信息,还可以得到目标方法的返回值
	 * 注意:如果有JoinPoint joinPoint参数一定要放参数的第一个位置
	 */
	//@AfterReturning(value="myTransactionPointCut()",returning="returnValue")
	public void commit(JoinPoint joinPoint,Object returnValue){
		System.out.println("事务提交");
		System.out.println("目标方法的返回值:"+returnValue);
	}
	/**
	 * 事务的回滚
	 * 在异常通知中得到目标方法的参数及其他信息,不能得到目标方法的返回值,但能得到异常信息
	 */
	//@AfterThrowing(value="myTransactionPointCut()",throwing="ex")
	public void rollback(JoinPoint joinPoint,Throwable ex){
		System.out.println("事务回滚");
		System.out.println("目标方法的异常信息:"+ex.getMessage());
	}
	//@After(value="myTransactionPointCut()")
	public void finalMethod(JoinPoint joinPoint){
		System.out.println("finally");
	}
	/**
	 * 环绕通知,可以完全替换前4个通知
	 * 用环绕通知有一个最大的好处,能够控制目标方法的执行
	 * @param pjp
	 * @return
	 * @throws Throwable
	 */
	@Around(value="myTransactionPointCut()")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
	    Object retVal =null;
	    try{
	    	//前置通知
	    	System.out.println("前置通知");
	    	System.out.println("目标方法的参数:"+pjp.getArgs()[0].getClass());
			User user=(User)pjp.getArgs()[0];
			System.out.println("目标方法的参数的值:"+user.getName()+"  "+user.getPassword());
			System.out.println("目标方法的名称:"+pjp.getSignature().getName());
			System.out.println("调用目标方法的目标对象"+pjp.getTarget().getClass());
	    	
			retVal = pjp.proceed();//调用目标方法
	    	//后置通知
	    	System.out.println("后置通知");
	    	System.out.println("目标方法的返回值:"+retVal);
	    }catch(Exception e){
	    	//异常通知
	    	System.out.println("异常通知");
	    	e.printStackTrace();
	    }finally{
	    	//最终通知
	    	System.out.println("最终通知");
	    }	    
	    return retVal;
	}
}

spring.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"
			xmlns:tx="http://www.springframework.org/schema/tx"
			xmlns:p="http://www.springframework.org/schema/p"
			xmlns:util="http://www.springframework.org/schema/util" 
			xmlns:context="http://www.springframework.org/schema/context"
			xmlns:mvc="http://www.springframework.org/schema/mvc"
			xsi:schemaLocation="
		        http://www.springframework.org/schema/beans
		        file:///c:/schema/beans/spring-beans-4.3.xsd
		        http://www.springframework.org/schema/aop 
		        file:///c:/schema/aop/spring-aop-4.3.xsd
		        http://www.springframework.org/schema/tx 
		        http://www.springframework.org/schema/tx/spring-tx.xsd
		        http://www.springframework.org/schema/util 
		        http://www.springframework.org/schema/util/spring-util.xsd
		        http://www.springframework.org/schema/context
		        file:///c:/schema/context/spring-context-4.3.xsd
		        http://www.springframework.org/schema/mvc
		        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
		   <!-- 解析@Controller,@Service,@Repository@Component
		            @Resource @Autowired@Qualifier
		    -->
		   <context:component-scan base-package="com.arno.dao"></context:component-scan>
		   <context:component-scan base-package="com.arno.service"></context:component-scan>
		   <context:component-scan base-package="com.arno.other"></context:component-scan>
		   
		   <!-- 解析有关于aop的注解
		   @Aspect @Pointcut 
		   @Before @AfterReturning  @AfterThrowing @After 
		   @Around
		    -->
		   <aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
		      
		</beans>
一个需求或目的:
老的业务
新的业务
新的业务横切到老的业务上

代理模式能够实现

静态代理:

 用静态代理类方式把老业务和新的业务耦合在一起
 在编译期间就确认老业务和新业务的耦合关系

动态代理:

jdk的动态代理
     动态生成代理类(由jdk底层来创建,程序员是看不见的)
     用动态生成的代理类和InvocationHandler接口一起做到老业务和新业务的耦合
     代理类是在运行期间才出现的,所以叫做动态代理
      代理类和目标类是兄弟关系
cglib动态代理
     动态生成代理类(由asm.jar来创建,程序员是看不见的)
     用动态生成的代理类和MethodInterceptor接口一起做到老业务和新业务的耦合
     代理类是在运行期间才出现的,所以叫做动态代理
     代理类是目标类的子类

动态代理实现,会把新的业务功能全部横切到老业务的方法的前或后
spring aop解决了把新业务横切到老业务的部分方法的前或后

开发效率
spring aop > 动态代理 >静态代理
执行效率
静态代理 > 动态代理 >spring aop

猜你喜欢

转载自blog.csdn.net/yu342107056/article/details/86021268
今日推荐