BeanNameAutoProxyCreator 根据Bean名称创建代理
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理
AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理
1、新建一个学生接口
public interface StudentDao { public void find(); public void update(); public void save(); public void delete(); }2、实现StudentDao接口
public class StudentDaoImpl implements StudentDao { public void find() { System.out.println("查询学生"); } public void update() { System.out.println("修改学生"); } public void save() { System.out.println("保存学生"); } public void delete() { System.out.println("删除学生"); } }3、再创建一个类CustomerDao:
public class CustomerDao { public void find(){ System.out.println("查询客户"); } public void save(){ System.out.println("保存客户"); } public void update(){ System.out.println("修改客户"); } public void delete(){ System.out.println("删除客户"); } }4、创建一个前置增强类MybeforeAdvice实现MethodBeforeAdvice接口,重写方法
import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class MybeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("这是前置增强(通知)"); } }5、再写一个环绕增强类吧。。MyAroundAdvice实现MethodInterceptor
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAroundAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("环绕前增强"); //执行目标方法 Object obj = invocation.proceed(); System.out.println("环绕后增强"); return obj; } }6、配置文件
<!--配置目标类--> <bean id="studentDao" class="com.aop.demo5.StudentDaoImpl"/> <bean id="customerDao" class="com.aop.demo5.CustomerDao"/> <!--配置增强(通知)--> <bean id="myBeforeAdvice" class="com.aop.demo5.MybeforeAdvice"/> <bean id="myAroundAdvice" class="com.aop.demo5.MyAroundAdvice"/> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!--以BeanName创建代理,*Dao就是所有以Dao结尾的BeanId--> <property name="beanNames" value="*Dao"/> <property name="interceptorNames" value="myBeforeAdvice"/> </bean>7、测试
@Test public void demo1(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext4.xml"); StudentDao studentDao = (StudentDao) applicationContext.getBean("studentDao"); CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDao"); studentDao.save(); studentDao.find(); studentDao.delete(); studentDao.update(); customerDao.save(); customerDao.find(); customerDao.delete(); customerDao.update(); }只需改变配置文件
<!--配置目标类--> <bean id="studentDao" class="com.aop.demo5.StudentDaoImpl"/> <bean id="customerDao" class="com.aop.demo5.CustomerDao"/> <!--配置增强(通知)--> <bean id="myBeforeAdvice" class="com.aop.demo5.MybeforeAdvice"/> <bean id="myAroundAdvice" class="com.aop.demo5.MyAroundAdvice"/> <!--自动代理1:根据Bean名称创建代理--> <!--<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="*Dao"/> <property name="interceptorNames" value="myBeforeAdvice"/> </bean>--> <!--配置切面,--> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!--指定增强方法需要使用转义字符--> <property name="pattern" value="com\.aop\.demo5\.CustomerDao\.save"/> <!--选择增强方法--> <property name="advice" ref="myAroundAdvice"/> </bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>需要jar:
spring-aopcom.springsource.org.aopalliancespring-aspectscom.springsource.org.aspectj.wever 环境准备 <?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"> <!--开启AspectJ的注解开发,自动代理--> <aop:aspectj-autoproxy/> </beans>@AspectJ提供不同的通知类型
@Before 前置通知,相当于BeforeAdvice@AfterReturning 后置通知,相当于AfterReturningAdvice@Around 环绕通知,相当于MethodInterceptor@AfterThrowing 异常抛出通知,相当于ThrowAdvice@After最终final通知,不管是否异常,该通知都会执行@DeclareParents 引介通知,相当于IntroductionInterceptor在通知中通过value属性定义切点 通过execution函数,可以定义切点的方法切入
语法 execution(<访问修饰符>?<返回值类型><方法名>(<参数>)<异常>) 例如匹配所有类public方法 execution(public * * (…))匹配指定包下所有类方法 execution(* com.dao.*( . . ))不包含子包execution(* com.dao…*(…)) …*表示包、子孙包下所有类匹配指定类所有方法 execution(* com.service.UserService.*(…))匹配实现特定接口所有类方法 execution(* com.dao.UserDao+.*(…))匹配所有save开头的方法 execution(* save*(…))代码演示 1、创建ProductDao
public class ProductDao { public void save(){ System.out.println("保存商品"); } public void update(){ System.out.println("修改商品"); } public void delete(){ System.out.println("删除商品"); } public String findOne(){ System.out.println("查询一个商品"); return "hello"; } public void findAll(){ System.out.println("查询所有商品"); //int i = 1/0;//发生异常通知 } }2、配置文件配置信息,完成自动代理、目标类、切面的定义
<?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"> <!--开启AspectJ的注解开发,自动代理--> <aop:aspectj-autoproxy/> <!--目标类--> <bean id="productDao" class="com.aspectJ.demo1.ProductDao"/> <!--定义切面--> <bean class="com.aspectJ.demo1.MyAspectAnno"/> </beans>3、创建增强类
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; /** *切面类 */ @Aspect public class MyAspectAnno { //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息 @Before(value = "execution(* com.aspectJ.demo1.ProductDao.save(..))") public void before(JoinPoint joinPoint){ System.out.println("前置通知"+joinPoint); } //@AfterReturning后置通知:通过return属性可以定义方法返回值,作为参数 @AfterReturning(value = "execution(* com.aspectJ.demo1.ProductDao.update(..))",returning = "result") public void afterReturning(Object result){ System.out.println("后置通知"+result); } //@Around环绕通知: around方法的返回值就是目标代理方法执行返回值 //参数为ProceedingJoinPoint可以调用拦截目标方法执行 @Around(value = "execution(* com.aspectJ.demo1.ProductDao.delete(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前通知==="); Object obj = joinPoint.proceed();//执行目标方法,不调用不执行目标方法(即拦截目标方法) System.out.println("环绕后通知==="); return obj; } //@AfterThrowing 异常通知:当目标方法发生异常时调用,可输出异常信息 @AfterThrowing(value = "execution(* com.aspectJ.demo1.ProductDao.findAll(..))",throwing = "e") //设置参数,打印异常信息 public void afterThrowing(Throwable e){ //目标类发生异常 System.out.println("异常通知"+e); } //@After最终通知:无论是否出现异常,最终通知总是会被执行 @After(value = "execution(* com.aspectJ.demo1.ProductDao.findAll(..))") public void after(){ System.out.println("最终通知"); } }4、测试 jar要求:junit版本要求4以上,并且要与spring-test版本一致
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext5.xml") public class SpringDemo1 { @Resource(name = "productDao") private ProductDao productDao; @Test public void demo1(){ productDao.update(); productDao.save(); productDao.findAll(); productDao.findOne(); productDao.delete(); } }总结 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义
private void 无参数方法,方法名为切入点。当通知多个切点时,可以使用 || 进行连接
使用前:
//@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息 @Before(value = "execution(* com.aspectJ.demo1.ProductDao.save(..))") public void before(JoinPoint joinPoint){ System.out.println("前置通知"+joinPoint); }使用后。方便对具有相同通知类型进行统一管理
@Pointcut("execution(* com.aspectJ.demo1.ProductDao.save(..))") public void myPointcut1(){} //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息 @Before(value = "myPointcut1()") public void before1(JoinPoint joinPoint){ System.out.println("前置通知"+joinPoint); }1、新建一个接口CustomerDao
public interface CustomerDao { public void save(); public void delete(); public void findOne(); public String update(); public void findAll(); }2、新建CustomerDao实现类CustomerDaoImpl
public class CustomerDaoImpl implements CustomerDao { public void save() { System.out.println("保存客户"); } public void delete() { System.out.println("删除客户"); // int i=1/0; } public void findOne() { System.out.println("查询单个客户"); } public String update() { System.out.println("修改用户"); return "Spring"; } public void findAll() { System.out.println("查询所有用户"); } }3、新建增强(通知)类
import org.aspectj.lang.ProceedingJoinPoint; public class MyAspectXML { public void before(){ System.out.println("前置增强"); } //获取返回值,参数名与XML配置文件returning属性要相同 public void afterReturning(Object result){ System.out.println("后置增强"+result); } public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前通知==="); Object obj = joinPoint.proceed();//执行目标方法,不调用不执行目标方法(即拦截目标方法) System.out.println("环绕后通知==="); return obj; } public void afterThrowing(Throwable e){ //目标类发生异常 System.out.println("异常通知"+e.getMessage()); } public void after(){ System.out.println("最终通知"); } }4、配置文件
<?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"> <!-- 使用XML配置方式完成AOP的开发--> <!--配置目标类--> <bean id="customerDao" class="com.aspectJ.demo2.CustomerDaoImpl"/> <!--配置切面类--> <bean id="myAspectXML" class="com.aspectJ.demo2.MyAspectXML"/> <!--aop的相关配置--> <aop:config> <aop:pointcut id="pointcut1" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.save(..))"/> <aop:pointcut id="pointcut2" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.update(..))" /> <aop:pointcut id="pointcut3" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.findOne(..))" /> <aop:pointcut id="pointcut4" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.delete(..))" /> <aop:pointcut id="pointcut5" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.delete(..))" /> <!--配置切面--> <aop:aspect ref="myAspectXML"> <!--配置前置通知,method与切面类增强方法名相同--> <aop:before method="before" pointcut-ref="pointcut1"/> <aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result"/> <aop:around method="around" pointcut-ref="pointcut3"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/> <aop:after method="after" pointcut-ref="pointcut5"/> </aop:aspect> </aop:config> </beans>5、测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(value = "classpath:applicationContext6.xml") public class SpringDemo2 { @Resource(name = "customerDao") private CustomerDao customerDao; @Test public void demo1(){ customerDao.save(); customerDao.update(); customerDao.findOne(); customerDao.delete(); customerDao.findAll(); } }