(1)代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能。
(2)代理模式通常是通过持有目标对象的引用来实现的,为了便于描述我们将代理模式中被代理对象称为目标对象,负责进行代理的对象被称为代理对象。
(1)静态代理概述
若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。
(2)静态代理步骤
① 新建业务层接口 ProductService 接口
public interface ProductService<E> {
public void insertProduct() throws Exception;
public void deleteProduct() throws Exception;
}
② 新建业务层 ProductServiceImp 实现类实现业务层接口,在业务实现类中完成业务逻辑代码
public class ProductServiceImp<E> implements ProductService<E>{
@Override
public void insertProduct() throws Exception {
System.out.println("执行业务层新增操作代码");
}
@Override
public void deleteProduct() throws Exception {
System.out.println("执行业务层删除操作代码");
}
}
③ 新建业务层代理类 ServiceProxy 实现业务层接口,在代理类中定义一个业务层接口的成员变量
public class ServiceProxy<E> implements ProductService<E>{
private ProductService<Products> productService;
public ServiceProxy(ProductService<Products> productService){
this.productService = productService;
}
@Override
public void insertProduct() throws Exception {
try {
System.out.println("执行获取连接,打开事务的代码");
productService.insertProduct();
System.out.println("执行关闭事务的代码");
} catch(Exception e) {
System.out.println("执行事务回滚的代码");
throw e;
} finally{
System.out.println("关闭连接");
}
}
@Override
public void deleteProduct() throws Exception {
try {
System.out.println("执行获取连接,打开事务的代码");
productService.deleteProduct();
System.out.println("执行关闭事务的代码");
} catch(Exception e) {
System.out.println("执行事务回滚的代码");
throw e;
} finally{
System.out.println("关闭连接");
}
}
}
④ 测试类
public class Test {
public static void main(String[] args) {
ProductService<Products> productService = new ProductServiceImp<Products>();
ServiceProxy<Products> proxy = new ServiceProxy<Products>(productService);
try {
proxy.insertProduct();
proxy.deleteProduct();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(3)静态代理缺点
静态代理适用性不强,代理对象和被代理对象之前是一种强耦合,代理对象必须知道被代理对象具体的变量或方法,从而进行调用,一旦被代理对象多起来,那就需要创建多个代理,工作量太大,同样不好维护和管理。
(1)JDK 动态代理概述
java.lang.reflect.InvocationHandler
接口,通过 InvocationHandler
接口实现的动态代理只能代理接口的实现类。(2)重要API
(3)JDK 动态代理步骤:
① 新建业务层接口
public interface ProductsService {
public void insert();
}
② 新建业务层实现类实现业务层接口,在业务实现类中完成业务逻辑代码
public class ProductsServiceImp implements ProductsService{
@Override
public void insert(){
System.out.println("正在执行新增操作");
}
}
③ 新建调用处理器封装代理类中的方法体
public class TransactionHander implements InvocationHandler{
private Object target;
public TransactionHander(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("获取连接打开事务");
result = method.invoke(target, args);
System.out.println("提交事务");
}catch (Exception e) {
System.out.println("事务回滚");
}
return result;
}
}
④ 使用Proxy类创建代理类对象
public class Test {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//创建目标对象
ProductsService productsService = new ProductsServiceImp();
//创建调用处理器对象
TransactionHander handler = new TransactionHander(productsService);
//创建代理对象
ProductsService serviceProxy = (ProductsService) Proxy.newProxyInstance(productsService.getClass().getClassLoader(), productsService.getClass().getInterfaces(),handler);
serviceProxy.insert();
}
}
Proxy.newProxyInstance()
方法用于创建代理对象,其中第一个参数表示动态代理对象的class文件生成完毕之后交给哪一个类加载器来加载。通常使用目标对象类对应的加载器。第二个参数表示的是在生成class文件时这个代理类需要实现的接口,由于最后生成的代理对象必须是可以强转为目标对象的,所以代理对象实现的接口必须和目标对象保持一致,第三个参数是调用处理器对象,调用处理器对象封装了代理对象执行的方法体。
⑤ 创建代理对象工厂类(可选)
JDK动态代理已经足够优秀,可以使代理方式更加灵活更易于程序的拓展,但是美中不足的是,JDK动态代理只能代理实现了接口的类,如果要创建没有实现接口的类,需要使用第三方提供的CGLIB代理。
(1)CGLIB 动态代理概述
CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,通俗说CGLIB可以在运行时动态生成字节码。
(2)重要步骤
① 新建业务层接口
public interface CGLIBService {
public void insert();
}
② 新建业务层实现类实现业务层接口,在业务实现类中完成业务逻辑代码
public class CGLIBServiceImp implements CGLIBService{
public void insert() {
System.out.println("执行业务层新增操作");
}
}
③ 新建方法拦截器 CGLIBInterceptor 类,通过该类的interceptor方法对目标方法进行增强处理
public class CGLIBInterceptor implements MethodInterceptor{
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object object) {
this.target = object;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;;
try {
System.out.println("获取连接,开启事务");
result = method.invoke(target, args);
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("事务回滚");
}
return result;
}
}
④ 在方法拦截器 CGLIBInterceptor 类中自定义 getProxy() 方法用于生成代理对象
public Object getProxy(Object object) {
//将目标对象保存到成员变量中
this.target = object;
//增强器,动态代码生成器,创建代理对象
Enhancer enhancer = new Enhancer();
//设置回调对象
enhancer.setCallback(this);
//设置类的父类类型,其实就是目标对象的类型,生成的代理对象汇集成目标对象
//设置目标对象的类信息,代理类汇集成目标类
enhancer.setSuperclass(object.getClass());
//动态生成字节码,并返回代理对象
return enhancer.create();
}
⑤ 新建测试类,调用方法拦截器类的 getProxy 方法生成代理对象
public class CGLIBTest {
public static void main(String[] args) {
CGLIBService service = new CGLIBServiceImp();
CGLIBInterceptor interceptor = new CGLIBInterceptor();
CGLIBService proxy = (CGLIBService) interceptor.getProxy(service);
proxy.insert();
}
}
java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。而 cglib 动态代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换