核心目标:
以最小的代价给目标方法加上前置和后置处理功能。
比如某账本类的查询方法,想在查询前增加鉴权,和查询后记录查询日志。
基本原理:
在目标类前面加一个代理类,将直接调用目标方法改为调用代理类的同名方法,再由代理类的同名方法调用目标方法,前/后置逻辑写在代理类的同名方法中。
//TO-DO 添加示意图
核心理论和技术:
1、代理模式
2、反射
3、JDK动态代理
4、Cglib动态代理
5、spring AOP面向切面编程
6、回调模式
技术实现举例:逻辑由易至难,开发由难至易
以账本为例,具有查询和更新两个方法,要在这两个方法前后加上前置和后置处理
IAccount接口:
package com.common;
public interface IAccount {
public void queryAccount();
public void updateAccount();
}
AccountImpl1实现:
package com.common;
public class AccountImpl1 implements IAccount {
public void queryAccount() {
System.out.println("查询账户.......");
}
public void updateAccount() {
System.out.println("更新账户......");
}
}
AccountImpl2实现2:
package com.common;
public class AccountImpl2 implements IAccount {
public void queryAccount() {
System.out.println("当前账户名为:匿名账户");
}
public void updateAccount() {
System.out.println("更新账户名为:张三");
}
}
菜鸟:天下无招
设计思路:
修改账本实现类
新增两个方法:前置方法、后置方法
在执行查询和更新前后,调用前置和后置方法
package com.general;
import com.common.IAccount;
public class AccountImpl implements IAccount {
public void queryAccount() {
beforeOperate();
System.out.println("查询账户.......");
afterOperate();
}
public void updateAccount() {
beforeOperate();
System.out.println("更新账户......");
afterOperate();
}
public void beforeOperate(){
System.out.println("前置处理.....");
}
public void afterOperate(){
System.out.println("后置处理.....");
}
}
优点:
逻辑简单,不费脑
缺点:
核心业务代码与辅助性代码高耦合
前置和后置属于通用代码,调用代码重复开发
前后置内容容易随需求发生改变,代码修改会造成核心业务风险
大虾:静态代理,看到武功秘籍,功力大增
设计思路:
1、使用代理设计模式
2、新增代理,即新增一个跟被代理账本1实现了相同账本接口的代理类,具备相同的方法
3、绑定代理关系,即将被代理的账本实现类作为自身的一个属性
4、代理类新增前置和后置处理方法
5、强化需要被代理的方法,即真正的业务处理仍由被代理的对方受理,但在将请求转发给被代理对象之前和之后,分别加上前置和后置处理逻辑
6、修改业务调用逻辑,即将直接调用被代理对象的方法,改为调用代理类的同名方法
package com.staticProxy.staticProxy1;
import com.common.AccountImpl1;
import com.common.IAccount;
public class AccountProxy1 implements IAccount{
private AccountImpl1 accountImpl;
public AccountProxy1(AccountImpl1 accountImpl){
this.accountImpl=accountImpl;
}
public void queryAccount() {
beforeOperate();
accountImpl.queryAccount();
afterOperate();
System.out.println();
}
public void updateAccount() {
beforeOperate();
accountImpl.updateAccount();
afterOperate();
System.out.println();
}
public void beforeOperate(){
System.out.println("前置处理.....");
}
public void afterOperate(){
System.out.println("后置处理.....");
}
public AccountImpl1 getAccountImpl() {
return accountImpl;
}
public void setAccountImpl(AccountImpl1 accountImpl) {
this.accountImpl = accountImpl;
}
}
测试:
package com.staticProxy.staticProxy1;
import com.common.AccountImpl1;
import com.common.IAccount;
public class test {
public static void main(String[] args) {
AccountImpl1 accountImpl=new AccountImpl1();
IAccount account=new AccountProxy1(accountImpl);
account.queryAccount();
account.updateAccount();
}
}
若再来一个账本实现类,则有需要写一个代理类,而且除了代理的实现类不一样外,其余全部一样
package com.staticProxy.staticProxy2;
import com.common.AccountImpl2;
import com.common.IAccount;
public class AccountProxy2 implements IAccount{
private AccountImpl2 accountImpl;
public AccountProxy2(AccountImpl2 accountImpl){
this.accountImpl=accountImpl;
}
public void queryAccount() {
beforeOperate();
accountImpl.queryAccount();
afterOperate();
System.out.println();
}
public void updateAccount() {
beforeOperate();
accountImpl.updateAccount();
afterOperate();
System.out.println();
}
public void beforeOperate(){
System.out.println("前置处理.....");
}
public void afterOperate(){
System.out.println("后置处理.....");
}
public AccountImpl2 getAccountImpl() {
return accountImpl;
}
public void setAccountImpl(AccountImpl2 accountImpl) {
this.accountImpl = accountImpl;
}
}
测试:
package com.staticProxy.staticProxy2;
import com.common.AccountImpl2;
import com.common.IAccount;
public class test {
public static void main(String[] args) {
AccountImpl2 accountImpl=new AccountImpl2();
IAccount account=new AccountProxy2(accountImpl);
account.queryAccount();
account.updateAccount();
}
}
优点:
1、前后置辅助代码跟业务代码完全解耦
2、修改前后置方法不影响原核心业务
缺点:
1、每代理一个类,就得新增一个代理类,开发量大
2、需要修改被代理方法的调用代码,若被调用的地方很多,改造量大
牛人:动态代理,悟性高,会借力
设计思路:
1、代理模式必备要素:要实现代理模式,抽象出来无外乎五样东西
1.1 有一个代理类,用于写前置和后置处理方法
1.2 有一个被代理类,不关心具体是什么,以object抽象
1.3 需要绑定代理关系,即将被代理类跟代理类绑定
1.4 需要知道被强化的方法,不关心具体方法名,以method抽象,不关心传入参数,以args[]抽象,不关心返回参数,以object抽象,可以通过反射来调用
1.5 每个被代理对象需要一个专用的前置代理实例,不关心代理实例是否有真身(java类),只要存在于内存中,具备被代理对象的所有方法即可
1.6 代理实例的方法不需实现任何业务逻辑,只需要接收调用请求,拿到被调用的方法名、参数即可,然后将方法、参数和代理实例本身,传给代理类的回调函数,使用反射原理统一加强处理
2、动态代理简要流程
2.1 生成代码已有两套现成的,直接拿来用
2.1.1 jdk动态代理,只能给实现了接口的类动态生成代理(java自带,所以首选该方案)
2.1.2 Cglib动态代理,能给没有实现接口的类动态生成代理,实现接口的也支持(第三方代码,所以次选)
全称Code generation library 代码生成类库
2.2 生成流程
2.2.1 代理类和被代理类绑定(属性)
2.2.2 使用jdk或者Cglib,根据代理类、被代理类、回调方法,动态生成一个代理实例,该实例只存在于内存中,负责具体的代理工作
2.2.3 动态生成的代理实例,具有被代理对象的所有方法,该方法被调用时,会回调预设的回调方法(jdk是invoke(),Cglib是intercept())
2.2.3 代理类需要有一个回调方法,该方法实现被代理方法的强化,即加上前置后后置方法
借助JDK动态代理(被代理的类必须实现接口)
package com.jdkDynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class AccountJdkDynamicProxy implements InvocationHandler{
private Object proxyedObject;
public Object getProxy(Object proxyedObject){
this.proxyedObject=proxyedObject;
return Proxy.newProxyInstance(proxyedObject.getClass().getClassLoader(), proxyedObject.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
beforeOperate();
Object result=method.invoke(proxyedObject, args);
afterOperate();
System.out.println();
return result;
}
public void beforeOperate(){
System.out.println("前置处理.....");
}
public void afterOperate(){
System.out.println("后置处理.....");
}
public Object getProxyedObject() {
return proxyedObject;
}
public void setProxyedObject(Object proxyedObject) {
this.proxyedObject = proxyedObject;
}
}
测试:写一次代理类,动态代理多个实现类
package com.jdkDynamicProxy;
import com.common.AccountImpl1;
import com.common.AccountImpl2;
import com.common.IAccount;
public class test1 {
public static void main(String[] args) {
AccountJdkDynamicProxy proxy=new AccountJdkDynamicProxy();
/*代理账本1*/
IAccount account=new AccountImpl1();
IAccount accountProxy=(IAccount)proxy.getProxy(account);
accountProxy.queryAccount();
accountProxy.updateAccount();
/*代理账本2*/
account=new AccountImpl2();
accountProxy=(IAccount)proxy.getProxy(account);
accountProxy.queryAccount();
accountProxy.updateAccount();
}
}
借Cglib动态代理(不要求被代理的类必须实现接口)
package com.cglibDynamicProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class AccountCglibDynamicProxy implements MethodInterceptor {
private Object proxyedObject;
public Object getProxy(Object proxedObject){
this.proxyedObject=proxedObject;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(proxyedObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy proxy) throws Throwable {
beforeOperate();
Object result=proxy.invokeSuper(arg0, arg2);
afterOperate();
System.out.println();
return result;
}
public void beforeOperate(){
System.out.println("前置处理.....");
}
public void afterOperate(){
System.out.println("后置处理.....");
}
public Object getProxyedObject() {
return proxyedObject;
}
public void setProxyedObject(Object proxyedObject) {
this.proxyedObject = proxyedObject;
}
}
测试:
package com.cglibDynamicProxy;
import com.common.AccountImpl2;
import com.common.AccountImpl4;
public class test {
public static void main(String[] args) {
/*使用代理_非接口类*/
AccountCglibDynamicProxy proxy=new AccountCglibDynamicProxy();
AccountImpl4 account=new AccountImpl4();
AccountImpl4 accountProxy=(AccountImpl4)proxy.getProxy(account);
accountProxy.queryAccount();
accountProxy.updateAccount();
/*使用代理_接口类*/
AccountImpl2 account2=new AccountImpl2();
AccountImpl2 accountProxy2=(AccountImpl2)proxy.getProxy(account2);
accountProxy2.queryAccount();
accountProxy2.updateAccount();
}
}
优点:
代理类只需写一份,具体被代理的代理实例动态生成,开发量小
缺点:
1、接口类和非接口类,需要不同的实现
2、依赖于JDK和Cglib的代理规范,属于重开发
3、代理类编写复杂,技术要求高,未实现POJO编程
4、被代理对象的所有方法均被代理,可自定义程度低
大牛:拳无拳意无意,无意之中是真意:Spring的AOP面向切面编程
设计思路:
1、spring的POJO编程思想,即Plain Ordinary Java Object简单的Java对象
2、业务类,就写业务方法
3、代理类,就写前置和后置方法
4、依赖关系由xml配置文件维护
5、spring封装jdk和cglib,对用户不可见
代理类:
package com.springDynamicProxy;
public class AspectBean {
public void beforeOperate(){
System.out.println("前置处理.....");
}
public void afterOperate(){
System.out.println("后置处理.....");
}
}
代理关系注册:
测试:
package com.springDynamicProxy;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.common.AccountImpl4;
import com.common.IAccount;
public class test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/springDynamicProxy/test.xml");
/*接口类:使用JDK代理*/
IAccount accountImpl1=(IAccount) ctx.getBean("accountImpl1");
System.out.println(accountImpl1.getClass().getSimpleName());
System.out.println("super class is "+accountImpl1.getClass().getSuperclass());
accountImpl1.queryAccount();
/*非接口类:使用Cglib代理*/
AccountImpl4 accountImpl4=(AccountImpl4)ctx.getBean("accountImpl4");
System.out.println(accountImpl4.getClass().getSimpleName());
System.out.println("super class is "+accountImpl4.getClass().getSuperclass());
accountImpl4.queryAccount();
}
}
优点:
1、不直接依赖于JDK和Cglib的规范,真正实现POJO编程
2、代理关系统一配置,维护简单,使用灵活
3、代理类编写简单,开发量小