作者:youstar | 来源:互联网 | 2023-09-16 03:40
SpringAOP入门1、AOP1.1、概念AOP为面向切面编程,采用动态代理实现。1.2、优点采用动态代理的方式,可以增强原有的目标类的方法,我们可以在目标方法执行前后分别做一些
1.2、优点
采用动态代理的方式,可以增强原有的目标类的方法,我们可以在目标方法执行前后分别做一些事情。
对于aop就可以在5种通知里做一些事情,比如说数据库连接的释放,日志的打印,事务的操作。
这种方式,使得不用修改原有程序,就可以增加功能,降低了耦合。
1.3、结构
2、AOP入门案例
2.1、AOP前
AOP采用动态代理实现,故很有必要在看AOP前先看动态代理的实现。动态代理分为两种,对于有接口,有实现类的可以采用jdk动态代理,对于没有接口,只有实现类的,需要采用cglib代理。
2.1.1、依赖包
org.springframework
spring-core
3.2.7.RELEASE
org.springframework
spring-context
3.2.7.RELEASE
org.springframework
spring-beans
3.2.7.RELEASE
org.springframework
spring-expression
3.2.7.RELEASE
org.springframework
spring-aop
3.2.7.RELEASE
org.aopalliance
com.springsource.org.aopalliance
1.0
org.aspectj
aspectjweaver
1.6.12
commons-logging
commons-logging
1.1
2.2、jdk动态代理(AOP手动方式)
jdk动态代理中,代理类需要实现目标类的所有接口。
2.2.1、目标类:接口+实现类
接口:
public interface UserService {
public void addUser();
public void updateUser();
public void deleteUser();
}
实现类:
public class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("addUser");
}
public void updateUser() {
System.out.println("updateUser");
}
public void deleteUser() {
System.out.println("deleteUser");
}
}
2.2.2切面类:用于保存通知
public class MyAspect {
public void before() {
System.out.println("brfore");
}
public void after() {
System.out.println("after");
}
}
2.2.3、代理类:生成代理对象
public class MyBeanFactory {
public static UserService createService() {
// 1、目标类
final UserService userService = new UserServiceImpl();
// 2、切面类
final MyAspect myAspect = new MyAspect();
// 3、代理对象
// 参数1、类加载器
当前类.class.getClassLoader()
目标类对象.getClass().getClassLoader()
// 参数2、目标类所实现的所有接口
目标类对象.getClass().getInterfaces()
new Class[]{接口.class}
// 参数3、InvocationHandler处理类,采用匿名内部类实现
UserService proxyService = (UserService)Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(),
userService.getClass().getInterfaces(), new InvocationHandler() {
//参数1proxy、代理对象
//参数2method、要执行方法的描述对象
//参数3arg2、方法的实际参数
public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable {
myAspect.before();
Object obj = method.invoke(userService, arg2);
myAspect.after();
return obj;
}
});
return proxyService;
}
}
2.2.4、测试类:
@Test
public void fun() {
UserService userService = MyBeanFactory.createService();
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
2.3、cglib代理方式
cglib代理适用于没有接口只有实现类的情况。cglib是通过让代理类继承目标类,从而增强目标类的方法。
2.3.1、目标类等同于jdk动态代理的接口的实现类
2.3.2、切面类同上
2.3.3、测试类同上
2.3.4、代理类
public class MyBeanFactory {
public static UserServiceImpl createService() {
// 1、目标类
final UserServiceImpl userService = new UserServiceImpl();
// 2、切面类
final MyAspect myAspect = new MyAspect();
// 3、代理类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(userService.getClass());
/* 设置回调函数 , MethodInterceptor接口 等效 jdk中 InvocationHandler接口
* intercept() 等效 jdk invoke()
*/
enhancer.setCallback(new MethodInterceptor() {
//参数1、代理对象
//参数2、要执行方法的描述对象(当前)
//参数3、方法的实际参数
//参数4、代理类方法的代理
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
myAspect.before();
//通过反射的方式执行要目标类的方法
Object obj = arg1.invoke(userService, arg2);
//执行代理类的父类(目标类)
arg3.invokeSuper(arg0, arg2);
myAspect.after();
return obj;
}
});
//创建代理对象,向上转型
UserServiceImpl proxyService = (UserServiceImpl) enhancer.create();
return proxyService;
}
}
2.4、AOP半自动代理
半自动代理采用spring提供的org.springframework.aop.framework.ProxyFactoryBean类(一个代理工厂)生成代理对象
2.4.1、目标类与jdk动态代理相同
2.4.2、切面类:环绕通知(此处与其他不同)
public class MyAspect implements MethodInterceptor
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("before");
Object obj = arg0.proceed();
System.out.println("after");
return obj;
}
}
2.4.3、配置文件beans.xml
2.4.4、测试类
@Test
public void fun() {
String xmlPath = "beanfactory/beans.xml";
ApplicationContext applicatiOnContext= new ClassPathXmlApplicationContext(xmlPath);
// 获得代理对象
UserService userService = (UserService) applicationContext.getBean("proxyService");
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
2.4.5、结果
before
addUser
after
before
updateUser
after
before
deleteUser
after
2.5、AOP全自动代理
全自动代理仅需要在beans.xml中配置一下就好,使用。
spring会自动判断使用那种代理方式,有接口时采用jdk方式,没有接口时采用cglib方式。
2.5.1、目标类与jdk动态代理相同
2.5.2、测试类与半自动方式相同
2.5.3、切面类
AOP提供了5中通知类型,分别为,前置通知,返回通知,异常通知,最终通知,环绕通知,其中环绕通知最为强大。
除环绕通知,其他可以没有参数。
public class MyAspect {
//前置通知,在目标类方法执行前执行
public void before(JoinPoint joinPoint) {
System.out.println("before");
}
//返回通知,在目标类方法执行后执行,可以拿到目标类方法的返回值,如果没有就是null
public void afterreturning(JoinPoint joinPoint, Object ret) {
System.out.println("afterreturning" + ret);
}
//异常通知,当目标类方法抛出异常时执行,可以拿到异常信息,类似于在catch块里面的方法
public void throwable(JoinPoint joinPoint, Throwable e) {
System.out.println("throwable" + " " + e.getMessage());
}
//在最后执行
public void afterafter(JoinPoint joinPoint) {
System.out.println("afterafter");
}
//在方法前后都会执行
public Object invoke(ProceedingJoinPoint jontPoint) throws Throwable {
System.out.println("环绕通知-------->前");
Object obj = jontPoint.proceed();
System.out.println("环绕通知---------->后");
return obj;
}
}
2.5.4、配置文件beans.xml
2.5.5、结果
因为环绕通知中有return语句,故执行顺序和配置有关。
2.5.5.1、配置环绕通知时
before
环绕通知-------->前
addUser
afterafter
环绕通知---------->后
afterreturning1
------------------------------------
before
环绕通知-------->前
deleteUser
afterafter
环绕通知---------->后
afterreturningnull
------------------------------------
before
环绕通知-------->前
updateUser
afterafter
throwable / by zero
2.5.5.2、不配置环绕通知
before
addUser
afterreturning1
afterafter
------------------------------------
before
deleteUser
afterreturningnull
afterafter
------------------------------------
before
updateUser
throwable / by zero
afterafter
2.5.5.3、其他
异常通知,只有在产生异常后才会执行,此时,返回通知会被异常挡住不在执行。
返回通知可以拿到目标类方法拿到的返回值,如果没有返回值,就是null。
环绕通知会使得最终通知提前。类似于,return执行前,finally块中的会先执行。
2.5.6、补充
全自动方式也可将切面做成类似于半自动的形式。
切面类:
前置通知:
public class MyAspectBefore implements MethodBeforeAdvice {
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("before");
}
}
环绕通知:
public class MyAspectMethod implements MethodInterceptor {
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕前");
Object obj = arg0.proceed();
System.out.println("环绕后");
return obj;
}
}
配置方式: