JAVA-Spring AOP基础 - 代理设计模式
利用IOC DI实现软件分层,虽然解决了耦合问题,但是很多地方仍然存在非该层应该实现的功能,造成了无法“高内聚”的现象,同时存在大量重复的代码,开发效率低下。
1 @Service
2 public class UserServiceImpl implements UserService {
3 @Autowired
4 private UserDao userDao;
5
6 @Override
7 public void registUser(User user) {
8 try {
9 System.out.println("校验权限。。。");
10 System.out.println("开启事务。。。");
11 System.out.println("记录日志。。。");
12 userDao.addUser(user);
13 System.out.println("提交事务。。。");
14 } catch (Exception e) {
15 System.out.println("回滚事务");
16 e.printStackTrace();
17 }
18 }
19
20 @Override
21 public void upToVIP(User user) {
22 try {
23 System.out.println("校验权限。。。");
24 System.out.println("开启事务。。。");
25 System.out.println("记录日志。。。");
26 userDao.updateUser(user);
27 System.out.println("提交事务。。。");
28 } catch (Exception e) {
29 System.out.println("回滚事务");
30 e.printStackTrace();
31 }
32
33 }
34
35 @Override
36 public void removeUser(User user) {
37 try {
38 System.out.println("校验权限。。。");
39 System.out.println("开启事务。。。");
40 System.out.println("记录日志。。。");
41 userDao.deleteUser(user.getId());
42 System.out.println("提交事务。。。");
43 } catch (Exception e) {
44 System.out.println("回滚事务");
45 e.printStackTrace();
46 }
47 }
48
49 }
此时,可以通过代理设计模式,将这部分代码提取到代理者中,简化层中的代码。
一、静态代理模式
优点:结构清晰,便于理解
缺点:如果被代理者有多个方法,则代理者也需要开发多个方法,其中往往存在大量重复代码,仍然存在代码重复。
静态代码设计模式解决了软件分层过程中 额外的功能代码侵入模块的问题,将额外的功能代码提取到了代理者中进行,但是静态代理实现的代理者中存在大量重复代码,并没有解决代码重复问题。所以在开发中 --包括spring的底层,基本不会使用静态代理。
1 package cn.tedu.staticproxy;
2 public interface SJSkill {
3 public void 吃();
4 public void 唱歌();
5 }
6
7 package cn.tedu.staticproxy;
8 public class FBB implements SJSkill{
9 public void 吃(){
10 System.out.println("fbb吃饭。。。");
11 }
12 public void 唱歌(){
13 System.out.println("fbb唱歌。。。");
14 }
15 }
16
17 package cn.tedu.staticproxy;
18 public class JJRStaticProxy implements SJSkill{
19
20 private FBB fbb = new FBB();
21
22 @Override
23 public void 吃() {
24 System.out.println("权限认证:你谁啊????");
25 fbb.吃();
26 System.out.println("记录日志:等我,我记一下来访记录");
27 }
28
29 @Override
30 public void 唱歌() {
31 System.out.println("权限认证:你谁啊????");
32 fbb.唱歌();
33 System.out.println("记录日志:等我,我记一下来访记录");
34 }
35
36 }
37
38 package cn.tedu.staticproxy;
39 import org.junit.Test;
40 public class StaticProxyTest {
41 @Test
42 public void test01(){
43 JJRStaticProxy jjr = new JJRStaticProxy();
44 jjr.吃();
45 jjr.唱歌();
46 }
47 }
二、动态代理-jdk内置的动态代理
在jdk提供了动态代理实现的工具类,直接使用该工具类就可以创建出代理者,并且可以通过内置的回调函数指定代理在工作时的执行逻辑,从而实现基于jdk原生api的动态代理机制。
java动态代理的特点:
优点:不需要像静态代理一样被代理方法都要实现一遍,而只需要在回调函数中进行处理就可以了,重复代码只需编写一次。
缺点:java的动态代理是通过代理者实现和被代理者相同的接口来保证两者具有相同的方法的,如果被代理者想要被代理的方法不属于任何接口,则生成的代理者自然无法具有这个方法,也就无法实现对该方法的代理。所以java的动态代理机制是基于接口进行的,受限于要代理的方法是否有接口的支持。
案例:
1 package cn.tedu.javaproxy;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5 import java.lang.reflect.Proxy;
6
7 import org.junit.Test;
8
9 public class JavaProxyTest {
10 @Test
11 public void test01(){
12
13 //被代理者
14 final FBB fbb = new FBB();
15
16 //java动态代理方式 生成fbb的代理者
17 /**
18 * classLoader:用来生成代理者类的类加载器,通常可以传入被代理者类的类加载器
19 * interfaces: 要求生成的代理者实现的接口们,通常就是实现和被代理者相同的接口,保证具有和被代理者相同的方法
20 * invocationHandler: 用来设定回调函数的回调接口,使用者需要写一个类实现此接口,从而实现其中的invoke方法,
21 * 在其中编写代码处理代理者调用方法时的回调过程,通常在这里调用真正对象身上的方法,并且在方法之前或之后做额外操作。
22 */
23 SJSkill proxy = (SJSkill) Proxy.newProxyInstance(FBB.class.getClassLoader(),FBB.class.getInterfaces()
24 ,new InvocationHandler() {
25 @Override
26 /**
27 * proxy: 代理者
28 * method:当前调用的方法对象
29 * args:挡墙调用的方法的参数数组
30 */
31 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
32 if("拍电影".equals(method.getName())){
33 System.out.println("不好意思,给多少钱不拍了~~");
34 return null;
35 }else{
36 System.out.println("检验权限。。。。");
37 Object returnObj = method.invoke(fbb, args);
38 System.out.println("记录日志。。。。");
39 return returnObj;
40 }
41 }
42 });
43 //从此之后,不允许直接调用被代理者身上的方法,而是要通过代理者来调用
44 //fbb.吃();
45 //fbb.唱歌();
46 proxy.吃();
47 proxy.唱歌();
48 proxy.拍电影();
49 }
50 }
java动态代理原理图:
三、动态代理-第三方包cglib实现的动态代理
CGLIB是第三方提供的动态代理的实现工具,不管有没有接口都可以实现动态代理。
CGLIB实现动态代理的原理:生成的动态代理是被代理者的子类,所以代理者具有和父类即被代理者相同的方法,从而实现代理。
CGLIB动态代理的特点:
优点:无论是否有接口都可以实现动态代理,使用场景基本不受限制
缺点:第三方提供的动态代理机制不是原生的,需要导入第三方的开发包才可以使用。
案例:
先导入CGLIB相关包:spring-core-3.2.3.RELEASE.jar
如果导入了spring包就包含了CGLIB
1 package cn.tedu.cglibproxy;
2
3 import java.lang.reflect.Method;
4
5 import org.junit.Test;
6 import org.springframework.cglib.proxy.Enhancer;
7 import org.springframework.cglib.proxy.MethodInterceptor;
8 import org.springframework.cglib.proxy.MethodProxy;
9
10 public class CglibProxyTest {
11 @Test
12 public void test01(){
13 final FBB fbb = new FBB();
14
15 //增强器
16 Enhancer enhancer = new Enhancer();
17
18 //设定接口 -- 此方法要求生成的动态代理额外实现指定接口们 ,单cglib动态代理不是靠接口实现的,所以可以不设置
19 enhancer.setInterfaces(fbb.getClass().getInterfaces());
20
21 //设定父类 -- 此处要传入被代理者的类,cglib是通过集成被代理者的类来持有和被代理者相同的方法的,此方法必须设置
22 enhancer.setSuperclass(fbb.getClass());
23
24 //设定回调函数 -- 为增强器设定回调函数,之后通过增强器生成的代理对象调用任何方法都会走到此回调函数中,实现调用真正被代理对象的方法的效果
25 enhancer.setCallback(new MethodInterceptor() {
26 @Override
27 public Object intercept(Object proxy, Method method, Object[] args,
28 MethodProxy methodProxy) throws Throwable {
29 if("拍电影".equals(method.getName())){
30 System.out.println("对不起,不拍了~~~");
31 return null;
32 }else{
33 System.out.println("检查权限。。。");
34 Object returnObj = method.invoke(fbb, args);
35 System.out.println("记录日志。。。");
36 return returnObj;
37 }
38 }
39 });
40
41 //生成代理对象
42 FBB proxy = (FBB) enhancer.create();
43 proxy.吃();
44 proxy.唱歌();
45 proxy.拍电影();
46 }
47 }
CGLIB动态代理原理图:
将功能代码提取到代理者中,实现 “高内聚” 的效果。
1 package cn.tedu.em.service;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.stereotype.Service;
5
6 import cn.tedu.em.dao.UserDao;
7 import cn.tedu.em.domain.User;
8
9 @Service
10 public class UserServiceImpl implements UserService {
11
12 @Autowired
13 private UserDao userDao;
14
15 public void upToVIP(User user){
16 userDao.updateUser(user);
17 }
18
19 public void removeUser(User user){
20 userDao.deleteUser(5);
21 }
22
23 public void registUser(User user){
24 userDao.addUser(user);
25 }
26 }
27
28
29 package cn.tedu.em.service;
30
31 import java.lang.reflect.Method;
32
33 import org.springframework.beans.factory.annotation.Autowired;
34 import org.springframework.beans.factory.annotation.Qualifier;
35 import org.springframework.cglib.proxy.Enhancer;
36 import org.springframework.cglib.proxy.MethodInterceptor;
37 import org.springframework.cglib.proxy.MethodProxy;
38 import org.springframework.stereotype.Service;
39
40 @Service
41 public class UserServiceImplCglibProxy {
42
43 @Autowired
44 @Qualifier("userServiceImpl")
45 private UserService userService;
46
47 public UserServiceImplCglibProxy() {
48
49 }
50
51 public UserService getCglibProxy() {
52 Enhancer enhancer = new Enhancer();
53 enhancer.setInterfaces(userService.getClass().getInterfaces());
54 enhancer.setSuperclass(userService.getClass());
55 enhancer.setCallback(new MethodInterceptor() {
56 @Override
57 public Object intercept(Object proxy, Method method, Object[] args,
58 MethodProxy mproxy) throws Throwable {
59 try {
60 System.out.println("校验权限。。。");
61 System.out.println("开启事务。。。");
62 System.out.println("记录日志。。。");
63
64 Object returnObj = method.invoke(userService, args);
65
66 System.out.println("提交事务。。。");
67 return returnObj;
68 } catch (Exception e) {
69 System.out.println("回滚事务");
70 e.printStackTrace();
71 throw new RuntimeException(e);
72 }
73 }
74 });
75 return (UserService) enhancer.create();
76 }
77 }
78
79 package cn.tedu.em.web;
80
81 import org.springframework.beans.factory.annotation.Autowired;
82 import org.springframework.stereotype.Controller;
83
84 import cn.tedu.em.domain.User;
85 import cn.tedu.em.service.UserServiceImplCglibProxy;
86
87 @Controller
88 public class RegistServlet {
89
90 @Autowired
91 //private UserService userService;
92 //private UserServiceImplJavaProxy proxy;
93 private UserServiceImplCglibProxy proxy;
94 public void regist(){
95 User user = new User(1,"LK","断桥残雪","4192@qq.com");
96 //proxy.getJavaProxy().upToVIP(user);
97 proxy.getCglibProxy().upToVIP(user);
98 }
99 }