代理模式

代理模式

1、代理模式

1.1、应用场景

当程序涉及修改数据库中数据操作时,此时的一般操作流程是:

  • 1、开启事务
  • 2、修改操作
  • 3、提交事务或者回滚事务

随着时间的推移,代码中开启事务、提交事务及回滚事务的代码将变得冗余。记录日志在程序中也是推荐使用,那么记录日志也将变得冗余,同时设计具体业务操作的时候,往往还得考虑事务、日志等方面的操作,不能专注于业务操作。

为了解决代码冗余以及业务对象专注于业务操作,产生了代理模式。

1.2、组成部分

代理模式有以下5部分组成:

  • 1、target(目标对象/业务对象)
  • 2、proxy(代理对象)
  • 2、function(功能对象/事务对象、日志对象)
  • 3、interceptor(拦截器对象)
  • 4、client(客户端)

2、静态代理模式

通过代理类去完成目标类的目标方面,并且在该目标的类特定的代理类中附加上额外的功能

静态代理模式代码实例:

目标类接口

1
2
3
4
5
6
7
8
9
10
11
/**
* @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();
}

目标类实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @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 !~");
}
}

事务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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 !~");
}
}

拦截器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* @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;
}
}

客户端对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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();
}
}

程序执行结果:

1
2
3
4
5
6
7
begin transaction !~
delete Person !~
transaction commit !~

begin transaction !~
update Person !~
transaction commit !~

优点:。
1、可以在不改变目标类的基础上,增加额外的功能,耦合度低;
2、让目标类专注于业务开发,无需关注业务之外的功能;
缺点:
1、对于每一个目标类都需要编写一个handler,代码冗余,增加代码维护成本;

3、动态代理

为了解决静态代理模式的代码冗余的缺点,以及继续保持低耦合的优点,也就有了动态代理模式。其中包括cglib动态代理模式和jdk动态代理模式。

3.1 jdk动态代理模式

jdk动态代理模式代码实例:

PersonDao接口

1
2
3
4
5
6
7
8
9
10
11
/**
* @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接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @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 !~");
}
}

功能类(事务类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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 !~");
}
}

拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* @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方法
}

客户端测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* @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();
}
}

代码执行结果:

1
2
3
4
5
6
7
begin transaction !~
delete Person !~
transaction commit !~

begin transaction !~
update Person !~
transaction commit !~

如果想再加入其他功能,比如日志,则需要在MyHandler类中,新增成员变量Logger,修改对应的构造方法,在invoke方法中增加对应操作。同时,如果觉得不需要事务,也同样可以操作MyHandler类来去除事务,这样的操作与目标类没有任何影响,完全松耦合。

3.2 cglib动态代理模式

cglib动态代理模式与jdk动态代理模式类似,cglib动态代理模式代码如下:
PersonDao接口

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @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接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @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 !~");
}
}

事务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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代理类拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* @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方法
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @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();
}
}

程序运行结果:

1
2
3
4
5
6
7
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的获得目标类以及目标类的所有接口

1
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myIntercepter);

cglib 将目标类设置为代理对象的超类

1
2
3
4
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方法,这样可以执行附加多个功能,并且不频繁改动拦截器类。