赞
踩
这些执行的通用的内容可以是记录日志、开启事务。
package com.dz.aop_jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理类:LogHandler;实现InvocationHandler接口 * */ public class LogHandler implements InvocationHandler { private Object targetObject; // 目标对象,或者是被代理类 /** * 生成被代理类的对象 * * @param targetObject * @return */ public Object creatProxy(Object targetObject) { this.targetObject = targetObject; /* * 创建一个被代理类 (参数 详情) -获取被代理类的类加载器 -获取被代理类的接口 -使用当前的代理类this来执行被代理的类 */ return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } /** * 代理类执行的方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result =null; try { //伪代码,这就是在执行方法之前横向切入的执行内容 System.out.println("准备~~开始记录日志..."); result = method.invoke(this.targetObject, args);//执行被代理类的方法 //伪代码:这是在方法执行之后横向切入的内容 System.out.println("日志记录完毕..."); } catch (Exception e) { e.printStackTrace(); } return result; } }
被代理的类必须要提供接口
package com.dz.aop_jdk;
public interface UserDao {
// 假装添加一个用户
void addUser();
}
实现接口
package com.dz.aop_jdk;
public class UserDaoImpl implements UserDao{
@Override
public void addUser() {
//伪代码
System.out.println("执行一个添加用户的操作...");
}
}
package com.dz.aop_jdk;
public class TestProxy {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
LogHandler log=new LogHandler();
//根据被代理的对象,生成代理的对象--根据指定的接口返回对象
UserDao proxyUser = (UserDao) log.creatProxy(userDao);
//让代理对象来执行方法
proxyUser.addUser();
}
}
流行的AOP框架目前有两个:SpringAOP和AspectJ。后来,从Spring2.0开始SpringAOP引入了对AspectJ的支持。并且SpringAOP建议使用AspectJ开发AOP。
开始AOP的配置之前,先来认识一下AOP中的一些术语:
通知又分为几个类型,下面在代码里面可以看到通知的类型。(也就是这个通知在方法的什么位置执行)。
定义一个切面
package com.dz.aop_spring; import org.aspectj.lang.ProceedingJoinPoint; /** * * 配置一个切面类 */ public class AspectXML { public void beforeAdvice() { System.out.println("前置通知..执行的方法,比如说:开启事务???"); } public void afterAdvice() { System.out.println("最终通知..执行的方法,比如说:关闭事务??"); } public void afterReturnAdvice() { System.out.println("后置通知"); } public void afterThrowingAdvice() { System.out.println("异常通知"); } public Object aroundAdvice(ProceedingJoinPoint pjp) { Object object = null; try { System.out.println("环绕通知开始..."); object = pjp.proceed();// 执行切点,切中的方法 System.out.println("环绕通知结束..."); } catch (Throwable e) { e.printStackTrace(); } return object; } }
定义切点切入的类
package com.dz.aop_spring;
public interface UserService {
public void insertUser();
}
package com.dz.aop_spring;
public class UserServiceImpl implements UserService {
//假装插入一条数据
@Override
public void insertUser() {
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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd "> <bean id="userService" class="com.dz.aop_spring.UserServiceImpl"></bean> <!--配置切面 --> <bean id="aspectXmL" class="com.dz.aop_spring.AspectXML"></bean> <!-- 配置切面 --> <aop:config> <aop:aspect id="myAspect" ref="aspectXmL"> <!-- 配置切入点 :根据表达式找切入点,满足切入点执行通知--> <aop:pointcut expression="execution(* com.dz.aop_spring.*.*(..))" id="myCut" /> <!-- 根据切入点,执行一些类型的通知方法 --> <aop:before method="beforeAdvice" pointcut-ref="myCut" /> <aop:after method="afterAdvice" pointcut-ref="myCut"/> <aop:after-returning method="afterReturnAdvice" pointcut-ref="myCut" /> <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="myCut" /> <aop:around method="aroundAdvice" pointcut-ref="myCut"/> </aop:aspect> </aop:config> </beans>
测试类
package com.dz.aop_spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//满足切入点的类,和类下面所有的方法
UserService userService = ac.getBean("userService", UserService.class);
userService.insertUser();
}
}
结果
异常通知在方法出现异常的时候才会执行。
注解配置AOP也是要声明切面的!!!
package com.dz.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; /** * * 配置一个切面类 */ @Aspect // 注解声明切面 //@Component //注解声明需要一个扫描 public class AspectAnno { // 定义切入点:任何返回类型,aop_anno包下的任何类下的任何方法包括任意参数都满足切入点 @Pointcut("execution(* com.dz.aop_anno.*.*(..))") public void pointCut() { } //这里也可以直接将表达式放进去 @Before(value = "execution(* com.dz.aop_anno.*.*(..))") //@Before("pointCut()") // 根据切点切入通知 public void beforeAdvice() { System.out.println("注解:前置通知..执行的方法,比如说:开启事务???"); } @After("pointCut()") public void afterAdvice() { System.out.println("最终通知..执行的方法,比如说:关闭事务??"); } @AfterReturning("pointCut()") public void afterReturnAdvice() { System.out.println("后置通知"); } @AfterThrowing("pointCut()") public void afterThrowingAdvice() { System.out.println("异常通知"); } @Around("pointCut()") public Object aroundAdvice(ProceedingJoinPoint pjp) { Object object = null; try { System.out.println("环绕通知开始..."); object = pjp.proceed();// 执行切点,切中的方法 System.out.println("环绕通知结束..."); } catch (Throwable e) { e.printStackTrace(); } return object; } }
定义一个UserService.java接口
package com.dz.aop_anno;
public interface UserService {
public void updatetUser();
}
并实现它
package com.dz.aop_anno;
public class UserServiceImpl implements UserService {
//假装修改一条数据
@Override
public void updatetUser() {
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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/context "> <!-- 这里没有使用扫描,而是直接装配的bean --> <bean id="userService" class="com.dz.aop_anno.UserServiceImpl"></bean> <bean id="aspectAnno" class="com.dz.aop_anno.AspectAnno"></bean> <!--启动注解配置切入方式 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
测试注解配置的AOP
package com.dz.aop_anno;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.updatetUser();
}
}
结果
咦???仔细对比了两张结果图发现不一样?
基于XML与注解的方式执行的结果是相同的,只是目标方法前后的通知顺序发生了改变。
如果同一个连接点有多个通知执行,那么前置通知与环绕通知的执行顺序未知,后置通知与环绕通知的执行顺序未知。
无论使用的哪种方法的配置,都需要定义:切面、切点、通知。(除非有人帮你定义好了,你只需要引用。比如:事务管理。)
附上我的目录结构
依赖
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。