笔者最近想学学Spring源码,各类设计模式在Spring的源码中运用得淋漓尽致,笔者也不得不感叹原来广大的开发者一直站在牛人的肩膀上进行编程。谈到Spring,面试问得最多的就是Spring的两大核心,IOC以及AOP。IOC本质上来讲就是对bean反射以及依赖注入,管理bean生命周期的容器。而AOP本质上就是动态代理。今天笔者就来说讲动态代理。接下来我将从下面几个方面阐述动态代理:java
静态代理很简单,我们本身在写代码的时候都会写到这种相似静态代理的代码。简单来讲,就是把被代理类做为参数传给代理类的构造方法,让代理类替被代理类实现更强大的功能。程序员
1package com.bingo.designPatterns.proxy;
2
3/**
4 * Description:静态代理
5 * User: bingo
6 */
7
8public class StaticProxyTest {
9
10 public static void main(String[] args) {
11
12 UserService userService = new UserService();
13
14 LogProxy logProxy = new LogProxy(userService);
15 logProxy.addUser();
16 logProxy.deleteUser();
17 }
18}
19
20interface IUserService{
21 void addUser();
22 void deleteUser();
23}
24
25
26class UserService implements IUserService{
27
28 @Override
29 public void addUser() {
30 System.out.println("添加用户");
31 }
32
33 @Override
34 public void deleteUser() {
35 System.out.println("删除用户");
36 }
37}
38
39//日志代理
40class LogProxy implements IUserService{
41
42 //目标类
43 private UserService target;
44
45 public LogProxy(UserService target){
46 this.target = target;
47 }
48
49 @Override
50 public void addUser() {
51 System.out.println("记录日志开始");
52 target.addUser();
53 System.out.println("记录日志结束");
54 }
55
56 @Override
57 public void deleteUser() {
58 System.out.println("记录日志开始");
59 target.deleteUser();
60 System.out.println("记录日志结束");
61 }
62}
复制代码
虽然静态代理实现比较简单,可是在实际项目中咱们须要为每一个类都写一个代理类,须要写不少重复冗余的代码,不利于代码的解耦与扩展。可是动态代理便很好的解决了上述问题,真真正正地实现了业务逻辑代码与加强功能代码的解耦。面试
JDK动态代理的代理类通常须要实现接口spring
1package com.bingo.designPatterns.proxy;
2
3import java.lang.reflect.InvocationHandler;
4import java.lang.reflect.Method;
5import java.lang.reflect.Proxy;
6
7/**
8 * Description: jdk动态代理
9 * User: bingo
10 */
11public class JdkProxyTest {
12
13 public static void main(String[] args) {
14 IPersonService personService = JdkDynamicProxy.getProxy();
15 personService.addPerson();
16 personService.deletePerson();
17 }
18}
19
20interface IPersonService{
21 void addPerson();
22 void deletePerson();
23}
24
25class PersonService implements IPersonService{
26 @Override
27 public void addPerson() {
28 System.out.println("添加人物");
29 }
30
31 @Override
32 public void deletePerson() {
33 System.out.println("删除人物");
34 }
35}
36
37
38/**
39 * newProxyInstance方法参数说明:
40 * ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
41 * Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
42 * InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
43 */
44class JdkDynamicProxy{
45
46 public static IPersonService getProxy(){
47
48 IPersonService personService = new PersonService();
49
50 IPersonService proxy = (IPersonService) Proxy.newProxyInstance(IPersonService.class.getClassLoader(), new Class<?>[]{IPersonService.class}, new InvocationHandler() {
51
52 @Override
53 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
54 System.out.println("记录日志开始");
55 Object obj = method.invoke(personService, args);
56 System.out.println("记录日志结束");
57 return obj;
58 }
59 });
60
61 return proxy;
62 }
63}
复制代码
须要导入cglib.jar,asm.jar包才能使用此代理编程
1package com.bingo.designPatterns.proxy;
2
3import net.sf.cglib.proxy.Enhancer;
4import net.sf.cglib.proxy.MethodInterceptor;
5import net.sf.cglib.proxy.MethodProxy;
6
7import java.lang.reflect.Method;
8
9/**
10 * Description: cglib动态代理
11 * User: bingo
12 */
13
14public class CglibProxyTest {
15 public static void main(String[] args) {
16
17 CglibProxy proxy = new CglibProxy();
18 Train t = (Train)proxy.getProxy(Train.class);
19 t.move();
20 }
21}
22
23class Train {
24
25 public void move(){
26 System.out.println("火车行驶中...");
27 }
28}
29
30class CglibProxy implements MethodInterceptor {
31
32 private Enhancer enhancer = new Enhancer();
33
34 public Object getProxy(Class clazz){
35 //设置建立子类的类
36 enhancer.setSuperclass(clazz);
37 enhancer.setCallback(this);
38
39 return enhancer.create();
40 }
41
42 /**
43 * 拦截全部目标类方法的调用
44 * obj 目标类的实例
45 * m 目标方法的反射对象
46 * args 方法的参数
47 * proxy代理类的实例
48 */
49 @Override
50 public Object intercept(Object obj, Method m, Object[] args,
51 MethodProxy proxy) throws Throwable {
52 System.out.println("日志开始...");
53 //代理类调用父类的方法
54 proxy.invokeSuper(obj, args);
55 System.out.println("日志结束...");
56 return null;
57 }
58}
复制代码
笔者以前在开发小程序服务接口时遇到的问题:
1.在Spring配置文件中配置了事务管理器,以下:小程序
1<!--事务管理器配置,单数据源事务-->
2<tx:annotation-driven transaction-manager="transactionManager" />
3<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
4 <property name="dataSource" ref="dataSource" />
5</bean>
复制代码
2.配置了事务管理器后,加入了@Transactional注解设计模式
1@Service
2@Transactional
3public class AccountService{
4 //to do something
5}
复制代码
乍一看,这个配置与使用都没什么毛病,对吧,可是项目一放到Tomcat服务上就报错,具体报什么错,笔者已经记不得了,可是笔者很记得出错的缘由就是transaction注解的问题,因而没办法,翻看原来公司的项目代码,对比本身的代码,发现各类配置也不差,不知道问题出在哪,可是原来的项目启动正常,个人项目却报错。细看发现,公司原来的项目中Service都定义了一个接口做为规范,@Transactional注解都是加在接口实现类上的。因而乎,我半信半疑的为每一个Service的类定义了一个接口规范,实现类加上@Transactional注解,以下:缓存
1@Service
2@Transactional
3public class AccountService implements IAccountService {
4 //to do something
5}
复制代码
配置没变,只是简单地为Service层定义了接口,并实现接口,项目就运行正常了。微信
1<tx:annotation-driven transaction-manager="txManager"/>
复制代码
上面配置默认启用JDK动态代理,JDK只能代理接口不能代理类。而个人项目中用的是这个配置,却由于没有定义Service接口致使项目启动报错。app
若是须要使用CGLIB动态代理:配置以下:
1<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
复制代码
不知道有没有读者遇到过跟我一样的坑,若是不了解动态代理,就有可能出现和我相同的问题。可是写程序规范很重要,MVC三层结构中,除了Controller层,Service、Dao层都须要定义接口,这是企业开发的规范。那段时间仍是个刚毕业不久的实习生,不免有时候会写出这种不规范的代码,可是但愿之后可以愈来愈规范,多看看《阿里巴巴Java开发手册》,多看看源码,看看大牛们写的代码,相信不久以后本身也能够写出如此优雅的代码,让编程变成一种艺术。
我是广州的java程序员小彬,刚毕业半年,一直致力于java的学习,对个人文章感兴趣能够关注个人微信公众号(J2彬彬),感谢支持!