动态代理模式
在介绍这个模式之前我们,先看看背景需求:
查看工资的需求:进行安全性检查,开启日志记录,(权限判断)如果有权限则查看工资,否则提示无权限。
通常的实现方式
安全性框架检查类:
public class Security {
public void security(){
System.out.println("checking security....");
}
}
日志记录
public class Logger {
public void log() {
System.out.println("starting log....");
}
}
权限判断
public class Privilege {
private String access;
public String getAccess() {
return access;
}
public void setAccess(String access) {
this.access = access;
}
}
目标类
public class SalaryManager {
private Logger logger;
private Privilege privilege;
private Security security;
public SalaryManager(Security security, Logger log, Privilege privilege) {
super();
this.security = security;
this.logger = log;
this.privilege = privilege;
}
public void showSalary() {
this.security.security();
this.logger.log();
if ("admin".equals(privilege.getAccess())) {
System.out.println("这个月工资涨了10%...");
} else {
System.out.println("对不起,你无权限查看工资!");
}
}
}
测试类
public class ShowSalaryTest {
[@Test](https://my.oschina.net/azibug)
public void test() {
Security security = new Security();
Logger log = new Logger();
Privilege privilege = new Privilege();
privilege.setAccess("admin");
SalaryManager sm = new SalaryManager(security,log,privilege);
sm.showSalary();
}
}
小结:
目标类和一些公共事务耦合在一起了,而且目标类也是被固定写死了,无法做到动态执行某个目标类。其实这类公共的事务:安全性验证,日志记录和权限验证是可以被其他业务(客户)使用的,应该独立出来,达到复用的效果。
手写动态代理
首先将工资管理,即目标类用接口去封装,如下:
public interface SalaryManage {
// 目标动作
public void showSalary();
}
SalaryManage的实现类
public class SalaryManageImpl implements SalaryManage {
[@Override](https://my.oschina.net/u/1162528)
public void showSalary() {
System.out.println("涨工资了。。。。");
}
}
再建立目标类的代理类
/**
* 目标类的代理类
*
*/
public class SalaryManageProxy {
private Logger log;
private Privilege privilege;
private Security security;
private SalaryManage salaryManager;
public SalaryManageProxy() {
super();
}
public SalaryManageProxy(Logger log, Privilege privilege, Security security, SalaryManage salaryManager) {
super();
this.log = log;
this.privilege = privilege;
this.security = security;
this.salaryManager = salaryManager;
}
/**
* 优点:添加了代理类,将目标类和公共事务分离
* 缺点:代理类的代理方法中目标类和目标方法被固定死了,无法动态变化,不可重用。
*/
//代理方法
public void showSalary() {
this.log.log();
this.security.security();
if ("admin".equals(privilege.getAccess())) {
salaryManager.showSalary();// 目标类的目标方法
} else {
System.out.println("对不起,你没有权限访问!");
}
}
}
测试类:
public class ShowSalaryTest {
/**
* 通过引入代理类,将目标类和公共事务分离
*/
[@Test](https://my.oschina.net/azibug)
public void test() {
Security security = new Security();
Logger log = new Logger();
Privilege privilege = new Privilege();
privilege.setAccess("admin");
SalaryManage sm = new SalaryManageImpl();
/**
* 代理类调用代理方法,执行目标类目标方法。达到了预期效果
*/
new SalaryManageProxy(log, privilege, security, sm).showSalary();;
}
}
小结:
优点:添加了代理类,将目标类和公共事务分离
缺点:代理类的代理方法中目标类和目标方法被固定死了,无法动态变化,不可重用。
JDK动态代理
目标类的接口
public interface SalaryManage {
// 目标动作
public void showSalary();
}
目标类的实现
public class SalaryManageImpl implements SalaryManage {
[@Override](https://my.oschina.net/u/1162528)
public void showSalary() {
System.out.println("涨工资了。。。。");
}
}
拦截器(即代理类)
/**
* 拦截器,基于实现jdk InvocationHandler 的拦截器
*
*/
public class SalaryManageJDKProxy implements InvocationHandler{
private Logger log;
private Privilege privilege;
private Security security;
private Object target;
public SalaryManageJDKProxy() {
super();
}
public SalaryManageJDKProxy(Logger log, Privilege privilege, Security security) {
super();
this.log = log;
this.privilege = privilege;
this.security = security;
}
[@Override](https://my.oschina.net/u/1162528)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.security.security();
this.log.log();
if ("admin".equals(this.privilege.getAccess())) {
method.invoke(this.target, args);
} else {
System.out.println("对不起,你没有权限访问!");
}
return null;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
测试类
public class JDKProxySalaryTest {
@Test
public void test() {
Security security = new Security();
Logger log = new Logger();
Privilege privilege = new Privilege();
privilege.setAccess("admin");
SalaryManage sm = new SalaryManageImpl();
//拦截器
SalaryManageJDKProxy salaryManageJDKProxy = new SalaryManageJDKProxy(log, privilege, security);
salaryManageJDKProxy.setTarget(sm);
/**
* 生成代理对象
* ClassLoader loader, 目标类的类加载器
* Class<?>[] interfaces,目标类的接口数组
* InvocationHandler h,代理类实例
*/
SalaryManage newProxyInstance = (SalaryManage) Proxy.newProxyInstance(SalaryManage.class.getClassLoader(),
new Class[] { SalaryManage.class }, salaryManageJDKProxy);//代理对象,被创建的代理对象实现过了目标类的接口
newProxyInstance.showSalary();
}
}
小结:
概念:目标类,代理类,拦截器
目标接口,由目标类实现目标接口
目标类和代理类实现了共同的接口
cglib代理模式
需求说明:模拟hibernate编程
开启事务
进行增删改查(目标类的目标方法)
结束事务
事务类
public class Transaction {
public void beginTransaction() {
System.out.println("begin transaction");
}
public void commit() {
System.out.println("commit");
}
}
Dao类
public class PersonDao {
public void updatePerson() {
System.out.println("update person");
}
public void addPerson() {
System.out.println("add person");
}
public void deletePerson() {
System.out.println("delete person");
}
public void listPersons() {
System.out.println("list person");
}
}
拦截类
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 PersonDaoInterceptor implements MethodInterceptor {
private Transaction transaction;
private Object target;
public PersonDaoInterceptor(Transaction transaction, Object target) {
super();
this.transaction = transaction;
this.target = target;
}
/**
* 产生代理对象
* @return
*/
public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());//设置目标类为代理类的父类
enhancer.setCallback(this);//设置拦截器为回调函数
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
Object obj;
String methodName = method.getName();
if ("updatePerson".equals(methodName) || "addPerson".equals(methodName) || "deletePerson".equals(methodName)) {
//开启事务
this.transaction.beginTransaction();
//调用目标类的目标方法
obj = method.invoke(this.target, args);
//做是否提交事务
this.transaction.commit();
} else {
//调用目标类的目标方法
obj = method.invoke(this.target, args);
}
return obj;
}
public Transaction getTransaction() {
return transaction;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
测试类:
import org.junit.Test;
public class PersonDaoTest {
@Test
public void test() {
Transaction transaction = new Transaction();
PersonDao personDao = new PersonDao();
PersonDaoInterceptor inteceptor = new PersonDaoInterceptor(transaction,personDao);
//代理类是目标类的子类。
PersonDao proxy = (PersonDao)inteceptor.createProxy();
proxy.addPerson();
}
}
总结
概念:目标类,代理类,拦截器
jdk:
目标类和代理类实现了共同的接口
拦截器必须实现jdk提供的InvocationHandler,而这个接口中的invoke方法体内容=代理对象方法体内容
当客户端用代理对象调用方法时,invoke方法执行
cglib:
目标类是代理类的父类
拦截器实现了MethodInterceptor,而接口中的intercept方法=代理对象方法体
使用字节码增强机制创建代理对象