SSM-3-AOP
- Spring AOP简介
- AOP术语
AOP简介
面向切面编程,比如日志记录,横向插入
动态代理
jdk动态代理
- 什么是静态代理
- 什么是动态代理
首先得先了解这两个是什么,静态代理的优缺点,为什么要引入动态代理
设计接口类
1 2 3 4 5 6
| package com.itheima.jdk;
public interface UserDao { public void AddUser(); public void DeleteUser(); }
|
实现接口类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.itheima.jdk;
public class UserDaoImpl implements UserDao {
@Override public void AddUser() { System.out.println("添加用户"); }
@Override public void DeleteUser() { System.out.println("删除用户"); }
}
|
实现切面类,实际就是代理的方法
1 2 3 4 5 6 7 8 9 10 11 12
| package com.itheima.aspect;
public class MyAspect { public void check_Perminssions() { System.out.println("模拟检查权限"); }
public void log() { System.out.println("模拟记录日志"); } }
|
实现JdkProxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.itheima.jdk;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
import com.itheima.aspect.MyAspect;
public class JdkProxy implements InvocationHandler { private UserDao userDao; public Object createProxy(UserDao userDao) { this.userDao = userDao; ClassLoader classLoader = JdkProxy.class.getClassLoader(); Class<?>[] clazz = userDao.getClass().getInterfaces(); return Proxy.newProxyInstance(classLoader, clazz, this); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MyAspect myAspect = new MyAspect(); myAspect.check_Perminssions(); Object obj = method.invoke(userDao, args); myAspect.log(); return obj; }
}
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.itheima.jdk;
public class JdkTest {
public static void main(String[] args) { JdkProxy jdkProxy = new JdkProxy(); UserDao userDao = new UserDaoImpl(); UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao); userDao1.AddUser(); userDao1.DeleteUser(); }
}
|
缺点
- 必须有接口方法才能使用这种模式
思考
- 如何只代理其中的一种方法呢?
- 代码中类加载器为什么是ClassLoader classLoader = JdkProxy.class.getClassLoader();
结论:
- 只需在invoke里添加if (“xxx”.equals(method.getName())) 判断方法名就行
- 这里只要是非自定义的加载器就行,随意,可以用userDao,userImple等等
CGLIB代理
创建普通类
1 2 3 4 5 6 7 8 9 10
| package com.itheima.cglib;
public class UserDao { public void addUser() { System.out.println("添加用户"); } public void deleteUser() { System.out.println("删除用户"); } }
|
创建动态代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.itheima.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;
import com.itheima.aspect.MyAspect;
public class CglibProxy implements MethodInterceptor {
public Object createProxy(Object target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); }
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { MyAspect myAspect = new MyAspect(); myAspect.check_Perminssions(); Object object = methodProxy.invokeSuper(proxy, args); myAspect.log(); return null; }
}
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.itheima.cglib;
public class CglibTest {
public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); UserDao userDao = new UserDao(); UserDao userDao1 = (UserDao) cglibProxy.createProxy(userDao); userDao1.addUser(); userDao1.deleteUser(); }
}
|
过程相对简单一点了
基于代理类的AOP实现
- spring的通知类型
- ProxyFactoryBean
直接实现aop的切面方法类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.itheima.factorybean;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation;
public class MyAspect implements MethodInterceptor {
@Override public Object invoke(MethodInvocation mi) throws Throwable { check_Permissions(); Object obj = mi.proceed(); log(); return null; }
public void check_Permissions() { System.out.println("模拟检查权限"); } public void log() { System.out.println("模拟记录日志"); }
}
|
写配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="userDao" class="com.itheima.jdk.UserDaoImpl"></bean> <bean id="myAspect" class="com.itheima.factorybean.MyAspect"></bean> <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.itheima.jdk.UserDao"></property> <property name="target" ref="userDao"></property> <property name="interceptorNames" value="myAspect"></property> <property name="proxyTargetClass" value="true"></property> </bean> </beans>
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.itheima.factorybean;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.jdk.UserDao;
public class ProxyFactoryBeanTest { public static void main(String[] args) { String xmlPath = "com/itheima/factorybean/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy"); userDao.AddUser(); userDao.DeleteUser(); } }
|
这里直接用构造器实例化了,过程相对比较简单,写完在看下就可以了
AspectJ开发
基于XML的声明式AspectJ
配置切面
- id
- ref
全局跟局部
切入点表达式
execution(* com.itheima.jdk.*.*(…))
切入点表达式的基本格式
execution(modifiers-pattern?ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern)throws-pattern?)
配置通知
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
名词解释:
pointcut 切入点
pointcut-ref 切入点引用
method 增强的方法
throwing 异常处理的名称
returning 返回值
测试
首先编写切面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package com.itheima.aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect { public void myBefore(JoinPoint joinPoint) { System.out.println("前置通知:模拟执行权限检查"); System.out.println("目标类是: " + joinPoint.getTarget()); System.out.println("织入的增强处理目标方法为"+joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint) { System.out.println("后置通知: 模拟记录日志"); System.out.println("被织入增强处理的目标方法为: " + joinPoint.getSignature().getName()); }
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕开始: 执行目标方法之前,模拟开启事务..."); Object obj = proceedingJoinPoint.proceed(); System.out.println("环绕结束"); return obj; } public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("异常通知: " + "出错了" + e.getMessage()); } public void myAfter() { System.out.println("最终通知: 模拟方法结束后的释放资源"); } }
|
接着编写配置文件,步骤
- 设置目标类
- 设置切面
- aop编程
- 配置切面
- 配置切入点
- 配置通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 1.目标类 --> <bean id="userDao" class="com.itheima.jdk.UserDaoImpl"></bean> <!-- 2.切面 --> <bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect"></bean> <!-- 3. aop编程 --> <aop:config> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 3.1 配置切入点,通知最后增强哪些方法 --> <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))" id="myPointCut"/> <!-- 3.2 关联通知Advice和切入点pointcut --> <!-- 3.2.1 前置通知 --> <aop:before method="myBefore" pointcut-ref="myPointCut"/> <!-- 3.2.2 后置通知 --> <!-- returning属性: 用于设置后 --> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/> <!-- 3.2.3 环绕通知 --> <aop:around method="myAround" pointcut-ref="myPointCut"/> <!-- 3.2.4 异常通知,没异常则不执行 --> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> <!-- 3.2.5 最终通知 相当于异常处理中final--> <aop:after method="myAfter" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>
|
然后是测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.itheima.aspectj.xml;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.jdk.UserDao;
public class TestXmlAspectj {
public static void main(String[] args) { String xmlPath = "com/itheima/aspectj/xml/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserDao userDao = (UserDao)applicationContext.getBean("userDao"); userDao.AddUser(); }
}
|
基于注解的声明式AspectJ-new
由于误操作,笔记删了。。。随缘了
本文作者:NoOne
本文地址: https://noonegroup.xyz/posts/2527f84c/
版权声明:转载请注明出处!