spring AOP 面向切面编程 Aspect Oriented Programming(适用于权限检查,日志记录,性能分析,事务管理,异常管理) 。
AOP有三种织入切面的方法:
其一是编译期织入,这要求使用特殊的Java编译器,AspectJ是其中的代表者;
其二是类装载期织入,而这要求使用特殊的类装载器,AspectJ和AspectWerkz是其中的代表者;
其三为动态代理织入,在运行期为目标类添加增强生成子类的方式,Spring AOP采用动态代理织入切面。
Spring AOP使用了两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以需要两种代理机制,很大程度上是因为JDK本身只提供基于接口的代理,不支持类的代理。
基于JDK的代理和基于CGLib的代理是Spring AOP的核心实现技术,认识这两代理技术,有助于探究Spring AOP的实现机理。只要你愿意,你甚至可以抛开Spring,提供自己的AOP实现。
实现aop 有两种方式:
- jdk动态代理
- cglib 动态代理
jdk动态代理
jdk 动态代理必须具备四个条件:
目标接口,目标类,拦截器(处理器),代理类
1.利用jdkProxy 生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有
2.生成的代理类的所有方法都拦截了目标类的所有的方法,而拦截器中的invoke方法的内容就是代理类的各个方法的组合体。
3.利用jdkProxy方式必须有接口的存在。
4.invoke方法中的三个参数
(Object proxy, Method method, Object[] args)
可以访问目标类的被调用方法的api,被调用犯法的参数,被调用方法的返回类型。
/**
* <p>
* 服务类
* </p>
*
* @author WangCong
* @since 2020-04-30
*/
public interface SysUserLogService extends IService<SysUserLog> {
int addLog(SysUserLog sysUserLog);
String delLog(String userName);
}
实现类
/**
* <p>
* 服务实现类
* </p>
*
* @author WangCong
* @since 2020-04-30
*/
@Service
public class SysUserLogServiceImpl extends ServiceImpl<SysUserLogMapper, SysUserLog> implements SysUserLogService {
@Autowired
private SysUserLogMapper sysUserLogMapper;
@Override
@Transactional
public int addLog(SysUserLog sysUserLog) {
return sysUserLogMapper.addLog(sysUserLog);
}
@Override
public String delLog(String userName) {
System.out.println(userName);
return "删除成功";
}
}
代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by IntelliJ IDEA.
*
* @Author : WangCong
* @create 2020/5/6 10:35
*/
public class MyJDKProxy implements InvocationHandler {
//需要被代理的目标对象
private Object proxyObject;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("========JDK动态代理,监听开始!========");
Object result = method.invoke(proxyObject, args);
System.out.println("========JDK动态代理,监听结束!========");
return result;
}
private Object getProxyInstance(Object targetObject) {
//为目标对象target赋值
this.proxyObject = targetObject;
//JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader()
, targetObject.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
//实例化JDKProxy对象
MyJDKProxy myJDKProxy = new MyJDKProxy();
//获取代理对象
SysUserLogService userService = (SysUserLogService)myJDKProxy.getProxyInstance(new SysUserLogServiceImpl());
userService.delLog("asdasdas");
}
}
执行输出
cglib 动态代理
重点:jdk动态代理只能为接口创建代理。
而cglib动态代理是给没有接口的业务类使用的。
1.cglib 是一个很强大的code 生成类库,具有在运行期扩展java类与实现java接口的。
2. cglib 可以生成代理类是目标类的子类
3. 使用cglib不需要代理类实现接口
4. cglib的代理类会重写父类的各个方法
5. 拦截器中等intercept方法内容就是代理类中的方法体
cglib 代理类
import com.zk.zkapi.service.impl.SysUserLogServiceImpl;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by IntelliJ IDEA.
*
* @Author : WangCong
* @create 2020/5/6 10:39
*/
public class CglibProxy implements MethodInterceptor {
private Object proxyObject;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("==========CGlib动态代理,监听开始!==========");
Object result = method.invoke(proxyObject, objects);
System.out.println("==========CGlib动态代理,监听结束!==========");
return result;
}
private Object getCglibProxy(Object objectTarget){
this.proxyObject = objectTarget;
Enhancer enhancer = new Enhancer();
//设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);// 设置回调
return enhancer.create();//创建并返回代理对象
}
public static void main(String[] args) {
//实例化CglibProxy对象
CglibProxy cglib = new CglibProxy();
//获取代理对象
SysUserLogServiceImpl agentService = (SysUserLogServiceImpl) cglib.getCglibProxy(new SysUserLogServiceImpl());
agentService.delLog("嘻嘻");
}
运行中
总结
spring aop在底层是利用jdk动态代理或cglib动态代理为目标实体类植入横切逻辑。
spring aop 通过切点指定在那个类的那些方法上横切逻辑
通过 增强 描述横切逻辑和方法的具体植入点。
还通过切面 组合 切点和增强,有了切面的信息,spring就可以 利用jdk动态代理或cglib动态代理 来目标实体类创建植入的切面的代理对象了。
对于单例的代理对象或者具有实例池的代理,不需要频繁的创建代理对象,比较适合用cglib动态代理。
对于不是单例的对象,就可以用jdk 动态代理。此外由于cglib采用生成子类的方式创建代理对象,所以不能对目标类中的final修饰的方法进行代理。