一、使用jdk动态代理实现AOP
-
科普:JDK动态代理是通过java.lang.reflect.Proxy类来实现的,可以调用Proxy类的
newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。由java jdk提供
-
接口类
public interface UserDao { /** * 添加用户 */ public void addUser(); /** * 删除用户 */ public void deleteUser(); }
-
实现类
public class UserDaoImpl implements UserDao { @Override public void addUser() { // TODO Auto-generated method stub System.out.println("我是添加用户"); } @Override public void deleteUser() { // TODO Auto-generated method stub System.out.println("我是删除用户"); } }
-
切面类(模拟检查权限,模拟记录日志)
public class MyAspect { /** * 检查权限 */ public void check() { System.out.println("模拟检查方法"); } /** * 记录日志权限 */ public void log() { System.out.println("模拟记录日志"); } }
-
jdk代理类
public class JdkProxy implements InvocationHandler { // 声明目标类的接口 private UserDao userDao; public Object createProxy(UserDao userDao) { this.userDao = userDao; // 类加载器 ClassLoader classLoader = JdkProxy.class.getClassLoader(); // 被代理对象实现所有接口 Class[] classes = userDao.getClass().getInterfaces(); // 使用代理类,进行增强,返回的是代理后的对象 return Proxy.newProxyInstance(classLoader, classes, this); } /** 所有动态代理的方法调用,都会有invoke方法处理 proxy:被代理后的对象 method:将要执行的方法信息 args执行方法时需要的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //声明切面 MyAspect myAspect = new MyAspect(); myAspect.check(); //在目标类调用方法,传入参数 Object obj = method.invoke(userDao, args); myAspect.log(); return obj; } }
-
测试
public class JdkTest { @Test public void test2() { //实例化jdk代理类 JdkProxy jdkProxy = new JdkProxy(); //创建目标对象 UserDao useDao = new UserDaoImpl(); //从代理类中获取增强后的目标对象 UserDao cp = (UserDao) jdkProxy.createProxy(useDao); // 执行方法 cp.addUser(); cp.deleteUser(); } }
二、使用cglib实现Aop
- 科普:cglib代理是一个高性能开源的代码生成包,利用底层字节码技术,对指定的目标生成一个子类,对子类进行增强,由spring提供
- 任务描述:创建UserDaoImpl类,不用实现接口。把UserDaoImpl作为目标类,对其中的方法进行增强处理。
-
目标类
public class UserDaoImpl { /** * 添加用户 */ public void addUser() { // TODO Auto-generated method stub System.out.println("我是添加用户"); } /** * 删除用户 */ public void deleteUser() { // TODO Auto-generated method stub System.out.println("我是删除用户"); } }
-
代理类:
public class Cglibproxy implements MethodInterceptor { public Object createProxy(Object target) { // 创建动态类对象 Enhancer enhancer = new Enhancer(); // 确定需要增强的类,设置其为父类 enhancer.setSuperclass(target.getClass()); // 添加回调函数enhancer enhancer.setCallback(this); return enhancer.create(); } //method:拦截的方法,arg2:拦截方法的参数数组,MethodProxy方法的代理对象,用于执行父类的方法 @Override public Object intercept(Object proxy, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable { // TODO Auto-generated method stub // 创建切面对象 MyAspect myAspect = new MyAspect(); // 前置增强 myAspect.check(); // 目标方法执行 Object obj = methodProxy.invokeSuper(proxy, arg2); // 后置增强 myAspect.log(); return obj; } }
-
测试
public class CglibTest { @Test public void test2() { // 创建代理对象 Cglibproxy cp = new Cglibproxy(); // 创建目标对象 UserDaoImpl userDaoImpl = new UserDaoImpl(); // 获取增强后的目标对象 UserDao udi = (UserDao) cp.createProxy(userDao); // 执行方法 udi.addUser(); udi.deleteUser(); } }
三、spring代理类实现Aop
- 任务描述:创建配置文件指定代理对象,指定UserDao为代理实现的接口,指定UserDaoImpl为目标类,对其中的方法进行增强处理。
- 实现思路:创建Spring配置文件,通过定义目标类和切面,然后使用ProxyFactoryBean类定义代理对象。在定义代理对象中,通过子元素指定代理实现的接口,代理的目标对象,需要织入目标类的通知以及代理方式
-
切面类
public class MyAspect implements MethodInterceptor { /** * 日志信息 */ private void log() { // TODO Auto-generated method stub System.out.println("模拟检查日志"); } /** * 模拟检查权限 */ private void check() { // TODO Auto-generated method stub System.out.println("模拟检查权限"); } @Override public Object invoke(MethodInvocation mi) throws Throwable { // TODO Auto-generated method stub check(); // 执行目录方法 Object object = mi.proceed(); log(); return object; } }
-
spring配置文件
<!-- 目标类 --> <bean id="userDao" class="com.ruanyuan.jdk.UserDaoImpl"></bean> <!-- 切面类 --> <bean id="myAspect" class="com.ruanyuan.factorybean.MyAspect"></bean> <!-- 使用spring代理工厂定义一个名称为UserDaoProxy对象 --> <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 指定实现代理接口 --> <property name="proxyInterfaces" value="com.ruanyuan.jdk.UserDao"></property> <!-- 指定目标对象 --> <property name="target" ref="userDao"></property> <!-- 指定切面,植入环绕通知 --> <property name="interceptorNames" value="myAspect"></property> <property name="proxyTargetClass" value="true"></property> </bean>
-
测试类
public class ProxyFactoryBeanTest { @SuppressWarnings("resource") @Test public void test2() { // 定义配置路径 String path="com/ruanyuan/factorybean/applicationContext.xml"; // 加载配置路径 ApplicationContext context = new ClassPathXmlApplicationContext(path); // 获取bean UserDao userDao = (UserDao) context.getBean("userDaoProxy"); // 执行方法 userDao.addUser(); userDao.deleteUser(); } }
四、基于xml的声明式AspectJ
- 任务描述:通过XML文件来定义切面,切入点及通知
-
接口类
public interface UserDao { /** * 添加用户 */ public void addUser(); /** * 删除用户 */ public void deleteUser(); }
-
实现类
public class UserDaoImpl implements UserDao{ /** * 添加 */ public void addUser() { // TODO Auto-generated method stub System.out.println("我是添加用户"); } /** * 删除 */ public void deleteUser() { // TODO Auto-generated method stub System.out.println("我是删除用户"); } }
-
切面类
public class MyAspect { /** * 前置通知 * @param joinPoint */ public void Before(JoinPoint joinPoint) { System.out.println("前置通知,模拟检查权限"); System.out.println("目标类是"+joinPoint.getTarget()); System.out.println(",被织入增强处理的目标方法"+joinPoint.getSignature().getName()); System.out.println("----------------前置分隔----------------------"); } /** * 后置返回通知 * @param joinPoint */ public void AfterReturning(JoinPoint joinPoint) { System.out.println("后置通知,模拟记录日志"); System.out.println("目标是"+joinPoint.getTarget()); System.out.println(",被织入增强处理的目标方法"+joinPoint.getSignature().getName()); System.out.println("----------------后置返回分隔----------------------"); } /** * 环绕通知 * @param proceedingJoinPoint * @return * @throws Throwable */ public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 环绕开始 System.out.println("环绕开始,执行目标方法之前,模拟开启事物"); Object obj = proceedingJoinPoint.proceed(); // 环绕结束 System.out.println("环绕结束,模拟事物关闭释放资源"); System.out.println("----------------环绕分隔----------------------"); return obj; } /** * 异常通知 * @param joinPoint * @param e */ public void AfterThrowing(JoinPoint joinPoint,Throwable e) { System.out.println("异常通知"+e.getMessage()); System.out.println("----------------异常分隔----------------------"); } /** * 最终通知 */ public void finish() { System.out.println("最终通知,模拟方法结束后的释放资源"); System.out.println("----------------最终分隔----------------------"); } }
-
配置文件
<!-- 目标类 --> <bean id="userDao" class="com.ry.aspectj.UserDaoImpl"></bean> <!-- 切面 --> <bean id="myAspect" class="com.ry.aspectj.MyAspect"></bean> <!-- aop编程 --> <aop:config> <!--配置切面 --> <aop:aspect ref="myAspect"> <!-- 配置切入点 --> <aop:pointcut expression="execution(* com.ry.aspectj.*.*(..))" id="myPointCut" /> <!-- 前置通知 --> <aop:before method="Before" pointcut-ref="myPointCut" /> <!-- 后置通知 --> <aop:after-returning method="AfterReturning" pointcut-ref="myPointCut" returning="returnVal" /> <!-- 环绕通知 --> <aop:around method="Around" pointcut-ref="myPointCut" /> <!-- 异常通知 --> <aop:after-throwing method="AfterThrowing" pointcut-ref="myPointCut" throwing="e" /> <!-- 最终通知 --> <aop:after method="finish" pointcut-ref="myPointCut" /> </aop:aspect> </aop:config>
-
测试类
public class TestXmlAspectj { @Test public void test2() { String path = "com/ruanyuan/aspectj/applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(path); // 从Spring容器获得内容 UserDao userDao = (UserDao) context.getBean("userDao"); // 执行方法 userDao.addUser(); } }
五、注解实现AOP
- 通过注解来定义切面,切入点及通知,所有的切面,切入点和通知。
-
接口类
public interface UserDao { /** * 添加用户 */ public void addUser(); /** * 删除用户 */ public void deleteUser(); }
-
实现类
@Repository("userDao1") public class UserDaoImpl implements UserDao{ @Override public void addUser() { // TODO Auto-generated method stub System.out.println("添加用户。。。"); } @Override public void deleteUser() { // TODO Auto-generated method stub System.out.println("删除用户。。。"); } }
-
切面类
@Aspect @Component public class MyAspect { // 定义切入点表达式 @Pointcut("execution(* com.ruanyuan.aspectj.annotation.*.*(..))") // 使用方法体为空的方法作为切入点 private void myPointCut() { } /** * 前置通知 * * @param joinPoint */ @Before("myPointCut()") public void Before1(JoinPoint joinPoint) { System.out.println("前置通知,模拟检查权限"); System.out.println("目标类是" + joinPoint.getTarget()); System.out.println(",被织入增强处理的目标方法" + joinPoint.getSignature().getName()); System.out.println("----------------前置分隔----------------------"); } /** * 后置返回通知 * * @param joinPoint */ @After(value = "myPointCut()") public void AfterReturning(JoinPoint joinPoint) { System.out.println("后置通知,模拟记录日志"); System.out.println("目标是" + joinPoint.getTarget()); System.out.println(",被织入增强处理的目标方法" + joinPoint.getSignature().getName()); System.out.println("----------------后置返回分隔----------------------"); } /** * 环绕通知 * * @param proceedingJoinPoint * @return * @throws Throwable */ @Around("myPointCut()") public Object Around1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 环绕开始 System.out.println("环绕开始,执行目标方法之前,模拟开启事物"); Object obj = proceedingJoinPoint.proceed(); // 环绕结束 System.out.println("环绕结束,模拟事物关闭释放资源"); System.out.println("----------------环绕通知分隔----------------------"); return obj; } /** * 异常通知 * * @param joinPoint * @param e */ @AfterThrowing(value = "myPointCut()", throwing = "e") public void AfterThrowing1(JoinPoint joinPoint, Throwable e) { System.out.println("异常通知" + e.getMessage()); System.out.println("----------------异常分隔----------------------"); } /** * 最终通知 */ @After("myPointCut()") public void finish() { System.out.println("最终通知,模拟方法结束后的释放资源"); 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/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"> <context:component-scan base-package="com.ruanyuan.aspectj.annotation"/> <!-- 基于注解的声明式事物支持 --> <aop:aspectj-autoproxy/> </beans>
-
测试类
public class AnnotationTest { @Test public void test2() { String path = "com/ruanyuan/aspectj/annotation/applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(path); // 从Spring容器获得内容 UserDao userDao = (UserDao) context.getBean("userDao1"); // 执行方法 userDao.addUser(); } }