文章目录
2. 应用XML编程实现AOP
面向切面编程(Aspect Oriented Programming),其目的是为了拦截某些类下的某些方法,参数、返回值内容,在此基础上,通过增强的形式,使用代理设计模式显示AOP编程。
2.1 AOP代理实现形式
2.1.1 通过JDK动态代理实现
动态代理是指:代理类对象是在运行时,由JVM根据反射机制动生成的。动态代理不需要定义代理类的java源文件
动态代理其实就是JDK运行期间,动态创建class字节码文件加载到JVM
2.1.1.1 优点
1.不用创建类文件
2.当修改了接口中的方法,不会影响代理类
3.不用给不同的目标随时创建代理
2.1.1.2 术语概念
1.目标类:实现了功能接口的实现类对象
2.代理类:作为调用类和目标类之间的桥接
3.调用类:需要调用目标类方法来完成值的获取
4.OCP原则:程序设计的一个考虑,类设计的时候尽量避免方法中代码的二次修改,但是欢迎类设计者扩展一个类的功能(方法)
5.耦合度:一个类过于依赖另一个类,就会产生耦合性
package com.csi.proxy;
import com.csi.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyProxy implements InvocationHandler {
private UserService userService ;
public MyProxy(UserService userService) {
this.userService = userService ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = null ;
if("list".equals(method.getName())) {
System.out.println("前置通知");
o = method.invoke(userService,args) ;
System.out.println("后置通知");
}
return o;
}
}
package com.csi.proxy;
import com.csi.service.UserService;
import com.csi.service.impl.UserServiceImpl;
import java.lang.reflect.Proxy;
public class TestMyProxy {
public static void main(String[] args) {
UserService userService = new UserServiceImpl() ;
MyProxy myProxy = new MyProxy(userService) ;
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), myProxy);
userServiceProxy.list();
}
}
JDK的动态代理,是要求必须拥有接口的。Spring能够对任何的一个对象都进行代理设计,此时如果某个对象没有对应的接口,只是单单的利用JDK动态代理,则无法创建对应的代理对象。Spring AOP就会采用CGLib生成。
2.1.2 通过CGLib动态代理实现
CGLib是通过继承目标类实现的一种代理方式,因此不需要提前准备目标对应的接口。在Spring生成代理对象时,实际上是生成代理目标的一个子类。
public class SampleClass {
public void test(){
System.out.println("hello world");
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//告诉代理类,他的父类是谁
enhancer.setSuperclass(SampleClass.class);
//通过setCallback方法,拦截对应的调用方法进行增强
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
System.out.println("before method run...");
//返回值
Object result = proxy.invokeSuper(obj, args);
System.out.println("after method run...");
return result;
}
});
SampleClass sample = (SampleClass) enhancer.create();
sample.test();
}
}
2.1.3 AOP术语

- 连接点(JoinPoint): 正在执行的方法,例如:update()、delete()、select()、等都是连接点。
- 切入点(PointCut):进行功能增强的方法,例如:update()、delete()方法,select()方法沒有被增强所以不是切入点,但是是连接点。
- 在Spring AOP中,一个切入点只能描述一个具体的方法,也可以匹配多个方法
- 一个具体的方法:com.csi.dao包下的Dao接口中的无参无返回值的save方法
- 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao接口中的任意方法,所有带有一个参数的方法
- 在Spring AOP中,一个切入点只能描述一个具体的方法,也可以匹配多个方法
- 通知(Advice):在切入点前后执行的操作,也就是增强的共性功能
- 在Spring AOP中,功能最终以方法的形式呈现
- 通知类:通知方法所在的类叫做通知类
- 切面(Aspect):描述通知与切入点的对应关系,也就是那些通知方法对应那哪些切入点方法。
2.1.4 通过编程形式基于XML实现AOP
-
引入aspectjweaver环境
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.9.1</version> </dependency> -
修改核心配置文件
<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"> -
编写增强通知类
public class LoggerService { /** * 前置通知 * @param joinPoint 连接点 */ public void beforeRecord(JoinPoint joinPoint) { /** * joinPoint.getTarget():获取到目标的类 * joinPoint.getSignature().getName():获取到方法 * joinPoint.getArgs():获取到参数 */ System.out.println("前置通知:" + joinPoint.getTarget() + "的" + joinPoint.getSignature().getName() + ",参数是" + Arrays.toString(joinPoint.getArgs())); } /** * 后置通知 * @param joinPoint 连接点 * @param result 返回值结果 */ public void afterReturning(JoinPoint joinPoint, Object result) { System.out.println("后置通知:" + joinPoint.getTarget() + "的" + joinPoint.getSignature().getName() + ",返回值是:" + result); } } -
在核心配置文件中增加aop的配置
<bean id="loggerService" class="com.csi.aop.LoggerService" /> <bean id="xxx" class="符合与pointcut规则的实现类" /> <aop:config> <aop:pointcut id="pointcut" expression="execution(* com.csi.service..*.*(..))" /> <aop:aspect ref="loggerService"> <aop:before method="xxxxx" pointcut-ref="pointcut" /> <aop:after-returning method="xxxx" pointcut-ref="pointcut" returning="方法参数" /> </aop:aspect> </aop:config> -
测试
ApplicationContext ctx = new ClasspathXMLApplicationContext("applicationXXX.xml") ; Object obj = ctx.getBean("id") ; obj.xxx() ; -
结果
前置通知:com.csi.service.impl.PersonServiceImpl@1dde4cb2的list,参数是[]调用了list方法后置通知:com.csi.service.impl.PersonServiceImpl@1dde4cb2的list,返回值是:[com.csi.domain.UserInfo@72057ecf,
com.csi.domain.UserInfo@1afd44cb]
从实验的结果中,发现AOP灵活度非常高,当需要或不需要前置或后置通知时,只需要进行配置即可。
2.1.5 使用场景
在业界公认的包含以下场景
- 日志
会降低日志输出灵活度,没有办法进行个性化设置 - 权限设置
如果下沉到了service层,那么基本上没有太大意义 - 事务管理
- 由于事务管理基本上是一成不变的,所以是最适合的使用场景
本文介绍了Spring中的面向切面编程(AOP),包括通过JDK和CGLib动态代理实现AOP的方式。JDK动态代理适用于有接口的情况,而CGLib则能在无接口的情况下进行代理。AOP关键术语包括连接点、切入点和通知,通过XML配置可以方便地实现AOP增强。文章还讨论了AOP的使用场景,如日志、权限设置和事务管理,并提供了具体的代码示例。
1954

被折叠的 条评论
为什么被折叠?



