Spring之AOP(面向切面编程)

一、概念:

         Aop编程:

                   关注点代码与业务代码分离!(jdk/cglib代理)

         关注点:

                   重复执行的代码, 也叫关注点代码!

public void add(User user) { 
		Session session = null; 
		Transaction trans = null; 
		try { 
			session = HibernateSessionFactoryUtils.getSession();   【关注点代码】
			trans = session.beginTransaction();                    【关注点代码】
			 
			session.save(user);     // 业务代码
			 
			trans.commit();                                        【关注点代码】
		} catch (Exception e) { 
			e.printStackTrace(); 
			if(trans != null){ 
				trans.rollback(); 
			} 
		} finally{ 
			HibernateSessionFactoryUtils.closeSession(session); 
		} 
   } 

              切面:

                   关注点代码形成的类,就叫做切面

                   SpringAop编程,也叫面向切面编程!

                   Aop: Aspect Object Programming 面向切面编程!     

    

                   哪些是切面?:事务、权限控制、日志…


         切入点表达式:

                   拦截指定方法,给给指定方法所在的类,生成代理对象!

                   Spring在初始化容器的时候,会根据切入点表达式的规则,会符合拦截规则的方法所在的类生成代理对象!


二、使用Aop注解方式开发步骤:

1. 引入Aop相关jar文件

     (aspectj是在spring之前,面向切面开发的公用组件)

aopalliance.jar              spring-framework-2.5.6\lib\aopalliance

aspectjrt.jar                 spring-framework-2.5.6\lib\aspectj

aspectjweaver.jar            spring-framework-2.5.6\lib\aspectj

spring-aop-3.2.5.RELEASE.jar  Spring3.2源码】

2.引入Aop名称空间

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="cn.itcast.e_aop_anno"></context:component-scan>
	<!-- 开启aop注解 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	
</beans>    

3. 开启Aop注解

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

4. 使用Aop相关注解

@Aspect        指定一个类为切面类

                                (切面类也需要实例化)

                                (切面类中的方法,也叫做通知)

@Before           前置通知  【在执行目标对象方法之前执行】

@After            后置通知  【在执行目标对象方法之后执行】

@AfterReturning   返回后通知 【在执行目标对象方法结束后执行, 出现异常不执行】

@AfterThrowing    异常通知  【在执行目标对象方法出现异常时候执行】

@Around           环绕通知  【环绕目标方法执行】

@Pointcut         定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可!)

如:

public class TransactionAop {
	
	// 定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可)
	@Pointcut("execution(* spring_ioc.g_aop_anno.UserDao.*(..))")
	public void pointcut_(){
	}

	//【前置通知】
	// 在执行业务方法,之前执行
	@Before("pointcut_()")
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}

Spring生成代理对象的过程?

            1. 创建容器对象的时候,根据"切入点表达式"拦截的类,生成代理对象。

            2. 如果目标对象有实现接口,使用jdk代理!

            3. 如果目标对象没有实现接口,使用cglib代理!

            4. 从容器获取代理后的对象。

            5. 执行代理对象的方法,在运行时期动态植入"切面"类中的"通知"!


四、使用AopXML方式开发步骤:

         1.引入Aop 相关jar文件

         2. bean.xml  引入Aop名称空间

         3. Aop配置

<?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:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- UserDao实例加入Spring的IOC容器 -->
	<bean id = "userDao" class = "spring_ioc.h_aop_xml.UserDao"></bean>
	
	<!-- 切面类实例加入容器 -->
	<bean id = "transactionAop" class = "spring_ioc.h_aop_xml.TransactionAop"></bean>
	
	<!-- OrderDao实例加入容器 -->
	<bean id = "orderDao" class = "spring_ioc.h_aop_xml.OrderDao"></bean>
	
	<!-- AOP的相关配置 -->
	<aop:config>
		<!-- 切入点表达式的定义 -->
		<aop:pointcut expression="execution(* spring_ioc.h_aop_xml.UserDao.*(..))" id="pt"/>
	
		<!-- 切面配置 -->
		<aop:aspect ref = "transactionAop">
			<!-- 环绕通知配置 -->
			<aop:around method="arroud" pointcut-ref = "pt"/>
			
			<!-- 前置通知的配置:在目标方法之前执行 -->
			<aop:before method="beginTransaction" pointcut-ref = "pt"/>
			
			<!-- 后置通知的配置:在目标方法之后执行 -->
			<aop:after method="commit" pointcut-ref = "pt"/>
			
			<!-- 返回后通知的配置:在执行目标方法结束后执行,出现异常不会执行 -->
			<aop:after-returning method="afterReturing" pointcut-ref = "pt"/>
			
			<!-- 异常通知的配置:在执行目标方法的时候出现异常执行 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref = "pt"/>
		</aop:aspect>	
		
	</aop:config>
	
</beans>    
  

五、 切入点表达式语法详解

切入点表达式:

         拦截指定的类,生成代理对象!

expression="execution(* cn.itcast.g_execution.UserDao.save(..))"

execution(

modifiers-pattern?                拦截的方法的访问修饰符(? 表示0或多,可以省略!)

ret-type-pattern                  方法返回类型,必须指定(*)

declaring-type-pattern?           拦截的方法所在的类

name-pattern(param-pattern)       拦截的方法(以及方法的参数列表)

throws-pattern?)   方法声明的异常

总结:拦截,一定要指定到方法!

		<!-- 切入点表达式定义 -->
		
		<!-- 1. 拦截指定的方法(通用)  -->
		<!--<aop:pointcut expression="execution(* cn.itcast.g_execution.UserDao.save(..))" id="pt"/>-->
		
		<!-- 2. 拦截指定的类下所有的方法 -->
		<!--<aop:pointcut expression="execution(* cn.itcast.g_execution.UserDao.*(..))" id="pt"/>-->
		
		<!-- 3. 拦截指定包下所有的类的所有方法 -->
		<!--<aop:pointcut expression="execution(* cn.itcast.g_execution.*.*(..))" id="pt"/>-->
		
		<!-- 3. 拦截指定包,以及其子包下所有类的所有方法 -->
		<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
		
		<!-- 5. 拦截所有的public方法 -->
		<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
		
		<!-- 6. 拦截所有的包含save方法 -->
		<!--<aop:pointcut expression="execution(* *save*(..))" id="pt"/>-->
		
		<!-- 7. 拦截UserDao.save()方法与OrderDao.save() -->
		<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) || execution(* cn..OrderDao.save(..))" id="pt"/>-->
		<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) or execution(* cn..OrderDao.save(..))" id="pt"/>-->
		
		<!-- 8. 不拦截UserDao.save()方法 -->
		<!--<aop:pointcut expression="!execution(* cn..UserDao.save(..))" id="pt"/>-->
		<!--<aop:pointcut expression=" not execution(* cn..UserDao.save(..))" id="pt"/>-->
		
		<!-- 9. 拦截UserDao.save()同时拦截OrderDao.save() -->
		<!-- 注意: 这种很少用,一般都是或者的关系即: || 、 or  -->
		<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) and execution(* cn..OrderDao.save(..))" id="pt"/>-->
		<aop:pointcut expression="execution(* cn..UserDao.save(..)) && execution(* cn..OrderDao.save(..))" id="pt"/>

六、Aop编程之注解方式代码案例:


package spring_ioc.g_aop_anno;
public interface IUserDao {
	void save();
}

package spring_ioc.g_aop_anno;
import org.springframework.stereotype.Repository;
@Repository	// 把对象加入ioc容器
public class UserDao implements IUserDao{
	public void save() {
		System.out.println("保存...");
	}
}

package spring_ioc.g_aop_anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

// 重复代码
@Component("aop")
@Aspect      // 指定一个类为切面类
public class TransactionAop {
	
	// 定义一个切入点表达式变量  (后面使用这个切入点表达式的时候,直接引用方法名即可)
	@Pointcut("execution(* spring_ioc.g_aop_anno.UserDao.*(..))")
	public void pointcut_(){
	}

	//【前置通知】
	// 在执行业务方法,之前执行
	@Before("pointcut_()")
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}
	
	//【后置通知】
	// 在执行业务方法,之后执行
	@After("pointcut_()")
	public void commit() {
		System.out.println("[后置通知] 提交事务..");
	}
	
	// 【返回后通知】   在执行目标方法结束后执行, 出现异常不会执行
	@AfterReturning("pointcut_()")
	public void afterReturing(){
		System.out.println("[返回后通知]");
	}
	
	// 【异常通知】   在执行目标方法的时候出现异常执行
	@AfterThrowing("pointcut_()")
	public void afterThrowing(){
		System.out.println("[异常通知]");
	}
	
	// 【环绕通知】 会环绕目标方法执行
	@Around("pointcut_()")
	public void arroud(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("[环绕前:]");
		pjp.proceed();    			   // 执行目标方法
		System.out.println("[环绕后:]");
	}

}

package spring_ioc.g_aop_anno;
import org.springframework.stereotype.Repository;
@Repository
public class OrderDao {
	public void save(){
		System.out.println("订单已保存!");
	}
}

<?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:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="spring_ioc.g_aop_anno"></context:component-scan>
	<!-- 开启aop注解 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	
</beans> 

package spring_ioc.g_aop_anno;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
	 private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass());
	 // jdk代理(注意:目标类UserDao实现IUserDao接口!)
	@Test
	public void testApp() throws Exception {
		//从Spring的IOC容器中获取对象,使用接口接收!
		IUserDao userDao = (IUserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
		
		/*//使用接口接收会报错:java.lang.ClassCastException: $Proxy13 cannot be cast to spring_ioc.g_aop_anno.UserDao
		UserDao userDao = (UserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();*/
		
		/*总结:在Spring的AOP编程中,符合切入点表达式的目标类,如果目标类有实现接口,从容器中获取目标对象的时候一定要通过接口接收否则会报错!
		 * 
		 */
	}
	
	
	//cglib代理(注意:目标类UserDao未实现IUserDao接口!)
	@Test
	public void testApp2(){
		UserDao userDao = (UserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
	}
	
	
	//测试:没有被切入点表达式拦截的类(OrderDao)是不会生成代理对象的!
	@Test
	public void testApp3(){
		OrderDao orderDao = (OrderDao)ac.getBean("orderDao");
		orderDao.save();
	}
}

testApp():

testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)


testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)



七、Aop编程之xml方式代码案例:

package spring_ioc.h_aop_xml;
public interface IUserDao {
	void save();
}

package spring_ioc.h_aop_xml;
public class UserDao implements IUserDao{
	public void save() {
		System.out.println("保存...");
	}
}

package spring_ioc.h_aop_xml;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
public class TransactionAop {
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}
	
	public void commit() {
		System.out.println("[后置通知] 提交事务..");
	}
	
	public void afterReturing(){
		System.out.println("[返回后通知]");
	}
	
	public void afterThrowing(){
		System.out.println("[异常通知]");
	}
	
	public void arroud(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("[环绕前:]");
		pjp.proceed();    			   // 执行目标方法
		System.out.println("[环绕后:]");
	}

}

package spring_ioc.h_aop_xml;
public class OrderDao {
	public void save(){
		System.out.println("订单已保存!");
	}
}

<?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:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- UserDao实例加入Spring的IOC容器 -->
	<bean id = "userDao" class = "spring_ioc.h_aop_xml.UserDao"></bean>
	
	<!-- 切面类实例加入容器 -->
	<bean id = "transactionAop" class = "spring_ioc.h_aop_xml.TransactionAop"></bean>
	
	<!-- OrderDao实例加入容器 -->
	<bean id = "orderDao" class = "spring_ioc.h_aop_xml.OrderDao"></bean>
	
	<!-- AOP的相关配置 -->
	<aop:config>
		<!-- 切入点表达式的定义 -->
		<aop:pointcut expression="execution(* spring_ioc.h_aop_xml.UserDao.*(..))" id="pt"/>
	
		<!-- 切面配置 -->
		<aop:aspect ref = "transactionAop">
			<!-- 环绕通知配置 -->
			<aop:around method="arroud" pointcut-ref = "pt"/>
			
			<!-- 前置通知的配置:在目标方法之前执行 -->
			<aop:before method="beginTransaction" pointcut-ref = "pt"/>
			
			<!-- 后置通知的配置:在目标方法之后执行 -->
			<aop:after method="commit" pointcut-ref = "pt"/>
			
			<!-- 返回后通知的配置:在执行目标方法结束后执行,出现异常不会执行 -->
			<aop:after-returning method="afterReturing" pointcut-ref = "pt"/>
			
			<!-- 异常通知的配置:在执行目标方法的时候出现异常执行 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref = "pt"/>
		</aop:aspect>	
		
	</aop:config>
	
</beans>  

package spring_ioc.h_aop_xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
	 private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass());
	 // jdk代理(注意:目标类UserDao实现IUserDao接口!)
	@Test
	public void testApp() throws Exception {
		//从Spring的IOC容器中获取对象,使用接口接收!
		IUserDao userDao = (IUserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
		
		/*//使用接口接收会报错:java.lang.ClassCastException: $Proxy13 cannot be cast to spring_ioc.g_aop_anno.UserDao
		UserDao userDao = (UserDao)ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();*/
		
		/*总结:在Spring的AOP编程中,符合切入点表达式的目标类,如果目标类有实现接口,从容器中获取目标对象的时候一定要通过接口接收否则会报错!
		 * 
		 */
	}
	
	
	//cglib代理(注意:目标类UserDao未实现IUserDao接口!)
	@Test
	public void testApp2(){
		UserDao userDao = (UserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());
		userDao.save();
	}
	
	
	//测试:没有被切入点表达式拦截的类(OrderDao)是不会生成代理对象的!
	@Test
	public void testApp3(){
		OrderDao orderDao = (OrderDao)ac.getBean("orderDao");
		orderDao.save();
	}
}

tespApp():


testApp2():(注意:UserDao实现IUserDao要取消,cglib代理目标类不能实现接口!)


testApp3():(切入点表达式未拦截OrderDao类,所以切面类没有起作用,仅仅输出OrderDao中的save()方法!)



参考资料:传智播客袁杰老师SpringAop讲解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值