您的位置:首页 > 编程语言 > Java开发

Spring之AOP(面向切面编程)

2017-11-05 17:23 816 查看
一、概念:

         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-
4000
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: $P
bd37
roxy13 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讲解
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: