? 客户端直接使用的是代理对象,不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介的作用。
作用:
? 在程序运行之前就已经存在了代理类的字节码文件,代理对象与真实对象的关系在运行前就已经确定了
public interface AService{
void save();
}
public class AServiceImpl implements AServic{
public void save()
}
public classs AServiceProxy implements AService{
private AService target
public void save(){
// 开启事务
target.save();
// 提交事务/回滚事务
}
}
优点:
缺点:
java文件-------->编译器(javac)------->字节码文件------->类加载器-------->java虚拟机
如何动态创建一份字节码:
由于JVM通过字节码的二进制信息加载类的,如果我们在运行期间中,通过Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后把这个二进制数据加载转换成对应的类,如此,就完成了在代码中动态创建一个类的能力。
要求真实对象必须要有接口
1 java.lang.reflect.Proxy:java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
主要方法:
public static Object newProxyInstance(ClassLoader loader ,class<?>[]interfaces,InvocationHandler handler)
为指定类加载器、一组接口及处理器生成动态代理类实例
参数:
loader 类加载器,一般为真实对象的类加载器
interfaces 代理类需要实现的接口
handler 代理对象如何做增强
返回:
创建的代理对象
2 java.lang.reflect.InvocationHandler接口:
主要方法:
public Object invoke(Object proxy,Method method,Object[] args)
负责集中处理动态代理类上的所有方法调用
参数
proxy 生成的代理对象
method 当前调用的真实方法对象
args 当前调用方法的实参
JDK动态代理的步骤:
import java.lang.reflect.InvocationHandler
public classs TransactionManagerAdvice implements InvocationHandler{
private Object target;
private TransactionManagerAdvice txmanager;
public void setTarget(){
this.target=target;
}
public Object getProxyObject(){
return Proxy.newProxyInstance(target.getClass.getClassLoader,
target.getClass.getInterface,
new XX());
}
// 定义如何做增强
public Object invoke(Object proxy,Method method,Object[] args){
Object ret=null;
txmanager.begin()
try{
ret=method.invoke(target,args);
txmanager.commit()
}catch(Exception e){
txmanager.rollback()
}
return ret;
}
}
动态代理原理
proxy对象中方法 ----------TransactionManagerAdvice中invoke--------------method.invoke(target,args)------真实对象中的方法
JDK动态代理总结:
JDK动态代理:必须要求真实对象有接口
CGLIB动态代理:可以针对没有接口
import org.springframework.cglib.proxy.InvocationHandler
public classs TransactionManagerAdvice implements InvocationHandler{
private Object target;
private TransactionManagerAdvice txmanager;
public void setTarget(){
this.target=target;
}
public <T>T getProxyObject(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperClass(target.getClass());//将继承与哪个类来增强
enhancer.setCallBack(this); //设置增强的对象
return enhancer.create(); //创建代理对象
}
// 定义如何做增强
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
Object ret=null;
txmanager.begin()
try{
ret=method.invoke(target,args);
txmanager.commit()
}catch(Exception e){
txmanager.rollback()
}
return ret;
}
}
CGLIB动态代理总结:
AOP术语
我们采用@Aspect的注解方式讨论AOP的开发。因为 Spring AOP只能对方法进行拦截,所以首先要确定拦截什么方法,让它能织入到约定的流程中。
首先我们需要确定连接点(在Spring中就是什么类的什么方法)
切面主要包括连接点和增强两个部分
@Aspect 用于标注面
@Before 表示前置增强
@After 表示后置增强
@AfterReturning 表示后置返回增强
@AfterThowing 表示异常增强
@Around 表示环绕增强
execution(*com.demo.service.impl.AServiceImpl.a(..)) 定义了连接点
通常我们使用正则表达式来定义切点
package com.demo.aspect
@Aspect
public class MyAspect{
@Before("execution(*com.demo.service.impl.AServiceImpl.a(..))")
public void before(){
System.out.println("before");
}
@After("execution(*com.demo.service.impl.AServiceImpl.a(..))")
public void afer(){
System.out.println("after");
}
@AfterReturning("execution(*com.demo.service.impl.AServiceImpl.a(..))")
public void afterReturning(){
System.out.println("afterReturning");
}
@AfterThrowing("execution(*com.demo.service.impl.AServiceImpl.a(..))")
public void afterThrowing(){
System.out.println("afterThrowing");
}
}
在使用@Before、 @After 、@AfterReturning 、@AfterThowing 时还会定义一个正则式,用于表示何处启用AOP,但是上面荣誉的使用了这个正则式,可以进行如下额替换;
package com.demo.aspect
@Aspect
public class MyAspect{
@PointCut("execution(*com.demo.service.impl.AServiceImpl.a(..))")
public void pointCut(){
}
@Before("pointCut()")
public void before(){
System.out.println("before");
}
@After("pointCut()")
public void afer(){
System.out.println("after");
}
@AfterReturning("pointCut()")
public void afterReturning(){
System.out.println("afterReturning");
}
@AfterThrowing("pointCut()")
public void afterThrowing(){
System.out.println("afterThrowing");
}
}
@PointCut 用于定义切点,将它定义到方法上方,那么在后面的增强注解当中就可以使用方法名来进行定义了
看下方的正则式:
execution(* com.demo.service.impl.AServiceImpl.a(..))
- execution 表示在执行的时候,拦截里面的正则匹配的方法:
- *表示任意返回类型的方法;
- com.demo.service.impl.AServiceImpl 表示目标对象的全类名
- a 表示目标对象的方法
- (..)表示任意函数进行匹配
对于正则式而言,还可以使用AspectJ的指示器
项目类型 | 描述 |
---|---|
arg() | 限定连接点方法参数 |
@args() | 通过连接点方法参数上的注解进行限定 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配AOP代理Bean引用为指定的类型 |
target() | 目标对象(即被代理对象) |
@target() | 限制目标对象的配置了指定的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限定连接点带有匹配注解类型 |
@annotation() | 限定带有指定注解的连接点 |
环绕增强是所有增强中最为强大的增强,使用它的场景是在你需要大幅度修改原有目标对象的服务逻辑时,否则都尽量使用其他的通知。
环绕增强是一个取代原有目标对象方法的增强,同时它还是提供了回调原有目标对象方法的能力
@Around("pointCut()")
public void around(PrceedingJoinPoint jp) throws Throwable{
System.out.println("around before......");
//回调目标对象的原有方法
jp.proceed();
System.out.println("around after ......");
}
它有一个ProceedingJoinPoint类型的参数 ,这个参数对象有一个proceed方法,可以通过这个方法调用原有目标对象的方法
jp.procced();
Spring AOP 引入的定义就能增强接口的功能,如下用接口Validator增强了AService接口
package com.demo.aspect
public interface Validator{
public boolean Validate(A a);
}
package com.demo.aspect.impl
public class ValidatorImpl implements Validator{
@override
public boolean validate(A a){
System.out.printla("引入新的接口:"+Validator.class.getSimpleName);
}
}
@Aspect
public class MyAspect{
@DeclareParents(
value="com.demo.service.impl.AServiceImpl +";
defaultImpl="UserServiceImpl.class"
)
}
@DeclareParents 它的作用是引入新的类来增强服务,它有两个必须配置的属性:value 和defaultImpl
- value: 指向你要增强功能的目标对象
- defaultImpl:引入增强的类
使用时要强制转化为Validatoer类型才能使用增强的内容
织入是一个生成动态代理对象并且将切面和目标对象方法编织成为约定流程的过程 ,spring boot推荐采用接口加实现类的模式。即通过JDK和CGLIB来实现
对于JDK而言,它是要求被代理的目标对象必须要有接口,而对CGLIB则不做要求。因此在默认的情况下,当你当你需要使用 AOP 的类拥有接口时 , 它会 以 JDK 动态代理运行,否则以 CGLIB 运行 。
原文:https://www.cnblogs.com/yanquhonggui/p/11093431.html