AOP 概述
什么是 AOP
AOP:全称是 Aspect Oriented Programming 即:面向切面编程。
AOP技术是对OOP技术的一种延伸,AOP是面向纵向,OOP是面向横向。简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的
基础上,对我们的已有方法进行增强。
AOP 的作用以及优势
作用:在程序运行期间,不修改源码对已有方法前面和后面进行增强。
优势:减少重复代码、提高开发效率、维护方便
AOP实现方式
使用动态代理技术
基于xml方式的aop配置
实现步骤如下
导入spring-aop jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
下面几个是必须要导的包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
需要增强方法的StudentDao类,和写增强方法的Userlogger类
package com.ithema.jdbc.dao;
public class StudentDao {
//在执行add方法之前执行日志
public int add(int a,int b){
System.out.println("执行了add方法");
return a+b;
}
public int jian(int a,int b){
System.out.println("执行了减法");
return a-b;
}
/* public void testexe(){
int i=10/0;
}*/
}
package com.ithema.jdbc.aop; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import java.util.Arrays; /** * 切面: * 前置增强、后置增强、异常增强、最终增强、环绕增强 * * Spring aop完全是用动态代理实现的 * 但是动态代理又有两种,jdkk代理,cglib代理(更强大一些) * spirng aop:默认使用jdk代理,但是你的目标类没接口,会自动切换到cglib代理 */ public class UserLogger { //在目标方法调用之前执行该方法 Logger logger=Logger.getLogger(UserLogger.class); public void before(JoinPoint jp){ logger.info("前置增强,目标类名"+jp.getTarget()+"里面的"+jp.getSignature().getName()+ "参数为:"+ Arrays.toString(jp.getArgs())); } //在目标方法调用之后执行该方法 //当方法抛出异常就不会执行了,所有该方法不适合做日志收集,因为发生了异常不执行该方法无法将日志保存下来 //所有后置增强用的很少 public void after(JoinPoint jp,Object result){ logger.info("后置增强,执行完类"+jp.getTarget()+"里面的"+jp.getSignature().getName()+ "方法,结果为:"+ result); } //在方法抛出异常后就会增强、没有异常就不会执行、如果用户try-catch,也不会增强 //执行错误的话,非常适合该增强(异常增强) //Java异常体系,根类是Throwable public void afterThorwing(JoinPoint jp,Throwable e){ logger.info("异常增强,执行类"+jp.getTarget()+"里面的"+jp.getSignature().getName()+ "方法抛出异常:"+e.getMessage()); } //最终增强:无论是否有异常,都会执行,类似与finally,可以或者是认为是后置增强的升级版 public void finalafter(JoinPoint jp){ logger.info("最终增强:执行完类"+jp.getTarget()+"里面的"+jp.getSignature().getName()); } //环绕增强,集合了前置添加后置,并且更加强大、当执行方法发生异常后面的就不执行了 //1、可以修改方法的参数 2、也可以修改方法的返回值( return result) public Object around(ProceedingJoinPoint jp)throws Throwable{ Object [] args=jp.getArgs();//获取方法参数 //可以修改方法的参数 //args = new Object[]{20,1}; logger.info("环绕增强:方法为:"+jp.getArgs()); Object result=jp.proceed();//执行目标方法 //也可以修改方法的返回值 //return 100; return result; } }
配置aop约束到配置文件中,并实现ioc配置
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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就是一个对象>
<bean id="studentDao" class="com.ithema.jdbc.dao.StudentDao">
</bean>
<bean id="userLogger" class="com.ithema.jdbc.aop.UserLogger">
</bean>
使用 aop:aspect 配置切面,使用 aop:pointcut 配置切入点表达式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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="studentDao" class="com.ithema.jdbc.dao.StudentDao">
</bean>
<bean id="userLogger" class="com.ithema.jdbc.aop.UserLogger">
</bean>
<!--aop配置,实现dao里面的方法前后增强-->
<aop:config>
<!--定义切入点-->
<!--标准写法格式:(返回值:全类名(包名.类名.方法名(参数类型列表))-->
<aop:pointcut id="pointcut" expression="execution(* com.ithema.jdbc..*.*(..))"></aop:pointcut>
<!--组织增强-->
<!--切面ref:应用其他bean对象-->
<aop:aspect ref="userLogger">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after-returning method="after" pointcut-ref="pointcut" returning="result"></aop:after-returning>
<aop:after-throwing method="afterThorwing" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
<aop:after method="finalafter" pointcut-ref="pointcut"></aop:after>
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
基于注解方式的aop配置
导入spring-aop jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
下面几个是必须要导的包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
配置aop约束到配置文件中,在配置文件中指定 spring 要扫描的包,开启 spring 对注解 AOP 的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--告诉Spring容器在创建的时候要扫描包,配置所需要的标签不在bean约束中,而是
在一个叫做context的空降名称和约束中-->
<context:component-scan base-package="com.bdqn"></context:component-scan>
<!-- 开启注解aop-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
使用注解配置增强方法
package com.bdqn.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
public class UserLogger {
//在目标方法调用之前执行该方法
Logger logger=Logger.getLogger(UserLogger.class);
//定义切点
@Pointcut("execution(* com.bdqn.aop..*.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void before(JoinPoint jp){
logger.info("前置增强,目标类名"+jp.getTarget()+"里面的"+jp.getSignature().getName()+
"参数为:"+ Arrays.toString(jp.getArgs()));
}
//在目标方法调用之后执行该方法
//当方法抛出异常就不会执行了,所有该方法不适合做日志收集,因为发生了异常不执行该方法无法将日志保存下来
//所有后置增强用的很少
public void after(JoinPoint jp,Object result){
logger.info("后置增强,执行完类"+jp.getTarget()+"里面的"+jp.getSignature().getName()+
"方法,结果为:"+ result);
}
//在方法抛出异常后就会增强、没有异常就不会执行、如果用户try-catch,也不会增强
//执行错误的话,非常适合该增强(异常增强)
//Java异常体系,根类是Throwable
@AfterThrowing(pointcut = "execution(* com.bdqn.aop..*.*(..))",throwing = "e")
public void afterThorwing(JoinPoint jp,Throwable e){
logger.info("异常增强,执行类"+jp.getTarget()+"里面的"+jp.getSignature().getName()+
"方法抛出异常:"+e.getMessage());
}
//最终增强:无论是否有异常,都会执行,类似与finally,可以或者是认为是后置增强的升级版
@After("execution(* com.bdqn.aop..*.*(..))")
public void finalafter(JoinPoint jp){
logger.info("最终增强:执行完类"+jp.getTarget()+"里面的"+jp.getSignature().getName());
}
//环绕增强,集合了前置添加后置,并且更加强大、当执行方法发生异常后面的就不执行了
//1、可以修改方法的参数 2、也可以修改方法的返回值( return result)
@Around("execution(* com.bdqn.aop..*.*(..))")
public Object around(ProceedingJoinPoint jp)throws Throwable{
Object [] args=jp.getArgs();//获取方法参数
//可以修改方法的参数
//args = new Object[]{20,1};
logger.info("环绕增强:方法为:"+jp.getArgs());
Object result=jp.proceed();//执行目标方法
//也可以修改方法的返回值
//return 100;
return result;
}
}
使用spring整合junit4的方式写测试类
package com.bdqn.test;
import com.bdqn.dao.StudentDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:ApplicationContext.xml")
public class Testaop {
@Autowired
private StudentDao studentDao;
@Test
public void testaop(){
studentDao.add();
studentDao.jian(3,1);
System.out.println("执行了减法,结果为:");
}
}