3.1 Spring AOP简介
JDK动态代理
JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
定义接口与实现类
package com.itheima.jdk;
public interface UserDao {
public void addUser();
public void deleteUser();
}
package com.itheima.jdk;
import org.springframework.stereotype.Repository;
// 目标类
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void addUser() {
// int i = 10/0;
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}
定义切面类
//在代理中,代理类不仅可以实现必须要实现的业务,同时还可以扩展目标对象的功能
package com.itheima.aspect;
//切面类:可以存在多个通知Advice(即增强的方法)
public class MyAspect {
public void check_Permissions() {
System.out.println("模拟检查权限...");
}
public void log() {
System.out.println("模拟记录日志...");
}
}
定义代理类
// 三个方法 1.设置代理类要实现的接口setRent 2.返回代理类的实例 3.实现InvocationHandler接口的invoke方法(处理代理实例上的方法调用并返回结果)
//实现InvocationHandler接口生成代理类
// 三个方法 1.设置代理类要实现的接口setRent 2.返回代理类的实例 3.实现InvocationHandler接口的invoke方法(处理代理实例上的方法调用并返回结果)
package com.itheima.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.itheima.aspect.MyAspect;
/**
* JDK代理类
*/
public class JdkProxy implements InvocationHandler {
// 声明目标类接口
private UserDao userDao;
// 创建代理方法
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
// 1.类加载器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
// 2.被代理对象实现的所有接口
Class[] clazz = userDao.getClass().getInterfaces();
// 3.使用代理类,进行增强,返回的是代理后的对象
return Proxy.newProxyInstance(classLoader, clazz, this);
}
/*
* 所有动态代理类的方法调用,都会交由invoke()方法去处理
* proxy 被代理后的对象
* method 将要被执行的方法信息(反射)
* args 执行方法时需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 声明切面
MyAspect myAspect = new MyAspect();
// 前增强
myAspect.check_Permissions();
// 在目标类上调用方法,并传入参数
Object obj = method.invoke(userDao, args);
// 后增强
myAspect.log();
return obj;
}
}
定义测试类 1.实例化带被代理的对象 并用接口接收, 2.实例化代理类 3.执行代理类的方法一,执行代理类的方法二并将返回值转换为接口类型 ,4.执行接口相关的方法。
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();
}
}
ProxyFactoryBean实现代理
xml 需要配置目标类,自定义的切面类(需要实现MethodInterceptor)、使用ProxyFactoryBean 并设置三个参数
<?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">
<!-- 1 目标类 -->
<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
<!-- 2 切面类 -->
<bean id="myAspect" class="com.itheima.factorybean.MyAspect" />
<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
<bean id="userDaoProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 3.1 指定代理实现的接口-->
<property name="proxyInterfaces"
value="com.itheima.jdk.UserDao" />
<!-- 3.2 指定目标对象 -->
<property name="target" ref="userDao" />
<!-- 3.3 指定切面,织入环绕通知 -->
<property name="interceptorNames" value="myAspect" />
<!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
</beans>
省略UserDaoImpl
切面类需要实现MethodInterceptor
package com.itheima.factorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
// 切面类需要实现MethodInterceptor接口的invoke()方法
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
check_Permissions();
// 执行目标方法
// 继续到链中的下一个拦截器 Joinpoint.proceed()
Object obj = mi.proceed();
log();
return obj;
}
public void check_Permissions() {
System.out.println("模拟检查权限...");
}
public void log() {
System.out.println("模拟记录日志...");
}
}
测试方法
// 测试类
public class ProxyFactoryBeanTest {
public static void main(String args[]) {
String xmlPath = "com/itheima/factorybean/applicationContext.xml";
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(xmlPath);
// 从Spring容器获得内容
UserDao userDao =
(UserDao) applicationContext.getBean("userDaoProxy");
// 执行方法
userDao.addUser();
userDao.deleteUser();
}
}
基于注解的声明式AspectJ
@Aspect 切面注解使用 - 秃了也变强了 - 博客园 (cnblogs.com)https://www.cnblogs.com/dd1992dd/p/12851620.html