1、代理模式
1.1、应用场景
当程序涉及修改数据库中数据操作时,此时的一般操作流程是:
- 1、开启事务
- 2、修改操作
- 3、提交事务或者回滚事务
随着时间的推移,代码中开启事务、提交事务及回滚事务的代码将变得冗余。记录日志在程序中也是推荐使用,那么记录日志也将变得冗余,同时设计具体业务操作的时候,往往还得考虑事务、日志等方面的操作,不能专注于业务操作。
为了解决代码冗余以及业务对象专注于业务操作,产生了代理模式。
1.2、组成部分
代理模式有以下5部分组成:
- 1、target(目标对象/业务对象)
- 2、proxy(代理对象)
- 2、function(功能对象/事务对象、日志对象)
- 3、interceptor(拦截器对象)
- 4、client(客户端)
2、静态代理模式
通过代理类去完成目标类的目标方面,并且在该目标的类特定的代理类中附加上额外的功能
静态代理模式代码实例:
目标类接口
/**
* @ClassName: PersonDao
* @Description: PersonDao接口
* @author Yue Chang
* @date 2017年3月23日 上午10:42:46
* @since 1.0
*/
public interface PersonDao {
public void deletePerson();
public void updatePerson();
}
目标类实现类
/**
* @ClassName: PersonDaoImpl
* @Description: PersonDao接口实现类
* @author Yue Chang
* @date 2017年3月23日 上午10:42:05
* @since 1.0
*/
public class PersonDaoImpl implements PersonDao {
@Override
public void deletePerson() {
System.out.println("delete Person !~");
}
@Override
public void updatePerson() {
System.out.println("update Person !~");
}
}
事务类
/**
* @ClassName: Transaction
* @Description: 事务类
* @author Yue Chang
* @date 2017年3月23日 上午10:45:19
* @since 1.0
*/
public class Transaction {
public void beginTransaction(){
System.out.println("begin transaction !~");
}
public void commit(){
System.out.println("transaction commit !~");
}
public void rollback(){
System.out.println("transaction rollback !~");
}
}
拦截器类
/**
* @ClassName: MyHandler
* @Description: 拦截器类
* @author Yue Chang
* @date 2017年3月23日 上午11:54:30
* @since 1.0
*/
public class MyHandler implements PersonDao {
private Transaction transaction;
private PersonDao personDaoTarget;
/**
* @param transaction
* @param personDao
*/
public MyHandler(Transaction transaction, PersonDao personDao) {
super();
this.transaction = transaction;
this.personDaoTarget = personDao;
}
@Override
public void deletePerson() {
try {
// 开启事务
transaction.beginTransaction();
// 目标类对象调用目标方法
personDaoTarget.deletePerson();
// 提交事务
transaction.commit();
} catch (Exception e) {
// 回滚事务
transaction.rollback();
}
}
@Override
public void updatePerson() {
try {
// 开启事务
transaction.beginTransaction();
// 目标类对象调用目标方法
personDaoTarget.updatePerson();
// 提交事务
transaction.commit();
} catch (Exception e) {
// 回滚事务
transaction.rollback();
}
}
public Transaction getTransaction() {
return transaction;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public PersonDao getPersonDaoTarget() {
return personDaoTarget;
}
public void setPersonDaoTarget(PersonDao personDaoTarget) {
this.personDaoTarget = personDaoTarget;
}
}
客户端对象
/**
* @ClassName: StaticProxyTest
* @Description: 静态代理模式测试类
* @author Yue Chang
* @date 2017年3月23日 下午12:01:49
* @since 1.0
*/
public class StaticProxyTest {
@Test
public void testStaticProxy(){
PersonDao personDao = new PersonDaoImpl();
Transaction transaction = new Transaction();
MyHandler myHandler = new MyHandler(transaction, personDao);
myHandler.deletePerson();
System.out.println();
myHandler.updatePerson();
}
}
程序执行结果:
begin transaction !~
delete Person !~
transaction commit !~
begin transaction !~
update Person !~
transaction commit !~
优点:。
1、可以在不改变目标类的基础上,增加额外的功能,耦合度低;
2、让目标类专注于业务开发,无需关注业务之外的功能;
缺点:
1、对于每一个目标类都需要编写一个handler,代码冗余,增加代码维护成本;
3、动态代理
为了解决静态代理模式的代码冗余的缺点,以及继续保持低耦合的优点,也就有了动态代理模式。其中包括cglib动态代理模式和jdk动态代理模式。
3.1 jdk动态代理模式
jdk动态代理模式代码实例:
PersonDao接口
/**
* @ClassName: PersonDao
* @Description: PersonDao接口
* @author Yue Chang
* @date 2017年3月23日 上午10:42:46
* @since 1.0
*/
public interface PersonDao {
public void deletePerson();
public void updatePerson();
}
PersonDao接口实现类
/**
* @ClassName: PersonDaoImpl
* @Description: PersonDao接口实现类
* @author Yue Chang
* @date 2017年3月23日 上午10:42:05
* @since 1.0
*/
public class PersonDaoImpl implements PersonDao {
@Override
public void deletePerson() {
System.out.println("delete Person !~");
}
@Override
public void updatePerson() {
System.out.println("update Person !~");
}
}
功能类(事务类)
/**
* @ClassName: Transaction
* @Description: 事务类
* @author Yue Chang
* @date 2017年3月23日 上午10:45:19
* @since 1.0
*/
public class Transaction {
public void beginTransaction(){
System.out.println("begin transaction !~");
}
public void commit(){
System.out.println("transaction commit !~");
}
public void rollback(){
System.out.println("transaction rollback !~");
}
}
拦截器
/**
* @ClassName: MyHandler
* @Description: 拦截器
* @author Yue Chang
* @date 2017年3月23日 上午10:49:05
* @since 1.0
*/
public class MyHandler implements InvocationHandler {
private Object target;
private Transaction transaction;
/**
* @param target 目标对象
* @param tansaction 事务对象
*/
public MyHandler(Object target, Transaction transaction) {
super();
this.target = target;
this.transaction = transaction;
}
// invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 开启事务
transaction.beginTransaction();
// 调用目标类的目标方法,
method.invoke(target, args);
// 提交事务
transaction.commit();
} catch (Exception e) {
// 回滚事务
transaction.rollback();
}
return null;
}
// getter/setter方法
}
客户端测试类
/**
* @ClassName: JdkProxyTest
* @Description: jdk动态代理客户端
* @author Yue Chang
* @date 2017年3月23日 上午10:56:36
* @since 1.0
*/
public class JdkProxyTest {
@Test
public void testJdkProxy(){
// 目标对象
PersonDao target = new PersonDaoImpl();
// 事务对象
Transaction transaction = new Transaction();
// 拦截器对象
MyHandler myIntercepter = new MyHandler(target, transaction);
// PersonDao代理对象
PersonDao proxy = (PersonDao) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), myIntercepter);
proxy.deletePerson();
System.out.println();
proxy.updatePerson();
}
}
代码执行结果:
begin transaction !~
delete Person !~
transaction commit !~
begin transaction !~
update Person !~
transaction commit !~
如果想再加入其他功能,比如日志,则需要在MyHandler类中,新增成员变量Logger,修改对应的构造方法,在invoke方法中增加对应操作。同时,如果觉得不需要事务,也同样可以操作MyHandler类来去除事务,这样的操作与目标类没有任何影响,完全松耦合。
3.2 cglib动态代理模式
cglib动态代理模式与jdk动态代理模式类似,cglib动态代理模式代码如下:
PersonDao接口
/**
* @ClassName: PersonDao
* @Description: PersonDao接口
* @author Yue Chang
* @date 2017年3月23日 上午10:42:46
* @since 1.0
*/
public interface PersonDao {
public void deletePerson();
public void updatePerson();
}
PersonDao接口实现类
/**
* @ClassName: PersonDaoImpl
* @Description: PersonDao接口实现类
* @author Yue Chang
* @date 2017年3月23日 上午10:42:05
* @since 1.0
*/
public class PersonDaoImpl implements PersonDao {
@Override
public void deletePerson() {
System.out.println("delete Person !~");
}
@Override
public void updatePerson() {
System.out.println("update Person !~");
}
}
事务类
/**
* @ClassName: Transaction
* @Description: 事务类
* @author Yue Chang
* @date 2017年3月23日 上午10:45:19
* @since 1.0
*/
public class Transaction {
public void beginTransaction(){
System.out.println("begin transaction !~");
}
public void commit(){
System.out.println("transaction commit !~");
}
public void rollback(){
System.out.println("transaction rollback !~");
}
}
cglib代理类拦截器
/**
* @ClassName: MyInterceptor
* @Description: cglib代理类拦截器
* @author Yue Chang
* @date 2017年3月23日 下午12:19:35
* @since 1.0
*/
public class MyInterceptor implements MethodInterceptor {
private Object target;
private Transaction transaction;
/**
* @param target
* @param transaction
*/
public MyInterceptor(Object target, Transaction transaction) {
super();
this.target = target;
this.transaction = transaction;
}
/**
* @category 创建代理对象,可以在客户端完成
* @since cglib proxy
* @author Yue Chang
* @date 2017年3月31日 上午11:41:50
* @return
*/
public Object createProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setCallback(this);
enhancer.setSuperclass(target.getClass());
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
try {
transaction.beginTransaction();
method.invoke(target, args);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
return null;
}
// getter/setter方法
}
客户端
/**
* @ClassName: CglibProxyTest
* @Description: cglib代理模式测试类
* @author Yue Chang
* @date 2017年3月25日 下午3:48:01
* @since 1.0
*/
public class CglibProxyTest {
@Test
public void testCglibProxy(){
PersonDao target = new PersonDaoImpl();
Transaction transaction = new Transaction();
MyInterceptor myInterceptor = new MyInterceptor(target, transaction);
PersonDao proxy = (PersonDao) myInterceptor.createProxy();
proxy.deletePerson();
System.out.println();
proxy.updatePerson();
}
}
程序运行结果:
begin transaction !~
delete Person !~
transaction commit !~
begin transaction !~
update Person !~
transaction commit !~
3.3 jdk动态代理模式和cglib动态代理模式的区别
均在invoke方法中,对于复杂的逻辑处理比较困难。其中,具体方法由对应method参数决定。
jdk动态代理模式中,是通过Proxy.newProxyInstance来创建代理对象;
cglib动态代理模式中,是通过Enhancer对象来创建代理对象;
jdk动态代理模式中,代理类与目标类实现了相同的接口;
cglib动态代理模式中,代理类是目标类的子类;
这一点区别其实也可以从创建代理对象中发现,jdk的获得目标类以及目标类的所有接口
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myIntercepter);
cglib 将目标类设置为代理对象的超类
Enhancer enhancer = new Enhancer();
enhancer.setCallback(this);
enhancer.setSuperclass(target.getClass());
return enhancer.create();
4、拓展
可以申明一个Interceptor类,里面定义interceptor方法,将所有需要附加的功能类都实现Interceptor接口,将需要的操作在interceptor方法中实现,然后在动态代理模式中定义List interceptorList成员变量,初始化时传入interceptorList,在invoke方法中迭代此List,并执行Interceptor对象的interceptor方法,这样可以执行附加多个功能,并且不频繁改动拦截器类。